go dlv 调试代码

概要

在工作中有时要用到go dlv 工具做代码调试或追踪的事情,本文专门记录下相关指令的用法。

dlv调试代码命令主要有三个:
debug:调试未编译源码;
exec:调试编译后的但未运行的可执行文件;
attach:调试正在运行的程序。

但三者进入调试阶段后的操作都一样的。

示例调试代码如下:

//main.go文件内容
  1 package main
  2
  3 import (
  4     "fmt"
  5     "runtime"
  6     "time"
  7 )
  8
  9 func deadloop() {
 10     for {
 11     }
 12 }
 13
 14 func worker() {
 15     for {
 16         fmt.Println("worker is running")
 17         time.Sleep(time.Second * 1)
 18     }
 19 }
 20
 21 func main() {
 22     fmt.Printf("There are %d cores.\n", runtime.NumCPU())
 23     readFileLine()
 24     x:=9
 25     go worker()
 26
 27     go deadloop()
 28
 29     i := 3
 30     for {
 31         fmt.Printf("main is running, i=%d\n", i)
 32          i--
 33         if i == x {
 34             runtime.GC()
 35         }
 36
 37         time.Sleep(time.Second * 1)
 38     }
 39 }
//file.go文件内容
  1 package main
  2 import (
  3         "fmt"
  4         //"gopkg.in/yaml.v2"
  5         "io"
  6         "os"
  7         //"unsafe"
  8 )
  9 func readFileLine() {
 10         file, err := os.Open("/home/fpf/public/a.txt")
 11         defer file.Close()
 12         if err != nil {
 13                 fmt.Println("open file err", err.Error())
 14                 return
 15         }
 16         // 使用bufio读取
 17         //r := bufio.NewReader(file)
 18         data := make([]byte, 3)
 19         for {
 20                 //按字节读
 21                 _, err = file.Read(data)
 22                 // 分行读取文件  ReadLine返回单个行,不包括行尾字节(\n  或 \r\n)
 23                 //data, _, err := r.ReadLine()
 24
 25                 // 以分隔符形式读取,比如此处设置的分割符是\n,则遇到\n就返回,且包括\n本身 直接返回字符串
 26                 //str, err := r.ReadString('\n')
 27
 28                 // 以分隔符形式读取,比如此处设置的分割符是\n,则遇到\n就返回,且包括\n本身 直接返回字节数数组
 29                 //data, err := r.ReadBytes('\n')
 30
 31                 // 读取到末尾退出
 32                 if err == io.EOF {
 33                         fmt.Println("read over!!!")
 34                         break
 35                 }
 36
 37                 if err != nil {
 38                         fmt.Println("read err", err.Error())
 39                         break
 40                 }
 41
 42                 // 打印出内容
 43                 fmt.Printf("%s\n", data)
 44         }
 45 }

调试的服务器信息:Centos Linux 7 ,CPU AMD x86_64
Go version 1.23.4

指令说明

[root@name goschedule]# dlv debug main.go file.go
(dlv) help
The following commands are available:

Running the program:
    call -------------------------------- 恢复进程,注入一个函数调用(还在实验阶段!!) 本人没用过
    continue (alias: c) ----------------- 运行到断点或程序终止
    next (alias: n) --------------------- 转到下行代码
    next-instruction (alias: ni | nexti)  Single step a single cpu instruction, skipping function calls 
    rebuild ----------------------------- Rebuild the target executable and restarts it. It does not work if the executable was not built by delve.
    restart (alias: r) ------------------ 重启进程,调试进度重新开始.
    step (alias: s) --------------------- 单步执行,遇到函数调用,会进入函数,并停止
    step-instruction (alias: si | stepi)  Single step a single cpu instruction. 单步执行提条cpu指令
    stepout (alias: so) ----------------- 跳出当前函数,回到入口处.

