This is a creation in Article, where the information may have evolved or changed. In [the first part] (https://studygolang.com/articles/12553), we first introduced the development environment and implemented a simple debugger (tracer) that would allow the child process (Tracee) to stop at the very beginning, It then resumes execution and displays its standard output. Now is the time to extend this program. In general, the debugger allows stepping through the code being debugged, which can be achieved through the ptrace_singlestep command of [Ptrace] (http://man7.org/linux/man-pages/man2/ptrace.2.html). It tells Tracee to stop running after executing an instruction. "' Gopackage mainimport (" Flag "" Log "" OS "" Os/exec "" Syscall ") func main () {flag. Parse () Input: = flag. ARG (0) cmd: = Exec.command (input) cmd. Args = []string{input}cmd. Stdout = OS. Stdoutcmd.stderr = OS. Stderrcmd.sysprocattr = &syscall. Sysprocattr{ptrace:true}err: = cmd. Start () if err! = Nil {log. Fatal (err)}err = cmd. Wait () log. Printf ("state:%v\n", err) wpid: = cmd. Process.pidpgid, err: = Syscall. Getpgid (cmd. PROCESS.PID) If err! = Nil {log. Panic (err)}err = Syscall. Ptracesetoptions (cmd. Process.pid, Syscall. Ptrace_o_traceclone) If err! = Nil {log. Fatal (err)}err = Syscall. Ptracesinglestep (WPID) if err! = Nil {log. Fatal (err)}steps: = 1for {var ws syscall. Waitstatuswpid, err = Syscall. WAIT4 ( -1*pgid, &wS, Syscall. WALL, nil) if wpid = =-1 {log. Fatal (err)}if wpid = = cmd. Process.pid && ws. Exited () {break}if!ws. Exited () {err: = Syscall. Ptracesinglestep (WPID) if err! = Nil {log. Fatal (err)}steps + = 1}}log. Printf ("Steps:%d\n", Steps)} ' builds and runs this code, and the output should look like this (the number of steps shown in each call may be different) ' ' > Go install-gcflags= '-n-l ' github.com/ mlowicki/hello> Go install github.com/mlowicki/debugger> debugger/go/bin/hello2017/06/09 19:54:42 state:stop Signal:trace/breakpoint Traphello world2017/06/09 19:54:49 steps:297583 "The first half of the program is the same as in the previous article, where the new addition is to [Syscall. Ptracesinglestep] (https://golang.org/pkg/syscall/#PtraceSingleStep) call, which causes the program to be debugged (here is Hello) to stop after executing an instruction. The Ptrace_o_traceclone option is also set > Ptrace_o_traceclone (since Linux 2.5.46) > Stop the Tracee at the next clone (2) and auto matically start tracing the newly cloned process ... (http://man7.org/linux/man-pages/man2/ptrace.2.html) Because our debugger knows what time the new thread starts and can skip it, So the last steps shown are the number of instructions executed by the total number of instructions executed by all processes, but it contains some other initialization code in the Go runtime (people with C language experience should understand [libc] (https:(www.gnu.org/software/libc/) initialization process). We can write a very simple program to verify that our debugger is working properly. Let's create a assembly file Src/github.com/mlowicki/hello/hello.asm: "Asmsection. Datamsg db" Hello, world! ", 0xAlen equ $-msgsection . Textglobal _start_start:mov Rax, 1; Write Syscall (https://linux.die.net/man/2/write) mov rdi, 1; Stdoutmov RSI, Msgmov RDX, Len; Passing parameters to ' syscall ' instruction described in; Https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscallsyscallmov Rax, 60; Exit Syscall (https://linux.die.net/man/2/exit) mov rdi, 0; Exit Codesyscall "in the container to build our" Hello World "program, see how many instructions executed" shell> pwd/go> apt-get install nasm> nasm-f elf64-o hello.o src/github.com/mlowicki/hello/hello.asm && ld-o Hello hello.o>./hellohello, world!> debugger./h ELLO2017/06/17 17:58:43 state:stop signal:trace/breakpoint Traphello, world!2017/06/17 17:58:43 steps:8 ' ' Output is good, just equal to Number of instructions in hello.asm so far, we already know how to get the program to stop at the beginning, how to execute the code step after time, and look at the status of the process/thread, now is where it's needed to set breakpoints and monitor the status of processes like variable values. Let's start with aAs a simple example, Hello.go has a main function "Gopackage mainimport" FMT "Func Main () {FMT. Println ("Hello World")} "how do I set breakpoints at the beginning of this function?" After our program has been compiled and linked, the result is a series of machine instructions. How do we set a breakpoint in a source file that contains only a few binary code (a format that only the CPU can understand)? # # # Linetablegolang built-in features that allow access to debug information in compiled binary binaries. The structure of the mapping relationship between maintenance instruction counter ([PC] (Https://en.wikipedia.org/wiki/Program_counter)) and program code line is called [row table] (https://golang.org/pkg/debug/ gosym/#LineTable), let's look at an example of "Gopackage Mainimport (" debug/elf "" Debug/gosym "" Flag "" Log ") func main () {flag. Parse () Path: = Flag. ARG (0) exe, err: = Elf. Open (PATH) if err! = Nil {log. Fatal (ERR)}var Pclndat []byteif sec: = exe. Section (". Gopclntab"); Sec! = Nil {Pclndat, err = sec. Data () if err! = Nil {log. Fatalf ("Cannot read. Gopclntab section:%v", err)}}sec: = exe. Section (". Gosymtab") Symtabraw, Err: = Sec. Data () Pcln: = Gosym. Newlinetable (Pclndat, exe. Section (". Text"). ADDR) Symtab, err: = Gosym. NewTable (Symtabraw, PCLN) if err! = Nil {log. Fatal ("Cannot create symbol table:%v", err)}sym: = Symtab.lookupfunc ("main.main") filename, Lineno, _: = Symtab.pctoline (sym. Entry) log. Printf ("FileName:%v\n", filename) log. Printf ("Lineno:%v\n", Lineno)} "if the file passed to the above program contains the following code ' gopackage mainimport" FMT "Func Main () {FMT. Println ("Hello World")} "" Then the output should be such "shell> go install github.com/mlowicki/linetable> go install-gcflags="-N -L "github.com/mlowicki/hello> linetable/go/bin/hello2017/06/30 18:47:38 filename:/go/src/github.com/mlowicki/ Hello/hello.go2017/06/30 18:47:38 Lineno:5 "ELF is [executable and linkable Format] (https://en.wikipedia.org/wiki/ Executable_and_linkable_format) is an abbreviation for an executable file in the format ' shell> apt-get install file> File/go/bin/hello/go/bin/hello : Elf 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped ' elf contains many segments, we used three of them:. Text,. GOPCL Ntab and. Gosymtab. The first one contains the machine instructions, the second one implements the mapping of the instruction counter to the source line, and the last is a [symbol table] (https://en.wikipedia.org/wiki/Symbol_table) in the next article, we will learn how to use the " The Line table "sets breakpoints and how to monitor program status where needed."
Via:https://medium.com/golangspec/making-debugger-in-golang-part-ii-d2b8eb2f19e0
Author: Michałłowicki Translator: Jettyhan proofreading: polaris1119
This article by GCTT original compilation, go language Chinese network honor launches
This article was originally translated by GCTT and the Go Language Chinese network. Also want to join the ranks of translators, for open source to do some of their own contribution? Welcome to join Gctt!
Translation work and translations are published only for the purpose of learning and communication, translation work in accordance with the provisions of the CC-BY-NC-SA agreement, if our work has violated your interests, please contact us promptly.
Welcome to the CC-BY-NC-SA agreement, please mark and keep the original/translation link and author/translator information in the text.
The article only represents the author's knowledge and views, if there are different points of view, please line up downstairs to spit groove
316 reads ∙1 likes