This is a creation in Article, where the information may have evolved or changed.
William Kennedy wrote two articles about go debugging, very good, deliberately translated a bit, deepen memory. This article is one of these: Stack traces in Go. The other is Scheduler tracing in Go.
The stack trace represents the stacking trace, which is an ordered collection of one or more stack frames. When the program appears panic you will see that the console has stack trace information printed out.
Introduced
Having basic debugging Go program skills can save programmers a lot of time to spot problems. I certainly believe that you can use the log information to track the problem, but sometimes the log message does not provide sufficient information when panic occurs. If you understand the stack trace information, you can instantly find the bug, which is very different from the traditional use of log tracking bug, because you need to add more log and then wait for the same error to occur.
I've been looking at the stack trace since I started writing the Go program. There are places where we write silly code that causes the runtime to kill our program and throw stack trace information. I'll show you what information the stack trace can provide, including how to find the value of the parameter we passed to the function.
Function
Listing 1
12345678910 |
Package Mainfunc main () {Make ([]string, 2, 4) Example (Slice, "hello", ten)}func Example (Slice []stringint ) { panic |
Listing 1 is a simple program in which the main function calls the example function on line 5th. The example function declares in line 8th that it has three arguments, a string slice, a string, and an integer. Its method body is also very simple, only one row, throws a panic, this will immediately produce a stack trace information:
Listing 2
123456789101112131415161718192021 |
Panic:want Stack Tracegoroutine1[Running]:main. Example(0X2080C3F50,0X2,0X4,0X425C0,0X5,0XA)/users/bill/spaces/go/projects/src/github.com/goinaction/code/temp/main.Go: 9+0X64main.main ()/users/bill/spaces/go/projects/src/github.com/goinaction/code/temp/main.Go: 5+0X85goroutine2[Runnable]:runtime.forcegchelper ()/users/bill/Go/src/runtime/proc.Go:Runtime.goexit ()/users/bill/Go/src/runtime/asm_amd64.s: 2232+0X1goroutine3[Runnable]:runtime.bgsweep ()/users/bill/Go/src/runtime/mgc0.Go: theRuntime.goexit ()/users/bill/Go/src/runtime/asm_amd64.s: 2232+0X1 |
Listing 2 shows all the Goroutine, the state of each goroutine, the state of each goroutine, and the corresponding call stack when panic occurs. Leading to panic's gotoutine at the top, we only look at this stack of information.
Listing 3
1234567 |
Goroutine 1 [Running]:main. Example(0X2080c3f50, 0x2, 0x4, 0x425c0, 0X5, 0xa) / users/bill/spaces/go/projects/src/github.com/goinaction/code/ Temp/main. Go: 9 +0x64main.main () /users/bill/spaces/go/projects/src/github.com/goinaction /code/ Temp/main. Go: 5 +0x85 |
I tested it in Go 1.6, and the stack trace information only shows the stack information for the current panic, and no other goroutine information is displayed.
The first line of listing 3 shows that the goroutine running before panic occurs is a goroutine with ID 1. The second line is the code location where the panic occurs, located in the example function under the main package. It also shows the file and path where the code is located, and the number of rows that occurred in panic (line 9th).
Line 03 also calls the name of the example function, which is the main function of the main package. It also shows the file name and path, and the number of rows that call the example function.
The stack trace information shows the Goroutine function call chain when panic occurs. Now let's look at the values of the arguments passed to example.
List 4
123456789 |
//Declaration Main. Example (Slice []string , str string , I int ) //call to Example by main. Slice: = make ([]string , 2 , 4 ) Example (slice, "hello", ) // Stack Trace Main. Example (0 x2080c3f50, 0 x2, 0 X4, 0 x425c0, 0 x5, 0 xa) |
Listing 4 lists the declaration of the example function, the invocation, and the information passed to its value. When you compare the declaration of a function and the value passed, you find that they are not consistent. The function declaration receives only three parameters, while the stack shows 6 values that are 16 binary. The key to understanding this is to know the implementation mechanism for each parameter type.
Let's look at the first [parameter of type]string. Slice is a reference type, which means that the value is the header information of a pointer (header value), which points to a string. For slice, its header is three word numbers, pointing to an array. So the first three values represent this slice.
Listing 5
12345678910111213 |
//Slice parameter value Slice: = make ([]string , 2 , 4 ) //Slice header values Pointer: 0 x2080c3f50length: 0 X2capacity: 0 x4//Declaration Main. Example (Slice []string , str string , I int ) //Stack trace Main. Example (0 x2080c3f50, 0 x2, 0 X4, 0 x425c0, 0 x5, 0 xa) |
Listing 5 shows the 0x2080c3f50
pointer representing the first parameter []string, 0x2
representing the slice length, 0x4
representing the capacity. These three values represent the first parameter.
Let's take a look at the second argument, which is the string type. A string is also a reference type, but its head (header) is immutable. This header contains two word types, one for pointers to the underlying byte array, and one for the length of the string.
List 6
123456789101112 |
//String parameter value "Hello" //String header values pointer: 0 X425c0length: 0 x5//Declaration Main. Example (Slice []string , str string , I int ) //Stack trace Main. Example (0 x2080c3f50, 0 x2, 0 X4, 0 x425c0, 0 x5, 0 xa) |
Listing 6 shows the parameters of the string for the 4th and 5th parameters in the stack trace information. 0x425c0
is a pointer to the underlying array of the string, which is the length of the 0x5
"Hello" string, both of them as the second argument.
The third argument is an integer, which is a simple word value.
Listing 7
1234567891011 |
Integer parameter value//Integer valueBase: 0xa//Declaration Main. Example (Slice []stringstringint)//Stack traceMain. Example(0X2080c3f50, 0x2, 0x4, 0x425c0, 0X5, 0 |
Listing 7 shows that the last parameter in the stack is the third parameter in the example declaration, and its value is 0xa
, that is, the integer 10.
Method
Let's change the program a little bit so that example becomes a method.
Listing 8
1234567891011121314151617 |
Package mainimport' FMT 'typestruct{}funcmake ([] string, 2, 4)var"Hello", (+)}func (t * Trace) Example (slice []stringstringint) {fmt. Printf ("Receiver Address:%p\n", T)panic("Want stack Trace")} |
Listing 8 adds a new type of trace in line 5th, with a method of changing the example to the trace's pointer receiver in the 14th. Line 10th declares that the type of T is Trace, and the 11th line invokes its method.
Because this method is declared as a method of pointer receiver, go uses the pointer of T to support receiver type, even if the code uses a value to invoke the method. When the program runs, the stack trace information is as follows:
Listing 9
1234567891011 |
Receiver Address:
0 x1553a8
panic : Want stack Tracegoroutine
1 [Running]:main. (*trace). Example
(0 x1553a8,
0 x2081b7f50,
0 x2,
0 x4,
0 xdc1d0,
0 X5,
0 xa)/users/bill/spaces/go/projects/src/github.com/goinaction/code/temp/ Main.
go
:16
+0 X116main.main ()/users/bill/spaces/go/projects/src/github.com/goinaction/code/ Temp/main.
go
:11
+0 xae
|
The 5th line of listing 9 shows clearly that the receiver of the method is pointer type. The method name and the name of the report package are in the middle (*trace). The second noteworthy is that the first parameter of the method in the stack information is the value of receiver. A method call is always converted to a function call, and the value of receiver is used as the first parameter of the function. We can see the details of the implementation in the total stack information.
Since the rest of the example has not changed, the other values remain the same. The line number displays the line number in the new code.
Packing
When the parameters of a function can be populated into a single word type, the values of the parameters are packaged together.
Listing 10
123456789 |
Package Mainfunc main () {Example (truefalsetrue, +)} Funcbooluint8) {panic("Want stack Trace")} |
Listing 10 Changes the method of example, allowing it to receive 4 parameters. The first three arguments are of type Boolean, and the fourth parameter is a 8bit unsigned integer. The Boolean type is also represented in 8bit, so these four parameters can be packaged into one word, including 32-bit schemas and 64-bit schemas. When the program runs, it produces interesting stacks:
Listing 11
1234567 |
Goroutine 1 [Running]:main. Example(0x19010001) /users/bill/spaces/go/projects/src/github.com/goinaction/code/ temp/main. Go: 8 +0x64main.main () /users/bill/spaces/go/projects/src/github.com/goinaction/ code/ Temp/main. Go: 4 +0x32 |
You can see that four values are packaged into a single value 0x19010001
.
List
123456789101112131415 |
//Parameter valuestrue,false,true, -//Word valueBits Binary Hex Value00-07 00000001 on true08-15 00000000 xx false16-23 00000001 on true24-31 00011001 + -//DeclarationMain. Example (B1, B2, B3BOOL, Iuint8)//Stack traceMain. Example(0X19010001) |
Listing 12 shows how the stack's values match the parameters. True with 1, accounting for 8bit, false with 0, accounting for the 8bit,uint8 value of 25 of the 16 binary is x19, expressed in 8bit. We'll see how they are represented as a word value in class.
Conclusion
The Go runtime provides detailed information to help us debug the program. In this article, we focus on stack trace information. Decoding the parameters that pass through the methods in the stack helps a lot. It helps me to locate bugs more than once. Now you know how to read these stack traces, and hopefully you can apply this method in the next debug.