Manipulating breakpoints:
    break (alias: b) ------- 设置一个断点,格式:b  文件路径:行数,比如 b ./main.go:31
    breakpoints (alias: bp)  打印活跃的断点信息.
    clear ------------------ 删除某个断点,格式:clear 断点ID
    clearall ---------------  删除所有断点
    condition (alias: cond)  Set breakpoint condition.设置断点条件,只有满足条件时断点才会停止,比如:cond 2 i==-1
    on --------------------- Executes a command when a breakpoint is hit.在命中断点时执行命令,比如:on 2 print i,即遇到断点2时打印变量i的值
    toggle ----------------- Toggles on or off a breakpoint.停用/启用某个断点,格式:toggle 断点ID
    trace (alias: t) ------- Set tracepoint. 设置一个追踪点,和断点不同,执行到追踪点程序不会暂定,只是打印一定信息后继续执行,格式: t  文件路径:行数
    watch ------------------ Set watchpoint. 设置一个监听点,监听某个地址被读写了就会暂停程序 格式:watch [-r|-w|-rw] <expr>,但是**目前本人没搞明白这个地址怎么设置,哪位大神指导评论区说一下**

Viewing program variables and memory:
    args ----------------- 打印当前函数的参数
    display -------------- Print value of an expression every time the program stops.
    examinemem (alias: x)  Examine raw memory at the given address.
    locals --------------- 打印当前函数的本地(局部)变量
    print (alias: p) ----- 计算一个表达式,可以打印某个变量的值,格式: p 变量名
    regs ----------------- Print contents of CPU registers. 打印CPU寄存器的内容
    set ------------------ 改变一个变量的值,格式:set 变量名=xxx
    vars ----------------- 打印当前包的所有变量
    whatis --------------- 打印表达式的类型,可以打印某个变量的类型,格式:whatis int32(x)

Listing and switching between threads and goroutines:
    goroutine (alias: gr) -- Shows or changes current goroutine  打印当前所在协程信息,或切换指定协程,格式:gr 协程id
    goroutines (alias: grs)  List program goroutines. 打印所有协程信息
    thread (alias: tr) ----- Switch to the specified thread. 切换指定线程
    threads ---------------- Print out info for every traced thread.  打印所有线程信息

Viewing the call stack and selecting frames:
    deferred --------- Executes command in the context of a deferred call. 在延迟调用的上下文中执行命令
    down ------------- Move the current frame down. 将当前帧向下移动
    frame ------------ Set the current frame, or execute command on a different frame. 设置当前帧,或在不同的帧上执行命令,格式:frame 帧id,这个id通过 bt打印就可以看到
    stack (alias: bt)  Print stack trace. 打印堆栈跟踪信息
    up --------------- Move the current frame up. 将当前帧向上移动

Other commands:
    config --------------------- Changes configuration parameters.
    disassemble (alias: disass)  Disassembler.打印汇编代码
    dump ----------------------- Creates a core dump from the current process state
    edit (alias: ed) ----------- Open where you are in $DELVE_EDITOR or $EDITOR
    exit (alias: quit | q) ----- Exit the debugger.
    funcs ---------------------- Print list of functions.
    help (alias: h) ------------ Prints the help message.
    libraries ------------------ List loaded dynamic libraries
    list (alias: ls | l) ------- Show source code. 打印部分代码
    packages ------------------- Print list of packages.
    source --------------------- Executes a file containing a list of delve commands
    sources -------------------- Print list of source files.
    target --------------------- Manages child process debugging.
    transcript ----------------- Appends command output to a file.
    types ---------------------- Print list of types
(dlv) help display #看一下display具体用法
Print value of an expression every time the program stops.
        display -a [%format] <expression>
        display -d <number>

实战

以追踪os.Open函数调用链路为例。在项目编译目录下执行dlv debug main.go file.go

