Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
pubx
delve
提交
64e01bfe
D
delve
项目概览
pubx
/
delve
通知
0
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
delve
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
64e01bfe
编写于
12月 08, 2014
作者:
D
Derek Parker
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Begin thread code isolation
上级
d41bbbf5
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
321 addition
and
311 deletion
+321
-311
proctl/proctl_test.go
proctl/proctl_test.go
+1
-2
proctl/threads.go
proctl/threads.go
+315
-0
proctl/threads_linux_amd64.go
proctl/threads_linux_amd64.go
+5
-309
未找到文件。
proctl/proctl_test.go
浏览文件 @
64e01bfe
...
...
@@ -3,7 +3,6 @@ package proctl_test
import
(
"bytes"
"path/filepath"
"syscall"
"testing"
"github.com/derekparker/delve/helper"
...
...
@@ -12,7 +11,7 @@ import (
func
dataAtAddr
(
pid
int
,
addr
uint64
)
([]
byte
,
error
)
{
data
:=
make
([]
byte
,
1
)
_
,
err
:=
syscall
.
PtracePeekData
(
pid
,
uintptr
(
addr
),
data
)
_
,
err
:=
proctl
.
ReadMemory
(
pid
,
uintptr
(
addr
),
data
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
proctl/threads.go
0 → 100644
浏览文件 @
64e01bfe
package
proctl
import
(
"bytes"
"encoding/binary"
"fmt"
"syscall"
"github.com/derekparker/delve/dwarf/frame"
)
// ThreadContext represents a single thread of execution in the
// traced program.
type
ThreadContext
struct
{
Id
int
Process
*
DebuggedProcess
Status
*
syscall
.
WaitStatus
Regs
*
syscall
.
PtraceRegs
}
// Obtains register values from the debugged process.
func
(
thread
*
ThreadContext
)
Registers
()
(
*
syscall
.
PtraceRegs
,
error
)
{
err
:=
syscall
.
PtraceGetRegs
(
thread
.
Id
,
thread
.
Regs
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"could not get registers %s"
,
err
)
}
return
thread
.
Regs
,
nil
}
// Returns the current PC for this thread id.
func
(
thread
*
ThreadContext
)
CurrentPC
()
(
uint64
,
error
)
{
regs
,
err
:=
thread
.
Registers
()
if
err
!=
nil
{
return
0
,
err
}
return
regs
.
PC
(),
nil
}
// PrintInfo prints out the thread status
// including: PC, tid, file, line, and function.
func
(
thread
*
ThreadContext
)
PrintInfo
()
error
{
pc
,
err
:=
thread
.
CurrentPC
()
if
err
!=
nil
{
return
err
}
f
,
l
,
fn
:=
thread
.
Process
.
GoSymTable
.
PCToLine
(
pc
)
if
fn
!=
nil
{
fmt
.
Printf
(
"Thread %d at %#v %s:%d %s
\n
"
,
thread
.
Id
,
pc
,
f
,
l
,
fn
.
Name
)
}
else
{
fmt
.
Printf
(
"Thread %d at %#v
\n
"
,
thread
.
Id
,
pc
)
}
return
nil
}
// Sets a software breakpoint at addr, and stores it in the process wide
// break point table. Setting a break point must be thread specific due to
// ptrace actions needing the thread to be in a signal-delivery-stop in order
// to initiate any ptrace command. Otherwise, it really doesn't matter
// as we're only dealing with threads.
func
(
thread
*
ThreadContext
)
Break
(
addr
uintptr
)
(
*
BreakPoint
,
error
)
{
var
(
int3
=
[]
byte
{
0xCC
}
f
,
l
,
fn
=
thread
.
Process
.
GoSymTable
.
PCToLine
(
uint64
(
addr
))
originalData
=
make
([]
byte
,
1
)
)
if
fn
==
nil
{
return
nil
,
InvalidAddressError
{
address
:
addr
}
}
_
,
err
:=
ReadMemory
(
thread
.
Id
,
addr
,
originalData
)
if
err
!=
nil
{
fmt
.
Println
(
"PEEK ERR"
)
return
nil
,
err
}
if
bytes
.
Equal
(
originalData
,
int3
)
{
return
nil
,
BreakPointExistsError
{
f
,
l
,
addr
}
}
_
,
err
=
WriteMemory
(
thread
.
Id
,
addr
,
int3
)
if
err
!=
nil
{
fmt
.
Println
(
"POKE ERR"
)
return
nil
,
err
}
breakpoint
:=
&
BreakPoint
{
FunctionName
:
fn
.
Name
,
File
:
f
,
Line
:
l
,
Addr
:
uint64
(
addr
),
OriginalData
:
originalData
,
}
thread
.
Process
.
BreakPoints
[
uint64
(
addr
)]
=
breakpoint
return
breakpoint
,
nil
}
// Clears a software breakpoint, and removes it from the process level
// break point table.
func
(
thread
*
ThreadContext
)
Clear
(
pc
uint64
)
(
*
BreakPoint
,
error
)
{
bp
,
ok
:=
thread
.
Process
.
BreakPoints
[
pc
]
if
!
ok
{
return
nil
,
fmt
.
Errorf
(
"No breakpoint currently set for %#v"
,
pc
)
}
if
_
,
err
:=
WriteMemory
(
thread
.
Id
,
uintptr
(
bp
.
Addr
),
bp
.
OriginalData
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"could not clear breakpoint %s"
,
err
)
}
delete
(
thread
.
Process
.
BreakPoints
,
pc
)
return
bp
,
nil
}
func
(
thread
*
ThreadContext
)
Continue
()
error
{
// Check whether we are stopped at a breakpoint, and
// if so, single step over it before continuing.
regs
,
err
:=
thread
.
Registers
()
if
err
!=
nil
{
return
fmt
.
Errorf
(
"could not get registers %s"
,
err
)
}
if
_
,
ok
:=
thread
.
Process
.
BreakPoints
[
regs
.
PC
()
-
1
];
ok
{
err
:=
thread
.
Step
()
if
err
!=
nil
{
return
fmt
.
Errorf
(
"could not step %s"
,
err
)
}
}
return
syscall
.
PtraceCont
(
thread
.
Id
,
0
)
}
// Single steps this thread a single instruction, ensuring that
// we correctly handle the likely case that we are at a breakpoint.
func
(
thread
*
ThreadContext
)
Step
()
(
err
error
)
{
regs
,
err
:=
thread
.
Registers
()
if
err
!=
nil
{
return
err
}
bp
,
ok
:=
thread
.
Process
.
BreakPoints
[
regs
.
PC
()
-
1
]
if
ok
{
// Clear the breakpoint so that we can continue execution.
_
,
err
=
thread
.
Clear
(
bp
.
Addr
)
if
err
!=
nil
{
return
err
}
// Reset program counter to our restored instruction.
regs
.
SetPC
(
bp
.
Addr
)
err
=
syscall
.
PtraceSetRegs
(
thread
.
Id
,
regs
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"could not set registers %s"
,
err
)
}
// Restore breakpoint now that we have passed it.
defer
func
()
{
_
,
err
=
thread
.
Break
(
uintptr
(
bp
.
Addr
))
}()
}
err
=
syscall
.
PtraceSingleStep
(
thread
.
Id
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"step failed: %s"
,
err
.
Error
())
}
_
,
_
,
err
=
wait
(
thread
.
Id
,
0
)
if
err
!=
nil
{
return
err
}
return
nil
}
// Step to next source line. Next will step over functions,
// and will follow through to the return address of a function.
// Next is implemented on the thread context, however during the
// course of this function running, it's very likely that the
// goroutine our M is executing will switch to another M, therefore
// this function cannot assume all execution will happen on this thread
// in the traced process.
func
(
thread
*
ThreadContext
)
Next
()
(
err
error
)
{
pc
,
err
:=
thread
.
CurrentPC
()
if
err
!=
nil
{
return
err
}
if
_
,
ok
:=
thread
.
Process
.
BreakPoints
[
pc
-
1
];
ok
{
pc
--
// Decrement PC to account for BreakPoint
}
_
,
l
,
_
:=
thread
.
Process
.
GoSymTable
.
PCToLine
(
pc
)
fde
,
err
:=
thread
.
Process
.
FrameEntries
.
FDEForPC
(
pc
)
if
err
!=
nil
{
return
err
}
step
:=
func
()
(
uint64
,
error
)
{
err
=
thread
.
Step
()
if
err
!=
nil
{
return
0
,
err
}
return
thread
.
CurrentPC
()
}
ret
:=
thread
.
ReturnAddressFromOffset
(
fde
.
ReturnAddressOffset
(
pc
))
for
{
pc
,
err
=
step
()
if
err
!=
nil
{
return
err
}
if
!
fde
.
Cover
(
pc
)
&&
pc
!=
ret
{
err
:=
thread
.
continueToReturnAddress
(
pc
,
fde
)
if
err
!=
nil
{
if
_
,
ok
:=
err
.
(
InvalidAddressError
);
!
ok
{
return
err
}
}
pc
,
_
=
thread
.
CurrentPC
()
}
_
,
nl
,
_
:=
thread
.
Process
.
GoSymTable
.
PCToLine
(
pc
)
if
nl
!=
l
{
break
}
}
return
nil
}
func
(
thread
*
ThreadContext
)
continueToReturnAddress
(
pc
uint64
,
fde
*
frame
.
FrameDescriptionEntry
)
error
{
for
!
fde
.
Cover
(
pc
)
{
// Our offset here is be 0 because we
// have stepped into the first instruction
// of this function. Therefore the function
// has not had a chance to modify its' stack
// and change our offset.
addr
:=
thread
.
ReturnAddressFromOffset
(
0
)
bp
,
err
:=
thread
.
Break
(
uintptr
(
addr
))
if
err
!=
nil
{
if
_
,
ok
:=
err
.
(
BreakPointExistsError
);
!
ok
{
return
err
}
}
bp
.
temp
=
true
// Ensure we cleanup after ourselves no matter what.
defer
thread
.
clearTempBreakpoint
(
bp
.
Addr
)
for
{
err
=
thread
.
Continue
()
if
err
!=
nil
{
return
err
}
// We wait on -1 here because once we continue this
// thread, it's very possible the scheduler could of
// change the goroutine context on us, we there is
// no guarantee that waiting on this tid will ever
// return.
wpid
,
_
,
err
:=
trapWait
(
thread
.
Process
,
-
1
,
0
)
if
err
!=
nil
{
return
err
}
if
wpid
!=
thread
.
Id
{
thread
=
thread
.
Process
.
Threads
[
wpid
]
}
pc
,
_
=
thread
.
CurrentPC
()
if
(
pc
-
1
)
==
bp
.
Addr
{
break
}
}
}
return
nil
}
// Takes an offset from RSP and returns the address of the
// instruction the currect function is going to return to.
func
(
thread
*
ThreadContext
)
ReturnAddressFromOffset
(
offset
int64
)
uint64
{
regs
,
err
:=
thread
.
Registers
()
if
err
!=
nil
{
panic
(
"Could not obtain register values"
)
}
retaddr
:=
int64
(
regs
.
Rsp
)
+
offset
data
:=
make
([]
byte
,
8
)
syscall
.
PtracePeekText
(
thread
.
Id
,
uintptr
(
retaddr
),
data
)
return
binary
.
LittleEndian
.
Uint64
(
data
)
}
func
(
thread
*
ThreadContext
)
clearTempBreakpoint
(
pc
uint64
)
error
{
if
bp
,
ok
:=
thread
.
Process
.
BreakPoints
[
pc
];
ok
{
_
,
err
:=
thread
.
Clear
(
bp
.
Addr
)
if
err
!=
nil
{
return
err
}
// Reset program counter to our restored instruction.
regs
,
err
:=
thread
.
Registers
()
if
err
!=
nil
{
return
err
}
regs
.
SetPC
(
bp
.
Addr
)
return
syscall
.
PtraceSetRegs
(
thread
.
Id
,
regs
)
}
return
nil
}
proctl/threads_linux_amd64.go
浏览文件 @
64e01bfe
package
proctl
import
(
"bytes"
"encoding/binary"
"fmt"
import
"syscall"
"syscall"
"github.com/derekparker/delve/dwarf/frame"
)
// ThreadContext represents a single thread of execution in the
// traced program.
type
ThreadContext
struct
{
Id
int
Process
*
DebuggedProcess
Status
*
syscall
.
WaitStatus
Regs
*
syscall
.
PtraceRegs
}
// Obtains register values from the debugged process.
func
(
thread
*
ThreadContext
)
Registers
()
(
*
syscall
.
PtraceRegs
,
error
)
{
err
:=
syscall
.
PtraceGetRegs
(
thread
.
Id
,
thread
.
Regs
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"could not get registers %s"
,
err
)
}
return
thread
.
Regs
,
nil
}
// Returns the current PC for this thread id.
func
(
thread
*
ThreadContext
)
CurrentPC
()
(
uint64
,
error
)
{
regs
,
err
:=
thread
.
Registers
()
if
err
!=
nil
{
return
0
,
err
}
return
regs
.
PC
(),
nil
}
// PrintInfo prints out the thread status
// including: PC, tid, file, line, and function.
func
(
thread
*
ThreadContext
)
PrintInfo
()
error
{
pc
,
err
:=
thread
.
CurrentPC
()
if
err
!=
nil
{
return
err
}
f
,
l
,
fn
:=
thread
.
Process
.
GoSymTable
.
PCToLine
(
pc
)
if
fn
!=
nil
{
fmt
.
Printf
(
"Thread %d at %#v %s:%d %s
\n
"
,
thread
.
Id
,
pc
,
f
,
l
,
fn
.
Name
)
}
else
{
fmt
.
Printf
(
"Thread %d at %#v
\n
"
,
thread
.
Id
,
pc
)
}
return
nil
}
// Sets a software breakpoint at addr, and stores it in the process wide
// break point table. Setting a break point must be thread specific due to
// ptrace actions needing the thread to be in a signal-delivery-stop in order
// to initiate any ptrace command. Otherwise, it really doesn't matter
// as we're only dealing with threads.
func
(
thread
*
ThreadContext
)
Break
(
addr
uintptr
)
(
*
BreakPoint
,
error
)
{
var
(
int3
=
[]
byte
{
0xCC
}
f
,
l
,
fn
=
thread
.
Process
.
GoSymTable
.
PCToLine
(
uint64
(
addr
))
originalData
=
make
([]
byte
,
1
)
)
if
fn
==
nil
{
return
nil
,
InvalidAddressError
{
address
:
addr
}
}
_
,
err
:=
syscall
.
PtracePeekData
(
thread
.
Id
,
addr
,
originalData
)
if
err
!=
nil
{
fmt
.
Println
(
"PEEK ERR"
)
return
nil
,
err
}
if
bytes
.
Equal
(
originalData
,
int3
)
{
return
nil
,
BreakPointExistsError
{
f
,
l
,
addr
}
}
_
,
err
=
syscall
.
PtracePokeData
(
thread
.
Id
,
addr
,
int3
)
if
err
!=
nil
{
fmt
.
Println
(
"POKE ERR"
)
return
nil
,
err
}
breakpoint
:=
&
BreakPoint
{
FunctionName
:
fn
.
Name
,
File
:
f
,
Line
:
l
,
Addr
:
uint64
(
addr
),
OriginalData
:
originalData
,
}
thread
.
Process
.
BreakPoints
[
uint64
(
addr
)]
=
breakpoint
return
breakpoint
,
nil
}
// Clears a software breakpoint, and removes it from the process level
// break point table.
func
(
thread
*
ThreadContext
)
Clear
(
pc
uint64
)
(
*
BreakPoint
,
error
)
{
bp
,
ok
:=
thread
.
Process
.
BreakPoints
[
pc
]
if
!
ok
{
return
nil
,
fmt
.
Errorf
(
"No breakpoint currently set for %#v"
,
pc
)
}
if
_
,
err
:=
syscall
.
PtracePokeData
(
thread
.
Id
,
uintptr
(
bp
.
Addr
),
bp
.
OriginalData
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"could not clear breakpoint %s"
,
err
)
}
delete
(
thread
.
Process
.
BreakPoints
,
pc
)
return
bp
,
nil
}
func
(
thread
*
ThreadContext
)
Continue
()
error
{
// Check whether we are stopped at a breakpoint, and
// if so, single step over it before continuing.
regs
,
err
:=
thread
.
Registers
()
if
err
!=
nil
{
return
fmt
.
Errorf
(
"could not get registers %s"
,
err
)
}
if
_
,
ok
:=
thread
.
Process
.
BreakPoints
[
regs
.
PC
()
-
1
];
ok
{
err
:=
thread
.
Step
()
if
err
!=
nil
{
return
fmt
.
Errorf
(
"could not step %s"
,
err
)
}
}
return
syscall
.
PtraceCont
(
thread
.
Id
,
0
)
func
WriteMemory
(
tid
int
,
addr
uintptr
,
data
[]
byte
)
(
int
,
error
)
{
return
syscall
.
PtracePokeData
(
tid
,
addr
,
data
)
}
// Single steps this thread a single instruction, ensuring that
// we correctly handle the likely case that we are at a breakpoint.
func
(
thread
*
ThreadContext
)
Step
()
(
err
error
)
{
regs
,
err
:=
thread
.
Registers
()
if
err
!=
nil
{
return
err
}
bp
,
ok
:=
thread
.
Process
.
BreakPoints
[
regs
.
PC
()
-
1
]
if
ok
{
// Clear the breakpoint so that we can continue execution.
_
,
err
=
thread
.
Clear
(
bp
.
Addr
)
if
err
!=
nil
{
return
err
}
// Reset program counter to our restored instruction.
regs
.
SetPC
(
bp
.
Addr
)
err
=
syscall
.
PtraceSetRegs
(
thread
.
Id
,
regs
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"could not set registers %s"
,
err
)
}
// Restore breakpoint now that we have passed it.
defer
func
()
{
_
,
err
=
thread
.
Break
(
uintptr
(
bp
.
Addr
))
}()
}
err
=
syscall
.
PtraceSingleStep
(
thread
.
Id
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"step failed: %s"
,
err
.
Error
())
}
_
,
_
,
err
=
wait
(
thread
.
Id
,
0
)
if
err
!=
nil
{
return
err
}
return
nil
}
// Step to next source line. Next will step over functions,
// and will follow through to the return address of a function.
// Next is implemented on the thread context, however during the
// course of this function running, it's very likely that the
// goroutine our M is executing will switch to another M, therefore
// this function cannot assume all execution will happen on this thread
// in the traced process.
func
(
thread
*
ThreadContext
)
Next
()
(
err
error
)
{
pc
,
err
:=
thread
.
CurrentPC
()
if
err
!=
nil
{
return
err
}
if
_
,
ok
:=
thread
.
Process
.
BreakPoints
[
pc
-
1
];
ok
{
pc
--
// Decrement PC to account for BreakPoint
}
_
,
l
,
_
:=
thread
.
Process
.
GoSymTable
.
PCToLine
(
pc
)
fde
,
err
:=
thread
.
Process
.
FrameEntries
.
FDEForPC
(
pc
)
if
err
!=
nil
{
return
err
}
step
:=
func
()
(
uint64
,
error
)
{
err
=
thread
.
Step
()
if
err
!=
nil
{
return
0
,
err
}
return
thread
.
CurrentPC
()
}
ret
:=
thread
.
ReturnAddressFromOffset
(
fde
.
ReturnAddressOffset
(
pc
))
for
{
pc
,
err
=
step
()
if
err
!=
nil
{
return
err
}
if
!
fde
.
Cover
(
pc
)
&&
pc
!=
ret
{
err
:=
thread
.
continueToReturnAddress
(
pc
,
fde
)
if
err
!=
nil
{
if
_
,
ok
:=
err
.
(
InvalidAddressError
);
!
ok
{
return
err
}
}
pc
,
_
=
thread
.
CurrentPC
()
}
_
,
nl
,
_
:=
thread
.
Process
.
GoSymTable
.
PCToLine
(
pc
)
if
nl
!=
l
{
break
}
}
return
nil
}
func
(
thread
*
ThreadContext
)
continueToReturnAddress
(
pc
uint64
,
fde
*
frame
.
FrameDescriptionEntry
)
error
{
for
!
fde
.
Cover
(
pc
)
{
// Our offset here is be 0 because we
// have stepped into the first instruction
// of this function. Therefore the function
// has not had a chance to modify its' stack
// and change our offset.
addr
:=
thread
.
ReturnAddressFromOffset
(
0
)
bp
,
err
:=
thread
.
Break
(
uintptr
(
addr
))
if
err
!=
nil
{
if
_
,
ok
:=
err
.
(
BreakPointExistsError
);
!
ok
{
return
err
}
}
bp
.
temp
=
true
// Ensure we cleanup after ourselves no matter what.
defer
thread
.
clearTempBreakpoint
(
bp
.
Addr
)
for
{
err
=
thread
.
Continue
()
if
err
!=
nil
{
return
err
}
// We wait on -1 here because once we continue this
// thread, it's very possible the scheduler could of
// change the goroutine context on us, we there is
// no guarantee that waiting on this tid will ever
// return.
wpid
,
_
,
err
:=
trapWait
(
thread
.
Process
,
-
1
,
0
)
if
err
!=
nil
{
return
err
}
if
wpid
!=
thread
.
Id
{
thread
=
thread
.
Process
.
Threads
[
wpid
]
}
pc
,
_
=
thread
.
CurrentPC
()
if
(
pc
-
1
)
==
bp
.
Addr
{
break
}
}
}
return
nil
}
// Takes an offset from RSP and returns the address of the
// instruction the currect function is going to return to.
func
(
thread
*
ThreadContext
)
ReturnAddressFromOffset
(
offset
int64
)
uint64
{
regs
,
err
:=
thread
.
Registers
()
if
err
!=
nil
{
panic
(
"Could not obtain register values"
)
}
retaddr
:=
int64
(
regs
.
Rsp
)
+
offset
data
:=
make
([]
byte
,
8
)
syscall
.
PtracePeekText
(
thread
.
Id
,
uintptr
(
retaddr
),
data
)
return
binary
.
LittleEndian
.
Uint64
(
data
)
}
func
(
thread
*
ThreadContext
)
clearTempBreakpoint
(
pc
uint64
)
error
{
if
bp
,
ok
:=
thread
.
Process
.
BreakPoints
[
pc
];
ok
{
_
,
err
:=
thread
.
Clear
(
bp
.
Addr
)
if
err
!=
nil
{
return
err
}
// Reset program counter to our restored instruction.
regs
,
err
:=
thread
.
Registers
()
if
err
!=
nil
{
return
err
}
regs
.
SetPC
(
bp
.
Addr
)
return
syscall
.
PtraceSetRegs
(
thread
.
Id
,
regs
)
}
return
nil
func
ReadMemory
(
tid
int
,
addr
uintptr
,
data
[]
byte
)
(
int
,
error
)
{
return
syscall
.
PtracePeekData
(
tid
,
addr
,
data
)
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录