当前位置:网站首页>How to compile and debug go runtime source code

How to compile and debug go runtime source code

2022-06-24 17:05:00 luozhiyun

A friend asked me to read the source code , How to debug ? This time, let's take a brief look at how to compile and debug Go Of runtime Source code , Interested friends can operate it manually .

Compile changes Go Source code debugging

First download compilation

I'm using centos Environmental Science , So you need to install it first yum -y install gcc;

Then download go Source code :

[[email protected] src]# git clone https://github.com/golang/go.git

# Enter into src  Directory execution 
[[email protected] src]# ./all.bash

[[email protected] src]# ./all.bash
Building Go cmd/dist using /usr/local/go. (go1.15.8 linux/amd64)
Building Go toolchain1 using /usr/local/go.
Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.
Building Go toolchain2 using go_bootstrap and Go toolchain1.
Building Go toolchain3 using go_bootstrap and Go toolchain2.
Building packages and commands for linux/amd64.
...

##### API check
Go version is "go1.15.10", ignoring -next /data/learn/go/api/next.txt

ALL TESTS PASSED
---
Installed Go for linux/amd64 in /data/learn/go
Installed commands in /data/learn/go/bin
*** You need to add /data/learn/go/bin to your PATH.

compiled go and gofmt stay bin Under the table of contents :

[[email protected] src]# cd ../bin/
[[email protected] bin]# ls
go  gofmt

To prevent us from modifying go And those installed in the past go Conflict , establish mygo Soft connection , Point to modified go :

[[email protected] bin]# mkdir -p ~/mygo/bin

[[email protected] bin]# cd ~/testgo/bin

[[email protected] bin]# ln -sf /data/learn/go/bin/go mygo

Last , hold ~/testgo/bin Add to PATH

[[email protected] bin]# vim /etc/profile

export PATH=$PATH:/data/learn/mygo/bin

source /etc/profile

Under operation mygo, Check out the version :

[[email protected] bin]# mygo version
go version go1.15.10 linux/amd64

GODEBUG

When we modify the source code , Can use GODEBUG Variable to print debugging information .

schedtrace

schedtrace: setting schedtrace=X causes the scheduler to emit a single line to standard error every X milliseconds, summarizing the scheduler state.

schedtrace=X Indicates that the runtime runs every X One line of scheduler summary information is printed to the standard in milliseconds err Output in progress .

Such as setting schedtrace=1000 A line of scheduler profile is printed every second after the program starts :

[[email protected] gotest]# GOMAXPROCS=1 GODEBUG=schedtrace=1000 mygo run main.go SCHED 0ms: gomaxprocs=1 idleprocs=0 threads=4 spinningthreads=0 idlethreads=0 runqueue=0 [2]
# command-line-arguments
SCHED 0ms: gomaxprocs=1 idleprocs=0 threads=3 spinningthreads=0 idlethreads=1 runqueue=0 [2]
SCHED 0ms: gomaxprocs=1 idleprocs=0 threads=3 spinningthreads=0 idlethreads=0 runqueue=0 [2]

0ms : The number of milliseconds since the beginning of the program ;

gomaxprocs=1: Number of processors configured ;

idleprocs=0: Idle P( processor ) Count ;

threads=3: Number of threads managed at run time , at present 6 Threads ;

spinningthreads=0: Number of threads executing preemption ;

idlethreads=1: Number of idle threads ;

runqueue=0: On the whole run Queue goroutine Count ;

2: Local run Queue goroutine Count , There are two waiting ;

scheddetail

scheddetail: setting schedtrace=X and scheddetail=1 causes the scheduler to emit detailed multiline info every X milliseconds, describing state of the scheduler, processors, threads and goroutines.

schedtrace and scheddetail Set together to provide the processor P, Threads M and goroutine G The details of the .

for example :

[[email protected] gotest]# GOMAXPROCS=1 GODEBUG=schedtrace=1000,scheddetail=1 mygo run main.go 
SCHED 0ms: gomaxprocs=1 idleprocs=0 threads=4 spinningthreads=0 idlethreads=0 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0
  P0: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=2 gfreecnt=0 timerslen=0
  M3: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1
  M2: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=2 dying=0 spinning=false blocked=false lockedg=-1
  M1: p=-1 curg=17 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=17
  M0: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=1
  G1: status=1(chan receive) m=-1 lockedm=0
  G17: status=6() m=1 lockedm=1
  G2: status=1() m=-1 lockedm=-1
  G3: status=1() m=-1 lockedm=-1
  G4: status=4(GC scavenge wait) m=-1 lockedm=-1
