Movatterモバイル変換


[0]ホーム

URL:


  1. Documentation
  2. Debugging Go Code with GDB

Debugging Go Code with GDB

The following instructions apply to the standard toolchain(thegc Go compiler and tools).Gccgo has native gdb support.

Note thatDelve is a betteralternative to GDB when debugging Go programs built with the standardtoolchain. It understands the Go runtime, data structures, andexpressions better than GDB. Delve currently supports Linux, OSX,and Windows onamd64.For the most up-to-date list of supported platforms, please see the Delve documentation.

GDB does not understand Go programs well.The stack management, threading, and runtime contain aspects that differenough from the execution model GDB expects that they can confusethe debugger and cause incorrect results even when the program iscompiled with gccgo.As a consequence, although GDB can be useful in some situations (e.g.,debugging Cgo code, or debugging the runtime itself), it is nota reliable debugger for Go programs, particularly heavily concurrentones. Moreover, it is not a priority for the Go project to addressthese issues, which are difficult.

In short, the instructions below should be taken only as a guide to howto use GDB when it works, not as a guarantee of success.Besides this overview you might want to consult theGDB manual.

Introduction

When you compile and link your Go programs with thegc toolchainon Linux, macOS, FreeBSD or NetBSD, the resulting binaries contain DWARFv4debugging information that recent versions (≥7.5) of the GDB debugger canuse to inspect a live process or a core dump.

Pass the'-w' flag to the linker to omit the debug information(for example,gobuild-ldflags=-wprog.go).

The code generated by thegc compiler includes inlining offunction invocations and registerization of variables. These optimizationscan sometimes make debugging withgdb harder.If you find that you need to disable these optimizations,build your program usinggobuild-gcflags=all="-N -l".

If you want to use gdb to inspect a core dump, you can trigger a dumpon a program crash, on systems that permit it, by settingGOTRACEBACK=crash in the environment (see the runtime packagedocumentation for more info).

Common Operations

Go Extensions

A recent extension mechanism to GDB allows it to load extension scripts for agiven binary. The toolchain uses this to extend GDB with a handful ofcommands to inspect internals of the runtime code (such as goroutines) and topretty print the built-in map, slice and channel types.

If you'd like to see how this works, or want to extend it, take a look atsrc/runtime/runtime-gdb.py inthe Go source distribution. It depends on some special magic types(hash<T,U>) and variables (runtime.m andruntime.g) that the linker(src/cmd/link/internal/ld/dwarf.go) ensures are described inthe DWARF code.

If you're interested in what the debugging information looks like, runobjdump-Wa.out and browse through the.debug_*sections.