[root@name goschedule]# dlv debug main.go file.go
Type 'help' for list of commands.
(dlv) b ./main.go:22
Breakpoint 1 set at 0x4cad5a for main.main() ./main.go:22
(dlv) c
> [Breakpoint 1] main.main() ./main.go:22 (hits goroutine(1):1 total:1) (PC: 0x4cad5a)
    17:         time.Sleep(time.Second * 1)
    18:     }
    19: }
    20:
    21: func main() {
=>  22:     fmt.Printf("There are %d cores.\n", runtime.NumCPU())
    23:     readFileLine()
    24:
    25:     go worker()
    26:
    27:     go deadloop()
(dlv) n
There are 12 cores.
> main.main() ./main.go:23 (PC: 0x4cadfa)
    18:     }
    19: }
    20:
    21: func main() {
    22:     fmt.Printf("There are %d cores.\n", runtime.NumCPU())
=>  23:     readFileLine()
    24:
    25:     go worker()
    26:
    27:     go deadloop()
    28:
(dlv) s
> main.readFileLine() ./file.go:9 (PC: 0x4caf16)
     4:         //"gopkg.in/yaml.v2"
     5:         "io"
     6:         "os"
     7:         //"unsafe"
     8: )
=>   9: func readFileLine() {
    10:         file, err := os.Open("/home/fpf/public/a.txt")
    11:         defer file.Close()
    12:         if err != nil {
    13:                 fmt.Println("open file err", err.Error())
    14:                 return
(dlv) n
> main.readFileLine() ./file.go:10 (PC: 0x4caf1d)
     5:         "io"
     6:         "os"
     7:         //"unsafe"
     8: )
     9: func readFileLine() {
=>  10:         file, err := os.Open("/home/fpf/public/a.txt")
    11:         defer file.Close()
    12:         if err != nil {
    13:                 fmt.Println("open file err", err.Error())
    14:                 return
    15:         }
(dlv) s
> os.Open() /usr/local/go/src/os/file.go:364 (PC: 0x4bc8ae)
   359:
   360: // Open opens the named file for reading. If successful, methods on
   361: // the returned file can be used for reading; the associated file
   362: // descriptor has mode O_RDONLY.
   363: // If there is an error, it will be of type *PathError.
=> 364: func Open(name string) (*File, error) {
   365:         return OpenFile(name, O_RDONLY, 0)
   366: }
   367:
   368: // Create creates or truncates the named file. If the file already exists,
   369: // it is truncated. If the file does not exist, it is created with mode 0o666
   (dlv) n
> os.Open() /usr/local/go/src/os/file.go:365 (PC: 0x4bc8ce)
   360: // Open opens the named file for reading. If successful, methods on
   361: // the returned file can be used for reading; the associated file
   362: // descriptor has mode O_RDONLY.
   363: // If there is an error, it will be of type *PathError.
   364: func Open(name string) (*File, error) {
=> 365:         return OpenFile(name, O_RDONLY, 0)
   366: }
   367:
   368: // Create creates or truncates the named file. If the file already exists,
   369: // it is truncated. If the file does not exist, it is created with mode 0o666
   370: // (before umask). If successful, methods on the returned File can
(dlv) s
> os.OpenFile() /usr/local/go/src/os/file.go:383 (PC: 0x4bc993)
   378: // or Create instead. It opens the named file with specified flag
   379: // (O_RDONLY etc.). If the file does not exist, and the O_CREATE flag
   380: // is passed, it is created with mode perm (before umask). If successful,
   381: // methods on the returned File can be used for I/O.
   382: // If there is an error, it will be of type *PathError.
=> 383: func OpenFile(name string, flag int, perm FileMode) (*File, error) {
   384:         testlog.Open(name)
   385:         f, err := openFileNolog(name, flag, perm)
   386:         if err != nil {
   387:                 return nil, err
   388:         }
(dlv) n
> os.OpenFile() /usr/local/go/src/os/file.go:384 (PC: 0x4bc9c5)
   379: // (O_RDONLY etc.). If the file does not exist, and the O_CREATE flag
   380: // is passed, it is created with mode perm (before umask). If successful,
   381: // methods on the returned File can be used for I/O.
   382: // If there is an error, it will be of type *PathError.
   383: func OpenFile(name string, flag int, perm FileMode) (*File, error) {
=> 384:         testlog.Open(name)
   385:         f, err := openFileNolog(name, flag, perm)
   386:         if err != nil {
   387:                 return nil, err
   388:         }
   389:         f.appendMode = flag&O_APPEND != 0
(dlv) n
> os.OpenFile() /usr/local/go/src/os/file.go:385 (PC: 0x4bc9da)
   380: // is passed, it is created with mode perm (before umask). If successful,
   381: // methods on the returned File can be used for I/O.
   382: // If there is an error, it will be of type *PathError.
   383: func OpenFile(name string, flag int, perm FileMode) (*File, error) {
   384:         testlog.Open(name)
=> 385:         f, err := openFileNolog(name, flag, perm)
   386:         if err != nil {
   387:                 return nil, err
   388:         }
   389:         f.appendMode = flag&O_APPEND != 0
   390:
(dlv) s
> os.openFileNolog() /usr/local/go/src/os/file_unix.go:264 (PC: 0x4bdd33)
   259: // On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
   260: const DevNull = "/dev/null"
   261:
   262: // openFileNolog is the Unix implementation of OpenFile.
   263: // Changes here should be reflected in openDirAt and openDirNolog, if relevant.
=> 264: func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
   265:         setSticky := false
   266:         if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 {
   267:                 if _, err := Stat(name); IsNotExist(err) {
   268:                         setSticky = true
   269:                 }
   (dlv) n
> os.openFileNolog() /usr/local/go/src/os/file_unix.go:265 (PC: 0x4bdd68)
   260: const DevNull = "/dev/null"
   261:
   262: // openFileNolog is the Unix implementation of OpenFile.
   263: // Changes here should be reflected in openDirAt and openDirNolog, if relevant.
   264: func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
=> 265:         setSticky := false
   266:         if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 {
   267:                 if _, err := Stat(name); IsNotExist(err) {
   268:                         setSticky = true
   269:                 }
   270:         }
(dlv) n
> os.openFileNolog() /usr/local/go/src/os/file_unix.go:273 (PC: 0x4bdd6d)
   268:                         setSticky = true
   269:                 }
   270:         }
   271:
   272:         var (
=> 273:                 r int
   274:                 s poll.SysFile
   275:                 e error
   276:         )
   277:         // We have to check EINTR here, per issues 11180 and 39237.
   278:         ignoringEINTR(func() error {
(dlv) n
> os.openFileNolog() /usr/local/go/src/os/file_unix.go:274 (PC: 0x4bdd76)
   269:                 }
   270:         }
   271:
   272:         var (
   273:                 r int
=> 274:                 s poll.SysFile
   275:                 e error
   276:         )
   277:         // We have to check EINTR here, per issues 11180 and 39237.
   278:         ignoringEINTR(func() error {
   279:                 r, s, e = open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
(dlv) n
> os.openFileNolog() /usr/local/go/src/os/file_unix.go:275 (PC: 0x4bdd7f)
   270:         }
   271:
   272:         var (
   273:                 r int
   274:                 s poll.SysFile
=> 275:                 e error
   276:         )
   277:         // We have to check EINTR here, per issues 11180 and 39237.
   278:         ignoringEINTR(func() error {
   279:                 r, s, e = open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
   280:                 return e
(dlv) n
> os.openFileNolog() /usr/local/go/src/os/file_unix.go:278 (PC: 0x4bdd85)
   273:                 r int
   274:                 s poll.SysFile
   275:                 e error
   276:         )
   277:         // We have to check EINTR here, per issues 11180 and 39237.
=> 278:         ignoringEINTR(func() error {
   279:                 r, s, e = open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
   280:                 return e
   281:         })
   282:         if e != nil {
   283:                 return nil, &PathError{Op: "open", Path: name, Err: e}
   (dlv) s
> os.ignoringEINTR() /usr/local/go/src/os/file_posix.go:249 (PC: 0x4bd62e)
   244: // signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846.
   245: // Also #20400 and #36644 are issues in which a signal handler is
   246: // installed without setting SA_RESTART. None of these are the common case,
   247: // but there are enough of them that it seems that we can't avoid
   248: // an EINTR loop.
=> 249: func ignoringEINTR(fn func() error) error {
   250:         for {
   251:                 err := fn()
   252:                 if err != syscall.EINTR {
   253:                         return err
   254:                 }
(dlv) n
> os.ignoringEINTR() /usr/local/go/src/os/file_posix.go:250 (PC: 0x4bd63d)
   245: // Also #20400 and #36644 are issues in which a signal handler is
   246: // installed without setting SA_RESTART. None of these are the common case,
   247: // but there are enough of them that it seems that we can't avoid
   248: // an EINTR loop.
   249: func ignoringEINTR(fn func() error) error {
=> 250:         for {
   251:                 err := fn()
   252:                 if err != syscall.EINTR {
   253:                         return err
   254:                 }
   255:         }
(dlv) n
> os.ignoringEINTR() /usr/local/go/src/os/file_posix.go:251 (PC: 0x4bd640)
   246: // installed without setting SA_RESTART. None of these are the common case,
   247: // but there are enough of them that it seems that we can't avoid
   248: // an EINTR loop.
   249: func ignoringEINTR(fn func() error) error {
   250:         for {
=> 251:                 err := fn()
   252:                 if err != syscall.EINTR {
   253:                         return err
   254:                 }
   255:         }
   256: }
(dlv) s
> os.openFileNolog.func1() /usr/local/go/src/os/file_unix.go:278 (PC: 0x4be0d3)
   273:                 r int
   274:                 s poll.SysFile
   275:                 e error
   276:         )
   277:         // We have to check EINTR here, per issues 11180 and 39237.
=> 278:         ignoringEINTR(func() error {
   279:                 r, s, e = open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
   280:                 return e
   281:         })
   282:         if e != nil {
   283:                 return nil, &PathError{Op: "open", Path: name, Err: e}
   (dlv) s
> os.openFileNolog.func1() /usr/local/go/src/os/file_unix.go:279 (PC: 0x4be126)
   274:                 s poll.SysFile
   275:                 e error
   276:         )
   277:         // We have to check EINTR here, per issues 11180 and 39237.
   278:         ignoringEINTR(func() error {
=> 279:                 r, s, e = open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
   280:                 return e
   281:         })
   282:         if e != nil {
   283:                 return nil, &PathError{Op: "open", Path: name, Err: e}
   284:         }
(dlv) s
> os.syscallMode() /usr/local/go/src/os/file_posix.go:60 (PC: 0x4bd2aa)
    55:         runtime.KeepAlive(f)
    56:         return n, err
    57: }
    58:
    59: // syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
=>  60: func syscallMode(i FileMode) (o uint32) {
    61:         o |= uint32(i.Perm())
    62:         if i&ModeSetuid != 0 {
    63:                 o |= syscall.S_ISUID
    64:         }
    65:         if i&ModeSetgid != 0 {
(dlv) so
> os.openFileNolog.func1() /usr/local/go/src/os/file_unix.go:279 (PC: 0x4be12f)
Values returned:
        o: 0

   274:                 s poll.SysFile
   275:                 e error
   276:         )
   277:         // We have to check EINTR here, per issues 11180 and 39237.
   278:         ignoringEINTR(func() error {
=> 279:                 r, s, e = open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
   280:                 return e
   281:         })
   282:         if e != nil {
   283:                 return nil, &PathError{Op: "open", Path: name, Err: e}
   284:         }
(dlv) s

> os.open() /usr/local/go/src/os/file_open_unix.go:14 (PC: 0x4bceb3)
     9: import (
    10:         "internal/poll"
    11:         "syscall"
    12: )
    13:
=>  14: func open(path string, flag int, perm uint32) (int, poll.SysFile, error) {
    15:         fd, err := syscall.Open(path, flag, perm)
    16:         return fd, poll.SysFile{}, err
    17: }
    (dlv) s
> syscall.Open() /usr/local/go/src/syscall/syscall_linux.go:283 (PC: 0x4a052e)
   278:
   279: func Mknod(path string, mode uint32, dev int) (err error) {
   280:         return Mknodat(_AT_FDCWD, path, mode, dev)
   281: }
   282:
=> 283: func Open(path string, mode int, perm uint32) (fd int, err error) {
   284:         return openat(_AT_FDCWD, path, mode|O_LARGEFILE, perm)
   285: }
   286:
   287: //sys   openat(dirfd int, path string, flags int, mode uint32) (fd int, err error)
   288:
(dlv) n
> syscall.Open() /usr/local/go/src/syscall/syscall_linux.go:284 (PC: 0x4a0560)
   279: func Mknod(path string, mode uint32, dev int) (err error) {
   280:         return Mknodat(_AT_FDCWD, path, mode, dev)
   281: }
   282:
   283: func Open(path string, mode int, perm uint32) (fd int, err error) {
=> 284:         return openat(_AT_FDCWD, path, mode|O_LARGEFILE, perm)
   285: }
   286:
   287: //sys   openat(dirfd int, path string, flags int, mode uint32) (fd int, err error)
   288:
   289: func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
(dlv) s
> syscall.openat() /usr/local/go/src/syscall/zsyscall_linux_amd64.go:92 (PC: 0x4a1153)
    87:         return
    88: }
    89:
    90: // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
    91:
=>  92: func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
    93:         var _p0 *byte
    94:         _p0, err = BytePtrFromString(path)
    95:         if err != nil {
    96:                 return
    97:         }
(dlv) n
> syscall.openat() /usr/local/go/src/syscall/zsyscall_linux_amd64.go:93 (PC: 0x4a1193)
    88: }
    89:
    90: // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
    91:
    92: func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
=>  93:         var _p0 *byte
    94:         _p0, err = BytePtrFromString(path)
    95:         if err != nil {
    96:                 return
    97:         }
    98:         r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mode), 0, 0)
(dlv) n
> syscall.openat() /usr/local/go/src/syscall/zsyscall_linux_amd64.go:94 (PC: 0x4a119f)
    89:
    90: // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
    91:
    92: func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
    93:         var _p0 *byte
=>  94:         _p0, err = BytePtrFromString(path)
    95:         if err != nil {
    96:                 return
    97:         }
    98:         r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mode), 0, 0)
    99:         fd = int(r0)
(dlv) n
> syscall.openat() /usr/local/go/src/syscall/zsyscall_linux_amd64.go:95 (PC: 0x4a1241)
    90: // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
    91:
    92: func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
    93:         var _p0 *byte
    94:         _p0, err = BytePtrFromString(path)
=>  95:         if err != nil {
    96:                 return
    97:         }
    98:         r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mode), 0, 0)
    99:         fd = int(r0)
   100:         if e1 != 0 {
(dlv) n
> syscall.openat() /usr/local/go/src/syscall/zsyscall_linux_amd64.go:98 (PC: 0x4a1256)
    93:         var _p0 *byte
    94:         _p0, err = BytePtrFromString(path)
    95:         if err != nil {
    96:                 return
    97:         }
=>  98:         r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mode), 0, 0)
    99:         fd = int(r0)
   100:         if e1 != 0 {
   101:                 err = errnoErr(e1)
   102:         }
   103:         return
