github.com/undoio/delve@v1.9.0/pkg/proc/native/threads.go (about) 1 package native 2 3 import ( 4 "fmt" 5 6 "github.com/undoio/delve/pkg/proc" 7 ) 8 9 // Thread represents a single thread in the traced process 10 // ID represents the thread id or port, Process holds a reference to the 11 // Process struct that contains info on the process as 12 // a whole, and Status represents the last result of a `wait` call 13 // on this thread. 14 type nativeThread struct { 15 ID int // Thread ID or mach port 16 Status *waitStatus // Status returned from last wait call 17 CurrentBreakpoint proc.BreakpointState // Breakpoint thread is currently stopped at 18 19 dbp *nativeProcess 20 singleStepping bool 21 os *osSpecificDetails 22 common proc.CommonThread 23 } 24 25 // Continue the execution of this thread. 26 // 27 // If we are currently at a breakpoint, we'll clear it 28 // first and then resume execution. Thread will continue until 29 // it hits a breakpoint or is signaled. 30 func (t *nativeThread) Continue() error { 31 pc, err := t.PC() 32 if err != nil { 33 return err 34 } 35 // Check whether we are stopped at a breakpoint, and 36 // if so, single step over it before continuing. 37 if _, ok := t.dbp.FindBreakpoint(pc, false); ok { 38 if err := t.StepInstruction(); err != nil { 39 return err 40 } 41 } 42 return t.resume() 43 } 44 45 // StepInstruction steps a single instruction. 46 // 47 // Executes exactly one instruction and then returns. 48 // If the thread is at a breakpoint, we first clear it, 49 // execute the instruction, and then replace the breakpoint. 50 // Otherwise we simply execute the next instruction. 51 func (t *nativeThread) StepInstruction() (err error) { 52 t.singleStepping = true 53 defer func() { 54 t.singleStepping = false 55 }() 56 57 if bp := t.CurrentBreakpoint.Breakpoint; bp != nil && bp.WatchType != 0 && t.dbp.Breakpoints().M[bp.Addr] == bp { 58 err = t.clearHardwareBreakpoint(bp.Addr, bp.WatchType, bp.HWBreakIndex) 59 if err != nil { 60 return err 61 } 62 defer func() { 63 err = t.writeHardwareBreakpoint(bp.Addr, bp.WatchType, bp.HWBreakIndex) 64 }() 65 } 66 67 pc, err := t.PC() 68 if err != nil { 69 return err 70 } 71 72 bp, ok := t.dbp.FindBreakpoint(pc, false) 73 if ok { 74 // Clear the breakpoint so that we can continue execution. 75 err = t.clearSoftwareBreakpoint(bp) 76 if err != nil { 77 return err 78 } 79 80 // Restore breakpoint now that we have passed it. 81 defer func() { 82 err = t.dbp.writeSoftwareBreakpoint(t, bp.Addr) 83 }() 84 } 85 86 err = t.singleStep() 87 if err != nil { 88 if _, exited := err.(proc.ErrProcessExited); exited { 89 return err 90 } 91 return fmt.Errorf("step failed: %s", err.Error()) 92 } 93 return nil 94 } 95 96 // Location returns the threads location, including the file:line 97 // of the corresponding source code, the function we're in 98 // and the current instruction address. 99 func (t *nativeThread) Location() (*proc.Location, error) { 100 pc, err := t.PC() 101 if err != nil { 102 return nil, err 103 } 104 f, l, fn := t.dbp.bi.PCToLine(pc) 105 return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil 106 } 107 108 // BinInfo returns information on the binary. 109 func (t *nativeThread) BinInfo() *proc.BinaryInfo { 110 return t.dbp.bi 111 } 112 113 // Common returns information common across Process 114 // implementations. 115 func (t *nativeThread) Common() *proc.CommonThread { 116 return &t.common 117 } 118 119 // SetCurrentBreakpoint sets the current breakpoint that this 120 // thread is stopped at as CurrentBreakpoint on the thread struct. 121 func (t *nativeThread) SetCurrentBreakpoint(adjustPC bool) error { 122 t.CurrentBreakpoint.Clear() 123 124 var bp *proc.Breakpoint 125 126 if t.dbp.Breakpoints().HasHWBreakpoints() { 127 var err error 128 bp, err = t.findHardwareBreakpoint() 129 if err != nil { 130 return err 131 } 132 } 133 if bp == nil { 134 pc, err := t.PC() 135 if err != nil { 136 return err 137 } 138 139 // If the breakpoint instruction does not change the value 140 // of PC after being executed we should look for breakpoints 141 // with bp.Addr == PC and there is no need to call SetPC 142 // after finding one. 143 adjustPC = adjustPC && t.BinInfo().Arch.BreakInstrMovesPC() 144 145 var ok bool 146 bp, ok = t.dbp.FindBreakpoint(pc, adjustPC) 147 if ok { 148 if adjustPC { 149 if err = t.setPC(bp.Addr); err != nil { 150 return err 151 } 152 } 153 } 154 } 155 156 t.CurrentBreakpoint.Breakpoint = bp 157 return nil 158 } 159 160 // Breakpoint returns the current breakpoint that is active 161 // on this thread. 162 func (t *nativeThread) Breakpoint() *proc.BreakpointState { 163 return &t.CurrentBreakpoint 164 } 165 166 // ThreadID returns the ID of this thread. 167 func (t *nativeThread) ThreadID() int { 168 return t.ID 169 } 170 171 // clearSoftwareBreakpoint clears the specified breakpoint. 172 func (t *nativeThread) clearSoftwareBreakpoint(bp *proc.Breakpoint) error { 173 if _, err := t.WriteMemory(bp.Addr, bp.OriginalData); err != nil { 174 return fmt.Errorf("could not clear breakpoint %s", err) 175 } 176 return nil 177 } 178 179 // Registers obtains register values from the debugged process. 180 func (t *nativeThread) Registers() (proc.Registers, error) { 181 return registers(t) 182 } 183 184 // RestoreRegisters will set the value of the CPU registers to those 185 // passed in via 'savedRegs'. 186 func (t *nativeThread) RestoreRegisters(savedRegs proc.Registers) error { 187 return t.restoreRegisters(savedRegs) 188 } 189 190 // PC returns the current program counter value for this thread. 191 func (t *nativeThread) PC() (uint64, error) { 192 regs, err := t.Registers() 193 if err != nil { 194 return 0, err 195 } 196 return regs.PC(), nil 197 } 198 199 // ProcessMemory returns this thread's process memory. 200 func (t *nativeThread) ProcessMemory() proc.MemoryReadWriter { 201 return t.dbp.Memory() 202 }