GopherCon 2015: Derek Parker - Delve Into Go
Summary
TLDRDerrick Parker,一位软件工程师,在视频中介绍了他所参与的项目Delve,这是一个为Go语言设计的调试器。Delve通过解析Go二进制文件中的DWARF调试信息,与操作系统和CPU协作,帮助用户探索和诊断程序问题。Parker通过分享自己在调试Go程序时遇到的挑战,阐述了为何需要Delve这样的工具,并演示了Delve的基本用法。他还讨论了Delve的发展历程、当前状态以及未来的发展方向,包括对Windows的支持和更深入的编辑器集成。
Takeaways
- 😀 Derrick Parker 是一名软件工程师,他在 Hashrocket 工作,并且开发了一个名为 Delve 的项目。
- 🔍 Delve 是一个针对 Go 编程语言的调试器,它通过解析 Go 二进制文件中的 DWARF 调试信息来工作。
- 🤔 Derrick 选择开发 Delve 是因为他认为现有的调试工具(如 gdb)对于 Go 语言来说不够好,Go 语言有其独特的特性,需要专门的调试工具。
- 🛠️ Delve 的设计目标是简单易用,与 Go 语言的工具链一致,使得调试过程尽可能少地增加开发者的负担。
- 📖 Derrick 通过一个故事引入了 Delve 的开发背景,这个故事发生在芝加哥的一个冷冬夜晚,他在一个 meetup 上演示 gdb 时遭遇了尴尬的崩溃。
- 🔄 Go 语言的执行模型与 C 或 C++ 等传统语言不同,例如 Go 中的 defer 语句可以在函数返回后执行代码,这可能会使现有调试工具混淆。
- 🧵 Go 语言的 goroutine 和线程模型也给调试带来了挑战,因为 goroutine 是在线程之上进行合作调度的,这可能会导致调试时的上下文切换问题。
- 🔧 Delve 支持多种调试命令,包括设置断点、单步执行、查看变量值等,以帮助开发者更有效地调试程序。
- 📈 Delve 目前仍在积极开发中,已经获得了社区的贡献,支持 Linux 和 OS X,并且有初步的编辑器集成支持。
- 🚀 Derrick 对 Delve 的未来展望包括正式发布 1.0 版本、支持 Windows、增强编辑器集成以及增加更多高级调试命令。
Q & A
Delve 是什么?
-Delve 是一个为 Go 编程语言设计的调试器,它通过解析 Go 二进制文件中的 DWARF 调试信息,与操作系统和 CPU 协同工作,以便于用户操纵程序并探索问题所在。
为什么需要一个新的调试器 Delve,而不是使用现有的工具如 gdb?
-现有的调试器如 gdb 是为 C 或 C++ 等语言设计的,Go 语言有许多独特的特性,使得现有工具难以有效调试。Delve 是专门为 Go 设计的,能够更好地理解 Go 的执行模型和特性。
Delve 的设计理念是什么?
-Delve 的设计理念是简单易用,与 Go 的工具链保持一致,使得即使在程序出现问题时,用户也能轻松地使用调试器来解决问题。
Delve 如何处理 Go 中的 defer 语句?
-Delve 能够理解并跟踪 defer 语句的执行流程,即使 defer 语句中可能改变函数的返回值,Delve 也能够正确地处理。
Go 语言的执行模型与 C 或 C++ 有何不同,为何这会影响调试器?
-Go 语言有独特的执行模型,包括 goroutine 和调度器的概念,这与 C 或 C++ 的线程模型不同。这种差异会导致现有调试器在调试 Go 程序时出现混淆和问题。
Delve 是如何支持 Windows 系统的?
-脚本中提到 Delve 目前还没有 Windows 系统的支持,但作者希望社区能够贡献代码来实现这一点。
Delve 的开发状态是怎样的?
-根据脚本,Delve 目前处于活跃开发状态,虽然功能已经相当完善,但作者还在犹豫是否将其标记为 1.0 版本。
Delve 有哪些主要的控制流命令?
-Delve 提供了设置断点、继续执行、单步执行、切换线程、重启进程等控制流命令,以及打印变量值和 CPU 寄存器状态等信息命令。
Delve 如何处理 Go 程序中的编译优化问题?
-默认情况下,Go 编译器会进行一些优化,如函数内联和寄存器变量,这可能会给调试带来困难。Delve 允许用户通过传递标志给 Go 编译器来禁用这些优化。
Delve 的未来发展方向是什么?
-Delve 的未来发展方向包括正式发布 1.0 版本,增加对 Windows 的支持,完善编辑器集成,以及增加更多高级命令,如改进打印功能和允许调用函数等。
Outlines
😀 欢迎与Delve项目介绍
Derrick Parker,一名软件工程师,对组织者表示感谢,并介绍了他正在工作的项目Delve。Delve是一个为Go语言设计的调试器,它通过解析Go二进制文件中的DWARF调试信息与操作系统和CPU协同工作,帮助用户探索和发现程序中的问题。尽管存在像gdb这样的工具,Delve的创建是出于对现有工具无法满足Go语言特性的考虑,以及对更易用工具的需求。Derrick通过一个关于gdb在演示中崩溃的尴尬经历,引出了对Delve的需求和动机。
🤔 Go语言特性与现有调试工具的局限性
Derrick讨论了Go语言的特性,如defer语句和goroutines,以及它们如何使现有的调试工具如gdb和LDB变得不适用。他解释了Go的执行模型、栈管理和编译优化等问题,这些问题在传统调试器中可能导致混淆和程序崩溃。Delve旨在克服这些问题,提供对Go语言特性的深入理解和支持。
🛠️ Delve的设计理念与使用方法
Delve的设计理念是简单易用,与Go工具链的简洁性保持一致。Derrick介绍了如何开始使用Delve进行调试,包括编译、启动程序、附加到进程并进入调试提示符。他还提到了Delve支持的命令,如设置断点、跟踪点、单步执行、线程切换等,以及如何通过runtime breakpoint函数在代码中设置断点。
📚 Delve的发展历程和贡献者
Delve从最初的个人项目发展成为一个拥有22位贡献者的开源项目。Derrick回顾了Delve的历史,从最初的提交到在GopherCon上的讨论,再到现在的活跃开发状态。他感谢所有贡献者,并强调了开源社区的重要性。
🔍 Delve的当前状态与未来展望
Delve目前处于1.0版本之前的活跃开发阶段,已经实现了多种调试命令和编辑器集成。Derrick介绍了Delve的当前功能,包括控制流命令和信息命令,并展望了未来的发展方向,如Windows支持、更深入的编辑器集成和更高级的调试命令。
🎬 实际演示Delve的使用
Derrick通过一个实际的演示,展示了如何使用Delve设置断点、单步执行、检查变量和查看线程信息。他强调了Delve在处理Go语言特性时的优势,如正确处理defer语句和goroutines。演示旨在直观地展示Delve的易用性和强大功能。
Mindmap
Keywords
💡Delve
💡调试器(Debugger)
💡Go 编程语言
💡DWARF 调试信息
💡GDB
💡执行模型(Execution Model)
💡Go 协程(Goroutines)
💡堆栈管理(Stack Management)
💡编译器优化(Compiler Optimizations)
💡编辑器集成(Editor Integration)
Highlights
Delve是一个为Go语言设计的调试器,通过解析DWARF调试信息与操作系统和CPU协作,帮助用户探索和理解程序错误。
Delve与gdb类似,但专为Go设计,考虑到Go语言的特殊性,如defer语句和goroutine的调度。
Delve的创建源于作者在芝加哥的一次技术分享中gdb的崩溃经历,激发了开发一个更稳定、适合Go的调试器的想法。
Go语言的执行模型与C或C++不同,例如defer语句可能会改变函数的返回值,这可能使现有调试工具无法正确追踪程序流程。
Go的goroutine和线程模型给调试带来挑战,因为goroutine可以在任何调度点被挂起并由其他线程继续执行。
Delve支持设置断点、跟踪点、单步执行、跳过函数、切换线程等控制流程命令,提供丰富的调试功能。
Delve能够正确处理Go的defer语句,确保调试时不会错过任何可能引入bug的代码执行。
Delve支持编译优化的代码调试,尽管优化可能会给调试带来额外的复杂性。
Delve提供了简单的命令行接口,使得开始调试Go程序变得非常容易,只需一个命令即可启动。
Delve支持对现有二进制文件的调试以及对运行中进程的附加调试。
Delve的开发始于一个个人项目,经过几个月的独立开发后,逐渐吸引了社区的贡献者参与。
Delve目前处于活跃开发阶段,虽然还未达到1.0版本,但已经具备了相当程度的稳定性和功能完备性。
Delve的代码基础是Go和C的混合,其中C用于处理一些底层系统调用。
Delve支持Linux和OS X操作系统,但作者表示对Windows的支持需要社区的贡献。
Delve提供了对编辑器集成的初步支持,通过JSON RPC协议允许在IDE中进行调试。
Delve的未来发展计划包括正式发布1.0版本、增加对Windows的支持、完善编辑器集成以及增加更多高级调试命令。
作者通过现场演示展示了Delve的基本使用,包括设置断点、单步执行、变量检查等调试操作。
Delve的作者Derek Parker鼓励社区成员参与项目,共同推动Delve的发展和完善。
Transcripts
like Kelsey said my name is Derrick
Parker I'm a software engineer at hash
rocket and today I'm gonna be talking
about a project that I've been working
on called delve so right off the bat I'd
like to say a huge thanks to the
organizers not only for bringing this
all together which is phenomenal but for
giving me the opportunity to talk about
my project in front of everybody I
really appreciate this this opportunity
so the project I've been working on like
I said it's called delve so just just so
I can get a temperature how many people
have heard of delft before today oh that
is awesome very cool so for those of you
who haven't heard of delve I'll explain
a little bit about it delve is a
debugger for the go programming language
it works by parsing various information
out of a go binary collectively known as
the dwarf debugging information it
coordinates with the operating system
and the CPU to facilitate manipulating
your program so that you can explore it
and try to figure out what's going wrong
it does not do any kind of sorcery
writing it's more in line with a tool
like gdb speaking of gdb why a new
debugger why reinvent the wheel
especially such a large wheel right this
project I've been working on it for
quite some time and it's it's a it's a
relatively big undertaking so why go
through that hassle why why do that
aside from the fact that it's an
incredibly fun project project to work
on I'm having a blast working on it
there's also some technical reasons and
mumbo-jumbo that I'll get into later but
like Peter I'd like to begin with a
little story story time so it all began
a couple years back on a cold wintry
evening and
cago just slightly redundant it's pretty
much always cold in Chicago and I was
giving a talk at a local NGO meetup
aptly named Chicago Lange and I had
given several talks there before so I
was feeling pretty comfortable and this
time I happened to be giving a talk
about gdb now as I said there's a couple
years ago so I was young and naive I
didn't know what I was doing I knew gdb
had some faults but a lot of the folks
who go to this Meetup come from like a
ruby background where they're really
into pry and they have these this
powerful tool called pry that they can
use to debug and inspect their program a
lot of them haven't even heard of gdb so
I figured I'd go ahead and introduce it
so things are going well I'm going
through the slides presenting all of
this knowledge to these folks and it's
going really great then it comes time
for a live demo so this is an artist
rendering of me giving like them so it's
going good so I'm open I switch to my
terminal I got my shell going I'm about
to do some like hardcore computering in
front of all these folks you know I'm
about to fire up gdb start inspecting a
program maybe dump some registers just
for the wow factor for some of these
people and it's going pretty good
it starts off really well I'm able to do
some basic things but once I start to
get a little bit braver and try to get a
little bit creative
gdb decides to crash and it crashes
pretty hard so there was looking like a
fool in front of literally tens of
people right so it's just a nightmare
just absolutely not the position that
you want to be in and as I was up there
you know red-faced and embarrassed
the only thing I could think of was
gdb must be destroyed
wrong way again gdb must be destroyed
it's worth saying twice but with fire so
what we really need is an alternative
tool the existing tools out there don't
work they they were created a very very
long time ago
where you know the programming language
of choice would be C or C++ which still
kind of is today but we've grown a lot
as far as languages and the things that
we want to do and the types of languages
that we want to debug and existing tools
they're not one size fit all there's
problems we really need something that's
go central something that really
understands go and is built specifically
for Go Go has a lot of interesting
nuances which a lot of those are some of
the same features that have brought us
all to be go developers but as I'll
explain later they make it really tough
for existing tools to debug and lastly
and I think this is one of the most
important things at least to me we need
a tool that that should be easy to use
so the entire quote go tool train is
tool chain is extremely extremely simple
to use go build you've built a binary
with optimizations already everything is
good to go go test you can start running
your tests you don't have to do anything
special to get up and running with the
go tool chain and in my opinion the
debugging story shouldn't be any
different because let's face it if
you're using a debugger odds are things
already are not going your way right
like your programs not working something
is going wrong you're already frustrated
so why introduce more steps into finding
the solution why introduce more
frustration so so what happened why why
did gdb crash put simply it got confused
existing debuggers and existing tools go
is different enough that it can confuse
these these pieces of software
and I'll explain that a little bit more
so why what what parts about go confuse
existing debuggers and and existing
tools I'll touch on a couple things
first off is goes execution model it's
it's very different from from from
existing programming languages something
like C or C++ we'll also talk a little
bit about how go to stack management and
how that can affect certain debuggers
and lastly we'll talk about as I
mentioned earlier go build will just do
everything for you but sometimes that's
not what you want especially if you're
trying to figure out what's going wrong
so let's begin talking a little bit
about the execution model one of the
first things that that I'd like to bring
up is the defer statement now a lot of
times the first statement is going to be
used for something relatively
inconsequential right cleaning out file
descriptors closing up like connection
to a socket or something like that
but sometimes you do something in a
defer statement that might change the
value the return value of your function
and the code that gets executed in that
defer statement can have an impact and
can introduce bugs in your code if
you're using an existing tool something
like gdb or LDB or something those tools
don't know about defer statements
they're not gonna follow the flow of
execution to the defer statement so I'm
sure everybody is familiar with with the
Furze but just a quick overview it defer
is a function that is guaranteed to run
after your function returns so when an
existing tool starts to think it's
getting to the end of a function it's
going to figure out the return address
set a breakpoint there and continue
happily there
that doesn't work although always
especially with go because there's other
things that happen in the mean time and
if you're usually using an existing tool
this type thing this type thing is never
going to be exposed you're never going
to be you'll never enter in the defer
statement and you could overlook a bug
completely secondly I'd like to talk a
little bit about threads versus go
routines now not that they're on the
same same plane and as each other but if
we think about existing tools if you
think about something like gdb it's it's
planning to deal with a relatively
standard process so your standard
process is going to be depending on
operating operating system nuances a
process composed of potentially many
threads and in it in the case of an
existing tool that's going to be the
smallest unit of execution is a thread
and go that's simply not true go has
threads obviously it's a regular process
with threads but it also has an added
layer of of these these go routines
which are cooperatively scheduled on top
of the threads that are being scheduled
by the kernel so let's talk a little bit
about the ghost scheduler real quick for
some of the folks that that may not be
explicitly familiar may be familiar that
it exists but take a little bit of time
to introduce some of the cast of
characters that make up the go scheduler
so the first element that we have is the
pea known in the scheduler so that's the
processor so when you're setting go max
procs the environment variable we're
doing an urban run time this is actually
what you're modifying a processor is
needed to actually execute go code on
top of that we have what's known as an M
which is which represents an actual
operating system thread and M needs to
have have obtained a P to be executing
go code so you can have more threads and
you have go max procs go max procs as I
said actually only affects how many
threads can actually be running Dell
code in a given time and then we have
what we're all familiar with right one
of the things that probably brought a
lot of us to go in the first place
is the go routine so the processor is
the execution context it also holds the
runnable go routine q how does all that
affect debuggers it kind of goes back to
go having a non-traditional execution
model so it brings into in delight
things like context switches so what do
I mean by context switch there are
various various points in your program
where they're called scheduling points
where a go routine could be paused put
back on the run queue and potentially
picked up by another thread during
normal execution of your program that's
not a problem goes go scheduler is
really great at multiplexing these go
routines onto different threads and it
aids in a lot of the great parallelism
and concurrency but free a debugger it
can be kind of painful
it's the code that you're trying to
debug all of a sudden switches over to
another thread that can be very
confusing if you're not expecting it it
can also cause gdb to hang when a
context switch that it doesn't
that's not expecting happens and the
code switches GDB not knowing about the
scheduler at all could cause the program
to just hang when one of these context
switches is about to occur so what
points in your program could one of
these context switches happened so you
can apply one manually by calling the
runtime ghost good it can happen during
a blocking assist call a wait or a sleep
or something like that channel
operations could could trigger a go
routine to be paused and put back on the
run queue
garbage collection potentially cause it
as well and a ghost statement I believe
it's possible for I believe it's the the
go routine that invokes the go statement
to be paused and put back on the run
queue so that the other one can start
immediately so let's move on to the next
point stack management
so any peer go function is gonna do a
little bit of runtime stack inspection
and this is very very important for a
couple reasons but it can have effects
that can prevent certain things from
being able to happen in in a traditional
traditional debugger so any peer go
function is going to have a function
prologue stack check and that's
important because one of the great
things about going go routines is
they're very very cheap so they're
initialize with a small stack so when
you jump into a function checks to see
if it needs to grow the stack and if it
does it'll allocate a new stack copy
everything over and switch to it but
this can confuse debuggers if you want
to do something like call a go call one
of your call a function in your program
from within gdb what gdb will try to do
is set up a dummy stack frame and jump
into that function when gdb tries to
tries to do this it'll actually crash
the go program that you're trying to
debug because as soon as it jumps into
that function it's gonna start looking
up the stack and to find something it
doesn't like and it's just gonna panic
and blow up so that can be very
frustrating especially for a lot of
folks who come from dynamic languages
and are able to just just willy-nilly
invoke functions call methods and do
anything that they want to inspect their
program
lastly touch a little bit about on
compiler optimizations so by default the
go compiler is going to provide a couple
optimizations it's gonna do function
inlining and it's gonna register as
variables function inlining is
particularly a problem with debugging
because what the debugger is gonna try
to do is take a look at where you are
where the where the program counter says
you are and relate that via the symbol
table to where it came from in the
source code so that you can actually see
where you are in your program with
function inlining this can be a problem
also registering the variables depending
on how the door for information is
written out if it's written out
expecting everything to be on the stack
you can attempt to read a variable and
just get garbage back because the value
of the variable is actually in a
register it's not being stored on the
stack
so these optimizations these default
optimizations are great for production
but they're not great for trying to
debug a program so the solution is to
pass some flags to the go compiler but
again the whole goal toolchain is very
simple and easy to use why go through an
extra step so let's introduce delve a
little bit this is all you have to do to
get up and running to start debugging
your program with delve extremely simple
that single command is going to compile
your code with optimizations it's
disabled for you it's going to start
your program it's going to attach to the
process and it's going to land you at a
prompt ready to start debugging your
program if you have a standalone package
that doesn't have a main you can use
delve test hopefully your pending test
for your library that you're hoping
people use so this will do something
similar compile and test binary start
the program attach to the process and
Linda you had to prompt ready to start
eradicating bugs additionally you can
debug existing binaries and you can
attach to a running process these these
are come with the same caveat as I
introduced earlier where you could be
dealing with an optimized binary and you
can run into some of the same exact
issues even though you're using dev so
to continue the introduction let's take
a little trip down memory lane and go
over the history of delve so last year's
gopher Khan
there was a talk that got cancelled or
something and what ended up happening is
a panel Q&A panelist on the go core
developers which was really great one of
the questions I was brought up was
what's the go debugging story and I
believe was Rob Pike mentioned something
about it's something that you know they
admit as a problem it's it's it's
something that definitely needs to be
addressed but you know obviously those
folks are very very busy writing this
programming language that we're all
assembled around so doesn't leave a lot
of time for some other projects so I
kind of that planted a bug in my ear a
little bit and about a month later
I put the first committee in on Dell a
couple months later I announced it so it
was it was a solo effort for about the
first five four or five months and when
I announced today was still very very
alpha only supported Linux and it was
mostly to help drive contributions as
kind of kept under wraps a little bit it
was just a fun project to work on then
by the time I started becoming something
useful I figured might as well get some
other folks in on it and that brings us
to today the project has grown a lot and
thankfully I have the opportunity to
take the time to speak about it and and
hopefully get some more people
interested in it so the state of the
project as it is right now is pretty 1.0
so what does that mean the project is
still very much an active development
you could use it and it could do
something weird and frustrate you so
I've been hesitant to let to slap a 1.0
label on it so far we have 22
contributors to the project and I'd like
to take this time to say thank you to
anybody who's contributed to this
project open-source is amazing and I
appreciate anybody who takes the time to
help me on a project I'm very excited
and passionate about the codebase is a
mixture of go and see I would like to
write it in as much go as I possibly can
but to interface with some of the
lower-level syscalls on OS X some of the
mock stuff I was unfortunately forced
down into see it supports Linux in OS X
it has initial support for editor
integration through JSON RPC so you can
run it as just like a debug server and
it can be communicated to via your
favorite editor of them or whatever IDE
you you happen to enjoy using and a lot
more commands have been implemented when
I first announced it it's very
rudimentary break and continue in some
other just basic commands
so kind of want to go through quickly of
how can you use this let's talk about
some of the commands that you can use
particularly let's talk about some of
the control flow commands so you can set
a breakpoint right you can set a trace
point the difference there being if
delve hits a breakpoint it's gonna stop
the world stop your program landing back
at a prompt so that you can inspect a
trace points just gonna say hey I got
here and optionally you can provide
flags to say give me a stack trace of
ndepth
whenever you get here and also give me
the value of these variables whenever
you hit this trace point so prevents you
from having to do things like print line
debugging you have all the power there
so you can continue your program you can
single-step it so that's actually going
to be one CPU instruction it actually
does a real hardware single step one
instruction at a time will step into
functions next we'll step over to the
next source line stepping over functions
you can switch to another thread if
you're curious about what's happening
there and you can kill the current
process and restart it so that you can
start debugging your program again
without having to leave delve it also
provides some informational commands so
you can use the thread command to print
out the status of all threads of the
process go routines will print out the
status of all of the go routines that
are executing in the process breakpoints
list all the breakpoints that you've set
you can evaluate a variable and there's
also some of the gdb folks might be
familiar with the syntax an info command
with sub commands so it takes an
optional regex so let's go over some of
the commands args print the name and
value of all arguments to the current
function funks all of the defined
functions in the index are in the binary
locals all package locals are all all
locals in scope sources all of the
actual source code that went into
compiling this binary VARs
all package variables in that
application
and reg so you can print out the value
of the CPU registers
additionally the go standard library has
this function run time break point and
go will handle it by realizing that it's
within this function step out of it and
put you on the next line where you want
to be so it's a simple way of instead of
invoking the debugger and having to set
a breakpoint manually you can set this
function in your source code wherever
you want to stop fire up the debugger
hit continue and it'll put you where you
want to be so let's talk a little bit
about where delve is going so obviously
we'd like to get to 1.0 release I want
this to be a tool that everybody can use
just as part of their daily developer
toolkit something that they can reach
for a reliable debugger to help solve
their problems support for Windows I
believe in the go model of hopefully
somebody will contribute this because
I'm not gonna install Windows on any of
them at my machines it's an open source
project and I'm not paying for a Windows
license full support for editor
integration so I'd like to see more
integration with VAM or whatever your
favorite IDE is or emails and more
advanced more advanced commands so I'd
like to improve print and allow setting
of variables I'd like you to be able to
call a function and things like that so
I have a little bit of time so let me
get into a little bit of how to use it
now thankfully I think I'm lucky because
Kelsey's here he does live demos all the
time so hopefully some of that good live
demo viable rub off on me so like I said
we can run delft tests and compile a
test binary and jump into it continue it
and like I said if you have the run time
breakpoint function it's going to
recognize it it's going to it's going to
put you in the next line exactly where
you want to be to start inspecting your
program
so let's run let's run the main process
real quick so let's set a breakpoint at
main continue and kind of see what's
going on a little bit so we have this
function convert to the number ten if it
equals ten it works if not there's a bug
so let's do there's a bug right
Oh terrible so let's jump back in and
let's let's explore a little bit so I
just kind of want to go over a little
bit of some of the commands so let's set
a breakpoint at convert to the number
time there's a red line support which is
pretty nice so here we are we can
inspect things like take a look at what
threads are running it'll give you the
source file the line number the function
that it's on and also the address you
can also take a look at go routines so
that'll print out the go routine ID
where it's at and how many go routines
are currently running in the function we
can start next thing through the program
so we can kind of see what it's doing
and like I said it's gonna properly
follow through to defer statements and
keep going through flow of execution
there and then we can just keep going
right we can print out the value of a
variable and it'll return us to return
it to us obviously it's not ten so so
there's a bug yeah so that's just kind
of like a brief introduction I think I'm
running a little bit low on time of some
of the commands that you that you can do
if you if you want to see a little bit
more of a demo or if you want to use it
a little bit more check it out feel free
to find me around and I'll be happy to
show it off or help somebody hack on it
or something so I'm about all the time I
just want to say thank you again my name
is Derek Parker you can follow me on
twitter at Dirk the daring for some
updates on delve and probably a lot of
pictures of my dogs and that's it thank
you
Browse More Related Video
5.0 / 5 (0 votes)