Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
pubx
delve
提交
102d4c89
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,发现更多精彩内容 >>
提交
102d4c89
编写于
6月 20, 2015
作者:
D
Derek Parker
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
s/DebuggedProcess/Process/
上级
d265fa76
变更
12
隐藏空白更改
内联
并排
Showing
12 changed file
with
96 addition
and
96 deletion
+96
-96
proc/breakpoints.go
proc/breakpoints.go
+2
-2
proc/breakpoints_darwin_amd64.go
proc/breakpoints_darwin_amd64.go
+2
-2
proc/breakpoints_linux_amd64.go
proc/breakpoints_linux_amd64.go
+2
-2
proc/proc.go
proc/proc.go
+43
-43
proc/proc_darwin.go
proc/proc_darwin.go
+9
-9
proc/proc_linux.go
proc/proc_linux.go
+9
-9
proc/proc_test.go
proc/proc_test.go
+17
-17
proc/stack.go
proc/stack.go
+3
-3
proc/threads.go
proc/threads.go
+3
-3
proc/variables.go
proc/variables.go
+1
-1
proc/variables_test.go
proc/variables_test.go
+3
-3
service/debugger/debugger.go
service/debugger/debugger.go
+2
-2
未找到文件。
proc/breakpoints.go
浏览文件 @
102d4c89
...
@@ -63,7 +63,7 @@ func (iae InvalidAddressError) Error() string {
...
@@ -63,7 +63,7 @@ func (iae InvalidAddressError) Error() string {
return
fmt
.
Sprintf
(
"Invalid address %#v
\n
"
,
iae
.
address
)
return
fmt
.
Sprintf
(
"Invalid address %#v
\n
"
,
iae
.
address
)
}
}
func
(
dbp
*
Debugged
Process
)
setBreakpoint
(
tid
int
,
addr
uint64
,
temp
bool
)
(
*
Breakpoint
,
error
)
{
func
(
dbp
*
Process
)
setBreakpoint
(
tid
int
,
addr
uint64
,
temp
bool
)
(
*
Breakpoint
,
error
)
{
if
bp
,
ok
:=
dbp
.
FindBreakpoint
(
addr
);
ok
{
if
bp
,
ok
:=
dbp
.
FindBreakpoint
(
addr
);
ok
{
return
nil
,
BreakpointExistsError
{
bp
.
File
,
bp
.
Line
,
bp
.
Addr
}
return
nil
,
BreakpointExistsError
{
bp
.
File
,
bp
.
Line
,
bp
.
Addr
}
}
}
...
@@ -141,7 +141,7 @@ func (nbp NoBreakpointError) Error() string {
...
@@ -141,7 +141,7 @@ func (nbp NoBreakpointError) Error() string {
return
fmt
.
Sprintf
(
"no breakpoint at %#v"
,
nbp
.
addr
)
return
fmt
.
Sprintf
(
"no breakpoint at %#v"
,
nbp
.
addr
)
}
}
func
(
dbp
*
Debugged
Process
)
clearBreakpoint
(
tid
int
,
addr
uint64
)
(
*
Breakpoint
,
error
)
{
func
(
dbp
*
Process
)
clearBreakpoint
(
tid
int
,
addr
uint64
)
(
*
Breakpoint
,
error
)
{
thread
:=
dbp
.
Threads
[
tid
]
thread
:=
dbp
.
Threads
[
tid
]
if
bp
,
ok
:=
dbp
.
Breakpoints
[
addr
];
ok
{
if
bp
,
ok
:=
dbp
.
Breakpoints
[
addr
];
ok
{
if
_
,
err
:=
bp
.
Clear
(
thread
);
err
!=
nil
{
if
_
,
err
:=
bp
.
Clear
(
thread
);
err
!=
nil
{
...
...
proc/breakpoints_darwin_amd64.go
浏览文件 @
102d4c89
...
@@ -3,11 +3,11 @@ package proc
...
@@ -3,11 +3,11 @@ package proc
import
"fmt"
import
"fmt"
// TODO(darwin)
// TODO(darwin)
func
(
dbp
*
Debugged
Process
)
setHardwareBreakpoint
(
reg
,
tid
int
,
addr
uint64
)
error
{
func
(
dbp
*
Process
)
setHardwareBreakpoint
(
reg
,
tid
int
,
addr
uint64
)
error
{
return
fmt
.
Errorf
(
"not implemented on darwin"
)
return
fmt
.
Errorf
(
"not implemented on darwin"
)
}
}
// TODO(darwin)
// TODO(darwin)
func
(
dbp
*
Debugged
Process
)
clearHardwareBreakpoint
(
reg
,
tid
int
)
error
{
func
(
dbp
*
Process
)
clearHardwareBreakpoint
(
reg
,
tid
int
)
error
{
return
fmt
.
Errorf
(
"not implemented on darwin"
)
return
fmt
.
Errorf
(
"not implemented on darwin"
)
}
}
proc/breakpoints_linux_amd64.go
浏览文件 @
102d4c89
...
@@ -21,7 +21,7 @@ import "fmt"
...
@@ -21,7 +21,7 @@ import "fmt"
// debug register `reg` with the address of the instruction
// debug register `reg` with the address of the instruction
// that we want to break at. There are only 4 debug registers
// that we want to break at. There are only 4 debug registers
// DR0-DR3. Debug register 7 is the control register.
// DR0-DR3. Debug register 7 is the control register.
func
(
dbp
*
Debugged
Process
)
setHardwareBreakpoint
(
reg
,
tid
int
,
addr
uint64
)
error
{
func
(
dbp
*
Process
)
setHardwareBreakpoint
(
reg
,
tid
int
,
addr
uint64
)
error
{
if
reg
<
0
||
reg
>
3
{
if
reg
<
0
||
reg
>
3
{
return
fmt
.
Errorf
(
"invalid debug register value"
)
return
fmt
.
Errorf
(
"invalid debug register value"
)
}
}
...
@@ -72,6 +72,6 @@ func (dbp *DebuggedProcess) setHardwareBreakpoint(reg, tid int, addr uint64) err
...
@@ -72,6 +72,6 @@ func (dbp *DebuggedProcess) setHardwareBreakpoint(reg, tid int, addr uint64) err
// Clears a hardware breakpoint. Essentially sets
// Clears a hardware breakpoint. Essentially sets
// the debug reg to 0 and clears the control register
// the debug reg to 0 and clears the control register
// flags for that reg.
// flags for that reg.
func
(
dbp
*
Debugged
Process
)
clearHardwareBreakpoint
(
reg
,
tid
int
)
error
{
func
(
dbp
*
Process
)
clearHardwareBreakpoint
(
reg
,
tid
int
)
error
{
return
dbp
.
setHardwareBreakpoint
(
reg
,
tid
,
0
)
return
dbp
.
setHardwareBreakpoint
(
reg
,
tid
,
0
)
}
}
proc/proc.go
浏览文件 @
102d4c89
...
@@ -20,9 +20,9 @@ import (
...
@@ -20,9 +20,9 @@ import (
"github.com/derekparker/delve/source"
"github.com/derekparker/delve/source"
)
)
//
Debugged
Process represents all of the information the debugger
// Process represents all of the information the debugger
// is holding onto regarding the process we are debugging.
// is holding onto regarding the process we are debugging.
type
Debugged
Process
struct
{
type
Process
struct
{
Pid
int
// Process Pid
Pid
int
// Process Pid
Process
*
os
.
Process
// Pointer to process struct for the actual process we are debugging
Process
*
os
.
Process
// Pointer to process struct for the actual process we are debugging
...
@@ -53,8 +53,8 @@ type DebuggedProcess struct {
...
@@ -53,8 +53,8 @@ type DebuggedProcess struct {
ptraceDoneChan
chan
interface
{}
ptraceDoneChan
chan
interface
{}
}
}
func
New
(
pid
int
)
*
Debugged
Process
{
func
New
(
pid
int
)
*
Process
{
dbp
:=
&
Debugged
Process
{
dbp
:=
&
Process
{
Pid
:
pid
,
Pid
:
pid
,
Threads
:
make
(
map
[
int
]
*
Thread
),
Threads
:
make
(
map
[
int
]
*
Thread
),
Breakpoints
:
make
(
map
[
uint64
]
*
Breakpoint
),
Breakpoints
:
make
(
map
[
uint64
]
*
Breakpoint
),
...
@@ -88,7 +88,7 @@ func (pe ProcessExitedError) Error() string {
...
@@ -88,7 +88,7 @@ func (pe ProcessExitedError) Error() string {
}
}
// Attach to an existing process with the given PID.
// Attach to an existing process with the given PID.
func
Attach
(
pid
int
)
(
*
Debugged
Process
,
error
)
{
func
Attach
(
pid
int
)
(
*
Process
,
error
)
{
dbp
,
err
:=
initializeDebugProcess
(
New
(
pid
),
""
,
true
)
dbp
,
err
:=
initializeDebugProcess
(
New
(
pid
),
""
,
true
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
...
@@ -96,7 +96,7 @@ func Attach(pid int) (*DebuggedProcess, error) {
...
@@ -96,7 +96,7 @@ func Attach(pid int) (*DebuggedProcess, error) {
return
dbp
,
nil
return
dbp
,
nil
}
}
func
(
dbp
*
Debugged
Process
)
Detach
(
kill
bool
)
(
err
error
)
{
func
(
dbp
*
Process
)
Detach
(
kill
bool
)
(
err
error
)
{
// Clean up any breakpoints we've set.
// Clean up any breakpoints we've set.
for
_
,
bp
:=
range
dbp
.
Breakpoints
{
for
_
,
bp
:=
range
dbp
.
Breakpoints
{
if
bp
!=
nil
{
if
bp
!=
nil
{
...
@@ -115,13 +115,13 @@ func (dbp *DebuggedProcess) Detach(kill bool) (err error) {
...
@@ -115,13 +115,13 @@ func (dbp *DebuggedProcess) Detach(kill bool) (err error) {
// Returns whether or not Delve thinks the debugged
// Returns whether or not Delve thinks the debugged
// process has exited.
// process has exited.
func
(
dbp
*
Debugged
Process
)
Exited
()
bool
{
func
(
dbp
*
Process
)
Exited
()
bool
{
return
dbp
.
exited
return
dbp
.
exited
}
}
// Returns whether or not Delve thinks the debugged
// Returns whether or not Delve thinks the debugged
// process is currently executing.
// process is currently executing.
func
(
dbp
*
Debugged
Process
)
Running
()
bool
{
func
(
dbp
*
Process
)
Running
()
bool
{
return
dbp
.
running
return
dbp
.
running
}
}
...
@@ -130,7 +130,7 @@ func (dbp *DebuggedProcess) Running() bool {
...
@@ -130,7 +130,7 @@ func (dbp *DebuggedProcess) Running() bool {
// * Dwarf .debug_frame section
// * Dwarf .debug_frame section
// * Dwarf .debug_line section
// * Dwarf .debug_line section
// * Go symbol table.
// * Go symbol table.
func
(
dbp
*
Debugged
Process
)
LoadInformation
(
path
string
)
error
{
func
(
dbp
*
Process
)
LoadInformation
(
path
string
)
error
{
var
wg
sync
.
WaitGroup
var
wg
sync
.
WaitGroup
exe
,
err
:=
dbp
.
findExecutable
(
path
)
exe
,
err
:=
dbp
.
findExecutable
(
path
)
...
@@ -148,7 +148,7 @@ func (dbp *DebuggedProcess) LoadInformation(path string) error {
...
@@ -148,7 +148,7 @@ func (dbp *DebuggedProcess) LoadInformation(path string) error {
}
}
// Find a location by string (file+line, function, breakpoint id, addr)
// Find a location by string (file+line, function, breakpoint id, addr)
func
(
dbp
*
Debugged
Process
)
FindLocation
(
str
string
)
(
uint64
,
error
)
{
func
(
dbp
*
Process
)
FindLocation
(
str
string
)
(
uint64
,
error
)
{
// File + Line
// File + Line
if
strings
.
ContainsRune
(
str
,
':'
)
{
if
strings
.
ContainsRune
(
str
,
':'
)
{
fl
:=
strings
.
Split
(
str
,
":"
)
fl
:=
strings
.
Split
(
str
,
":"
)
...
@@ -194,7 +194,7 @@ func (dbp *DebuggedProcess) FindLocation(str string) (uint64, error) {
...
@@ -194,7 +194,7 @@ func (dbp *DebuggedProcess) FindLocation(str string) (uint64, error) {
// Sends out a request that the debugged process halt
// Sends out a request that the debugged process halt
// execution. Sends SIGSTOP to all threads.
// execution. Sends SIGSTOP to all threads.
func
(
dbp
*
Debugged
Process
)
RequestManualStop
()
error
{
func
(
dbp
*
Process
)
RequestManualStop
()
error
{
dbp
.
halt
=
true
dbp
.
halt
=
true
err
:=
dbp
.
requestManualStop
()
err
:=
dbp
.
requestManualStop
()
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -217,17 +217,17 @@ func (dbp *DebuggedProcess) RequestManualStop() error {
...
@@ -217,17 +217,17 @@ func (dbp *DebuggedProcess) RequestManualStop() error {
// hardware supports it, and there are free debug registers, Delve
// hardware supports it, and there are free debug registers, Delve
// will set a hardware breakpoint. Otherwise we fall back to software
// will set a hardware breakpoint. Otherwise we fall back to software
// breakpoints, which are a bit more work for us.
// breakpoints, which are a bit more work for us.
func
(
dbp
*
Debugged
Process
)
Break
(
addr
uint64
)
(
*
Breakpoint
,
error
)
{
func
(
dbp
*
Process
)
Break
(
addr
uint64
)
(
*
Breakpoint
,
error
)
{
return
dbp
.
setBreakpoint
(
dbp
.
CurrentThread
.
Id
,
addr
,
false
)
return
dbp
.
setBreakpoint
(
dbp
.
CurrentThread
.
Id
,
addr
,
false
)
}
}
// Sets a temp breakpoint, for the 'next' command.
// Sets a temp breakpoint, for the 'next' command.
func
(
dbp
*
Debugged
Process
)
TempBreak
(
addr
uint64
)
(
*
Breakpoint
,
error
)
{
func
(
dbp
*
Process
)
TempBreak
(
addr
uint64
)
(
*
Breakpoint
,
error
)
{
return
dbp
.
setBreakpoint
(
dbp
.
CurrentThread
.
Id
,
addr
,
true
)
return
dbp
.
setBreakpoint
(
dbp
.
CurrentThread
.
Id
,
addr
,
true
)
}
}
// Sets a breakpoint by location string (function, file+line, address)
// Sets a breakpoint by location string (function, file+line, address)
func
(
dbp
*
Debugged
Process
)
BreakByLocation
(
loc
string
)
(
*
Breakpoint
,
error
)
{
func
(
dbp
*
Process
)
BreakByLocation
(
loc
string
)
(
*
Breakpoint
,
error
)
{
addr
,
err
:=
dbp
.
FindLocation
(
loc
)
addr
,
err
:=
dbp
.
FindLocation
(
loc
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
...
@@ -236,12 +236,12 @@ func (dbp *DebuggedProcess) BreakByLocation(loc string) (*Breakpoint, error) {
...
@@ -236,12 +236,12 @@ func (dbp *DebuggedProcess) BreakByLocation(loc string) (*Breakpoint, error) {
}
}
// Clears a breakpoint in the current thread.
// Clears a breakpoint in the current thread.
func
(
dbp
*
Debugged
Process
)
Clear
(
addr
uint64
)
(
*
Breakpoint
,
error
)
{
func
(
dbp
*
Process
)
Clear
(
addr
uint64
)
(
*
Breakpoint
,
error
)
{
return
dbp
.
clearBreakpoint
(
dbp
.
CurrentThread
.
Id
,
addr
)
return
dbp
.
clearBreakpoint
(
dbp
.
CurrentThread
.
Id
,
addr
)
}
}
// Clears a breakpoint by location (function, file+line, address, breakpoint id)
// Clears a breakpoint by location (function, file+line, address, breakpoint id)
func
(
dbp
*
Debugged
Process
)
ClearByLocation
(
loc
string
)
(
*
Breakpoint
,
error
)
{
func
(
dbp
*
Process
)
ClearByLocation
(
loc
string
)
(
*
Breakpoint
,
error
)
{
addr
,
err
:=
dbp
.
FindLocation
(
loc
)
addr
,
err
:=
dbp
.
FindLocation
(
loc
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
...
@@ -250,16 +250,16 @@ func (dbp *DebuggedProcess) ClearByLocation(loc string) (*Breakpoint, error) {
...
@@ -250,16 +250,16 @@ func (dbp *DebuggedProcess) ClearByLocation(loc string) (*Breakpoint, error) {
}
}
// Returns the status of the current main thread context.
// Returns the status of the current main thread context.
func
(
dbp
*
Debugged
Process
)
Status
()
*
sys
.
WaitStatus
{
func
(
dbp
*
Process
)
Status
()
*
sys
.
WaitStatus
{
return
dbp
.
CurrentThread
.
Status
return
dbp
.
CurrentThread
.
Status
}
}
// Step over function calls.
// Step over function calls.
func
(
dbp
*
Debugged
Process
)
Next
()
error
{
func
(
dbp
*
Process
)
Next
()
error
{
return
dbp
.
run
(
dbp
.
next
)
return
dbp
.
run
(
dbp
.
next
)
}
}
func
(
dbp
*
Debugged
Process
)
next
()
error
{
func
(
dbp
*
Process
)
next
()
error
{
// Make sure we clean up the temp breakpoints created by thread.Next
// Make sure we clean up the temp breakpoints created by thread.Next
defer
dbp
.
clearTempBreakpoints
()
defer
dbp
.
clearTempBreakpoints
()
...
@@ -325,7 +325,7 @@ func (dbp *DebuggedProcess) next() error {
...
@@ -325,7 +325,7 @@ func (dbp *DebuggedProcess) next() error {
return
dbp
.
Halt
()
return
dbp
.
Halt
()
}
}
func
(
dbp
*
Debugged
Process
)
setChanRecvBreakpoints
()
(
int
,
error
)
{
func
(
dbp
*
Process
)
setChanRecvBreakpoints
()
(
int
,
error
)
{
var
count
int
var
count
int
allg
,
err
:=
dbp
.
GoroutinesInfo
()
allg
,
err
:=
dbp
.
GoroutinesInfo
()
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -350,7 +350,7 @@ func (dbp *DebuggedProcess) setChanRecvBreakpoints() (int, error) {
...
@@ -350,7 +350,7 @@ func (dbp *DebuggedProcess) setChanRecvBreakpoints() (int, error) {
}
}
// Resume process.
// Resume process.
func
(
dbp
*
Debugged
Process
)
Continue
()
error
{
func
(
dbp
*
Process
)
Continue
()
error
{
for
_
,
thread
:=
range
dbp
.
Threads
{
for
_
,
thread
:=
range
dbp
.
Threads
{
err
:=
thread
.
Continue
()
err
:=
thread
.
Continue
()
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -360,7 +360,7 @@ func (dbp *DebuggedProcess) Continue() error {
...
@@ -360,7 +360,7 @@ func (dbp *DebuggedProcess) Continue() error {
return
dbp
.
run
(
dbp
.
resume
)
return
dbp
.
run
(
dbp
.
resume
)
}
}
func
(
dbp
*
Debugged
Process
)
resume
()
error
{
func
(
dbp
*
Process
)
resume
()
error
{
thread
,
err
:=
dbp
.
trapWait
(
-
1
)
thread
,
err
:=
dbp
.
trapWait
(
-
1
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
...
@@ -391,7 +391,7 @@ func (dbp *DebuggedProcess) resume() error {
...
@@ -391,7 +391,7 @@ func (dbp *DebuggedProcess) resume() error {
}
}
// Single step, will execute a single instruction.
// Single step, will execute a single instruction.
func
(
dbp
*
Debugged
Process
)
Step
()
(
err
error
)
{
func
(
dbp
*
Process
)
Step
()
(
err
error
)
{
fn
:=
func
()
error
{
fn
:=
func
()
error
{
dbp
.
singleStepping
=
true
dbp
.
singleStepping
=
true
defer
func
()
{
dbp
.
singleStepping
=
false
}()
defer
func
()
{
dbp
.
singleStepping
=
false
}()
...
@@ -411,7 +411,7 @@ func (dbp *DebuggedProcess) Step() (err error) {
...
@@ -411,7 +411,7 @@ func (dbp *DebuggedProcess) Step() (err error) {
}
}
// Change from current thread to the thread specified by `tid`.
// Change from current thread to the thread specified by `tid`.
func
(
dbp
*
Debugged
Process
)
SwitchThread
(
tid
int
)
error
{
func
(
dbp
*
Process
)
SwitchThread
(
tid
int
)
error
{
if
th
,
ok
:=
dbp
.
Threads
[
tid
];
ok
{
if
th
,
ok
:=
dbp
.
Threads
[
tid
];
ok
{
fmt
.
Printf
(
"thread context changed from %d to %d
\n
"
,
dbp
.
CurrentThread
.
Id
,
tid
)
fmt
.
Printf
(
"thread context changed from %d to %d
\n
"
,
dbp
.
CurrentThread
.
Id
,
tid
)
dbp
.
CurrentThread
=
th
dbp
.
CurrentThread
=
th
...
@@ -422,7 +422,7 @@ func (dbp *DebuggedProcess) SwitchThread(tid int) error {
...
@@ -422,7 +422,7 @@ func (dbp *DebuggedProcess) SwitchThread(tid int) error {
// Returns an array of G structures representing the information
// Returns an array of G structures representing the information
// Delve cares about from the internal runtime G structure.
// Delve cares about from the internal runtime G structure.
func
(
dbp
*
Debugged
Process
)
GoroutinesInfo
()
([]
*
G
,
error
)
{
func
(
dbp
*
Process
)
GoroutinesInfo
()
([]
*
G
,
error
)
{
var
(
var
(
threadg
=
map
[
int
]
*
Thread
{}
threadg
=
map
[
int
]
*
Thread
{}
allg
[]
*
G
allg
[]
*
G
...
@@ -471,7 +471,7 @@ func (dbp *DebuggedProcess) GoroutinesInfo() ([]*G, error) {
...
@@ -471,7 +471,7 @@ func (dbp *DebuggedProcess) GoroutinesInfo() ([]*G, error) {
}
}
// Stop all threads.
// Stop all threads.
func
(
dbp
*
Debugged
Process
)
Halt
()
(
err
error
)
{
func
(
dbp
*
Process
)
Halt
()
(
err
error
)
{
for
_
,
th
:=
range
dbp
.
Threads
{
for
_
,
th
:=
range
dbp
.
Threads
{
if
err
:=
th
.
Halt
();
err
!=
nil
{
if
err
:=
th
.
Halt
();
err
!=
nil
{
return
err
return
err
...
@@ -482,47 +482,47 @@ func (dbp *DebuggedProcess) Halt() (err error) {
...
@@ -482,47 +482,47 @@ func (dbp *DebuggedProcess) Halt() (err error) {
// Obtains register values from what Delve considers to be the current
// Obtains register values from what Delve considers to be the current
// thread of the traced process.
// thread of the traced process.
func
(
dbp
*
Debugged
Process
)
Registers
()
(
Registers
,
error
)
{
func
(
dbp
*
Process
)
Registers
()
(
Registers
,
error
)
{
return
dbp
.
CurrentThread
.
Registers
()
return
dbp
.
CurrentThread
.
Registers
()
}
}
// Returns the PC of the current thread.
// Returns the PC of the current thread.
func
(
dbp
*
Debugged
Process
)
PC
()
(
uint64
,
error
)
{
func
(
dbp
*
Process
)
PC
()
(
uint64
,
error
)
{
return
dbp
.
CurrentThread
.
PC
()
return
dbp
.
CurrentThread
.
PC
()
}
}
// Returns the PC of the current thread.
// Returns the PC of the current thread.
func
(
dbp
*
Debugged
Process
)
CurrentBreakpoint
()
*
Breakpoint
{
func
(
dbp
*
Process
)
CurrentBreakpoint
()
*
Breakpoint
{
return
dbp
.
CurrentThread
.
CurrentBreakpoint
return
dbp
.
CurrentThread
.
CurrentBreakpoint
}
}
// Returns the value of the named symbol.
// Returns the value of the named symbol.
func
(
dbp
*
Debugged
Process
)
EvalVariable
(
name
string
)
(
*
Variable
,
error
)
{
func
(
dbp
*
Process
)
EvalVariable
(
name
string
)
(
*
Variable
,
error
)
{
return
dbp
.
CurrentThread
.
EvalVariable
(
name
)
return
dbp
.
CurrentThread
.
EvalVariable
(
name
)
}
}
// Returns a reader for the dwarf data
// Returns a reader for the dwarf data
func
(
dbp
*
Debugged
Process
)
DwarfReader
()
*
reader
.
Reader
{
func
(
dbp
*
Process
)
DwarfReader
()
*
reader
.
Reader
{
return
reader
.
New
(
dbp
.
dwarf
)
return
reader
.
New
(
dbp
.
dwarf
)
}
}
// Returns list of source files that comprise the debugged binary.
// Returns list of source files that comprise the debugged binary.
func
(
dbp
*
Debugged
Process
)
Sources
()
map
[
string
]
*
gosym
.
Obj
{
func
(
dbp
*
Process
)
Sources
()
map
[
string
]
*
gosym
.
Obj
{
return
dbp
.
goSymTable
.
Files
return
dbp
.
goSymTable
.
Files
}
}
// Returns list of functions present in the debugged program.
// Returns list of functions present in the debugged program.
func
(
dbp
*
Debugged
Process
)
Funcs
()
[]
gosym
.
Func
{
func
(
dbp
*
Process
)
Funcs
()
[]
gosym
.
Func
{
return
dbp
.
goSymTable
.
Funcs
return
dbp
.
goSymTable
.
Funcs
}
}
// Converts an instruction address to a file/line/function.
// Converts an instruction address to a file/line/function.
func
(
dbp
*
Debugged
Process
)
PCToLine
(
pc
uint64
)
(
string
,
int
,
*
gosym
.
Func
)
{
func
(
dbp
*
Process
)
PCToLine
(
pc
uint64
)
(
string
,
int
,
*
gosym
.
Func
)
{
return
dbp
.
goSymTable
.
PCToLine
(
pc
)
return
dbp
.
goSymTable
.
PCToLine
(
pc
)
}
}
// Finds the breakpoint for the given ID.
// Finds the breakpoint for the given ID.
func
(
dbp
*
Debugged
Process
)
FindBreakpointByID
(
id
int
)
(
*
Breakpoint
,
bool
)
{
func
(
dbp
*
Process
)
FindBreakpointByID
(
id
int
)
(
*
Breakpoint
,
bool
)
{
for
_
,
bp
:=
range
dbp
.
Breakpoints
{
for
_
,
bp
:=
range
dbp
.
Breakpoints
{
if
bp
.
ID
==
id
{
if
bp
.
ID
==
id
{
return
bp
,
true
return
bp
,
true
...
@@ -532,7 +532,7 @@ func (dbp *DebuggedProcess) FindBreakpointByID(id int) (*Breakpoint, bool) {
...
@@ -532,7 +532,7 @@ func (dbp *DebuggedProcess) FindBreakpointByID(id int) (*Breakpoint, bool) {
}
}
// Finds the breakpoint for the given pc.
// Finds the breakpoint for the given pc.
func
(
dbp
*
Debugged
Process
)
FindBreakpoint
(
pc
uint64
)
(
*
Breakpoint
,
bool
)
{
func
(
dbp
*
Process
)
FindBreakpoint
(
pc
uint64
)
(
*
Breakpoint
,
bool
)
{
// Check for software breakpoint. PC will be at
// Check for software breakpoint. PC will be at
// breakpoint instruction + size of breakpoint.
// breakpoint instruction + size of breakpoint.
if
bp
,
ok
:=
dbp
.
Breakpoints
[
pc
-
uint64
(
dbp
.
arch
.
BreakpointSize
())];
ok
{
if
bp
,
ok
:=
dbp
.
Breakpoints
[
pc
-
uint64
(
dbp
.
arch
.
BreakpointSize
())];
ok
{
...
@@ -548,8 +548,8 @@ func (dbp *DebuggedProcess) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
...
@@ -548,8 +548,8 @@ func (dbp *DebuggedProcess) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
return
nil
,
false
return
nil
,
false
}
}
// Returns a new
Debugged
Process struct.
// Returns a new Process struct.
func
initializeDebugProcess
(
dbp
*
DebuggedProcess
,
path
string
,
attach
bool
)
(
*
Debugged
Process
,
error
)
{
func
initializeDebugProcess
(
dbp
*
Process
,
path
string
,
attach
bool
)
(
*
Process
,
error
)
{
if
attach
{
if
attach
{
var
err
error
var
err
error
dbp
.
execPtraceFunc
(
func
()
{
err
=
sys
.
PtraceAttach
(
dbp
.
Pid
)
})
dbp
.
execPtraceFunc
(
func
()
{
err
=
sys
.
PtraceAttach
(
dbp
.
Pid
)
})
...
@@ -585,7 +585,7 @@ func initializeDebugProcess(dbp *DebuggedProcess, path string, attach bool) (*De
...
@@ -585,7 +585,7 @@ func initializeDebugProcess(dbp *DebuggedProcess, path string, attach bool) (*De
return
dbp
,
nil
return
dbp
,
nil
}
}
func
(
dbp
*
Debugged
Process
)
clearTempBreakpoints
()
error
{
func
(
dbp
*
Process
)
clearTempBreakpoints
()
error
{
for
_
,
bp
:=
range
dbp
.
Breakpoints
{
for
_
,
bp
:=
range
dbp
.
Breakpoints
{
if
!
bp
.
Temp
{
if
!
bp
.
Temp
{
continue
continue
...
@@ -597,7 +597,7 @@ func (dbp *DebuggedProcess) clearTempBreakpoints() error {
...
@@ -597,7 +597,7 @@ func (dbp *DebuggedProcess) clearTempBreakpoints() error {
return
nil
return
nil
}
}
func
(
dbp
*
Debugged
Process
)
handleBreakpointOnThread
(
id
int
)
(
*
Thread
,
error
)
{
func
(
dbp
*
Process
)
handleBreakpointOnThread
(
id
int
)
(
*
Thread
,
error
)
{
thread
,
ok
:=
dbp
.
Threads
[
id
]
thread
,
ok
:=
dbp
.
Threads
[
id
]
if
!
ok
{
if
!
ok
{
return
nil
,
fmt
.
Errorf
(
"could not find thread for %d"
,
id
)
return
nil
,
fmt
.
Errorf
(
"could not find thread for %d"
,
id
)
...
@@ -631,7 +631,7 @@ func (dbp *DebuggedProcess) handleBreakpointOnThread(id int) (*Thread, error) {
...
@@ -631,7 +631,7 @@ func (dbp *DebuggedProcess) handleBreakpointOnThread(id int) (*Thread, error) {
return
nil
,
NoBreakpointError
{
addr
:
pc
}
return
nil
,
NoBreakpointError
{
addr
:
pc
}
}
}
func
(
dbp
*
Debugged
Process
)
run
(
fn
func
()
error
)
error
{
func
(
dbp
*
Process
)
run
(
fn
func
()
error
)
error
{
if
dbp
.
exited
{
if
dbp
.
exited
{
return
fmt
.
Errorf
(
"process has already exited"
)
return
fmt
.
Errorf
(
"process has already exited"
)
}
}
...
@@ -649,7 +649,7 @@ func (dbp *DebuggedProcess) run(fn func() error) error {
...
@@ -649,7 +649,7 @@ func (dbp *DebuggedProcess) run(fn func() error) error {
return
nil
return
nil
}
}
func
(
dbp
*
Debugged
Process
)
handlePtraceFuncs
()
{
func
(
dbp
*
Process
)
handlePtraceFuncs
()
{
// We must ensure here that we are running on the same thread during
// We must ensure here that we are running on the same thread during
// the execution of dbg. This is due to the fact that ptrace(2) expects
// the execution of dbg. This is due to the fact that ptrace(2) expects
// all commands after PTRACE_ATTACH to come from the same thread.
// all commands after PTRACE_ATTACH to come from the same thread.
...
@@ -661,7 +661,7 @@ func (dbp *DebuggedProcess) handlePtraceFuncs() {
...
@@ -661,7 +661,7 @@ func (dbp *DebuggedProcess) handlePtraceFuncs() {
}
}
}
}
func
(
dbp
*
Debugged
Process
)
execPtraceFunc
(
fn
func
())
{
func
(
dbp
*
Process
)
execPtraceFunc
(
fn
func
())
{
dbp
.
ptraceChan
<-
fn
dbp
.
ptraceChan
<-
fn
<-
dbp
.
ptraceDoneChan
<-
dbp
.
ptraceDoneChan
}
}
proc/proc_darwin.go
浏览文件 @
102d4c89
...
@@ -33,7 +33,7 @@ type OSProcessDetails struct {
...
@@ -33,7 +33,7 @@ type OSProcessDetails struct {
// custom fork/exec process in order to take advantage of
// custom fork/exec process in order to take advantage of
// PT_SIGEXC on Darwin which will turn Unix signals into
// PT_SIGEXC on Darwin which will turn Unix signals into
// Mach exceptions.
// Mach exceptions.
func
Launch
(
cmd
[]
string
)
(
*
Debugged
Process
,
error
)
{
func
Launch
(
cmd
[]
string
)
(
*
Process
,
error
)
{
argv0Go
,
err
:=
filepath
.
Abs
(
cmd
[
0
])
argv0Go
,
err
:=
filepath
.
Abs
(
cmd
[
0
])
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
...
@@ -70,7 +70,7 @@ func Launch(cmd []string) (*DebuggedProcess, error) {
...
@@ -70,7 +70,7 @@ func Launch(cmd []string) (*DebuggedProcess, error) {
return
dbp
,
err
return
dbp
,
err
}
}
func
(
dbp
*
Debugged
Process
)
requestManualStop
()
(
err
error
)
{
func
(
dbp
*
Process
)
requestManualStop
()
(
err
error
)
{
var
(
var
(
task
=
C
.
mach_port_t
(
dbp
.
os
.
task
)
task
=
C
.
mach_port_t
(
dbp
.
os
.
task
)
thread
=
C
.
mach_port_t
(
dbp
.
CurrentThread
.
os
.
thread_act
)
thread
=
C
.
mach_port_t
(
dbp
.
CurrentThread
.
os
.
thread_act
)
...
@@ -83,7 +83,7 @@ func (dbp *DebuggedProcess) requestManualStop() (err error) {
...
@@ -83,7 +83,7 @@ func (dbp *DebuggedProcess) requestManualStop() (err error) {
return
nil
return
nil
}
}
func
(
dbp
*
Debugged
Process
)
updateThreadList
()
error
{
func
(
dbp
*
Process
)
updateThreadList
()
error
{
var
(
var
(
err
error
err
error
kret
C
.
kern_return_t
kret
C
.
kern_return_t
...
@@ -116,7 +116,7 @@ func (dbp *DebuggedProcess) updateThreadList() error {
...
@@ -116,7 +116,7 @@ func (dbp *DebuggedProcess) updateThreadList() error {
return
nil
return
nil
}
}
func
(
dbp
*
Debugged
Process
)
addThread
(
port
int
,
attach
bool
)
(
*
Thread
,
error
)
{
func
(
dbp
*
Process
)
addThread
(
port
int
,
attach
bool
)
(
*
Thread
,
error
)
{
if
thread
,
ok
:=
dbp
.
Threads
[
port
];
ok
{
if
thread
,
ok
:=
dbp
.
Threads
[
port
];
ok
{
return
thread
,
nil
return
thread
,
nil
}
}
...
@@ -133,7 +133,7 @@ func (dbp *DebuggedProcess) addThread(port int, attach bool) (*Thread, error) {
...
@@ -133,7 +133,7 @@ func (dbp *DebuggedProcess) addThread(port int, attach bool) (*Thread, error) {
return
thread
,
nil
return
thread
,
nil
}
}
func
(
dbp
*
Debugged
Process
)
parseDebugFrame
(
exe
*
macho
.
File
,
wg
*
sync
.
WaitGroup
)
{
func
(
dbp
*
Process
)
parseDebugFrame
(
exe
*
macho
.
File
,
wg
*
sync
.
WaitGroup
)
{
defer
wg
.
Done
()
defer
wg
.
Done
()
if
sec
:=
exe
.
Section
(
"__debug_frame"
);
sec
!=
nil
{
if
sec
:=
exe
.
Section
(
"__debug_frame"
);
sec
!=
nil
{
...
@@ -149,7 +149,7 @@ func (dbp *DebuggedProcess) parseDebugFrame(exe *macho.File, wg *sync.WaitGroup)
...
@@ -149,7 +149,7 @@ func (dbp *DebuggedProcess) parseDebugFrame(exe *macho.File, wg *sync.WaitGroup)
}
}
}
}
func
(
dbp
*
Debugged
Process
)
obtainGoSymbols
(
exe
*
macho
.
File
,
wg
*
sync
.
WaitGroup
)
{
func
(
dbp
*
Process
)
obtainGoSymbols
(
exe
*
macho
.
File
,
wg
*
sync
.
WaitGroup
)
{
defer
wg
.
Done
()
defer
wg
.
Done
()
var
(
var
(
...
@@ -184,7 +184,7 @@ func (dbp *DebuggedProcess) obtainGoSymbols(exe *macho.File, wg *sync.WaitGroup)
...
@@ -184,7 +184,7 @@ func (dbp *DebuggedProcess) obtainGoSymbols(exe *macho.File, wg *sync.WaitGroup)
dbp
.
goSymTable
=
tab
dbp
.
goSymTable
=
tab
}
}
func
(
dbp
*
Debugged
Process
)
parseDebugLineInfo
(
exe
*
macho
.
File
,
wg
*
sync
.
WaitGroup
)
{
func
(
dbp
*
Process
)
parseDebugLineInfo
(
exe
*
macho
.
File
,
wg
*
sync
.
WaitGroup
)
{
defer
wg
.
Done
()
defer
wg
.
Done
()
if
sec
:=
exe
.
Section
(
"__debug_line"
);
sec
!=
nil
{
if
sec
:=
exe
.
Section
(
"__debug_line"
);
sec
!=
nil
{
...
@@ -200,7 +200,7 @@ func (dbp *DebuggedProcess) parseDebugLineInfo(exe *macho.File, wg *sync.WaitGro
...
@@ -200,7 +200,7 @@ func (dbp *DebuggedProcess) parseDebugLineInfo(exe *macho.File, wg *sync.WaitGro
}
}
}
}
func
(
dbp
*
Debugged
Process
)
findExecutable
(
path
string
)
(
*
macho
.
File
,
error
)
{
func
(
dbp
*
Process
)
findExecutable
(
path
string
)
(
*
macho
.
File
,
error
)
{
if
path
==
""
{
if
path
==
""
{
path
=
C
.
GoString
(
C
.
find_executable
(
C
.
int
(
dbp
.
Pid
)))
path
=
C
.
GoString
(
C
.
find_executable
(
C
.
int
(
dbp
.
Pid
)))
}
}
...
@@ -216,7 +216,7 @@ func (dbp *DebuggedProcess) findExecutable(path string) (*macho.File, error) {
...
@@ -216,7 +216,7 @@ func (dbp *DebuggedProcess) findExecutable(path string) (*macho.File, error) {
return
exe
,
nil
return
exe
,
nil
}
}
func
(
dbp
*
Debugged
Process
)
trapWait
(
pid
int
)
(
*
Thread
,
error
)
{
func
(
dbp
*
Process
)
trapWait
(
pid
int
)
(
*
Thread
,
error
)
{
var
(
var
(
th
*
Thread
th
*
Thread
err
error
err
error
...
...
proc/proc_linux.go
浏览文件 @
102d4c89
...
@@ -29,7 +29,7 @@ type OSProcessDetails interface{}
...
@@ -29,7 +29,7 @@ type OSProcessDetails interface{}
// Create and begin debugging a new process. First entry in
// Create and begin debugging a new process. First entry in
// `cmd` is the program to run, and then rest are the arguments
// `cmd` is the program to run, and then rest are the arguments
// to be supplied to that process.
// to be supplied to that process.
func
Launch
(
cmd
[]
string
)
(
*
Debugged
Process
,
error
)
{
func
Launch
(
cmd
[]
string
)
(
*
Process
,
error
)
{
var
(
var
(
proc
*
exec
.
Cmd
proc
*
exec
.
Cmd
err
error
err
error
...
@@ -54,13 +54,13 @@ func Launch(cmd []string) (*DebuggedProcess, error) {
...
@@ -54,13 +54,13 @@ func Launch(cmd []string) (*DebuggedProcess, error) {
return
initializeDebugProcess
(
dbp
,
proc
.
Path
,
false
)
return
initializeDebugProcess
(
dbp
,
proc
.
Path
,
false
)
}
}
func
(
dbp
*
Debugged
Process
)
requestManualStop
()
(
err
error
)
{
func
(
dbp
*
Process
)
requestManualStop
()
(
err
error
)
{
return
sys
.
Kill
(
dbp
.
Pid
,
sys
.
SIGSTOP
)
return
sys
.
Kill
(
dbp
.
Pid
,
sys
.
SIGSTOP
)
}
}
// Attach to a newly created thread, and store that thread in our list of
// Attach to a newly created thread, and store that thread in our list of
// known threads.
// known threads.
func
(
dbp
*
Debugged
Process
)
addThread
(
tid
int
,
attach
bool
)
(
*
Thread
,
error
)
{
func
(
dbp
*
Process
)
addThread
(
tid
int
,
attach
bool
)
(
*
Thread
,
error
)
{
if
thread
,
ok
:=
dbp
.
Threads
[
tid
];
ok
{
if
thread
,
ok
:=
dbp
.
Threads
[
tid
];
ok
{
return
thread
,
nil
return
thread
,
nil
}
}
...
@@ -112,7 +112,7 @@ func (dbp *DebuggedProcess) addThread(tid int, attach bool) (*Thread, error) {
...
@@ -112,7 +112,7 @@ func (dbp *DebuggedProcess) addThread(tid int, attach bool) (*Thread, error) {
return
dbp
.
Threads
[
tid
],
nil
return
dbp
.
Threads
[
tid
],
nil
}
}
func
(
dbp
*
Debugged
Process
)
updateThreadList
()
error
{
func
(
dbp
*
Process
)
updateThreadList
()
error
{
var
attach
bool
var
attach
bool
tids
,
_
:=
filepath
.
Glob
(
fmt
.
Sprintf
(
"/proc/%d/task/*"
,
dbp
.
Pid
))
tids
,
_
:=
filepath
.
Glob
(
fmt
.
Sprintf
(
"/proc/%d/task/*"
,
dbp
.
Pid
))
for
_
,
tidpath
:=
range
tids
{
for
_
,
tidpath
:=
range
tids
{
...
@@ -131,7 +131,7 @@ func (dbp *DebuggedProcess) updateThreadList() error {
...
@@ -131,7 +131,7 @@ func (dbp *DebuggedProcess) updateThreadList() error {
return
nil
return
nil
}
}
func
(
dbp
*
Debugged
Process
)
findExecutable
(
path
string
)
(
*
elf
.
File
,
error
)
{
func
(
dbp
*
Process
)
findExecutable
(
path
string
)
(
*
elf
.
File
,
error
)
{
if
path
==
""
{
if
path
==
""
{
path
=
fmt
.
Sprintf
(
"/proc/%d/exe"
,
dbp
.
Pid
)
path
=
fmt
.
Sprintf
(
"/proc/%d/exe"
,
dbp
.
Pid
)
}
}
...
@@ -154,7 +154,7 @@ func (dbp *DebuggedProcess) findExecutable(path string) (*elf.File, error) {
...
@@ -154,7 +154,7 @@ func (dbp *DebuggedProcess) findExecutable(path string) (*elf.File, error) {
return
elffile
,
nil
return
elffile
,
nil
}
}
func
(
dbp
*
Debugged
Process
)
parseDebugFrame
(
exe
*
elf
.
File
,
wg
*
sync
.
WaitGroup
)
{
func
(
dbp
*
Process
)
parseDebugFrame
(
exe
*
elf
.
File
,
wg
*
sync
.
WaitGroup
)
{
defer
wg
.
Done
()
defer
wg
.
Done
()
if
sec
:=
exe
.
Section
(
".debug_frame"
);
sec
!=
nil
{
if
sec
:=
exe
.
Section
(
".debug_frame"
);
sec
!=
nil
{
...
@@ -170,7 +170,7 @@ func (dbp *DebuggedProcess) parseDebugFrame(exe *elf.File, wg *sync.WaitGroup) {
...
@@ -170,7 +170,7 @@ func (dbp *DebuggedProcess) parseDebugFrame(exe *elf.File, wg *sync.WaitGroup) {
}
}
}
}
func
(
dbp
*
Debugged
Process
)
obtainGoSymbols
(
exe
*
elf
.
File
,
wg
*
sync
.
WaitGroup
)
{
func
(
dbp
*
Process
)
obtainGoSymbols
(
exe
*
elf
.
File
,
wg
*
sync
.
WaitGroup
)
{
defer
wg
.
Done
()
defer
wg
.
Done
()
var
(
var
(
...
@@ -205,7 +205,7 @@ func (dbp *DebuggedProcess) obtainGoSymbols(exe *elf.File, wg *sync.WaitGroup) {
...
@@ -205,7 +205,7 @@ func (dbp *DebuggedProcess) obtainGoSymbols(exe *elf.File, wg *sync.WaitGroup) {
dbp
.
goSymTable
=
tab
dbp
.
goSymTable
=
tab
}
}
func
(
dbp
*
Debugged
Process
)
parseDebugLineInfo
(
exe
*
elf
.
File
,
wg
*
sync
.
WaitGroup
)
{
func
(
dbp
*
Process
)
parseDebugLineInfo
(
exe
*
elf
.
File
,
wg
*
sync
.
WaitGroup
)
{
defer
wg
.
Done
()
defer
wg
.
Done
()
if
sec
:=
exe
.
Section
(
".debug_line"
);
sec
!=
nil
{
if
sec
:=
exe
.
Section
(
".debug_line"
);
sec
!=
nil
{
...
@@ -221,7 +221,7 @@ func (dbp *DebuggedProcess) parseDebugLineInfo(exe *elf.File, wg *sync.WaitGroup
...
@@ -221,7 +221,7 @@ func (dbp *DebuggedProcess) parseDebugLineInfo(exe *elf.File, wg *sync.WaitGroup
}
}
}
}
func
(
dbp
*
Debugged
Process
)
trapWait
(
pid
int
)
(
*
Thread
,
error
)
{
func
(
dbp
*
Process
)
trapWait
(
pid
int
)
(
*
Thread
,
error
)
{
for
{
for
{
wpid
,
status
,
err
:=
wait
(
pid
,
0
)
wpid
,
status
,
err
:=
wait
(
pid
,
0
)
if
err
!=
nil
{
if
err
!=
nil
{
...
...
proc/proc_test.go
浏览文件 @
102d4c89
...
@@ -18,7 +18,7 @@ func TestMain(m *testing.M) {
...
@@ -18,7 +18,7 @@ func TestMain(m *testing.M) {
protest
.
RunTestsWithFixtures
(
m
)
protest
.
RunTestsWithFixtures
(
m
)
}
}
func
withTestProcess
(
name
string
,
t
*
testing
.
T
,
fn
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
))
{
func
withTestProcess
(
name
string
,
t
*
testing
.
T
,
fn
func
(
p
*
Process
,
fixture
protest
.
Fixture
))
{
fixture
:=
protest
.
BuildFixture
(
name
)
fixture
:=
protest
.
BuildFixture
(
name
)
p
,
err
:=
Launch
([]
string
{
fixture
.
Path
})
p
,
err
:=
Launch
([]
string
{
fixture
.
Path
})
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -30,7 +30,7 @@ func withTestProcess(name string, t *testing.T, fn func(p *DebuggedProcess, fixt
...
@@ -30,7 +30,7 @@ func withTestProcess(name string, t *testing.T, fn func(p *DebuggedProcess, fixt
fn
(
p
,
fixture
)
fn
(
p
,
fixture
)
}
}
func
getRegisters
(
p
*
Debugged
Process
,
t
*
testing
.
T
)
Registers
{
func
getRegisters
(
p
*
Process
,
t
*
testing
.
T
)
Registers
{
regs
,
err
:=
p
.
Registers
()
regs
,
err
:=
p
.
Registers
()
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatal
(
"Registers():"
,
err
)
t
.
Fatal
(
"Registers():"
,
err
)
...
@@ -57,7 +57,7 @@ func assertNoError(err error, t *testing.T, s string) {
...
@@ -57,7 +57,7 @@ func assertNoError(err error, t *testing.T, s string) {
}
}
}
}
func
currentPC
(
p
*
Debugged
Process
,
t
*
testing
.
T
)
uint64
{
func
currentPC
(
p
*
Process
,
t
*
testing
.
T
)
uint64
{
pc
,
err
:=
p
.
PC
()
pc
,
err
:=
p
.
PC
()
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
...
@@ -66,7 +66,7 @@ func currentPC(p *DebuggedProcess, t *testing.T) uint64 {
...
@@ -66,7 +66,7 @@ func currentPC(p *DebuggedProcess, t *testing.T) uint64 {
return
pc
return
pc
}
}
func
currentLineNumber
(
p
*
Debugged
Process
,
t
*
testing
.
T
)
(
string
,
int
)
{
func
currentLineNumber
(
p
*
Process
,
t
*
testing
.
T
)
(
string
,
int
)
{
pc
:=
currentPC
(
p
,
t
)
pc
:=
currentPC
(
p
,
t
)
f
,
l
,
_
:=
p
.
goSymTable
.
PCToLine
(
pc
)
f
,
l
,
_
:=
p
.
goSymTable
.
PCToLine
(
pc
)
...
@@ -74,7 +74,7 @@ func currentLineNumber(p *DebuggedProcess, t *testing.T) (string, int) {
...
@@ -74,7 +74,7 @@ func currentLineNumber(p *DebuggedProcess, t *testing.T) (string, int) {
}
}
func
TestExit
(
t
*
testing
.
T
)
{
func
TestExit
(
t
*
testing
.
T
)
{
withTestProcess
(
"continuetestprog"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"continuetestprog"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
err
:=
p
.
Continue
()
err
:=
p
.
Continue
()
pe
,
ok
:=
err
.
(
ProcessExitedError
)
pe
,
ok
:=
err
.
(
ProcessExitedError
)
if
!
ok
{
if
!
ok
{
...
@@ -91,7 +91,7 @@ func TestExit(t *testing.T) {
...
@@ -91,7 +91,7 @@ func TestExit(t *testing.T) {
func
TestHalt
(
t
*
testing
.
T
)
{
func
TestHalt
(
t
*
testing
.
T
)
{
runtime
.
GOMAXPROCS
(
2
)
runtime
.
GOMAXPROCS
(
2
)
withTestProcess
(
"testprog"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"testprog"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
go
func
()
{
go
func
()
{
for
{
for
{
if
p
.
Running
()
{
if
p
.
Running
()
{
...
@@ -120,7 +120,7 @@ func TestHalt(t *testing.T) {
...
@@ -120,7 +120,7 @@ func TestHalt(t *testing.T) {
}
}
func
TestStep
(
t
*
testing
.
T
)
{
func
TestStep
(
t
*
testing
.
T
)
{
withTestProcess
(
"testprog"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"testprog"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
helloworldfunc
:=
p
.
goSymTable
.
LookupFunc
(
"main.helloworld"
)
helloworldfunc
:=
p
.
goSymTable
.
LookupFunc
(
"main.helloworld"
)
helloworldaddr
:=
helloworldfunc
.
Entry
helloworldaddr
:=
helloworldfunc
.
Entry
...
@@ -142,7 +142,7 @@ func TestStep(t *testing.T) {
...
@@ -142,7 +142,7 @@ func TestStep(t *testing.T) {
}
}
func
TestBreakpoint
(
t
*
testing
.
T
)
{
func
TestBreakpoint
(
t
*
testing
.
T
)
{
withTestProcess
(
"testprog"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"testprog"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
helloworldfunc
:=
p
.
goSymTable
.
LookupFunc
(
"main.helloworld"
)
helloworldfunc
:=
p
.
goSymTable
.
LookupFunc
(
"main.helloworld"
)
helloworldaddr
:=
helloworldfunc
.
Entry
helloworldaddr
:=
helloworldfunc
.
Entry
...
@@ -163,7 +163,7 @@ func TestBreakpoint(t *testing.T) {
...
@@ -163,7 +163,7 @@ func TestBreakpoint(t *testing.T) {
}
}
func
TestBreakpointInSeperateGoRoutine
(
t
*
testing
.
T
)
{
func
TestBreakpointInSeperateGoRoutine
(
t
*
testing
.
T
)
{
withTestProcess
(
"testthreads"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"testthreads"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
fn
:=
p
.
goSymTable
.
LookupFunc
(
"main.anotherthread"
)
fn
:=
p
.
goSymTable
.
LookupFunc
(
"main.anotherthread"
)
if
fn
==
nil
{
if
fn
==
nil
{
t
.
Fatal
(
"No fn exists"
)
t
.
Fatal
(
"No fn exists"
)
...
@@ -192,7 +192,7 @@ func TestBreakpointInSeperateGoRoutine(t *testing.T) {
...
@@ -192,7 +192,7 @@ func TestBreakpointInSeperateGoRoutine(t *testing.T) {
}
}
func
TestBreakpointWithNonExistantFunction
(
t
*
testing
.
T
)
{
func
TestBreakpointWithNonExistantFunction
(
t
*
testing
.
T
)
{
withTestProcess
(
"testprog"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"testprog"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
_
,
err
:=
p
.
Break
(
0
)
_
,
err
:=
p
.
Break
(
0
)
if
err
==
nil
{
if
err
==
nil
{
t
.
Fatal
(
"Should not be able to break at non existant function"
)
t
.
Fatal
(
"Should not be able to break at non existant function"
)
...
@@ -201,7 +201,7 @@ func TestBreakpointWithNonExistantFunction(t *testing.T) {
...
@@ -201,7 +201,7 @@ func TestBreakpointWithNonExistantFunction(t *testing.T) {
}
}
func
TestClearBreakpoint
(
t
*
testing
.
T
)
{
func
TestClearBreakpoint
(
t
*
testing
.
T
)
{
withTestProcess
(
"testprog"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"testprog"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
fn
:=
p
.
goSymTable
.
LookupFunc
(
"main.sleepytime"
)
fn
:=
p
.
goSymTable
.
LookupFunc
(
"main.sleepytime"
)
bp
,
err
:=
p
.
Break
(
fn
.
Entry
)
bp
,
err
:=
p
.
Break
(
fn
.
Entry
)
assertNoError
(
err
,
t
,
"Break()"
)
assertNoError
(
err
,
t
,
"Break()"
)
...
@@ -230,7 +230,7 @@ type nextTest struct {
...
@@ -230,7 +230,7 @@ type nextTest struct {
}
}
func
testnext
(
program
string
,
testcases
[]
nextTest
,
initialLocation
string
,
t
*
testing
.
T
)
{
func
testnext
(
program
string
,
testcases
[]
nextTest
,
initialLocation
string
,
t
*
testing
.
T
)
{
withTestProcess
(
program
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
program
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
bp
,
err
:=
p
.
BreakByLocation
(
initialLocation
)
bp
,
err
:=
p
.
BreakByLocation
(
initialLocation
)
assertNoError
(
err
,
t
,
"Break()"
)
assertNoError
(
err
,
t
,
"Break()"
)
assertNoError
(
p
.
Continue
(),
t
,
"Continue()"
)
assertNoError
(
p
.
Continue
(),
t
,
"Continue()"
)
...
@@ -303,7 +303,7 @@ func TestNextFunctionReturnDefer(t *testing.T) {
...
@@ -303,7 +303,7 @@ func TestNextFunctionReturnDefer(t *testing.T) {
}
}
func
TestRuntimeBreakpoint
(
t
*
testing
.
T
)
{
func
TestRuntimeBreakpoint
(
t
*
testing
.
T
)
{
withTestProcess
(
"testruntimebreakpoint"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"testruntimebreakpoint"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
err
:=
p
.
Continue
()
err
:=
p
.
Continue
()
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
...
@@ -320,7 +320,7 @@ func TestRuntimeBreakpoint(t *testing.T) {
...
@@ -320,7 +320,7 @@ func TestRuntimeBreakpoint(t *testing.T) {
}
}
func
TestFindReturnAddress
(
t
*
testing
.
T
)
{
func
TestFindReturnAddress
(
t
*
testing
.
T
)
{
withTestProcess
(
"testnextprog"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"testnextprog"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
var
(
var
(
fdes
=
p
.
frameEntries
fdes
=
p
.
frameEntries
gsd
=
p
.
goSymTable
gsd
=
p
.
goSymTable
...
@@ -370,7 +370,7 @@ func TestFindReturnAddress(t *testing.T) {
...
@@ -370,7 +370,7 @@ func TestFindReturnAddress(t *testing.T) {
}
}
func
TestSwitchThread
(
t
*
testing
.
T
)
{
func
TestSwitchThread
(
t
*
testing
.
T
)
{
withTestProcess
(
"testnextprog"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"testnextprog"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
// With invalid thread id
// With invalid thread id
err
:=
p
.
SwitchThread
(
-
1
)
err
:=
p
.
SwitchThread
(
-
1
)
if
err
==
nil
{
if
err
==
nil
{
...
@@ -430,7 +430,7 @@ func TestStacktrace(t *testing.T) {
...
@@ -430,7 +430,7 @@ func TestStacktrace(t *testing.T) {
[]
loc
{{
8
,
"main.func1"
},
{
16
,
"main.main"
}},
[]
loc
{{
8
,
"main.func1"
},
{
16
,
"main.main"
}},
[]
loc
{{
8
,
"main.func1"
},
{
12
,
"main.func2"
},
{
17
,
"main.main"
}},
[]
loc
{{
8
,
"main.func1"
},
{
12
,
"main.func2"
},
{
17
,
"main.main"
}},
}
}
withTestProcess
(
"stacktraceprog"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"stacktraceprog"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
bp
,
err
:=
p
.
BreakByLocation
(
"main.stacktraceme"
)
bp
,
err
:=
p
.
BreakByLocation
(
"main.stacktraceme"
)
assertNoError
(
err
,
t
,
"BreakByLocation()"
)
assertNoError
(
err
,
t
,
"BreakByLocation()"
)
...
@@ -471,7 +471,7 @@ func TestStacktraceGoroutine(t *testing.T) {
...
@@ -471,7 +471,7 @@ func TestStacktraceGoroutine(t *testing.T) {
mainStack
:=
[]
loc
{{
21
,
"main.main"
}}
mainStack
:=
[]
loc
{{
21
,
"main.main"
}}
agoroutineStack
:=
[]
loc
{{
-
1
,
"runtime.goparkunlock"
},
{
-
1
,
"runtime.chansend"
},
{
-
1
,
"runtime.chansend1"
},
{
8
,
"main.agoroutine"
}}
agoroutineStack
:=
[]
loc
{{
-
1
,
"runtime.goparkunlock"
},
{
-
1
,
"runtime.chansend"
},
{
-
1
,
"runtime.chansend1"
},
{
8
,
"main.agoroutine"
}}
withTestProcess
(
"goroutinestackprog"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"goroutinestackprog"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
bp
,
err
:=
p
.
BreakByLocation
(
"main.stacktraceme"
)
bp
,
err
:=
p
.
BreakByLocation
(
"main.stacktraceme"
)
assertNoError
(
err
,
t
,
"BreakByLocation()"
)
assertNoError
(
err
,
t
,
"BreakByLocation()"
)
...
...
proc/stack.go
浏览文件 @
102d4c89
...
@@ -30,14 +30,14 @@ func (thread *Thread) Stacktrace(depth int) ([]Location, error) {
...
@@ -30,14 +30,14 @@ func (thread *Thread) Stacktrace(depth int) ([]Location, error) {
// Returns the stack trace for a goroutine
// Returns the stack trace for a goroutine
// Note that it doesn't include the current frame and the locations in the array are return addresses not call addresses
// Note that it doesn't include the current frame and the locations in the array are return addresses not call addresses
func
(
dbp
*
Debugged
Process
)
GoroutineStacktrace
(
g
*
G
,
depth
int
)
([]
Location
,
error
)
{
func
(
dbp
*
Process
)
GoroutineStacktrace
(
g
*
G
,
depth
int
)
([]
Location
,
error
)
{
if
g
.
thread
!=
nil
{
if
g
.
thread
!=
nil
{
return
g
.
thread
.
Stacktrace
(
depth
)
return
g
.
thread
.
Stacktrace
(
depth
)
}
}
return
dbp
.
stacktrace
(
g
.
PC
,
g
.
SP
,
depth
)
return
dbp
.
stacktrace
(
g
.
PC
,
g
.
SP
,
depth
)
}
}
func
(
dbp
*
Debugged
Process
)
GoroutineLocation
(
g
*
G
)
*
Location
{
func
(
dbp
*
Process
)
GoroutineLocation
(
g
*
G
)
*
Location
{
f
,
l
,
fn
:=
dbp
.
PCToLine
(
g
.
PC
)
f
,
l
,
fn
:=
dbp
.
PCToLine
(
g
.
PC
)
return
&
Location
{
PC
:
g
.
PC
,
File
:
f
,
Line
:
l
,
Fn
:
fn
}
return
&
Location
{
PC
:
g
.
PC
,
File
:
f
,
Line
:
l
,
Fn
:
fn
}
}
}
...
@@ -48,7 +48,7 @@ func (n NullAddrError) Error() string {
...
@@ -48,7 +48,7 @@ func (n NullAddrError) Error() string {
return
"NULL address"
return
"NULL address"
}
}
func
(
dbp
*
Debugged
Process
)
stacktrace
(
pc
,
sp
uint64
,
depth
int
)
([]
Location
,
error
)
{
func
(
dbp
*
Process
)
stacktrace
(
pc
,
sp
uint64
,
depth
int
)
([]
Location
,
error
)
{
var
(
var
(
ret
=
pc
ret
=
pc
data
=
make
([]
byte
,
dbp
.
arch
.
PtrSize
())
data
=
make
([]
byte
,
dbp
.
arch
.
PtrSize
())
...
...
proc/threads.go
浏览文件 @
102d4c89
...
@@ -12,7 +12,7 @@ import (
...
@@ -12,7 +12,7 @@ import (
// Thread represents a single thread in the traced process
// Thread represents a single thread in the traced process
// Id represents the thread id or port, Process holds a reference to the
// Id represents the thread id or port, Process holds a reference to the
//
Debugged
Process struct that contains info on the process as
// Process struct that contains info on the process as
// a whole, and Status represents the last result of a `wait` call
// a whole, and Status represents the last result of a `wait` call
// on this thread.
// on this thread.
type
Thread
struct
{
type
Thread
struct
{
...
@@ -20,7 +20,7 @@ type Thread struct {
...
@@ -20,7 +20,7 @@ type Thread struct {
Status
*
sys
.
WaitStatus
// Status returned from last wait call
Status
*
sys
.
WaitStatus
// Status returned from last wait call
CurrentBreakpoint
*
Breakpoint
// Breakpoint thread is currently stopped at
CurrentBreakpoint
*
Breakpoint
// Breakpoint thread is currently stopped at
dbp
*
Debugged
Process
dbp
*
Process
singleStepping
bool
singleStepping
bool
running
bool
running
bool
os
*
OSSpecificDetails
os
*
OSSpecificDetails
...
@@ -298,7 +298,7 @@ func (thread *Thread) getG() (g *G, err error) {
...
@@ -298,7 +298,7 @@ func (thread *Thread) getG() (g *G, err error) {
// we hit a breakpoint that isn't captured in our list of
// we hit a breakpoint that isn't captured in our list of
// known breakpoints.
// known breakpoints.
thread
.
dbp
.
halt
=
true
thread
.
dbp
.
halt
=
true
defer
func
(
dbp
*
Debugged
Process
)
{
dbp
.
halt
=
false
}(
thread
.
dbp
)
defer
func
(
dbp
*
Process
)
{
dbp
.
halt
=
false
}(
thread
.
dbp
)
if
_
,
err
=
thread
.
dbp
.
trapWait
(
-
1
);
err
!=
nil
{
if
_
,
err
=
thread
.
dbp
.
trapWait
(
-
1
);
err
!=
nil
{
return
return
}
}
...
...
proc/variables.go
浏览文件 @
102d4c89
...
@@ -65,7 +65,7 @@ func (g *G) ChanRecvBlocked() bool {
...
@@ -65,7 +65,7 @@ func (g *G) ChanRecvBlocked() bool {
}
}
// chanRecvReturnAddr returns the address of the return from a channel read.
// chanRecvReturnAddr returns the address of the return from a channel read.
func
(
g
*
G
)
chanRecvReturnAddr
(
dbp
*
Debugged
Process
)
(
uint64
,
error
)
{
func
(
g
*
G
)
chanRecvReturnAddr
(
dbp
*
Process
)
(
uint64
,
error
)
{
locs
,
err
:=
dbp
.
stacktrace
(
g
.
PC
,
g
.
SP
,
4
)
locs
,
err
:=
dbp
.
stacktrace
(
g
.
PC
,
g
.
SP
,
4
)
if
err
!=
nil
{
if
err
!=
nil
{
return
0
,
err
return
0
,
err
...
...
proc/variables_test.go
浏览文件 @
102d4c89
...
@@ -67,7 +67,7 @@ func TestVariableEvaluation(t *testing.T) {
...
@@ -67,7 +67,7 @@ func TestVariableEvaluation(t *testing.T) {
{
"NonExistent"
,
""
,
""
,
fmt
.
Errorf
(
"could not find symbol value for NonExistent"
)},
{
"NonExistent"
,
""
,
""
,
fmt
.
Errorf
(
"could not find symbol value for NonExistent"
)},
}
}
withTestProcess
(
"testvariables"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"testvariables"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
pc
,
_
,
_
:=
p
.
goSymTable
.
LineToPC
(
fixture
.
Source
,
57
)
pc
,
_
,
_
:=
p
.
goSymTable
.
LineToPC
(
fixture
.
Source
,
57
)
_
,
err
:=
p
.
Break
(
pc
)
_
,
err
:=
p
.
Break
(
pc
)
...
@@ -91,7 +91,7 @@ func TestVariableEvaluation(t *testing.T) {
...
@@ -91,7 +91,7 @@ func TestVariableEvaluation(t *testing.T) {
}
}
func
TestVariableFunctionScoping
(
t
*
testing
.
T
)
{
func
TestVariableFunctionScoping
(
t
*
testing
.
T
)
{
withTestProcess
(
"testvariables"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"testvariables"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
pc
,
_
,
_
:=
p
.
goSymTable
.
LineToPC
(
fixture
.
Source
,
57
)
pc
,
_
,
_
:=
p
.
goSymTable
.
LineToPC
(
fixture
.
Source
,
57
)
_
,
err
:=
p
.
Break
(
pc
)
_
,
err
:=
p
.
Break
(
pc
)
...
@@ -183,7 +183,7 @@ func TestLocalVariables(t *testing.T) {
...
@@ -183,7 +183,7 @@ func TestLocalVariables(t *testing.T) {
{
"baz"
,
"bazburzum"
,
"struct string"
,
nil
}}},
{
"baz"
,
"bazburzum"
,
"struct string"
,
nil
}}},
}
}
withTestProcess
(
"testvariables"
,
t
,
func
(
p
*
Debugged
Process
,
fixture
protest
.
Fixture
)
{
withTestProcess
(
"testvariables"
,
t
,
func
(
p
*
Process
,
fixture
protest
.
Fixture
)
{
pc
,
_
,
_
:=
p
.
goSymTable
.
LineToPC
(
fixture
.
Source
,
57
)
pc
,
_
,
_
:=
p
.
goSymTable
.
LineToPC
(
fixture
.
Source
,
57
)
_
,
err
:=
p
.
Break
(
pc
)
_
,
err
:=
p
.
Break
(
pc
)
...
...
service/debugger/debugger.go
浏览文件 @
102d4c89
...
@@ -12,14 +12,14 @@ import (
...
@@ -12,14 +12,14 @@ import (
// Debugger service.
// Debugger service.
//
//
// Debugger provides a higher level of
// Debugger provides a higher level of
// abstraction over proc.
Debugged
Process.
// abstraction over proc.Process.
// It handles converting from internal types to
// It handles converting from internal types to
// the types expected by clients. It also handles
// the types expected by clients. It also handles
// functionality needed by clients, but not needed in
// functionality needed by clients, but not needed in
// lower lever packages such as proc.
// lower lever packages such as proc.
type
Debugger
struct
{
type
Debugger
struct
{
config
*
Config
config
*
Config
process
*
proc
.
Debugged
Process
process
*
proc
.
Process
}
}
// Config provides the configuration to start a Debugger.
// Config provides the configuration to start a Debugger.
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录