The debugging go source code in this article is version 1.14.12. The debugging method introduced in this article has no relationship with the GO version

In the process of learning go, we may need to debug the source code of Go

But if we run the program directly, there is no way to achieve source debugging

So here is to introduce the go source debugging method

Debugging with GolAND can have a relatively clear graphical interface, which helps us to view some relevant parameters in the debugging process, but also makes debugging easier, so we use Goland for debugging

Write your program

To debug source code, you must first have your own code. Your own code will call the source code you want to debug

I’m using the example of determining whether a structure implements an interface

package main

import (
	"fmt"
)

type Duck interface {
	Quack()
}

type Cat struct{}

func (c Cat) Quacks(a) {
	fmt.Println("meow")}func main(a) {
	//./main.go:19:6: cannot use Cat literal (type Cat) as type Duck in assignment: Cat does not implement Duck (missing Quack method)
	var d Duck = Cat{}
	println(d)
}
Copy the code

Because Cat does not implement the Quack method, this code will not execute at line 19. It will be checked that Cat does not implement the interface Duck

Open the go source with Goland

And then we use Goland to open up the go source code, which is the SRC folder under Goroot, and notice that goland calls the Windows version of Go, and what we want to do is actually have Goland call the go source code and execute it step by step, So what you’re going to do is you’re going to turn on The Go in Windows, and of course if you’re a MAC you’re going to turn on the Go in The Goland, and that’s the same thing, but we’re going to run on goland.

Break point and set the Goland parameter

We found in the source code to debug you first part of the find method is varied, such as I so deliberately wrote a wrong code above right, we can directly under the SRC search this error, see where the error is come from, can also according to your rich knowledge, judge you to debug this behavior which is occurred in the source code package, Then go straight to main.go for that bag

Because go determines whether the structure implements an interface in the compilation stage after the abstract syntax tree is generated, and the error just reported was found to exist in go source code through searchsrc\cmd\compile\internal\gc\subr.go:610, so we can guess the debugging entry should besrc\cmd\compile\main.goThe main method in, as shown below

From reading this code, it’s clear that the real core logic must be in the gc.main (archInit) method at line 52, where we set line 52 as the starting point for debugging.

Let’s make a break point here.

Goland’s break point is something you probably know and I don’t have to explain it, but just click on the space to the right of line 52.

The effect of breaking a breakpoint is as follows

At this point we need to set goland’s parameters

Right click the green start arrow on the left of main in the screenshot, and three options will appear, namely [Run] [Debug] and [Modify Run Configuration]. Select [Modify Run Configuration], and a popup window will appear, as shown in the picture below

It’s the parameters in the configuration that we need to adjust

The first run type should be file.

The third output directory is to write the path of the code you just wrote yourself, to the folder, or click on the file icon behind the input box directly open the file selection box to find, my path is $GOPATH\ SRC \test

$GOPATH\ SRC \test = $GOPATH\ SRC \test

$GOPATH\ SRC \test\main.go = $GOPATH\ SRC \test\main.go = $GOPATH\ SRC \test\main.go = $GOPATH\ SRC \test\main.go

The eighth item, use all custom build tags, should be checked

After adjustment, the picture is as follows

Click “Apply” or “OK” to save

Start debugging

Now we are ready to debug the source code by right clicking on the green arrow to the left of main.go, and this time we select Debug

After goland has been running for a short time, we’ll be in debug mode

You can see the debug panel at the bottom, buttons at the far left and top of the debug panel, call stacks on the left side of the panel, and variables on the right

Let’s talk a little bit about the top five buttons, they’ll be used a lot

The first one is “show execution point”, which means that during debugging, the cursor will point to the logic that is currently executing.

The second one is called step by step, which actually executes the logic of the current line without exploring the internal logic of the function called by the current line, and uses step by step for those functions we don’t care about

The third one is called stepping, which means stepping, and if you’re executing a function you jump into that function. Note that with stepping, all functions go in, which allows you to go all the way to assembly code

The fourth one is called [step out], which is the automatic execution of the subsequent logic of this function, and we return to the next logic at the call of this function

The fifth one is “Execute to cursor”, literally, we can directly use the mouse to click on the line we are interested in during debugging, and then use this button to directly skip the middle logic and execute to the line where we just set the cursor

For example, if we break at line 52 and we debug, the program will stop at line 52, we can step into the main function, so we come to SRC \ CMD \compile\internal\ gc. main.go:144

Step into the later we can continue to use [a] to jump to we want to see the position, can also be a mouse click we want to see and then use the execution to the cursor 】, I focus on here is 594 lines, so I do click in 594, and then use the skip execution to the cursor 】 【 lines 144 to 594 in the middle of the other logic, the effect of the diagram below

Step into the typecheckslice function… If you have followed the operation, you will find that clicking step is not entertypechecksliceThe function goes inSliceFunction, of course, we need to know exactly what the argument is before we call the function, and that’s fine. At this point we can [step] [step] walk quicklySliceFunction, you can also use step out directly to leaveSliceThe function, it’s the same thing, ends up at 594, and then we click Step again to entertypechecksliceThe function

Click step continuously and we will entertypechecksliceIn thetypecheckFunction. A quick look at this function reveals that its actual core logic is called on line 300typecheck1Function, so we just click on line 300 and use Execute to cursor to execute directly to line 300

Click step to enter typecheck1 function

You can see that we’re now at line 327, typecheck1 function, which is a huge function hundreds of lines long, so let’s stop for a second

Notice that in the variables box we have n, top, res, they are two arguments to typecheck1 and they return a value top is an int of 1 there’s nothing to say, res must now be nil we don’t care about it, let’s look at this n, okay