(dlv) s
> syscall.Syscall6() /usr/local/go/src/syscall/syscall_linux.go:94 (PC: 0x4a2604)
    89: }
    90:
    91: //go:uintptrkeepalive
    92: //go:nosplit
    93: //go:linkname Syscall6
=>  94: func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
    95:         runtime_entersyscall()
    96:         r1, r2, err = RawSyscall6(trap, a1, a2, a3, a4, a5, a6)
    97:         runtime_exitsyscall()
    98:         return
    99: }
    (dlv) n
> syscall.Syscall6() /usr/local/go/src/syscall/syscall_linux.go:96 (PC: 0x4a2665)
    91: //go:uintptrkeepalive
    92: //go:nosplit
    93: //go:linkname Syscall6
    94: func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
    95:         runtime_entersyscall()
=>  96:         r1, r2, err = RawSyscall6(trap, a1, a2, a3, a4, a5, a6)
    97:         runtime_exitsyscall()
    98:         return
    99: }
   100:
   101: func rawSyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr)
(dlv) s
> syscall.RawSyscall6() /usr/local/go/src/syscall/syscall_linux.go:62 (PC: 0x4a2444)
    57:
    58: //go:uintptrkeepalive
    59: //go:nosplit
    60: //go:norace
    61: //go:linkname RawSyscall6