...

Let's take a look at G The meaning of the representative :

  • status:G Operating state ;
  • m: To which one M;
  • lockedm: Is there a lock M;

G The running state of has these meanings :

const (
	//   Just assigned and not initialized 
	_Gidle = iota // 0 
	//  No execution code , No ownership of the stack , Stored in the run queue 
	_Grunnable // 1 
	//  You can execute code , Ownership of the stack , Given kernel threads  M  And the processor  P
	_Grunning // 2 
	//  Executing system call , Ownership of the stack , No user code executed ,
	//  Given kernel threads  M  But not on the run queue 
	_Gsyscall // 3 
	//  Blocked by runtime , No user code executed and not on the run queue ,
	//  But it could exist in  Channel  On the waiting queue 
	_Gwaiting // 4  
	//  At present goroutine Not being used , No execution code , There may be allocated stacks 
	_Gdead // 6  
	//  The stack is being copied , No execution code , Not on the run queue 
	_Gcopystack // 8 
	//  Blocked by preemption , No user code executed and not on the run queue , Waiting to wake up 
	_Gpreempted // 9 
	// GC  Scanning stack space , No execution code , Can coexist with other states 
	_Gscan          = 0x1000 
	...
)

If you don't understand , So I need to look at the analysis of my scheduling loop :《 Detailed explanation Go Language scheduling loop source code implementation https://www.luozhiyun.com/archives/448

M The meaning of the representative :

M0: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=1
  • p: To which one P;
  • curg: Which is currently in use G;
  • mallocing: Whether memory is being allocated ;
  • throwing: Whether to throw an exception ;
  • preemptoff: Not equal to empty string ("") Words , keep curg In this m Up operation ;
  • runqsize: Run in the queue G Number ;
  • spinning: Whether it is preempting G;

P The meaning of the representative :

P0: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=2 gfreecnt=0 timerslen=0
  • status:P Operating state .
  • schedtick:P The number of times to schedule .
  • syscalltick:P The number of system calls for .
  • m: To which one M.
  • runqsize: Run in the queue G Number .
  • gfreecnt: Usable G( Status as Gdead).

P Of status State represents the meaning of :

const ( 
	//  Express P No user code or scheduler running  
	_Pidle = iota 
	//  Threaded  M  hold , And executing user code or scheduler 
	_Prunning 
	//  No user code executed , The current thread is trapped in a system call 
	_Psyscall
	//  Threaded  M  hold , The current processor due to garbage collection  STW  Stopped 
	_Pgcstop 
	//  The current processor is no longer in use 
	_Pdead
)

Modify compilation

Let's say we're in channel Here we make a change and add a print Print :

func makechan(t *chantype, size int) *hchan {
	...  
	if debug.schedtrace > 0 {
		print("bearluo makechan: chan=", c, "; elemsize=", elem.size, "; dataqsiz=", size, "\n")
	}
	...
	return c
}

And then into go Of src Recompile in the directory :

[[email protected] src]# ./make.bash
Building Go cmd/dist using /usr/local/go. (go1.15.8 linux/amd64)
Building Go toolchain1 using /usr/local/go.
Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.
Building Go toolchain2 using go_bootstrap and Go toolchain1.
Building Go toolchain3 using go_bootstrap and Go toolchain2.
Building packages and commands for linux/amd64.
---
Installed Go for linux/amd64 in /data/learn/go
Installed commands in /data/learn/go/bin

