github.com/undoio/delve@v1.9.0/pkg/proc/native/threads_windows.go (about) 1 package native 2 3 import ( 4 "errors" 5 "syscall" 6 7 sys "golang.org/x/sys/windows" 8 9 "github.com/undoio/delve/pkg/proc" 10 "github.com/undoio/delve/pkg/proc/amd64util" 11 "github.com/undoio/delve/pkg/proc/winutil" 12 ) 13 14 const enableHardwareBreakpoints = false // see https://github.com/go-delve/delve/issues/2768 15 16 // waitStatus is a synonym for the platform-specific WaitStatus 17 type waitStatus sys.WaitStatus 18 19 // osSpecificDetails holds information specific to the Windows 20 // operating system / kernel. 21 type osSpecificDetails struct { 22 hThread syscall.Handle 23 dbgUiRemoteBreakIn bool // whether thread is an auxiliary DbgUiRemoteBreakIn thread created by Windows 24 delayErr error 25 setbp bool 26 } 27 28 func (t *nativeThread) singleStep() error { 29 context := winutil.NewCONTEXT() 30 context.ContextFlags = _CONTEXT_ALL 31 32 // Set the processor TRAP flag 33 err := _GetThreadContext(t.os.hThread, context) 34 if err != nil { 35 return err 36 } 37 38 context.EFlags |= 0x100 39 40 err = _SetThreadContext(t.os.hThread, context) 41 if err != nil { 42 return err 43 } 44 45 suspendcnt := 0 46 47 // If a thread simultaneously hits a breakpoint and is suspended by the Go 48 // runtime it will have a suspend count greater than 1 and to actually take 49 // a single step we have to resume it multiple times here. 50 // We keep a counter of how many times it was suspended so that after 51 // single-stepping we can re-suspend it the corrent number of times. 52 for { 53 n, err := _ResumeThread(t.os.hThread) 54 if err != nil { 55 return err 56 } 57 suspendcnt++ 58 if n == 1 { 59 break 60 } 61 } 62 63 for { 64 var tid, exitCode int 65 t.dbp.execPtraceFunc(func() { 66 tid, exitCode, err = t.dbp.waitForDebugEvent(waitBlocking | waitSuspendNewThreads) 67 }) 68 if err != nil { 69 return err 70 } 71 if tid == 0 { 72 t.dbp.postExit() 73 return proc.ErrProcessExited{Pid: t.dbp.pid, Status: exitCode} 74 } 75 76 if t.dbp.os.breakThread == t.ID { 77 break 78 } 79 80 t.dbp.execPtraceFunc(func() { 81 err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.dbp.os.breakThread), _DBG_CONTINUE) 82 }) 83 } 84 85 for i := 0; i < suspendcnt; i++ { 86 if !t.os.dbgUiRemoteBreakIn { 87 _, err = _SuspendThread(t.os.hThread) 88 if err != nil { 89 return err 90 } 91 } 92 } 93 94 t.dbp.execPtraceFunc(func() { 95 err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE) 96 }) 97 if err != nil { 98 return err 99 } 100 101 // Unset the processor TRAP flag 102 err = _GetThreadContext(t.os.hThread, context) 103 if err != nil { 104 return err 105 } 106 107 context.EFlags &= ^uint32(0x100) 108 109 return _SetThreadContext(t.os.hThread, context) 110 } 111 112 func (t *nativeThread) resume() error { 113 var err error 114 t.dbp.execPtraceFunc(func() { 115 //TODO: Note that we are ignoring the thread we were asked to continue and are continuing the 116 //thread that we last broke on. 117 err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE) 118 }) 119 return err 120 } 121 122 // Stopped returns whether the thread is stopped at the operating system 123 // level. On windows this always returns true. 124 func (t *nativeThread) Stopped() bool { 125 return true 126 } 127 128 func (t *nativeThread) WriteMemory(addr uint64, data []byte) (int, error) { 129 if t.dbp.exited { 130 return 0, proc.ErrProcessExited{Pid: t.dbp.pid} 131 } 132 if len(data) == 0 { 133 return 0, nil 134 } 135 var count uintptr 136 err := _WriteProcessMemory(t.dbp.os.hProcess, uintptr(addr), &data[0], uintptr(len(data)), &count) 137 if err != nil { 138 return 0, err 139 } 140 return int(count), nil 141 } 142 143 var ErrShortRead = errors.New("short read") 144 145 func (t *nativeThread) ReadMemory(buf []byte, addr uint64) (int, error) { 146 if t.dbp.exited { 147 return 0, proc.ErrProcessExited{Pid: t.dbp.pid} 148 } 149 if len(buf) == 0 { 150 return 0, nil 151 } 152 var count uintptr 153 err := _ReadProcessMemory(t.dbp.os.hProcess, uintptr(addr), &buf[0], uintptr(len(buf)), &count) 154 if err == nil && count != uintptr(len(buf)) { 155 err = ErrShortRead 156 } 157 return int(count), err 158 } 159 160 func (t *nativeThread) restoreRegisters(savedRegs proc.Registers) error { 161 return _SetThreadContext(t.os.hThread, savedRegs.(*winutil.AMD64Registers).Context) 162 } 163 164 func (t *nativeThread) withDebugRegisters(f func(*amd64util.DebugRegisters) error) error { 165 if !enableHardwareBreakpoints { 166 return errors.New("hardware breakpoints not supported") 167 } 168 169 context := winutil.NewCONTEXT() 170 context.ContextFlags = _CONTEXT_DEBUG_REGISTERS 171 172 err := _GetThreadContext(t.os.hThread, context) 173 if err != nil { 174 return err 175 } 176 177 drs := amd64util.NewDebugRegisters(&context.Dr0, &context.Dr1, &context.Dr2, &context.Dr3, &context.Dr6, &context.Dr7) 178 179 err = f(drs) 180 if err != nil { 181 return err 182 } 183 184 if drs.Dirty { 185 return _SetThreadContext(t.os.hThread, context) 186 } 187 188 return nil 189 } 190 191 // SoftExc returns true if this thread received a software exception during the last resume. 192 func (t *nativeThread) SoftExc() bool { 193 return t.os.setbp 194 }