=>  62: func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
    63:         var errno uintptr
    64:         r1, r2, errno = runtimesyscall.Syscall6(trap, a1, a2, a3, a4, a5, a6)
    65:         err = Errno(errno)
    66:         return
    67: }
(dlv) n
> syscall.RawSyscall6() /usr/local/go/src/syscall/syscall_linux.go:63 (PC: 0x4a249e)
    58: //go:uintptrkeepalive
    59: //go:nosplit
    60: //go:norace
    61: //go:linkname RawSyscall6
    62: func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
=>  63:         var errno uintptr
    64:         r1, r2, errno = runtimesyscall.Syscall6(trap, a1, a2, a3, a4, a5, a6)
    65:         err = Errno(errno)
    66:         return
    67: }
    68:
(dlv) n
> syscall.RawSyscall6() /usr/local/go/src/syscall/syscall_linux.go:64 (PC: 0x4a24a7)
    59: //go:nosplit
    60: //go:norace
    61: //go:linkname RawSyscall6
    62: func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
    63:         var errno uintptr
=>  64:         r1, r2, errno = runtimesyscall.Syscall6(trap, a1, a2, a3, a4, a5, a6)
    65:         err = Errno(errno)
    66:         return
    67: }
    68:
    69: //go:uintptrkeepalive