Write a simple one demo( It can't be simpler ):

package main

import (
	"fmt"
)

func main() {

	c := make(chan int, 10)
	fmt.Println(c)
}

perform :

[[email protected] gotest]# GODEBUG=schedtrace=1000 mygo run main.go 
bearluo makechan: chan=0xc000036070; elemsize=8; dataqsiz=2
SCHED 0ms: gomaxprocs=16 idleprocs=13 threads=6 spinningthreads=1 idlethreads=0 runqueue=0 [1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
bearluo makechan: chan=0xc00010e000; elemsize=1; dataqsiz=0
bearluo makechan: chan=0xc00010e060; elemsize=0; dataqsiz=0
bearluo makechan: chan=0xc00010e180; elemsize=0; dataqsiz=0
bearluo makechan: chan=0xc0006a8000; elemsize=1; dataqsiz=35
bearluo makechan: chan=0xc0003f6660; elemsize=16; dataqsiz=2
bearluo makechan: chan=0xc000226540; elemsize=16; dataqsiz=2
bearluo makechan: chan=0xc0001381e0; elemsize=16; dataqsiz=2
bearluo makechan: chan=0xc0005043c0; elemsize=16; dataqsiz=2
bearluo makechan: chan=0xc00049c420; elemsize=16; dataqsiz=2
bearluo makechan: chan=0xc000594300; elemsize=16; dataqsiz=2
bearluo makechan: chan=0xc000090360; elemsize=16; dataqsiz=2
bearluo makechan: chan=0xc000220000; elemsize=16; dataqsiz=2
bearluo makechan: chan=0xc00075e000; elemsize=16; dataqsiz=2
bearluo makechan: chan=0xc000138840; elemsize=16; dataqsiz=2
bearluo makechan: chan=0xc000226780; elemsize=16; dataqsiz=2
bearluo makechan: chan=0xc0003ea420; elemsize=16; dataqsiz=2
bearluo makechan: chan=0xc00049d320; elemsize=16; dataqsiz=1
...

Delve debugging

at present Go Language support GDB、LLDB and Delve Several debuggers . Only Delve It's for Go Debugging tools for language design and development . and Delve It's also the use of Go Language development , Yes Windows The platform provides the same support . This section is based on Delve Simply explain how to debug Go runtime Code and assembler .

Project address :https://github.com/go-delve/delve

install :

go get github.com/go-delve/delve/cmd/dlv

First write a test.go An example of :

package main

import "fmt"

type A struct {
	test string
}
func main() {
	a := new(A)
	fmt.Println(a)
}

Then the command line goes to the directory where the package is located , Then input dlv debug Command into debug :

PS C:\document\code\test_go\src> dlv debug
Type 'help' for list of commands.

And then you can use break Command in main Bag main Method :

(dlv) break main.main
Breakpoint 1 set at 0x4bd30a for main.main() c:/document/code/test_go/src/test.go:8

adopt breakpoints View all breakpoints that have been set :

(dlv) breakpoints
Breakpoint runtime-fatal-throw at 0x4377e0 for runtime.fatalthrow() c:/software/go/src/runtime/panic.go:1162 (0)
Breakpoint unrecovered-panic at 0x437860 for runtime.fatalpanic() c:/software/go/src/runtime/panic.go:1189 (0)
        print runtime.curg._panic.arg
Breakpoint 1 at 0x4bd30a for main.main() c:/document/code/test_go/src/test.go:8 (0)

adopt continue Command to run the program to the next breakpoint :

(dlv) continue
> main.main() c:/document/code/test_go/src/test.go:8 (hits goroutine(1):1 total:1) (PC: 0x4bd30a)
     3: import "fmt"
     4:
     5: type A struct {
     6:         test string
     7: }
=>   8: func main() {
     9:         a := new(A)
    10:         fmt.Println(a)
    11: }
    12:
    13:

adopt disassemble Disassembly command view main Function corresponding to the assembly code :

(dlv) disassemble
TEXT main.main(SB) C:/document/code/test_go/src/test.go
        test.go:8       0x4bd2f0        65488b0c2528000000      mov rcx, qword ptr gs:[0x28]
        test.go:8       0x4bd2f9        488b8900000000          mov rcx, qword ptr [rcx]
        test.go:8       0x4bd300        483b6110                cmp rsp, qword ptr [rcx+0x10]
        test.go:8       0x4bd304        0f8697000000            jbe 0x4bd3a1
=>      test.go:8       0x4bd30a*       4883ec78                sub rsp, 0x78
        test.go:8       0x4bd30e        48896c2470              mov qword ptr [rsp+0x70], rbp
        test.go:8       0x4bd313        488d6c2470              lea rbp, ptr [rsp+0x70]
        test.go:9       0x4bd318        488d0581860100          lea rax, ptr [__image_base__+874912]
        test.go:9       0x4bd31f        48890424                mov qword ptr [rsp], rax
        test.go:9       0x4bd323        e8e800f5ff              call $runtime.newobject
        test.go:9       0x4bd328        488b442408              mov rax, qword ptr [rsp+0x8]
        test.go:9       0x4bd32d        4889442430              mov qword ptr [rsp+0x30], rax
        test.go:10      0x4bd332        4889442440              mov qword ptr [rsp+0x40], rax
        test.go:10      0x4bd337        0f57c0                  xorps xmm0, xmm0
        test.go:10      0x4bd33a        0f11442448              movups xmmword ptr [rsp+0x48], xmm0
        test.go:10      0x4bd33f        488d442448              lea rax, ptr [rsp+0x48]
        test.go:10      0x4bd344        4889442438              mov qword ptr [rsp+0x38], rax
        test.go:10      0x4bd349        8400                    test byte ptr [rax], al
        test.go:10      0x4bd34b        488b4c2440              mov rcx, qword ptr [rsp+0x40]
        test.go:10      0x4bd350        488d15099f0000          lea rdx, ptr [__image_base__+815712]
        test.go:10      0x4bd357        4889542448              mov qword ptr [rsp+0x48], rdx
        test.go:10      0x4bd35c        48894c2450              mov qword ptr [rsp+0x50], rcx
        test.go:10      0x4bd361        8400                    test byte ptr [rax], al
        test.go:10      0x4bd363        eb00                    jmp 0x4bd365
        test.go:10      0x4bd365        4889442458              mov qword ptr [rsp+0x58], rax
        test.go:10      0x4bd36a        48c744246001000000      mov qword ptr [rsp+0x60], 0x1
        test.go:10      0x4bd373        48c744246801000000      mov qword ptr [rsp+0x68], 0x1
        test.go:10      0x4bd37c        48890424                mov qword ptr [rsp], rax
        test.go:10      0x4bd380        48c744240801000000      mov qword ptr [rsp+0x8], 0x1
        test.go:10      0x4bd389        48c744241001000000      mov qword ptr [rsp+0x10], 0x1
        test.go:10      0x4bd392        e869a0ffff              call $fmt.Println
        test.go:11      0x4bd397        488b6c2470              mov rbp, qword ptr [rsp+0x70]
        test.go:11      0x4bd39c        4883c478                add rsp, 0x78
        test.go:11      0x4bd3a0        c3                      ret
        test.go:8       0x4bd3a1        e82a50faff              call $runtime.morestack_noctxt
        .:0             0x4bd3a6        e945ffffff              jmp $main.main

Now we can use it break Breakpoint to runtime.newobject Function call :

(dlv) break runtime.newobject
Breakpoint 2 set at 0x40d426 for runtime.newobject() c:/software/go/src/runtime/malloc.go:1164

Input continue Jump to the breakpoint :

(dlv) continue
> runtime.newobject() c:/software/go/src/runtime/malloc.go:1164 (hits goroutine(1):1 total:1) (PC: 0x40d426)
Warning: debugging optimized function
  1159: }
  1160:
  1161: // implementation of new builtin
  1162: // compiler (both frontend and SSA backend) knows the signature
  1163: // of this function
=>1164: func newobject(typ *_type) unsafe.Pointer {
  1165:         return mallocgc(typ.size, typ, true)
  1166: }
  1167:
  1168: //go:linkname reflect_unsafe_New reflect.unsafe_New
  1169: func reflect_unsafe_New(typ *_type) unsafe.Pointer {

print Order to see typ The data of :

(dlv) print typ
*runtime._type {size: 16, ptrdata: 8, hash: 875453117, tflag: tflagUncommon|tflagExtraStar|tflagNamed (7), align: 8, fieldAlign: 8, kind: 25, equal: runtime.strequal, gcdata: *1, str: 5418, ptrToThis: 37472}

You can see the print here size yes 16bytes, Because we A There's just one in the structure string Type of field.

Enter into mallocgc After the method , adopt args and locals Command to view the parameters and local variables of a function :

(dlv) args
size = (unreadable could not find loclist entry at 0x8b40 for address 0x40ca73)
typ = (*runtime._type)(0x4d59a0)
needzero = true
~r3 = (unreadable empty OP stack)
(dlv) locals
(no locals)

Reference

Installing Go from source https://golang.org/doc/install/source

Scheduler Tracing In Go https://www.ardanlabs.com/blog/2015/02/scheduler-tracing-in-go.html

GODEBUG https://golang.org/pkg/runtime/

use GODEBUG Look at the dispatch tracking https://eddycjy.com/posts/go/tools/2019-08-19-godebug-sched/

原网站

版权声明
本文为[luozhiyun]所创,转载请带上原文链接,感谢
https://yzsam.com/2021/03/20210331220449092e.html