After all, since this function is called typecheck, it’s going to be a typecheck, and top is an int, so it’s going to be the first argument n, and n is a pointer to Node, and this Node structure, if you go in there, is the structure of the Node in the abstract syntax tree of go, So the typecheck function is used to typecheck n

In the variables box we right-click n and select Check

The variable details popup opens

In this popup box, we can easily confirm the value of each attribute in the object N

We in fact can be judged by the specific value n the calls to this function is not what we want on that occasion, because in the process of logical execution, the same function may be called many times with different parameters, and only one is what we need, we found the inspection parameters after the call is not our concern, You can click step back to leave the call directly

The following detailed process is similar to opening the field and actually debugging the code, if you can judge by one of the attributes in the parameters

We can do this at any time during debugging

Okay, let’s go ahead and debug

Observe the typecheck1 function and find that the main logic of the function is in the switch starting at line 352, so we move the cursor to line 352 and click “Execute to cursor” to directly execute to line 352. Then we click “step” to go to line 549 and click “step” again to channel to line 1227. So if I step on line 1228, I’m going to start executing the logic in line 1227, so it looks like line 549, even though we’re going to step in there, doesn’t seem to be in the logic, I don’t know

Line 1227 is ONCALL, which is not what I want to see, indicating that n is not the target of my attention, I can use [step out] to skip the rest of the logic of this function. If you are debugging yourself, where to skip and where to follow step by step and you have to make your own judgment, it is best to take a look at the relevant source code, okay

Click “Step out” twice in a row to returntypechecksliceMethod does the next loop but there is no next loop, and then step will see that we exit the search and end up back in the gc\main.go loop

However, there is a next time to this loop. We step and step together to re-enter the Typecheckslice method and re-enter the switch in the Typecheck1 method using the same process as before

This case is OCOPY, which is not what we want, and we step back to the TypecheckSlice method for the next loop

By stepping, we can re-enter the Typecheck method in the second loop and follow the same process as abovetypecheck1This time, we’re going to go into a case called OAS

We step into the Typecheckas method

Use Execute to cursor to go directly to line 3181, and use Step to enterassignconvFunction, click step again to enterassignconvfnfunction

Click on line 838 and use Execute to cursor to proceed directly to line 838. Use Step to enterassignopfunction

This is the function that generated the error that we found in the beginning

Note the comment on line 583: DST is an interface, and SRC implements DST

So that tells us that what we want to look at, the logic to determine whether a structure implements an interface is right there

IsInterrface is used to determine whether SRC is an interface

So the logic we care about is in line 587 of the implements function

Click on line 587 and use Execute to cursor to go directly to line 587 and use Step to enterimplementsfunction

You can actually look at the variable at this point. The first parameter, t, is SRC, and as you can guess from the comments, SRC should be a structure; The second parameter, iface, is DST, and as the previous comment makes clear, DST is an interface

Let’s check these two parameters

Right-click t in the variable list, select Check, check box appears, check Sym properties, you can seeName=Cat

So this parameter t, this is actually the Cat structure that we defined in our program

Let’s do the same thing for iFace

Sym: Name=Duck

This confirms that iFace is the Duck interface defined in our own program

Well, then, the logic goes on

The logic in line 1660 is that it only gets in if T is interface, we don’t care about it, and then it goes down here

Let’s look at line 1692 first

The Fields method returns the field/method of the caller. If the caller is a struct, it returns a field. If the caller is an interface, it returns a method. The Slice method processes the return value of the Fields method into a Slice format, so 1692 lines are traversed through the Slice

Line 1693, I don’t know what I’m checking. It doesn’t matter. Let’s look at line 1696

When I is less than the length of TMS… Wait, what the hell is TMS?

If we look back at lines 1686 to 1689, we’ll see TMS = t.llMethods ().slice (), which means that TMS is a Slice of all functions of t. As we said before, t is the Cat structure we defined, and it has a Quacks method; So here we can see that TMS is a function slice of length 1, which contains Quacks functions

Ok, back to line 1696, I was defined before for started, and the initial value is 0, so at this point I is 0. If you don’t believe me, you can just look for I in the variable list at the bottom of the editor and see if it’s 0.

If I < len(TMS), let’s look at the second condition, TMS [I].sym! = im.Sym

TMS [0] is the Quacks method of Cat struct, im is the Quack method of Duck interface. Of course it’s not the same Quacks function name with an “s”, so we’re going to go into this for, i++, okay

At this point, we can also check TMS and IM through the variable box below to see if our judgment is correct

The zero of TMS,Sym.NameFor the Quacks

The sym. Name of im is Quack

This confirms what we have inferred about these two variables above

We can also realize that it is here that SRC \ CMD \compile\internal\ gc. go:1696 TMS [I].sym! = im.Sym, it’s a loop, it’s a loop, it’s a loop, it’s a loop, it’s a loop, it’s a loop, it’s a loop, it’s a loop

After I ++ I will be 1, no longer less than len(TMS), so this for will loop only once, and then we’ll get to line 1699, where I is equal to 1, and len(TMS) is equal to 1, so we’ll put in this if, and finally, at line 1703, return false

So we go back to line 587 of subr

Continue to use [step]

Finally, we come to line 610, where we initially searched for the error

It is in line 610 that an error such as “this structure does not implement this interface” is constructed

And then there’s some return logic that you’re interested in looking at for yourself, so I won’t go through it here

After this exploration, we learned how to use Goland for GO source debugging, and successfully found, where is go to determine whether the structure implements an interface