(dlv) s
> internal/runtime/syscall.Syscall6() /usr/local/go/src/internal/runtime/syscall/asm_linux_amd64.s:30 (PC: 0x478e80)
Warning: debugging optimized function
    25: // Note that this differs from "standard" ABI convention, which would pass 4th
    26: // arg in CX, not R10.
    27: TEXT ·Syscall6<ABIInternal>(SB),NOSPLIT,$0
    28:         // a6 already in R9.
    29:         // a5 already in R8.
=>  30:         MOVQ    SI, R10 // a4
    31:         MOVQ    DI, DX  // a3
    32:         MOVQ    CX, SI  // a2
    33:         MOVQ    BX, DI  // a1
    34:         // num already in AX.
    35:         SYSCALL

可以看到,通过b、c、n、s、so这几个指令轻松追踪到了os.Open的go源码在Linux系统下的调用链路:

os.Open->OpenFile->openFileNolog->os.open->syscall.Open->syscall.openat->syscall.Syscall6->syscall.RawSyscall6->runtimesyscall.Syscall6[汇编]

有兴趣可以多深入了解下syscall.Syscall6函数:

func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
         runtime_entersyscall() //进入系统调用,这里会将M和P进行解绑,这样P上的其他G可以被其他空闲的M执行,不至于其他G也等着
         r1, r2, err = RawSyscall6(trap, a1, a2, a3, a4, a5, a6)//进行系统调用,此时M会一直等待系统返回,无法运行其他协程,被当前协程任务独占了
         runtime_exitsyscall() //结束系统调用,M可以干其他任务了
         return
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值