Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(2578)

Delta Between Two Patch Sets: demo/ptrace-linux-amd64/main.go

Issue 85300043: code review 85300043: ogle/demo/ptrace-linux-amd64: new programs. (Closed)
Left Patch Set: Created 9 years, 11 months ago
Right Patch Set: diff -r 83f9669082bb https://code.google.com/p/ogle Created 9 years, 11 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | demo/ptrace-linux-amd64/tracee/main.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
(no file at all)
1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // This program is a standalone demo that demonstrates a Go tracer program
6 // (this program) forking, executing and inserting breakpoints into a (multi-
7 // threaded) Go tracee program. It logs all syscall.Wait4 results, and reading
8 // those logs, as well as ../../doc/ptrace-nptl.txt, should help understand how
9 // the (more complicated) code.google.com/p/ogle/program/server package works.
10 //
11 // Only tested on linux/amd64.
12 package main
13
14 // TODO: other threads are not stopped when one tracee thread hits a breakpoint.
15 // Thus, other threads could racily miss a breakpoint when they concurrently
16 // execute code where the trap should be, in between lifting and re-setting the
17 // trap.
18 //
19 // One option is to simply accept this behavior as is. Another option, "all-
20 // stop", is to stop all other threads when one traps. A third option, "non-
21 // stop", is to not lift the trap and not stop other threads, but to simulate
22 // the original instruction when single-stepping: the "Non-stop Multi-Threaded
23 // Debugging in GDB" paper calls this "displaced stepping". "Non-stop" is less
24 // intrusive than "all-stop" (e.g. network I/O can still happen while at a
25 // breakpoint), but it involves complicated, architecture-specific code.
26
27 import (
28 "fmt"
29 "log"
30 "os"
31 "runtime"
32 "syscall"
33 "time"
34
35 "code.google.com/p/ogle/debug/dwarf"
36 "code.google.com/p/ogle/debug/elf"
37 "code.google.com/p/ogle/debug/macho"
38 )
39
40 const (
41 exeFilename = "tracee/tracee"
42
43 // TODO: don't be amd64-specific.
44 breakpointInstr = 0xcc
45 breakpointInstrLen = 1
46 )
47
48 type breakpoint struct {
49 pc uint64
50 origInstr byte // TODO: don't be amd64-specific.
51 }
52
53 func main() {
54 go run()
55 time.Sleep(2 * time.Second)
56 }
57
58 func run() {
59 // If the debugger itself is multi-threaded, ptrace calls must come from
60 // the same thread that originally attached to the remote thread.
61 runtime.LockOSThread()
62
63 f, err := os.Open(exeFilename)
64 if err != nil {
65 log.Printf(`%q not found. Did you run "go build ." in that direc tory?`, exeFilename)
66 log.Fatalf("Open: %v", err)
67 }
68 defer f.Close()
69 dwarfData, err := loadDwarfData(f)
70 if err != nil {
71 log.Fatalf("loadDwarfData: %v", err)
72 }
73
74 proc, err := os.StartProcess(exeFilename, []string{exeFilename}, &os.Pro cAttr{
75 Files: []*os.File{
76 os.Stdin,
77 os.Stdout,
78 os.Stderr,
79 },
80 Sys: &syscall.SysProcAttr{
81 Ptrace: true,
82 Pdeathsig: syscall.SIGKILL,
83 },
84 })
85 if err != nil {
86 log.Fatalf("StartProcess: %v", err)
87 }
88
89 fmt.Printf("\tproc.Pid=%d\n", proc.Pid)
90
91 _, status, err := wait(proc.Pid)
92 if err != nil {
93 log.Fatalf("wait: %v", err)
94 }
95 if status != 0x00057f { // 0x05=SIGTRAP, 0x7f=stopped.
96 log.Fatalf("status: got %#x, want %#x", status, 0x57f)
97 }
98 err = syscall.PtraceSetOptions(proc.Pid, syscall.PTRACE_O_TRACECLONE|sys call.PTRACE_O_TRACEEXIT)
99 if err != nil {
100 log.Fatalf("PtraceSetOptions: %v", err)
101 }
102
103 addr, err := lookupSym(dwarfData, "fmt.Printf")
104 if err != nil {
105 log.Fatalf("lookupSym: %v", err)
106 }
107 fmt.Printf("\tfmt.Printf=%#x\n", addr)
108
109 var buf [1]byte
110 if err := peek(proc.Pid, addr, buf[:1]); err != nil {
111 log.Fatalf("peek: %v", err)
112 }
113 breakpoints := map[uint64]breakpoint{
114 addr: {pc: addr, origInstr: buf[0]},
115 }
116 buf[0] = breakpointInstr
117 if err := poke(proc.Pid, addr, buf[:1]); err != nil {
118 log.Fatalf("poke: %v", err)
119 }
120
121 err = syscall.PtraceCont(proc.Pid, 0)
122 if err != nil {
123 log.Fatalf("PtraceCont: %v", err)
124 }
125
126 for {
127 pid, status, err := wait(-1)
128 if err != nil {
129 log.Fatalf("wait: %v", err)
130 }
131
132 switch status {
133 case 0x00057f: // 0x05=SIGTRAP, 0x7f=stopped.
134 regs := syscall.PtraceRegs{}
135 if err := syscall.PtraceGetRegs(pid, &regs); err != nil {
136 log.Fatalf("PtraceGetRegs: %v", err)
137 }
138 regs.Rip -= breakpointInstrLen
139 if err := syscall.PtraceSetRegs(pid, &regs); err != nil {
140 log.Fatalf("PtraceSetRegs: %v", err)
141 }
142 bp, ok := breakpoints[regs.Rip]
143 if !ok {
144 log.Fatalf("no breakpoint for address %#x\n", re gs.Rip)
145 }
146 buf[0] = bp.origInstr
147 if err := poke(pid, addr, buf[:1]); err != nil {
148 log.Fatalf("poke: %v", err)
149 }
150 fmt.Printf("\thit breakpoint at %#x, pid=%5d\n", regs.Ri p, pid)
151 if err := syscall.PtraceSingleStep(pid); err != nil {
152 log.Fatalf("PtraceSingleStep: %v", err)
153 }
154 _, status, err := wait(pid)
155 if err != nil {
156 log.Fatalf("wait: %v", err)
157 }
158 if status != 0x00057f {
159 log.Fatalf("PtraceSingleStep: unexpected status %#x\n", status)
160 }
161 buf[0] = breakpointInstr
162 if err := poke(pid, addr, buf[:1]); err != nil {
163 log.Fatalf("poke: %v", err)
164 }
165
166 case 0x00137f: // 0x13=SIGSTOP, 0x7f=stopped.
167 // No-op.
168
169 case 0x03057f: // 0x05=SIGTRAP, 0x7f=stopped, 0x03=PTRACE_EVENT_ CLONE.
170 msg, err := syscall.PtraceGetEventMsg(pid)
171 if err != nil {
172 log.Fatalf("PtraceGetEventMsg: %v", err)
173 }
174 fmt.Printf("\tclone: new pid=%d\n", msg)
175
176 default:
177 log.Fatalf("unexpected status %#x\n", status)
178 }
179
180 err = syscall.PtraceCont(pid, 0)
181 if err != nil {
182 log.Fatalf("PtraceCont: %v", err)
183 }
184 }
185 }
186
187 func wait(pid int) (wpid int, status syscall.WaitStatus, err error) {
188 wpid, err = syscall.Wait4(pid, &status, syscall.WALL, nil)
189 if err != nil {
190 return 0, 0, err
191 }
192 fmt.Printf("\t\twait: wpid=%5d, status=0x%06x\n", wpid, status)
193 return wpid, status, nil
194 }
195
196 func peek(pid int, addr uint64, data []byte) error {
197 n, err := syscall.PtracePeekText(pid, uintptr(addr), data)
198 if err != nil {
199 return err
200 }
201 if n != len(data) {
202 return fmt.Errorf("peek: got %d bytes, want %d", len(data))
203 }
204 return nil
205 }
206
207 func poke(pid int, addr uint64, data []byte) error {
208 n, err := syscall.PtracePokeText(pid, uintptr(addr), data)
209 if err != nil {
210 return err
211 }
212 if n != len(data) {
213 return fmt.Errorf("poke: got %d bytes, want %d", len(data))
214 }
215 return nil
216 }
217
218 func lookupSym(dwarfData *dwarf.Data, name string) (uint64, error) {
219 r := dwarfData.Reader()
220 for {
221 entry, err := r.Next()
222 if err != nil {
223 return 0, err
224 }
225 if entry == nil {
226 // TODO: why don't we get an error here.
227 break
228 }
229 if entry.Tag != dwarf.TagSubprogram {
230 continue
231 }
232 nameAttr := lookupAttr(entry, dwarf.AttrName)
233 if nameAttr == nil {
234 // TODO: this shouldn't be possible.
235 continue
236 }
237 if nameAttr.(string) != name {
238 continue
239 }
240 addrAttr := lookupAttr(entry, dwarf.AttrLowpc)
241 if addrAttr == nil {
242 return 0, fmt.Errorf("symbol %q has no LowPC attribute", name)
243 }
244 addr, ok := addrAttr.(uint64)
245 if !ok {
246 return 0, fmt.Errorf("symbol %q has non-uint64 LowPC att ribute", name)
247 }
248 return addr, nil
249 }
250 return 0, fmt.Errorf("symbol %q not found", name)
251 }
252
253 func lookupAttr(e *dwarf.Entry, a dwarf.Attr) interface{} {
254 for _, f := range e.Field {
255 if f.Attr == a {
256 return f.Val
257 }
258 }
259 return nil
260 }
261
262 func loadDwarfData(f *os.File) (*dwarf.Data, error) {
263 if obj, err := elf.NewFile(f); err == nil {
264 return obj.DWARF()
265 }
266 if obj, err := macho.NewFile(f); err == nil {
267 return obj.DWARF()
268 }
269 return nil, fmt.Errorf("unrecognized binary format")
270 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b