Known Issues

  1. String pretty printing only triggers for type string, not for types derivedfrom it.
  2. Type information is missing for the C parts of the runtime library.
  3. GDB does not understand Go’s name qualifications and treats"fmt.Print" as an unstructured literal with a"."that needs to be quoted. It objects even more strongly to method names ofthe formpkg.(*MyType).Meth.
  4. As of Go 1.11, debug information is compressed by default.Older versions of gdb, such as the one available by default on MacOS,do not understand the compression.You can generate uncompressed debug information by usinggobuild -ldflags=-compressdwarf=false.(For convenience you can put the-ldflags option intheGOFLAGSenvironment variable so that you don't have to specify it each time.)

Tutorial

In this tutorial we will inspect the binary of theregexp package's unit tests. To build the binary,change to$GOROOT/src/regexp and rungotest-c.This should produce an executable file namedregexp.test.

Getting Started

Launch GDB, debuggingregexp.test:

$gdb regexp.testGNU gdb (GDB) 7.2-gg8Copyright (C) 2010 Free Software Foundation, Inc.License GPLv  3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>Type "show copying" and "show warranty" for licensing/warranty details.This GDB was configured as "x86_64-linux".Reading symbols from  /home/user/go/src/regexp/regexp.test...done.Loading Go Runtime support.(gdb)

The message "Loading Go Runtime support" means that GDB loaded theextension from$GOROOT/src/runtime/runtime-gdb.py.

To help GDB find the Go runtime sources and the accompanying support script,pass your$GOROOT with the'-d' flag:

$gdb regexp.test -d $GOROOT

If for some reason GDB still can't find that directory or that script, you can loadit by hand by telling gdb (assuming you have the go sources in~/go/):

(gdb)source ~/go/src/runtime/runtime-gdb.pyLoading Go Runtime support.

Inspecting the source

Use the"l" or"list" command to inspect source code.

(gdb)l

List a specific part of the source parameterizing"list" with afunction name (it must be qualified with its package name).

(gdb)l main.main

List a specific file and line number:

(gdb)l regexp.go:1(gdb)# Hit enter to repeat last command. Here, this lists next 10 lines.

Naming

Variable and function names must be qualified with the name of the packagesthey belong to. TheCompile function from theregexppackage is known to GDB as'regexp.Compile'.

Methods must be qualified with the name of their receiver types. For example,the*Regexp type’sString method is known as'regexp.(*Regexp).String'.

Variables that shadow other variables are magically suffixed with a number in the debug info.Variables referenced by closures will appear as pointers magically prefixed with '&'.

Setting breakpoints

Set a breakpoint at theTestFind function:

(gdb)b 'regexp.TestFind'Breakpoint 1 at 0x424908: file /home/user/go/src/regexp/find_test.go, line 148.

Run the program:

(gdb)runStarting program: /home/user/go/src/regexp/regexp.testBreakpoint 1, regexp.TestFind (t=0xf8404a89c0) at /home/user/go/src/regexp/find_test.go:148148func TestFind(t *testing.T) {

Execution has paused at the breakpoint.See which goroutines are running, and what they're doing:

(gdb)info goroutines  1  waiting runtime.gosched* 13  running runtime.goexit

the one marked with the* is the current goroutine.

Inspecting the stack

Look at the stack trace for where we’ve paused the program:

(gdb)bt# backtrace#0  regexp.TestFind (t=0xf8404a89c0) at /home/user/go/src/regexp/find_test.go:148#1  0x000000000042f60b in testing.tRunner (t=0xf8404a89c0, test=0x573720) at /home/user/go/src/testing/testing.go:156#2  0x000000000040df64 in runtime.initdone () at /home/user/go/src/runtime/proc.c:242#3  0x000000f8404a89c0 in ?? ()#4  0x0000000000573720 in ?? ()#5  0x0000000000000000 in ?? ()

The other goroutine, number 1, is stuck inruntime.gosched, blocked on a channel receive:

(gdb)goroutine 1 bt#0  0x000000000040facb in runtime.gosched () at /home/user/go/src/runtime/proc.c:873#1  0x00000000004031c9 in runtime.chanrecv (c=void, ep=void, selected=void, received=void) at  /home/user/go/src/runtime/chan.c:342#2  0x0000000000403299 in runtime.chanrecv1 (t=void, c=void) at/home/user/go/src/runtime/chan.c:423#3  0x000000000043075b in testing.RunTests (matchString={void (struct string, struct string, bool *, error *)} 0x7ffff7f9ef60, tests=  []testing.InternalTest = {...}) at /home/user/go/src/testing/testing.go:201#4  0x00000000004302b1 in testing.Main (matchString={void (struct string, struct string, bool *, error *)} 0x7ffff7f9ef80, tests= []testing.InternalTest = {...}, benchmarks= []testing.InternalBenchmark = {...})at /home/user/go/src/testing/testing.go:168#5  0x0000000000400dc1 in main.main () at /home/user/go/src/regexp/_testmain.go:98#6  0x00000000004022e7 in runtime.mainstart () at /home/user/go/src/runtime/amd64/asm.s:78#7  0x000000000040ea6f in runtime.initdone () at /home/user/go/src/runtime/proc.c:243#8  0x0000000000000000 in ?? ()

The stack frame shows we’re currently executing theregexp.TestFind function, as expected.

(gdb)info frameStack level 0, frame at 0x7ffff7f9ff88: rip = 0x425530 in regexp.TestFind (/home/user/go/src/regexp/find_test.go:148);    saved rip 0x430233 called by frame at 0x7ffff7f9ffa8 source language minimal. Arglist at 0x7ffff7f9ff78, args: t=0xf840688b60 Locals at 0x7ffff7f9ff78, Previous frame's sp is 0x7ffff7f9ff88 Saved registers:  rip at 0x7ffff7f9ff80

The commandinfolocals lists all variables local to the function and their values, but is a bitdangerous to use, since it will also try to print uninitialized variables. Uninitialized slices may cause gdb to tryto print arbitrary large arrays.

The function’s arguments:

(gdb)info argst = 0xf840688b60

When printing the argument, notice that it’s a pointer to aRegexp value. Note that GDB has incorrectly put the*on the right-hand side of the type name and made up a 'struct' keyword, in traditional C style.

(gdb)p re(gdb) p t$1 = (struct testing.T *) 0xf840688b60(gdb) p t$1 = (struct testing.T *) 0xf840688b60(gdb) p *t$2 = {errors = "", failed = false, ch = 0xf8406f5690}(gdb) p *t->ch$3 = struct hchan<*testing.T>

Thatstructhchan<*testing.T> is theruntime-internal representation of a channel. It is currently empty,or gdb would have pretty-printed its contents.

Stepping forward:

(gdb)n# execute next line149             for _, test := range findTests {(gdb)# enter is repeat150                     re := MustCompile(test.pat)(gdb)p test.pat$4 = ""(gdb)p re$5 = (struct regexp.Regexp *) 0xf84068d070(gdb)p *re$6 = {expr = "", prog = 0xf840688b80, prefix = "", prefixBytes =  []uint8, prefixComplete = true,  prefixRune = 0, cond = 0 '\000', numSubexp = 0, longest = false, mu = {state = 0, sema = 0},  machine =  []*regexp.machine}(gdb)p *re->prog$7 = {Inst =  []regexp/syntax.Inst = {{Op = 5 '\005', Out = 0, Arg = 0, Rune =  []int}, {Op =    6 '\006', Out = 2, Arg = 0, Rune =  []int}, {Op = 4 '\004', Out = 0, Arg = 0, Rune =  []int}},  Start = 1, NumCap = 2}

We can step into theStringfunction call with"s":

(gdb)sregexp.(*Regexp).String (re=0xf84068d070, noname=void) at /home/user/go/src/regexp/regexp.go:9797      func (re *Regexp) String() string {

Get a stack trace to see where we are:

(gdb)bt#0  regexp.(*Regexp).String (re=0xf84068d070, noname=void)    at /home/user/go/src/regexp/regexp.go:97#1  0x0000000000425615 in regexp.TestFind (t=0xf840688b60)    at /home/user/go/src/regexp/find_test.go:151#2  0x0000000000430233 in testing.tRunner (t=0xf840688b60, test=0x5747b8)    at /home/user/go/src/testing/testing.go:156#3  0x000000000040ea6f in runtime.initdone () at /home/user/go/src/runtime/proc.c:243....

Look at the source code:

(gdb)l92              mu      sync.Mutex93              machine []*machine94      }9596      // String returns the source text used to compile the regular expression.97      func (re *Regexp) String() string {98              return re.expr99      }100101     // Compile parses a regular expression and returns, if successful,

Pretty Printing

GDB's pretty printing mechanism is triggered by regexp matches on type names. An example for slices:

(gdb)p utf$22 =  []uint8 = {0 '\000', 0 '\000', 0 '\000', 0 '\000'}

Since slices, arrays and strings are not C pointers, GDB can't interpret the subscripting operation for you, butyou can look inside the runtime representation to do that (tab completion helps here):

(gdb)p slc$11 =  []int = {0, 0}(gdb)p slc-><TAB>array  slc    len(gdb)p slc->array$12 = (int *) 0xf84057af00(gdb)p slc->array[1]$13 = 0

The extension functions $len and $cap work on strings, arrays and slices:

(gdb)p $len(utf)$23 = 4(gdb)p $cap(utf)$24 = 4

Channels and maps are 'reference' types, which gdb shows as pointers to C++-like typeshash<int,string>*. Dereferencing will trigger prettyprinting

Interfaces are represented in the runtime as a pointer to a type descriptor and a pointer to a value. The Go GDB runtime extension decodes this and automatically triggers pretty printing for the runtime type. The extension function$dtype decodes the dynamic type for you (examples are taken from a breakpoint atregexp.go line 293.)

(gdb)p i$4 = {str = "cbb"}(gdb)whatis itype = regexp.input(gdb)p $dtype(i)$26 = (struct regexp.inputBytes *) 0xf8400b4930(gdb)iface iregexp.input: struct regexp.inputBytes *
go.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic.Learn more.

[8]ページ先頭

©2009-2025 Movatter.jp