proc: Improvements to Variable printing (#444)
* service/api: Removed unused fields of service/api.Function * proc/eval: Set return variable name to input expression * all: fine-grained control of loadValue for better variable printing Makes proc.(*Variable).loadValue loading parameters configurable through one extra argument of type LoadConfig. This interface is also exposed through the API so clients can control how much of a variable delve should read.
This commit is contained in:
parent
60946a759c
commit
473b66387c
@ -40,6 +40,10 @@ type B struct {
|
||||
ptr *A
|
||||
}
|
||||
|
||||
type D struct {
|
||||
u1, u2, u3, u4, u5, u6 uint32
|
||||
}
|
||||
|
||||
func afunc(x int) int {
|
||||
return x + 2
|
||||
}
|
||||
@ -181,6 +185,7 @@ func main() {
|
||||
aas[0].aas = aas
|
||||
b := B{A: A{-314}, C: &C{"hello"}, a: A{42}, ptr: &A{1337}}
|
||||
b2 := B{A: A{42}, a: A{47}}
|
||||
var sd D
|
||||
|
||||
for i := range benchparr {
|
||||
benchparr[i] = &benchstruct{}
|
||||
@ -192,5 +197,5 @@ func main() {
|
||||
fmt.Println(amb1)
|
||||
}
|
||||
runtime.Breakpoint()
|
||||
fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2)
|
||||
fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd)
|
||||
}
|
||||
|
@ -259,7 +259,7 @@ func traceCmd(cmd *cobra.Command, args []string) {
|
||||
return 1
|
||||
}
|
||||
for i := range funcs {
|
||||
_, err = client.CreateBreakpoint(&api.Breakpoint{FunctionName: funcs[i], Tracepoint: true, Line: -1, Stacktrace: traceStackDepth})
|
||||
_, err = client.CreateBreakpoint(&api.Breakpoint{FunctionName: funcs[i], Tracepoint: true, Line: -1, Stacktrace: traceStackDepth, LoadArgs: &terminal.ShortLoadConfig})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return 1
|
||||
|
@ -28,6 +28,8 @@ type Breakpoint struct {
|
||||
Goroutine bool // Retrieve goroutine information
|
||||
Stacktrace int // Number of stack frames to retrieve
|
||||
Variables []string // Variables to evaluate
|
||||
LoadArgs *LoadConfig
|
||||
LoadLocals *LoadConfig
|
||||
HitCount map[int]uint64 // Number of times a breakpoint has been reached in a certain goroutine
|
||||
TotalHitCount uint64 // Number of times a breakpoint has been reached
|
||||
|
||||
|
39
proc/eval.go
39
proc/eval.go
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
// EvalExpression returns the value of the given expression.
|
||||
func (scope *EvalScope) EvalExpression(expr string) (*Variable, error) {
|
||||
func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable, error) {
|
||||
t, err := parser.ParseExpr(expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -26,7 +26,10 @@ func (scope *EvalScope) EvalExpression(expr string) (*Variable, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ev.loadValue()
|
||||
ev.loadValue(cfg)
|
||||
if ev.Name == "" {
|
||||
ev.Name = expr
|
||||
}
|
||||
return ev, nil
|
||||
}
|
||||
|
||||
@ -113,7 +116,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
argv.loadValue()
|
||||
argv.loadValue(loadSingleValue)
|
||||
if argv.Unreadable != nil {
|
||||
return nil, argv.Unreadable
|
||||
}
|
||||
@ -277,7 +280,7 @@ func capBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
||||
case reflect.Slice:
|
||||
return newConstant(constant.MakeInt64(arg.Cap), arg.mem), nil
|
||||
case reflect.Chan:
|
||||
arg.loadValue()
|
||||
arg.loadValue(loadFullValue)
|
||||
if arg.Unreadable != nil {
|
||||
return nil, arg.Unreadable
|
||||
}
|
||||
@ -310,7 +313,7 @@ func lenBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
||||
}
|
||||
return newConstant(constant.MakeInt64(arg.Len), arg.mem), nil
|
||||
case reflect.Chan:
|
||||
arg.loadValue()
|
||||
arg.loadValue(loadFullValue)
|
||||
if arg.Unreadable != nil {
|
||||
return nil, arg.Unreadable
|
||||
}
|
||||
@ -340,8 +343,8 @@ func complexBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
||||
realev := args[0]
|
||||
imagev := args[1]
|
||||
|
||||
realev.loadValue()
|
||||
imagev.loadValue()
|
||||
realev.loadValue(loadSingleValue)
|
||||
imagev.loadValue(loadSingleValue)
|
||||
|
||||
if realev.Unreadable != nil {
|
||||
return nil, realev.Unreadable
|
||||
@ -387,7 +390,7 @@ func imagBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
||||
}
|
||||
|
||||
arg := args[0]
|
||||
arg.loadValue()
|
||||
arg.loadValue(loadSingleValue)
|
||||
|
||||
if arg.Unreadable != nil {
|
||||
return nil, arg.Unreadable
|
||||
@ -406,7 +409,7 @@ func realBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
||||
}
|
||||
|
||||
arg := args[0]
|
||||
arg.loadValue()
|
||||
arg.loadValue(loadSingleValue)
|
||||
|
||||
if arg.Unreadable != nil {
|
||||
return nil, arg.Unreadable
|
||||
@ -470,7 +473,7 @@ func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, err
|
||||
if xv.Kind != reflect.Interface {
|
||||
return nil, fmt.Errorf("expression \"%s\" not an interface", exprToString(node.X))
|
||||
}
|
||||
xv.loadInterface(0, false)
|
||||
xv.loadInterface(0, false, loadFullValue)
|
||||
if xv.Unreadable != nil {
|
||||
return nil, xv.Unreadable
|
||||
}
|
||||
@ -517,7 +520,7 @@ func (scope *EvalScope) evalIndex(node *ast.IndexExpr) (*Variable, error) {
|
||||
return xev.sliceAccess(int(n))
|
||||
|
||||
case reflect.Map:
|
||||
idxev.loadValue()
|
||||
idxev.loadValue(loadFullValue)
|
||||
if idxev.Unreadable != nil {
|
||||
return nil, idxev.Unreadable
|
||||
}
|
||||
@ -576,7 +579,7 @@ func (scope *EvalScope) evalReslice(node *ast.SliceExpr) (*Variable, error) {
|
||||
return nil, fmt.Errorf("second slice argument must be empty for maps")
|
||||
}
|
||||
xev.mapSkip += int(low)
|
||||
xev.loadValue()
|
||||
xev.loadValue(loadFullValue)
|
||||
if xev.Unreadable != nil {
|
||||
return nil, xev.Unreadable
|
||||
}
|
||||
@ -675,7 +678,7 @@ func (scope *EvalScope) evalUnary(node *ast.UnaryExpr) (*Variable, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
xv.loadValue()
|
||||
xv.loadValue(loadSingleValue)
|
||||
if xv.Unreadable != nil {
|
||||
return nil, xv.Unreadable
|
||||
}
|
||||
@ -774,8 +777,8 @@ func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
xv.loadValue()
|
||||
yv.loadValue()
|
||||
xv.loadValue(loadFullValue)
|
||||
yv.loadValue(loadFullValue)
|
||||
|
||||
if xv.Unreadable != nil {
|
||||
return nil, xv.Unreadable
|
||||
@ -943,7 +946,7 @@ func (v *Variable) asInt() (int64, error) {
|
||||
return 0, fmt.Errorf("can not convert constant %s to int", v.Value)
|
||||
}
|
||||
} else {
|
||||
v.loadValue()
|
||||
v.loadValue(loadSingleValue)
|
||||
if v.Unreadable != nil {
|
||||
return 0, v.Unreadable
|
||||
}
|
||||
@ -961,7 +964,7 @@ func (v *Variable) asUint() (uint64, error) {
|
||||
return 0, fmt.Errorf("can not convert constant %s to uint", v.Value)
|
||||
}
|
||||
} else {
|
||||
v.loadValue()
|
||||
v.loadValue(loadSingleValue)
|
||||
if v.Unreadable != nil {
|
||||
return 0, v.Unreadable
|
||||
}
|
||||
@ -1048,7 +1051,7 @@ func (v *Variable) mapAccess(idx *Variable) (*Variable, error) {
|
||||
first := true
|
||||
for it.next() {
|
||||
key := it.key()
|
||||
key.loadValue()
|
||||
key.loadValue(loadFullValue)
|
||||
if key.Unreadable != nil {
|
||||
return nil, fmt.Errorf("can not access unreadable map: %v", key.Unreadable)
|
||||
}
|
||||
|
@ -805,7 +805,7 @@ func (dbp *Process) execPtraceFunc(fn func()) {
|
||||
}
|
||||
|
||||
func (dbp *Process) getGoInformation() (ver GoVersion, isextld bool, err error) {
|
||||
vv, err := dbp.EvalPackageVariable("runtime.buildVersion")
|
||||
vv, err := dbp.EvalPackageVariable("runtime.buildVersion", LoadConfig{true, 0, 64, 0, 0})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Could not determine version number: %v\n", err)
|
||||
return
|
||||
|
@ -19,6 +19,8 @@ import (
|
||||
protest "github.com/derekparker/delve/proc/test"
|
||||
)
|
||||
|
||||
var normalLoadConfig = LoadConfig{true, 1, 64, 64, -1}
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(4)
|
||||
os.Setenv("GOMAXPROCS", "4")
|
||||
@ -960,7 +962,7 @@ func evalVariable(p *Process, symbol string) (*Variable, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return scope.EvalVariable(symbol)
|
||||
return scope.EvalVariable(symbol, normalLoadConfig)
|
||||
}
|
||||
|
||||
func setVariable(p *Process, symbol, value string) error {
|
||||
@ -1085,7 +1087,7 @@ func TestFrameEvaluation(t *testing.T) {
|
||||
scope, err := p.ConvertEvalScope(g.ID, frame)
|
||||
assertNoError(err, t, "ConvertEvalScope()")
|
||||
t.Logf("scope = %v", scope)
|
||||
v, err := scope.EvalVariable("i")
|
||||
v, err := scope.EvalVariable("i", normalLoadConfig)
|
||||
t.Logf("v = %v", v)
|
||||
if err != nil {
|
||||
t.Logf("Goroutine %d: %v\n", g.ID, err)
|
||||
@ -1109,7 +1111,7 @@ func TestFrameEvaluation(t *testing.T) {
|
||||
for i := 0; i <= 3; i++ {
|
||||
scope, err := p.ConvertEvalScope(g.ID, i+1)
|
||||
assertNoError(err, t, fmt.Sprintf("ConvertEvalScope() on frame %d", i+1))
|
||||
v, err := scope.EvalVariable("n")
|
||||
v, err := scope.EvalVariable("n", normalLoadConfig)
|
||||
assertNoError(err, t, fmt.Sprintf("EvalVariable() on frame %d", i+1))
|
||||
n, _ := constant.Int64Val(v.Value)
|
||||
t.Logf("frame %d n %d\n", i+1, n)
|
||||
@ -1138,7 +1140,7 @@ func TestPointerSetting(t *testing.T) {
|
||||
// change p1 to point to i2
|
||||
scope, err := p.CurrentThread.Scope()
|
||||
assertNoError(err, t, "Scope()")
|
||||
i2addr, err := scope.EvalExpression("i2")
|
||||
i2addr, err := scope.EvalExpression("i2", normalLoadConfig)
|
||||
assertNoError(err, t, "EvalExpression()")
|
||||
assertNoError(setVariable(p, "p1", fmt.Sprintf("(*int)(0x%x)", i2addr.Addr)), t, "SetVariable()")
|
||||
pval(2)
|
||||
@ -1278,10 +1280,10 @@ func TestBreakpointCountsWithDetection(t *testing.T) {
|
||||
}
|
||||
scope, err := th.Scope()
|
||||
assertNoError(err, t, "Scope()")
|
||||
v, err := scope.EvalVariable("i")
|
||||
v, err := scope.EvalVariable("i", normalLoadConfig)
|
||||
assertNoError(err, t, "evalVariable")
|
||||
i, _ := constant.Int64Val(v.Value)
|
||||
v, err = scope.EvalVariable("id")
|
||||
v, err = scope.EvalVariable("id", normalLoadConfig)
|
||||
assertNoError(err, t, "evalVariable")
|
||||
id, _ := constant.Int64Val(v.Value)
|
||||
m[id] = i
|
||||
@ -1409,7 +1411,7 @@ func BenchmarkLocalVariables(b *testing.B) {
|
||||
scope, err := p.CurrentThread.Scope()
|
||||
assertNoError(err, b, "Scope()")
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := scope.LocalVariables()
|
||||
_, err := scope.LocalVariables(normalLoadConfig)
|
||||
assertNoError(err, b, "LocalVariables()")
|
||||
}
|
||||
})
|
||||
@ -1634,7 +1636,7 @@ func TestPackageVariables(t *testing.T) {
|
||||
assertNoError(err, t, "Continue()")
|
||||
scope, err := p.CurrentThread.Scope()
|
||||
assertNoError(err, t, "Scope()")
|
||||
vars, err := scope.PackageVariables()
|
||||
vars, err := scope.PackageVariables(normalLoadConfig)
|
||||
assertNoError(err, t, "PackageVariables()")
|
||||
failed := false
|
||||
for _, v := range vars {
|
||||
|
@ -19,9 +19,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
maxVariableRecurse = 1 // How far to recurse when evaluating nested types.
|
||||
maxArrayValues = 64 // Max value for reading large arrays.
|
||||
maxErrCount = 3 // Max number of read errors to accept while evaluating slices, arrays and structs
|
||||
maxErrCount = 3 // Max number of read errors to accept while evaluating slices, arrays and structs
|
||||
|
||||
maxArrayStridePrefetch = 1024 // Maximum size of array stride for which we will prefetch the array contents
|
||||
|
||||
@ -68,6 +66,22 @@ type Variable struct {
|
||||
Unreadable error
|
||||
}
|
||||
|
||||
type LoadConfig struct {
|
||||
// FollowPointers requests pointers to be automatically dereferenced.
|
||||
FollowPointers bool
|
||||
// MaxVariableRecurse is how far to recurse when evaluating nested types.
|
||||
MaxVariableRecurse int
|
||||
// MaxStringLen is the maximum number of bytes read from a string
|
||||
MaxStringLen int
|
||||
// MaxArrayValues is the maximum number of elements read from an array, a slice or a map.
|
||||
MaxArrayValues int
|
||||
// MaxStructFields is the maximum number of fields read from a struct, -1 will read all fields.
|
||||
MaxStructFields int
|
||||
}
|
||||
|
||||
var loadSingleValue = LoadConfig{false, 0, 64, 0, 0}
|
||||
var loadFullValue = LoadConfig{true, 1, 64, 64, -1}
|
||||
|
||||
// M represents a runtime M (OS thread) structure.
|
||||
type M struct {
|
||||
procid int // Thread ID or port.
|
||||
@ -250,6 +264,7 @@ func newConstant(val constant.Value, mem memoryReadWriter) *Variable {
|
||||
}
|
||||
|
||||
var nilVariable = &Variable{
|
||||
Name: "nil",
|
||||
Addr: 0,
|
||||
Base: 0,
|
||||
Kind: reflect.Ptr,
|
||||
@ -355,7 +370,7 @@ func (gvar *Variable) parseG() (*G, error) {
|
||||
}
|
||||
return nil, NoGError{ tid: id }
|
||||
}
|
||||
gvar.loadValue()
|
||||
gvar.loadValue(loadFullValue)
|
||||
if gvar.Unreadable != nil {
|
||||
return nil, gvar.Unreadable
|
||||
}
|
||||
@ -393,7 +408,7 @@ func (v *Variable) toFieldNamed(name string) *Variable {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
v.loadValue()
|
||||
v.loadValue(loadFullValue)
|
||||
if v.Unreadable != nil {
|
||||
return nil
|
||||
}
|
||||
@ -435,8 +450,8 @@ func (g *G) Go() Location {
|
||||
}
|
||||
|
||||
// EvalVariable returns the value of the given expression (backwards compatibility).
|
||||
func (scope *EvalScope) EvalVariable(name string) (*Variable, error) {
|
||||
return scope.EvalExpression(name)
|
||||
func (scope *EvalScope) EvalVariable(name string, cfg LoadConfig) (*Variable, error) {
|
||||
return scope.EvalExpression(name, cfg)
|
||||
}
|
||||
|
||||
// SetVariable sets the value of the named variable
|
||||
@ -469,7 +484,7 @@ func (scope *EvalScope) SetVariable(name, value string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
yv.loadValue()
|
||||
yv.loadValue(loadSingleValue)
|
||||
|
||||
if err := yv.isType(xv.RealType, xv.Kind); err != nil {
|
||||
return err
|
||||
@ -482,13 +497,13 @@ func (scope *EvalScope) SetVariable(name, value string) error {
|
||||
return xv.setValue(yv)
|
||||
}
|
||||
|
||||
func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry) (*Variable, error) {
|
||||
func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry, cfg LoadConfig) (*Variable, error) {
|
||||
rdr := scope.DwarfReader()
|
||||
v, err := scope.extractVarInfoFromEntry(entry, rdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.loadValue()
|
||||
v.loadValue(cfg)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
@ -518,17 +533,17 @@ func (scope *EvalScope) extractVarInfo(varName string) (*Variable, error) {
|
||||
}
|
||||
|
||||
// LocalVariables returns all local variables from the current function scope.
|
||||
func (scope *EvalScope) LocalVariables() ([]*Variable, error) {
|
||||
return scope.variablesByTag(dwarf.TagVariable)
|
||||
func (scope *EvalScope) LocalVariables(cfg LoadConfig) ([]*Variable, error) {
|
||||
return scope.variablesByTag(dwarf.TagVariable, cfg)
|
||||
}
|
||||
|
||||
// FunctionArguments returns the name, value, and type of all current function arguments.
|
||||
func (scope *EvalScope) FunctionArguments() ([]*Variable, error) {
|
||||
return scope.variablesByTag(dwarf.TagFormalParameter)
|
||||
func (scope *EvalScope) FunctionArguments(cfg LoadConfig) ([]*Variable, error) {
|
||||
return scope.variablesByTag(dwarf.TagFormalParameter, cfg)
|
||||
}
|
||||
|
||||
// PackageVariables returns the name, value, and type of all package variables in the application.
|
||||
func (scope *EvalScope) PackageVariables() ([]*Variable, error) {
|
||||
func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) {
|
||||
var vars []*Variable
|
||||
reader := scope.DwarfReader()
|
||||
|
||||
@ -548,7 +563,7 @@ func (scope *EvalScope) PackageVariables() ([]*Variable, error) {
|
||||
}
|
||||
|
||||
// Ignore errors trying to extract values
|
||||
val, err := scope.extractVariableFromEntry(entry)
|
||||
val, err := scope.extractVariableFromEntry(entry, cfg)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@ -560,14 +575,14 @@ func (scope *EvalScope) PackageVariables() ([]*Variable, error) {
|
||||
|
||||
// EvalPackageVariable will evaluate the package level variable
|
||||
// specified by 'name'.
|
||||
func (dbp *Process) EvalPackageVariable(name string) (*Variable, error) {
|
||||
func (dbp *Process) EvalPackageVariable(name string, cfg LoadConfig) (*Variable, error) {
|
||||
scope := &EvalScope{Thread: dbp.CurrentThread, PC: 0, CFA: 0}
|
||||
|
||||
v, err := scope.packageVarAddr(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.loadValue()
|
||||
v.loadValue(cfg)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
@ -708,11 +723,11 @@ func (v *Variable) maybeDereference() *Variable {
|
||||
}
|
||||
|
||||
// Extracts the value of the variable at the given address.
|
||||
func (v *Variable) loadValue() {
|
||||
v.loadValueInternal(0)
|
||||
func (v *Variable) loadValue(cfg LoadConfig) {
|
||||
v.loadValueInternal(0, cfg)
|
||||
}
|
||||
|
||||
func (v *Variable) loadValueInternal(recurseLevel int) {
|
||||
func (v *Variable) loadValueInternal(recurseLevel int, cfg LoadConfig) {
|
||||
if v.Unreadable != nil || v.loaded || (v.Addr == 0 && v.Base == 0) {
|
||||
return
|
||||
}
|
||||
@ -722,30 +737,34 @@ func (v *Variable) loadValueInternal(recurseLevel int) {
|
||||
case reflect.Ptr, reflect.UnsafePointer:
|
||||
v.Len = 1
|
||||
v.Children = []Variable{*v.maybeDereference()}
|
||||
// Don't increase the recursion level when dereferencing pointers
|
||||
v.Children[0].loadValueInternal(recurseLevel)
|
||||
if cfg.FollowPointers {
|
||||
// Don't increase the recursion level when dereferencing pointers
|
||||
v.Children[0].loadValueInternal(recurseLevel, cfg)
|
||||
} else {
|
||||
v.Children[0].OnlyAddr = true
|
||||
}
|
||||
|
||||
case reflect.Chan:
|
||||
sv := v.clone()
|
||||
sv.RealType = resolveTypedef(&(sv.RealType.(*dwarf.ChanType).TypedefType))
|
||||
sv = sv.maybeDereference()
|
||||
sv.loadValueInternal(recurseLevel)
|
||||
sv.loadValueInternal(0, loadFullValue)
|
||||
v.Children = sv.Children
|
||||
v.Len = sv.Len
|
||||
v.Base = sv.Addr
|
||||
|
||||
case reflect.Map:
|
||||
if recurseLevel <= maxVariableRecurse {
|
||||
v.loadMap(recurseLevel)
|
||||
if recurseLevel <= cfg.MaxVariableRecurse {
|
||||
v.loadMap(recurseLevel, cfg)
|
||||
}
|
||||
|
||||
case reflect.String:
|
||||
var val string
|
||||
val, v.Unreadable = readStringValue(v.mem, v.Base, v.Len)
|
||||
val, v.Unreadable = readStringValue(v.mem, v.Base, v.Len, cfg)
|
||||
v.Value = constant.MakeString(val)
|
||||
|
||||
case reflect.Slice, reflect.Array:
|
||||
v.loadArrayValues(recurseLevel)
|
||||
v.loadArrayValues(recurseLevel, cfg)
|
||||
|
||||
case reflect.Struct:
|
||||
v.mem = cacheMemory(v.mem, v.Addr, int(v.RealType.Size()))
|
||||
@ -753,18 +772,21 @@ func (v *Variable) loadValueInternal(recurseLevel int) {
|
||||
v.Len = int64(len(t.Field))
|
||||
// Recursively call extractValue to grab
|
||||
// the value of all the members of the struct.
|
||||
if recurseLevel <= maxVariableRecurse {
|
||||
if recurseLevel <= cfg.MaxVariableRecurse {
|
||||
v.Children = make([]Variable, 0, len(t.Field))
|
||||
for i, field := range t.Field {
|
||||
if cfg.MaxStructFields >= 0 && len(v.Children) >= cfg.MaxStructFields {
|
||||
break
|
||||
}
|
||||
f, _ := v.toField(field)
|
||||
v.Children = append(v.Children, *f)
|
||||
v.Children[i].Name = field.Name
|
||||
v.Children[i].loadValueInternal(recurseLevel + 1)
|
||||
v.Children[i].loadValueInternal(recurseLevel+1, cfg)
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Interface:
|
||||
v.loadInterface(recurseLevel, true)
|
||||
v.loadInterface(recurseLevel, true, cfg)
|
||||
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
v.readComplex(v.RealType.(*dwarf.ComplexType).ByteSize)
|
||||
@ -853,10 +875,10 @@ func readStringInfo(mem memoryReadWriter, arch Arch, addr uintptr) (uintptr, int
|
||||
return addr, strlen, nil
|
||||
}
|
||||
|
||||
func readStringValue(mem memoryReadWriter, addr uintptr, strlen int64) (string, error) {
|
||||
func readStringValue(mem memoryReadWriter, addr uintptr, strlen int64, cfg LoadConfig) (string, error) {
|
||||
count := strlen
|
||||
if count > maxArrayValues {
|
||||
count = maxArrayValues
|
||||
if count > int64(cfg.MaxStringLen) {
|
||||
count = int64(cfg.MaxStringLen)
|
||||
}
|
||||
|
||||
val, err := mem.readMemory(addr, int(count))
|
||||
@ -869,16 +891,6 @@ func readStringValue(mem memoryReadWriter, addr uintptr, strlen int64) (string,
|
||||
return retstr, nil
|
||||
}
|
||||
|
||||
func readString(mem memoryReadWriter, arch Arch, addr uintptr) (string, int64, error) {
|
||||
addr, strlen, err := readStringInfo(mem, arch, addr)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
retstr, err := readStringValue(mem, addr, strlen)
|
||||
return retstr, strlen, err
|
||||
}
|
||||
|
||||
func (v *Variable) loadSliceInfo(t *dwarf.SliceType) {
|
||||
v.mem = cacheMemory(v.mem, v.Addr, int(t.Size()))
|
||||
|
||||
@ -900,14 +912,14 @@ func (v *Variable) loadSliceInfo(t *dwarf.SliceType) {
|
||||
}
|
||||
case "len":
|
||||
lstrAddr, _ := v.toField(f)
|
||||
lstrAddr.loadValue()
|
||||
lstrAddr.loadValue(loadSingleValue)
|
||||
err = lstrAddr.Unreadable
|
||||
if err == nil {
|
||||
v.Len, _ = constant.Int64Val(lstrAddr.Value)
|
||||
}
|
||||
case "cap":
|
||||
cstrAddr, _ := v.toField(f)
|
||||
cstrAddr.loadValue()
|
||||
cstrAddr.loadValue(loadSingleValue)
|
||||
err = cstrAddr.Unreadable
|
||||
if err == nil {
|
||||
v.Cap, _ = constant.Int64Val(cstrAddr.Value)
|
||||
@ -927,7 +939,7 @@ func (v *Variable) loadSliceInfo(t *dwarf.SliceType) {
|
||||
return
|
||||
}
|
||||
|
||||
func (v *Variable) loadArrayValues(recurseLevel int) {
|
||||
func (v *Variable) loadArrayValues(recurseLevel int, cfg LoadConfig) {
|
||||
if v.Unreadable != nil {
|
||||
return
|
||||
}
|
||||
@ -938,8 +950,8 @@ func (v *Variable) loadArrayValues(recurseLevel int) {
|
||||
|
||||
count := v.Len
|
||||
// Cap number of elements
|
||||
if count > maxArrayValues {
|
||||
count = maxArrayValues
|
||||
if count > int64(cfg.MaxArrayValues) {
|
||||
count = int64(cfg.MaxArrayValues)
|
||||
}
|
||||
|
||||
if v.stride < maxArrayStridePrefetch {
|
||||
@ -950,7 +962,7 @@ func (v *Variable) loadArrayValues(recurseLevel int) {
|
||||
|
||||
for i := int64(0); i < count; i++ {
|
||||
fieldvar := v.newVariable("", uintptr(int64(v.Base)+(i*v.stride)), v.fieldType)
|
||||
fieldvar.loadValueInternal(recurseLevel + 1)
|
||||
fieldvar.loadValueInternal(recurseLevel+1, cfg)
|
||||
|
||||
if fieldvar.Unreadable != nil {
|
||||
errcount++
|
||||
@ -979,8 +991,8 @@ func (v *Variable) readComplex(size int64) {
|
||||
|
||||
realvar := v.newVariable("real", v.Addr, ftyp)
|
||||
imagvar := v.newVariable("imaginary", v.Addr+uintptr(fs), ftyp)
|
||||
realvar.loadValue()
|
||||
imagvar.loadValue()
|
||||
realvar.loadValue(loadSingleValue)
|
||||
imagvar.loadValue(loadSingleValue)
|
||||
v.Value = constant.BinaryOp(realvar.Value, token.ADD, constant.MakeImag(imagvar.Value))
|
||||
}
|
||||
|
||||
@ -1131,7 +1143,7 @@ func (v *Variable) readFunctionPtr() {
|
||||
v.Value = constant.MakeString(fn.Name)
|
||||
}
|
||||
|
||||
func (v *Variable) loadMap(recurseLevel int) {
|
||||
func (v *Variable) loadMap(recurseLevel int, cfg LoadConfig) {
|
||||
it := v.mapIterator()
|
||||
if it == nil {
|
||||
return
|
||||
@ -1147,13 +1159,13 @@ func (v *Variable) loadMap(recurseLevel int) {
|
||||
count := 0
|
||||
errcount := 0
|
||||
for it.next() {
|
||||
if count >= maxArrayValues {
|
||||
if count >= cfg.MaxArrayValues {
|
||||
break
|
||||
}
|
||||
key := it.key()
|
||||
val := it.value()
|
||||
key.loadValueInternal(recurseLevel + 1)
|
||||
val.loadValueInternal(recurseLevel + 1)
|
||||
key.loadValueInternal(recurseLevel+1, cfg)
|
||||
val.loadValueInternal(recurseLevel+1, cfg)
|
||||
if key.Unreadable != nil || val.Unreadable != nil {
|
||||
errcount++
|
||||
}
|
||||
@ -1381,7 +1393,7 @@ func mapEvacuated(b *Variable) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *Variable) loadInterface(recurseLevel int, loadData bool) {
|
||||
func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig) {
|
||||
var typestring, data *Variable
|
||||
isnil := false
|
||||
|
||||
@ -1431,7 +1443,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool) {
|
||||
data = data.maybeDereference()
|
||||
v.Children = []Variable{*data}
|
||||
if loadData {
|
||||
v.Children[0].loadValueInternal(recurseLevel)
|
||||
v.Children[0].loadValueInternal(recurseLevel, cfg)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -1440,7 +1452,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool) {
|
||||
v.Unreadable = fmt.Errorf("invalid interface type")
|
||||
return
|
||||
}
|
||||
typestring.loadValue()
|
||||
typestring.loadValue(LoadConfig{false, 0, 512, 0, 0})
|
||||
if typestring.Unreadable != nil {
|
||||
v.Unreadable = fmt.Errorf("invalid interface type: %v", typestring.Unreadable)
|
||||
return
|
||||
@ -1468,13 +1480,15 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool) {
|
||||
|
||||
v.Children = []Variable{*data}
|
||||
if loadData {
|
||||
v.Children[0].loadValueInternal(recurseLevel)
|
||||
v.Children[0].loadValueInternal(recurseLevel, cfg)
|
||||
} else {
|
||||
v.Children[0].OnlyAddr = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Fetches all variables of a specific type in the current function scope
|
||||
func (scope *EvalScope) variablesByTag(tag dwarf.Tag) ([]*Variable, error) {
|
||||
func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg LoadConfig) ([]*Variable, error) {
|
||||
reader := scope.DwarfReader()
|
||||
|
||||
_, err := reader.SeekToFunction(scope.PC)
|
||||
@ -1489,7 +1503,7 @@ func (scope *EvalScope) variablesByTag(tag dwarf.Tag) ([]*Variable, error) {
|
||||
}
|
||||
|
||||
if entry.Tag == tag {
|
||||
val, err := scope.extractVariableFromEntry(entry)
|
||||
val, err := scope.extractVariableFromEntry(entry, cfg)
|
||||
if err != nil {
|
||||
// skip variables that we can't parse yet
|
||||
continue
|
||||
|
@ -27,6 +27,8 @@ func ConvertBreakpoint(bp *proc.Breakpoint) *Breakpoint {
|
||||
Stacktrace: bp.Stacktrace,
|
||||
Goroutine: bp.Goroutine,
|
||||
Variables: bp.Variables,
|
||||
LoadArgs: LoadConfigFromProc(bp.LoadArgs),
|
||||
LoadLocals: LoadConfigFromProc(bp.LoadLocals),
|
||||
TotalHitCount: bp.TotalHitCount,
|
||||
}
|
||||
|
||||
@ -217,3 +219,29 @@ func ConvertAsmInstruction(inst proc.AsmInstruction, text string) AsmInstruction
|
||||
AtPC: inst.AtPC,
|
||||
}
|
||||
}
|
||||
|
||||
func LoadConfigToProc(cfg *LoadConfig) *proc.LoadConfig {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
return &proc.LoadConfig{
|
||||
cfg.FollowPointers,
|
||||
cfg.MaxVariableRecurse,
|
||||
cfg.MaxStringLen,
|
||||
cfg.MaxArrayValues,
|
||||
cfg.MaxStructFields,
|
||||
}
|
||||
}
|
||||
|
||||
func LoadConfigFromProc(cfg *proc.LoadConfig) *LoadConfig {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
return &LoadConfig{
|
||||
cfg.FollowPointers,
|
||||
cfg.MaxVariableRecurse,
|
||||
cfg.MaxStringLen,
|
||||
cfg.MaxArrayValues,
|
||||
cfg.MaxStructFields,
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ func (v *Variable) SinglelineString() string {
|
||||
// MultilineString returns a representation of v on multiple lines.
|
||||
func (v *Variable) MultilineString(indent string) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(indent)
|
||||
v.writeTo(&buf, true, true, true, indent)
|
||||
return buf.String()
|
||||
}
|
||||
@ -48,7 +47,7 @@ func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType bool, inden
|
||||
case reflect.Ptr:
|
||||
if v.Type == "" {
|
||||
fmt.Fprintf(buf, "nil")
|
||||
} else if v.Children[0].OnlyAddr {
|
||||
} else if v.Children[0].OnlyAddr && v.Children[0].Addr != 0 {
|
||||
fmt.Fprintf(buf, "(%s)(0x%x)", v.Type, v.Children[0].Addr)
|
||||
} else {
|
||||
fmt.Fprintf(buf, "*")
|
||||
@ -82,7 +81,18 @@ func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType bool, inden
|
||||
fmt.Fprintf(buf, "%s(%s) ", v.Type, v.Children[0].Type)
|
||||
}
|
||||
}
|
||||
v.Children[0].writeTo(buf, false, newlines, false, indent)
|
||||
data := v.Children[0]
|
||||
if data.Kind == reflect.Ptr {
|
||||
if data.Children[0].Addr == 0 {
|
||||
fmt.Fprintf(buf, "nil")
|
||||
} else if data.Children[0].OnlyAddr {
|
||||
fmt.Fprintf(buf, "0x%x", v.Children[0].Addr)
|
||||
} else {
|
||||
v.Children[0].writeTo(buf, false, newlines, false, indent)
|
||||
}
|
||||
} else {
|
||||
v.Children[0].writeTo(buf, false, newlines, false, indent)
|
||||
}
|
||||
case reflect.Map:
|
||||
v.writeMapTo(buf, newlines, includeType, indent)
|
||||
case reflect.Func:
|
||||
@ -125,7 +135,7 @@ func (v *Variable) writeArrayTo(buf io.Writer, newlines, includeType bool, inden
|
||||
}
|
||||
|
||||
func (v *Variable) writeStructTo(buf io.Writer, newlines, includeType bool, indent string) {
|
||||
if int(v.Len) != len(v.Children) {
|
||||
if int(v.Len) != len(v.Children) && len(v.Children) == 0 {
|
||||
fmt.Fprintf(buf, "(*%s)(0x%x)", v.Type, v.Addr)
|
||||
return
|
||||
}
|
||||
@ -152,9 +162,15 @@ func (v *Variable) writeStructTo(buf io.Writer, newlines, includeType bool, inde
|
||||
}
|
||||
}
|
||||
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s", indent)
|
||||
if len(v.Children) != int(v.Len) {
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
|
||||
} else {
|
||||
fmt.Fprintf(buf, ",")
|
||||
}
|
||||
fmt.Fprintf(buf, "...+%d more", int(v.Len)-len(v.Children))
|
||||
}
|
||||
|
||||
fmt.Fprintf(buf, "}")
|
||||
}
|
||||
|
||||
@ -184,12 +200,16 @@ func (v *Variable) writeMapTo(buf io.Writer, newlines, includeType bool, indent
|
||||
}
|
||||
|
||||
if len(v.Children)/2 != int(v.Len) {
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
|
||||
if len(v.Children) != 0 {
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
|
||||
} else {
|
||||
fmt.Fprintf(buf, ",")
|
||||
}
|
||||
fmt.Fprintf(buf, "...+%d more", int(v.Len)-(len(v.Children)/2))
|
||||
} else {
|
||||
fmt.Fprintf(buf, ",")
|
||||
fmt.Fprintf(buf, "...")
|
||||
}
|
||||
fmt.Fprintf(buf, "...+%d more", int(v.Len)-(len(v.Children)/2))
|
||||
}
|
||||
|
||||
if nl {
|
||||
@ -277,12 +297,16 @@ func (v *Variable) writeSliceOrArrayTo(buf io.Writer, newlines bool, indent stri
|
||||
}
|
||||
|
||||
if len(v.Children) != int(v.Len) {
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
|
||||
if len(v.Children) != 0 {
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
|
||||
} else {
|
||||
fmt.Fprintf(buf, ",")
|
||||
}
|
||||
fmt.Fprintf(buf, "...+%d more", int(v.Len)-len(v.Children))
|
||||
} else {
|
||||
fmt.Fprintf(buf, ",")
|
||||
fmt.Fprintf(buf, "...")
|
||||
}
|
||||
fmt.Fprintf(buf, "...+%d more", int(v.Len)-len(v.Children))
|
||||
}
|
||||
|
||||
if nl {
|
||||
|
@ -51,8 +51,12 @@ type Breakpoint struct {
|
||||
Goroutine bool `json:"goroutine"`
|
||||
// number of stack frames to retrieve
|
||||
Stacktrace int `json:"stacktrace"`
|
||||
// variables to evaluate
|
||||
// expressions to evaluate
|
||||
Variables []string `json:"variables,omitempty"`
|
||||
// LoadArgs requests loading function arguments when the breakpoint is hit
|
||||
LoadArgs *LoadConfig
|
||||
// LoadLocals requests loading function locals when the breakpoint is hit
|
||||
LoadLocals *LoadConfig
|
||||
// number of times a breakpoint has been reached in a certain goroutine
|
||||
HitCount map[string]uint64 `json:"hitCount"`
|
||||
// number of times a breakpoint has been reached
|
||||
@ -129,10 +133,6 @@ type Function struct {
|
||||
Value uint64 `json:"value"`
|
||||
Type byte `json:"type"`
|
||||
GoType uint64 `json:"goType"`
|
||||
// Args are the function arguments in a thread context.
|
||||
Args []Variable `json:"args"`
|
||||
// Locals are the thread local variables.
|
||||
Locals []Variable `json:"locals"`
|
||||
}
|
||||
|
||||
// Variable describes a variable.
|
||||
@ -170,6 +170,20 @@ type Variable struct {
|
||||
Unreadable string `json:"unreadable"`
|
||||
}
|
||||
|
||||
// LoadConfig describes how to load values from target's memory
|
||||
type LoadConfig struct {
|
||||
// FollowPointers requests pointers to be automatically dereferenced.
|
||||
FollowPointers bool
|
||||
// MaxVariableRecurse is how far to recurse when evaluating nested types.
|
||||
MaxVariableRecurse int
|
||||
// MaxStringLen is the maximum number of bytes read from a string
|
||||
MaxStringLen int
|
||||
// MaxArrayValues is the maximum number of elements read from an array, a slice or a map.
|
||||
MaxArrayValues int
|
||||
// MaxStructFields is the maximum number of fields read from a struct, -1 will read all fields.
|
||||
MaxStructFields int
|
||||
}
|
||||
|
||||
// Goroutine represents the information relevant to Delve from the runtime's
|
||||
// internal G structure.
|
||||
type Goroutine struct {
|
||||
@ -201,6 +215,7 @@ type BreakpointInfo struct {
|
||||
Goroutine *Goroutine `json:"goroutine,omitempty"`
|
||||
Variables []Variable `json:"variables,omitempty"`
|
||||
Arguments []Variable `json:"arguments,omitempty"`
|
||||
Locals []Variable `json:"locals,omitempty"`
|
||||
}
|
||||
|
||||
type EvalScope struct {
|
||||
|
@ -56,9 +56,9 @@ type Client interface {
|
||||
GetThread(id int) (*api.Thread, error)
|
||||
|
||||
// ListPackageVariables lists all package variables in the context of the current thread.
|
||||
ListPackageVariables(filter string) ([]api.Variable, error)
|
||||
ListPackageVariables(filter string, cfg api.LoadConfig) ([]api.Variable, error)
|
||||
// EvalVariable returns a variable in the context of the current thread.
|
||||
EvalVariable(scope api.EvalScope, symbol string) (*api.Variable, error)
|
||||
EvalVariable(scope api.EvalScope, symbol string, cfg api.LoadConfig) (*api.Variable, error)
|
||||
|
||||
// SetVariable sets the value of a variable
|
||||
SetVariable(scope api.EvalScope, symbol, value string) error
|
||||
@ -70,9 +70,9 @@ type Client interface {
|
||||
// ListTypes lists all types in the process matching filter.
|
||||
ListTypes(filter string) ([]string, error)
|
||||
// ListLocals lists all local variables in scope.
|
||||
ListLocalVariables(scope api.EvalScope) ([]api.Variable, error)
|
||||
ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error)
|
||||
// ListFunctionArgs lists all arguments to the current function.
|
||||
ListFunctionArgs(scope api.EvalScope) ([]api.Variable, error)
|
||||
ListFunctionArgs(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error)
|
||||
// ListRegisters lists registers and their values.
|
||||
ListRegisters() (string, error)
|
||||
|
||||
@ -80,7 +80,7 @@ type Client interface {
|
||||
ListGoroutines() ([]*api.Goroutine, error)
|
||||
|
||||
// Returns stacktrace
|
||||
Stacktrace(int, int, bool) ([]api.Stackframe, error)
|
||||
Stacktrace(int, int, *api.LoadConfig) ([]api.Stackframe, error)
|
||||
|
||||
// Returns whether we attached to a running process or not
|
||||
AttachedToExistingProcess() bool
|
||||
|
@ -249,6 +249,8 @@ func copyBreakpointInfo(bp *proc.Breakpoint, requested *api.Breakpoint) (err err
|
||||
bp.Goroutine = requested.Goroutine
|
||||
bp.Stacktrace = requested.Stacktrace
|
||||
bp.Variables = requested.Variables
|
||||
bp.LoadArgs = api.LoadConfigToProc(requested.LoadArgs)
|
||||
bp.LoadLocals = api.LoadConfigToProc(requested.LoadLocals)
|
||||
bp.Cond = nil
|
||||
if requested.Cond != "" {
|
||||
bp.Cond, err = parser.ParseExpr(requested.Cond)
|
||||
@ -444,7 +446,7 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bpi.Stacktrace, err = d.convertStacktrace(rawlocs, false)
|
||||
bpi.Stacktrace, err = d.convertStacktrace(rawlocs, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -459,15 +461,21 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
|
||||
bpi.Variables = make([]api.Variable, len(bp.Variables))
|
||||
}
|
||||
for i := range bp.Variables {
|
||||
v, err := s.EvalVariable(bp.Variables[i])
|
||||
v, err := s.EvalVariable(bp.Variables[i], proc.LoadConfig{true, 1, 64, 64, -1})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bpi.Variables[i] = *api.ConvertVar(v)
|
||||
}
|
||||
vars, err := s.FunctionArguments()
|
||||
if err == nil {
|
||||
bpi.Arguments = convertVars(vars)
|
||||
if bp.LoadArgs != nil {
|
||||
if vars, err := s.FunctionArguments(*api.LoadConfigToProc(bp.LoadArgs)); err == nil {
|
||||
bpi.Arguments = convertVars(vars)
|
||||
}
|
||||
}
|
||||
if bp.LoadLocals != nil {
|
||||
if locals, err := s.LocalVariables(*api.LoadConfigToProc(bp.LoadLocals)); err == nil {
|
||||
bpi.Locals = convertVars(locals)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -542,7 +550,7 @@ func regexFilterFuncs(filter string, allFuncs []gosym.Func) ([]string, error) {
|
||||
|
||||
// PackageVariables returns a list of package variables for the thread,
|
||||
// optionally regexp filtered using regexp described in 'filter'.
|
||||
func (d *Debugger) PackageVariables(threadID int, filter string) ([]api.Variable, error) {
|
||||
func (d *Debugger) PackageVariables(threadID int, filter string, cfg proc.LoadConfig) ([]api.Variable, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
@ -560,7 +568,7 @@ func (d *Debugger) PackageVariables(threadID int, filter string) ([]api.Variable
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pv, err := scope.PackageVariables()
|
||||
pv, err := scope.PackageVariables(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -597,7 +605,7 @@ func convertVars(pv []*proc.Variable) []api.Variable {
|
||||
}
|
||||
|
||||
// LocalVariables returns a list of the local variables.
|
||||
func (d *Debugger) LocalVariables(scope api.EvalScope) ([]api.Variable, error) {
|
||||
func (d *Debugger) LocalVariables(scope api.EvalScope, cfg proc.LoadConfig) ([]api.Variable, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
@ -605,7 +613,7 @@ func (d *Debugger) LocalVariables(scope api.EvalScope) ([]api.Variable, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pv, err := s.LocalVariables()
|
||||
pv, err := s.LocalVariables(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -613,7 +621,7 @@ func (d *Debugger) LocalVariables(scope api.EvalScope) ([]api.Variable, error) {
|
||||
}
|
||||
|
||||
// FunctionArguments returns the arguments to the current function.
|
||||
func (d *Debugger) FunctionArguments(scope api.EvalScope) ([]api.Variable, error) {
|
||||
func (d *Debugger) FunctionArguments(scope api.EvalScope, cfg proc.LoadConfig) ([]api.Variable, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
@ -621,7 +629,7 @@ func (d *Debugger) FunctionArguments(scope api.EvalScope) ([]api.Variable, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pv, err := s.FunctionArguments()
|
||||
pv, err := s.FunctionArguments(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -630,7 +638,7 @@ func (d *Debugger) FunctionArguments(scope api.EvalScope) ([]api.Variable, error
|
||||
|
||||
// EvalVariableInScope will attempt to evaluate the variable represented by 'symbol'
|
||||
// in the scope provided.
|
||||
func (d *Debugger) EvalVariableInScope(scope api.EvalScope, symbol string) (*api.Variable, error) {
|
||||
func (d *Debugger) EvalVariableInScope(scope api.EvalScope, symbol string, cfg proc.LoadConfig) (*api.Variable, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
@ -638,7 +646,7 @@ func (d *Debugger) EvalVariableInScope(scope api.EvalScope, symbol string) (*api
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := s.EvalVariable(symbol)
|
||||
v, err := s.EvalVariable(symbol, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -677,7 +685,7 @@ func (d *Debugger) Goroutines() ([]*api.Goroutine, error) {
|
||||
// Stacktrace returns a list of Stackframes for the given goroutine. The
|
||||
// length of the returned list will be min(stack_len, depth).
|
||||
// If 'full' is true, then local vars, function args, etc will be returned as well.
|
||||
func (d *Debugger) Stacktrace(goroutineID, depth int, full bool) ([]api.Stackframe, error) {
|
||||
func (d *Debugger) Stacktrace(goroutineID, depth int, cfg *proc.LoadConfig) ([]api.Stackframe, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
@ -697,21 +705,21 @@ func (d *Debugger) Stacktrace(goroutineID, depth int, full bool) ([]api.Stackfra
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d.convertStacktrace(rawlocs, full)
|
||||
return d.convertStacktrace(rawlocs, cfg)
|
||||
}
|
||||
|
||||
func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, full bool) ([]api.Stackframe, error) {
|
||||
func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadConfig) ([]api.Stackframe, error) {
|
||||
locations := make([]api.Stackframe, 0, len(rawlocs))
|
||||
for i := range rawlocs {
|
||||
frame := api.Stackframe{Location: api.ConvertLocation(rawlocs[i].Call)}
|
||||
if full {
|
||||
if cfg != nil {
|
||||
var err error
|
||||
scope := rawlocs[i].Scope(d.process.CurrentThread)
|
||||
locals, err := scope.LocalVariables()
|
||||
locals, err := scope.LocalVariables(*cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
arguments, err := scope.FunctionArguments()
|
||||
arguments, err := scope.FunctionArguments(*cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ func (loc *AddrLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr str
|
||||
}
|
||||
return []api.Location{{PC: uint64(addr)}}, nil
|
||||
} else {
|
||||
v, err := scope.EvalExpression(loc.AddrExpr)
|
||||
v, err := scope.EvalExpression(loc.AddrExpr, proc.LoadConfig{true, 0, 0, 0, 0})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"net/rpc"
|
||||
"net/rpc/jsonrpc"
|
||||
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
)
|
||||
|
||||
@ -17,9 +16,6 @@ type RPCClient struct {
|
||||
client *rpc.Client
|
||||
}
|
||||
|
||||
// Ensure the implementation satisfies the interface.
|
||||
var _ service.Client = &RPCClient{}
|
||||
|
||||
// NewClient creates a new RPCClient.
|
||||
func NewClient(addr string) *RPCClient {
|
||||
client, err := jsonrpc.Dial("tcp", addr)
|
||||
|
@ -9,11 +9,14 @@ import (
|
||||
grpc "net/rpc"
|
||||
"net/rpc/jsonrpc"
|
||||
|
||||
"github.com/derekparker/delve/proc"
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
"github.com/derekparker/delve/service/debugger"
|
||||
)
|
||||
|
||||
var defaultLoadConfig = proc.LoadConfig{ true, 1, 64, 64, -1 }
|
||||
|
||||
type ServerImpl struct {
|
||||
s *RPCServer
|
||||
}
|
||||
@ -160,7 +163,11 @@ type StacktraceGoroutineArgs struct {
|
||||
}
|
||||
|
||||
func (s *RPCServer) StacktraceGoroutine(args *StacktraceGoroutineArgs, locations *[]api.Stackframe) error {
|
||||
locs, err := s.debugger.Stacktrace(args.Id, args.Depth, args.Full)
|
||||
var loadcfg *proc.LoadConfig = nil
|
||||
if args.Full {
|
||||
loadcfg = &defaultLoadConfig
|
||||
}
|
||||
locs, err := s.debugger.Stacktrace(args.Id, args.Depth, loadcfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -241,7 +248,7 @@ func (s *RPCServer) ListPackageVars(filter string, variables *[]api.Variable) er
|
||||
return fmt.Errorf("no current thread")
|
||||
}
|
||||
|
||||
vars, err := s.debugger.PackageVariables(current.ID, filter)
|
||||
vars, err := s.debugger.PackageVariables(current.ID, filter, defaultLoadConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -263,7 +270,7 @@ func (s *RPCServer) ListThreadPackageVars(args *ThreadListArgs, variables *[]api
|
||||
return fmt.Errorf("no thread with id %d", args.Id)
|
||||
}
|
||||
|
||||
vars, err := s.debugger.PackageVariables(args.Id, args.Filter)
|
||||
vars, err := s.debugger.PackageVariables(args.Id, args.Filter, defaultLoadConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -286,7 +293,7 @@ func (s *RPCServer) ListRegisters(arg interface{}, registers *string) error {
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListLocalVars(scope api.EvalScope, variables *[]api.Variable) error {
|
||||
vars, err := s.debugger.LocalVariables(scope)
|
||||
vars, err := s.debugger.LocalVariables(scope, defaultLoadConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -295,7 +302,7 @@ func (s *RPCServer) ListLocalVars(scope api.EvalScope, variables *[]api.Variable
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListFunctionArgs(scope api.EvalScope, variables *[]api.Variable) error {
|
||||
vars, err := s.debugger.FunctionArguments(scope)
|
||||
vars, err := s.debugger.FunctionArguments(scope, defaultLoadConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -309,7 +316,7 @@ type EvalSymbolArgs struct {
|
||||
}
|
||||
|
||||
func (s *RPCServer) EvalSymbol(args EvalSymbolArgs, variable *api.Variable) error {
|
||||
v, err := s.debugger.EvalVariableInScope(args.Scope, args.Symbol)
|
||||
v, err := s.debugger.EvalVariableInScope(args.Scope, args.Symbol, defaultLoadConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -190,9 +190,9 @@ func (c *RPCClient) GetThread(id int) (*api.Thread, error) {
|
||||
return out.Thread, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) EvalVariable(scope api.EvalScope, expr string) (*api.Variable, error) {
|
||||
func (c *RPCClient) EvalVariable(scope api.EvalScope, expr string, cfg api.LoadConfig) (*api.Variable, error) {
|
||||
var out EvalOut
|
||||
err := c.call("Eval", EvalIn{scope, expr}, &out)
|
||||
err := c.call("Eval", EvalIn{scope, expr, &cfg}, &out)
|
||||
return out.Variable, err
|
||||
}
|
||||
|
||||
@ -219,15 +219,15 @@ func (c *RPCClient) ListTypes(filter string) ([]string, error) {
|
||||
return types.Types, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListPackageVariables(filter string) ([]api.Variable, error) {
|
||||
func (c *RPCClient) ListPackageVariables(filter string, cfg api.LoadConfig) ([]api.Variable, error) {
|
||||
var out ListPackageVarsOut
|
||||
err := c.call("ListPackageVars", ListPackageVarsIn{filter}, &out)
|
||||
err := c.call("ListPackageVars", ListPackageVarsIn{filter, cfg}, &out)
|
||||
return out.Variables, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListLocalVariables(scope api.EvalScope) ([]api.Variable, error) {
|
||||
func (c *RPCClient) ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) {
|
||||
var out ListLocalVarsOut
|
||||
err := c.call("ListLocalVars", ListLocalVarsIn{scope}, &out)
|
||||
err := c.call("ListLocalVars", ListLocalVarsIn{scope, cfg}, &out)
|
||||
return out.Variables, err
|
||||
}
|
||||
|
||||
@ -237,9 +237,9 @@ func (c *RPCClient) ListRegisters() (string, error) {
|
||||
return out.Registers, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListFunctionArgs(scope api.EvalScope) ([]api.Variable, error) {
|
||||
func (c *RPCClient) ListFunctionArgs(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) {
|
||||
var out ListFunctionArgsOut
|
||||
err := c.call("ListFunctionArgs", ListFunctionArgsIn{scope}, &out)
|
||||
err := c.call("ListFunctionArgs", ListFunctionArgsIn{scope, cfg}, &out)
|
||||
return out.Args, err
|
||||
}
|
||||
|
||||
@ -249,9 +249,9 @@ func (c *RPCClient) ListGoroutines() ([]*api.Goroutine, error) {
|
||||
return out.Goroutines, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) Stacktrace(goroutineId, depth int, full bool) ([]api.Stackframe, error) {
|
||||
func (c *RPCClient) Stacktrace(goroutineId, depth int, cfg *api.LoadConfig) ([]api.Stackframe, error) {
|
||||
var out StacktraceOut
|
||||
err := c.call("Stacktrace", StacktraceIn{goroutineId, depth, full}, &out)
|
||||
err := c.call("Stacktrace", StacktraceIn{goroutineId, depth, false, cfg}, &out)
|
||||
return out.Locations, err
|
||||
}
|
||||
|
||||
|
@ -195,6 +195,7 @@ type StacktraceIn struct {
|
||||
Id int
|
||||
Depth int
|
||||
Full bool
|
||||
Cfg *api.LoadConfig
|
||||
}
|
||||
|
||||
type StacktraceOut struct {
|
||||
@ -202,7 +203,11 @@ type StacktraceOut struct {
|
||||
}
|
||||
|
||||
func (s *RPCServer) Stacktrace(arg StacktraceIn, out *StacktraceOut) error {
|
||||
locs, err := s.debugger.Stacktrace(arg.Id, arg.Depth, arg.Full)
|
||||
cfg := arg.Cfg
|
||||
if cfg == nil && arg.Full {
|
||||
cfg = &api.LoadConfig{ true, 1, 64, 64, -1 }
|
||||
}
|
||||
locs, err := s.debugger.Stacktrace(arg.Id, arg.Depth, api.LoadConfigToProc(cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -314,6 +319,7 @@ func (s *RPCServer) GetThread(arg GetThreadIn, out *GetThreadOut) error {
|
||||
|
||||
type ListPackageVarsIn struct {
|
||||
Filter string
|
||||
Cfg api.LoadConfig
|
||||
}
|
||||
|
||||
type ListPackageVarsOut struct {
|
||||
@ -331,7 +337,7 @@ func (s *RPCServer) ListPackageVars(arg ListPackageVarsIn, out *ListPackageVarsO
|
||||
return fmt.Errorf("no current thread")
|
||||
}
|
||||
|
||||
vars, err := s.debugger.PackageVariables(current.ID, arg.Filter)
|
||||
vars, err := s.debugger.PackageVariables(current.ID, arg.Filter, *api.LoadConfigToProc(&arg.Cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -362,6 +368,7 @@ func (s *RPCServer) ListRegisters(arg ListRegistersIn, out *ListRegistersOut) er
|
||||
|
||||
type ListLocalVarsIn struct {
|
||||
Scope api.EvalScope
|
||||
Cfg api.LoadConfig
|
||||
}
|
||||
|
||||
type ListLocalVarsOut struct {
|
||||
@ -369,7 +376,7 @@ type ListLocalVarsOut struct {
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListLocalVars(arg ListLocalVarsIn, out *ListLocalVarsOut) error {
|
||||
vars, err := s.debugger.LocalVariables(arg.Scope)
|
||||
vars, err := s.debugger.LocalVariables(arg.Scope, *api.LoadConfigToProc(&arg.Cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -379,6 +386,7 @@ func (s *RPCServer) ListLocalVars(arg ListLocalVarsIn, out *ListLocalVarsOut) er
|
||||
|
||||
type ListFunctionArgsIn struct {
|
||||
Scope api.EvalScope
|
||||
Cfg api.LoadConfig
|
||||
}
|
||||
|
||||
type ListFunctionArgsOut struct {
|
||||
@ -386,7 +394,7 @@ type ListFunctionArgsOut struct {
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListFunctionArgs(arg ListFunctionArgsIn, out *ListFunctionArgsOut) error {
|
||||
vars, err := s.debugger.FunctionArguments(arg.Scope)
|
||||
vars, err := s.debugger.FunctionArguments(arg.Scope, *api.LoadConfigToProc(&arg.Cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -397,6 +405,7 @@ func (s *RPCServer) ListFunctionArgs(arg ListFunctionArgsIn, out *ListFunctionAr
|
||||
type EvalIn struct {
|
||||
Scope api.EvalScope
|
||||
Expr string
|
||||
Cfg *api.LoadConfig
|
||||
}
|
||||
|
||||
type EvalOut struct {
|
||||
@ -404,7 +413,11 @@ type EvalOut struct {
|
||||
}
|
||||
|
||||
func (s *RPCServer) Eval(arg EvalIn, out *EvalOut) error {
|
||||
v, err := s.debugger.EvalVariableInScope(arg.Scope, arg.Expr)
|
||||
cfg := arg.Cfg
|
||||
if cfg == nil {
|
||||
cfg = &api.LoadConfig{ true, 1, 64, 64, -1 }
|
||||
}
|
||||
v, err := s.debugger.EvalVariableInScope(arg.Scope, arg.Expr, *api.LoadConfigToProc(cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
)
|
||||
|
||||
@ -35,18 +34,6 @@ type nextTest struct {
|
||||
begin, end int
|
||||
}
|
||||
|
||||
func countBreakpoints(t *testing.T, c service.Client) int {
|
||||
bps, err := c.ListBreakpoints()
|
||||
assertNoError(err, t, "ListBreakpoints()")
|
||||
bpcount := 0
|
||||
for _, bp := range bps {
|
||||
if bp.ID >= 0 {
|
||||
bpcount++
|
||||
}
|
||||
}
|
||||
return bpcount
|
||||
}
|
||||
|
||||
func testProgPath(t *testing.T, name string) string {
|
||||
fp, err := filepath.Abs(fmt.Sprintf("_fixtures/%s.go", name))
|
||||
if err != nil {
|
||||
@ -61,7 +48,27 @@ func testProgPath(t *testing.T, name string) string {
|
||||
return fp
|
||||
}
|
||||
|
||||
func findLocationHelper(t *testing.T, c service.Client, loc string, shouldErr bool, count int, checkAddr uint64) []uint64 {
|
||||
type BreakpointLister interface {
|
||||
ListBreakpoints() ([]*api.Breakpoint, error)
|
||||
}
|
||||
|
||||
func countBreakpoints(t *testing.T, c BreakpointLister) int {
|
||||
bps, err := c.ListBreakpoints()
|
||||
assertNoError(err, t, "ListBreakpoints()")
|
||||
bpcount := 0
|
||||
for _, bp := range bps {
|
||||
if bp.ID >= 0 {
|
||||
bpcount++
|
||||
}
|
||||
}
|
||||
return bpcount
|
||||
}
|
||||
|
||||
type LocationFinder interface {
|
||||
FindLocation(api.EvalScope, string) ([]api.Location, error)
|
||||
}
|
||||
|
||||
func findLocationHelper(t *testing.T, c LocationFinder, loc string, shouldErr bool, count int, checkAddr uint64) []uint64 {
|
||||
locs, err := c.FindLocation(api.EvalScope{-1, 0}, loc)
|
||||
t.Logf("FindLocation(\"%s\") → %v\n", loc, locs)
|
||||
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
"github.com/derekparker/delve/service/rpc1"
|
||||
)
|
||||
|
||||
func withTestClient1(name string, t *testing.T, fn func(c service.Client)) {
|
||||
func withTestClient1(name string, t *testing.T, fn func(c *rpc1.RPCClient)) {
|
||||
listener, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't start listener: %s\n", err)
|
||||
@ -56,7 +56,7 @@ func Test1RunWithInvalidPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1Restart_afterExit(t *testing.T) {
|
||||
withTestClient1("continuetestprog", t, func(c service.Client) {
|
||||
withTestClient1("continuetestprog", t, func(c *rpc1.RPCClient) {
|
||||
origPid := c.ProcessPid()
|
||||
state := <-c.Continue()
|
||||
if !state.Exited {
|
||||
@ -76,7 +76,7 @@ func Test1Restart_afterExit(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1Restart_breakpointPreservation(t *testing.T) {
|
||||
withTestClient1("continuetestprog", t, func(c service.Client) {
|
||||
withTestClient1("continuetestprog", t, func(c *rpc1.RPCClient) {
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 1, Name: "firstbreakpoint", Tracepoint: true})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
stateCh := c.Continue()
|
||||
@ -105,7 +105,7 @@ func Test1Restart_breakpointPreservation(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1Restart_duringStop(t *testing.T) {
|
||||
withTestClient1("continuetestprog", t, func(c service.Client) {
|
||||
withTestClient1("continuetestprog", t, func(c *rpc1.RPCClient) {
|
||||
origPid := c.ProcessPid()
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 1})
|
||||
if err != nil {
|
||||
@ -144,7 +144,7 @@ func Test1Restart_attachPid(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_exit(t *testing.T) {
|
||||
withTestClient1("continuetestprog", t, func(c service.Client) {
|
||||
withTestClient1("continuetestprog", t, func(c *rpc1.RPCClient) {
|
||||
state, err := c.GetState()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
@ -167,7 +167,7 @@ func Test1ClientServer_exit(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_step(t *testing.T) {
|
||||
withTestClient1("testprog", t, func(c service.Client) {
|
||||
withTestClient1("testprog", t, func(c *rpc1.RPCClient) {
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.helloworld", Line: 1})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
@ -190,7 +190,7 @@ func Test1ClientServer_step(t *testing.T) {
|
||||
}
|
||||
|
||||
func testnext(testcases []nextTest, initialLocation string, t *testing.T) {
|
||||
withTestClient1("testnextprog", t, func(c service.Client) {
|
||||
withTestClient1("testnextprog", t, func(c *rpc1.RPCClient) {
|
||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: initialLocation, Line: -1})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
@ -281,7 +281,7 @@ func Test1NextFunctionReturn(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_breakpointInMainThread(t *testing.T) {
|
||||
withTestClient1("testprog", t, func(c service.Client) {
|
||||
withTestClient1("testprog", t, func(c *rpc1.RPCClient) {
|
||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.helloworld", Line: 1})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
@ -302,7 +302,7 @@ func Test1ClientServer_breakpointInMainThread(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_breakpointInSeparateGoroutine(t *testing.T) {
|
||||
withTestClient1("testthreads", t, func(c service.Client) {
|
||||
withTestClient1("testthreads", t, func(c *rpc1.RPCClient) {
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.anotherthread", Line: 1})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
@ -321,7 +321,7 @@ func Test1ClientServer_breakpointInSeparateGoroutine(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_breakAtNonexistentPoint(t *testing.T) {
|
||||
withTestClient1("testprog", t, func(c service.Client) {
|
||||
withTestClient1("testprog", t, func(c *rpc1.RPCClient) {
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "nowhere", Line: 1})
|
||||
if err == nil {
|
||||
t.Fatal("Should not be able to break at non existent function")
|
||||
@ -330,7 +330,7 @@ func Test1ClientServer_breakAtNonexistentPoint(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_clearBreakpoint(t *testing.T) {
|
||||
withTestClient1("testprog", t, func(c service.Client) {
|
||||
withTestClient1("testprog", t, func(c *rpc1.RPCClient) {
|
||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sleepytime", Line: 1})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
@ -356,7 +356,7 @@ func Test1ClientServer_clearBreakpoint(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_switchThread(t *testing.T) {
|
||||
withTestClient1("testnextprog", t, func(c service.Client) {
|
||||
withTestClient1("testnextprog", t, func(c *rpc1.RPCClient) {
|
||||
// With invalid thread id
|
||||
_, err := c.SwitchThread(-1)
|
||||
if err == nil {
|
||||
@ -399,7 +399,7 @@ func Test1ClientServer_switchThread(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_infoLocals(t *testing.T) {
|
||||
withTestClient1("testnextprog", t, func(c service.Client) {
|
||||
withTestClient1("testnextprog", t, func(c *rpc1.RPCClient) {
|
||||
fp := testProgPath(t, "testnextprog")
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{File: fp, Line: 23})
|
||||
if err != nil {
|
||||
@ -420,7 +420,7 @@ func Test1ClientServer_infoLocals(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_infoArgs(t *testing.T) {
|
||||
withTestClient1("testnextprog", t, func(c service.Client) {
|
||||
withTestClient1("testnextprog", t, func(c *rpc1.RPCClient) {
|
||||
fp := testProgPath(t, "testnextprog")
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{File: fp, Line: 47})
|
||||
if err != nil {
|
||||
@ -448,7 +448,7 @@ func Test1ClientServer_infoArgs(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_traceContinue(t *testing.T) {
|
||||
withTestClient1("integrationprog", t, func(c service.Client) {
|
||||
withTestClient1("integrationprog", t, func(c *rpc1.RPCClient) {
|
||||
fp := testProgPath(t, "integrationprog")
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{File: fp, Line: 15, Tracepoint: true, Goroutine: true, Stacktrace: 5, Variables: []string{"i"}})
|
||||
if err != nil {
|
||||
@ -505,7 +505,7 @@ func Test1ClientServer_traceContinue(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_traceContinue2(t *testing.T) {
|
||||
withTestClient1("integrationprog", t, func(c service.Client) {
|
||||
withTestClient1("integrationprog", t, func(c *rpc1.RPCClient) {
|
||||
bp1, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 1, Tracepoint: true})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v\n", err)
|
||||
@ -548,7 +548,7 @@ func Test1ClientServer_traceContinue2(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_FindLocations(t *testing.T) {
|
||||
withTestClient1("locationsprog", t, func(c service.Client) {
|
||||
withTestClient1("locationsprog", t, func(c *rpc1.RPCClient) {
|
||||
someFunctionCallAddr := findLocationHelper(t, c, "locationsprog.go:26", false, 1, 0)[0]
|
||||
someFunctionLine1 := findLocationHelper(t, c, "locationsprog.go:27", false, 1, 0)[0]
|
||||
findLocationHelper(t, c, "anotherFunction:1", false, 1, someFunctionLine1)
|
||||
@ -595,17 +595,17 @@ func Test1ClientServer_FindLocations(t *testing.T) {
|
||||
findLocationHelper(t, c, "-1", false, 1, findLocationHelper(t, c, "locationsprog.go:32", false, 1, 0)[0])
|
||||
})
|
||||
|
||||
withTestClient1("testnextdefer", t, func(c service.Client) {
|
||||
withTestClient1("testnextdefer", t, func(c *rpc1.RPCClient) {
|
||||
firstMainLine := findLocationHelper(t, c, "testnextdefer.go:5", false, 1, 0)[0]
|
||||
findLocationHelper(t, c, "main.main", false, 1, firstMainLine)
|
||||
})
|
||||
|
||||
withTestClient1("stacktraceprog", t, func(c service.Client) {
|
||||
withTestClient1("stacktraceprog", t, func(c *rpc1.RPCClient) {
|
||||
stacktracemeAddr := findLocationHelper(t, c, "stacktraceprog.go:4", false, 1, 0)[0]
|
||||
findLocationHelper(t, c, "main.stacktraceme", false, 1, stacktracemeAddr)
|
||||
})
|
||||
|
||||
withTestClient1("locationsUpperCase", t, func(c service.Client) {
|
||||
withTestClient1("locationsUpperCase", t, func(c *rpc1.RPCClient) {
|
||||
// Upper case
|
||||
findLocationHelper(t, c, "locationsUpperCase.go:6", false, 1, 0)
|
||||
|
||||
@ -645,7 +645,7 @@ func Test1ClientServer_FindLocations(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_FindLocationsAddr(t *testing.T) {
|
||||
withTestClient1("locationsprog2", t, func(c service.Client) {
|
||||
withTestClient1("locationsprog2", t, func(c *rpc1.RPCClient) {
|
||||
<-c.Continue()
|
||||
|
||||
afunction := findLocationHelper(t, c, "main.afunction", false, 1, 0)[0]
|
||||
@ -657,7 +657,7 @@ func Test1ClientServer_FindLocationsAddr(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_EvalVariable(t *testing.T) {
|
||||
withTestClient1("testvariables", t, func(c service.Client) {
|
||||
withTestClient1("testvariables", t, func(c *rpc1.RPCClient) {
|
||||
state := <-c.Continue()
|
||||
|
||||
if state.Err != nil {
|
||||
@ -676,7 +676,7 @@ func Test1ClientServer_EvalVariable(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_SetVariable(t *testing.T) {
|
||||
withTestClient1("testvariables", t, func(c service.Client) {
|
||||
withTestClient1("testvariables", t, func(c *rpc1.RPCClient) {
|
||||
state := <-c.Continue()
|
||||
|
||||
if state.Err != nil {
|
||||
@ -698,7 +698,7 @@ func Test1ClientServer_SetVariable(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_FullStacktrace(t *testing.T) {
|
||||
withTestClient1("goroutinestackprog", t, func(c service.Client) {
|
||||
withTestClient1("goroutinestackprog", t, func(c *rpc1.RPCClient) {
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.stacktraceme", Line: -1})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
state := <-c.Continue()
|
||||
@ -771,7 +771,7 @@ func Test1ClientServer_FullStacktrace(t *testing.T) {
|
||||
|
||||
func Test1Issue355(t *testing.T) {
|
||||
// After the target process has terminated should return an error but not crash
|
||||
withTestClient1("continuetestprog", t, func(c service.Client) {
|
||||
withTestClient1("continuetestprog", t, func(c *rpc1.RPCClient) {
|
||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: -1})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
ch := c.Continue()
|
||||
@ -832,7 +832,7 @@ func Test1Disasm(t *testing.T) {
|
||||
// Tests that disassembly by current PC will return a disassembly containing the instruction at PC
|
||||
// Tests that stepping on a calculated CALL instruction will yield a disassembly that contains the
|
||||
// effective destination of the CALL instruction
|
||||
withTestClient1("locationsprog2", t, func(c service.Client) {
|
||||
withTestClient1("locationsprog2", t, func(c *rpc1.RPCClient) {
|
||||
ch := c.Continue()
|
||||
state := <-ch
|
||||
assertNoError(state.Err, t, "Continue()")
|
||||
@ -934,7 +934,7 @@ func Test1Disasm(t *testing.T) {
|
||||
|
||||
func Test1NegativeStackDepthBug(t *testing.T) {
|
||||
// After the target process has terminated should return an error but not crash
|
||||
withTestClient1("continuetestprog", t, func(c service.Client) {
|
||||
withTestClient1("continuetestprog", t, func(c *rpc1.RPCClient) {
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: -1})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
ch := c.Continue()
|
||||
@ -946,7 +946,7 @@ func Test1NegativeStackDepthBug(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_CondBreakpoint(t *testing.T) {
|
||||
withTestClient1("parallel_next", t, func(c service.Client) {
|
||||
withTestClient1("parallel_next", t, func(c *rpc1.RPCClient) {
|
||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: 1})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
bp.Cond = "n == 7"
|
||||
@ -976,7 +976,7 @@ func Test1ClientServer_CondBreakpoint(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1SkipPrologue(t *testing.T) {
|
||||
withTestClient1("locationsprog2", t, func(c service.Client) {
|
||||
withTestClient1("locationsprog2", t, func(c *rpc1.RPCClient) {
|
||||
<-c.Continue()
|
||||
|
||||
afunction := findLocationHelper(t, c, "main.afunction", false, 1, 0)[0]
|
||||
@ -992,7 +992,7 @@ func Test1SkipPrologue(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1SkipPrologue2(t *testing.T) {
|
||||
withTestClient1("callme", t, func(c service.Client) {
|
||||
withTestClient1("callme", t, func(c *rpc1.RPCClient) {
|
||||
callme := findLocationHelper(t, c, "main.callme", false, 1, 0)[0]
|
||||
callmeZ := findLocationHelper(t, c, "main.callme:0", false, 1, 0)[0]
|
||||
findLocationHelper(t, c, "callme.go:5", false, 1, callme)
|
||||
@ -1020,7 +1020,7 @@ func Test1SkipPrologue2(t *testing.T) {
|
||||
func Test1Issue419(t *testing.T) {
|
||||
// Calling service/rpc.(*Client).Halt could cause a crash because both Halt and Continue simultaneously
|
||||
// try to read 'runtime.g' and debug/dwarf.Data.Type is not thread safe
|
||||
withTestClient1("issue419", t, func(c service.Client) {
|
||||
withTestClient1("issue419", t, func(c *rpc1.RPCClient) {
|
||||
go func() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
d := time.Duration(rand.Intn(4) + 1)
|
||||
@ -1035,7 +1035,7 @@ func Test1Issue419(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1TypesCommand(t *testing.T) {
|
||||
withTestClient1("testvariables2", t, func(c service.Client) {
|
||||
withTestClient1("testvariables2", t, func(c *rpc1.RPCClient) {
|
||||
state := <-c.Continue()
|
||||
assertNoError(state.Err, t, "Continue()")
|
||||
types, err := c.ListTypes("")
|
||||
@ -1061,7 +1061,7 @@ func Test1TypesCommand(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1Issue406(t *testing.T) {
|
||||
withTestClient1("issue406", t, func(c service.Client) {
|
||||
withTestClient1("issue406", t, func(c *rpc1.RPCClient) {
|
||||
locs, err := c.FindLocation(api.EvalScope{-1, 0}, "issue406.go:146")
|
||||
assertNoError(err, t, "FindLocation()")
|
||||
_, err = c.CreateBreakpoint(&api.Breakpoint{Addr: locs[0].PC})
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
"os"
|
||||
|
||||
protest "github.com/derekparker/delve/proc/test"
|
||||
|
||||
@ -19,6 +20,12 @@ import (
|
||||
"github.com/derekparker/delve/service/rpc2"
|
||||
)
|
||||
|
||||
var normalLoadConfig = api.LoadConfig{true, 1, 64, 64, -1}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(protest.RunTestsWithFixtures(m))
|
||||
}
|
||||
|
||||
func withTestClient2(name string, t *testing.T, fn func(c service.Client)) {
|
||||
listener, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
@ -409,7 +416,7 @@ func TestClientServer_infoLocals(t *testing.T) {
|
||||
if state.Err != nil {
|
||||
t.Fatalf("Unexpected error: %v, state: %#v", state.Err, state)
|
||||
}
|
||||
locals, err := c.ListLocalVariables(api.EvalScope{-1, 0})
|
||||
locals, err := c.ListLocalVariables(api.EvalScope{-1, 0}, normalLoadConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
@ -437,7 +444,7 @@ func TestClientServer_infoArgs(t *testing.T) {
|
||||
if regs == "" {
|
||||
t.Fatal("Expected string showing registers values, got empty string")
|
||||
}
|
||||
locals, err := c.ListFunctionArgs(api.EvalScope{-1, 0})
|
||||
locals, err := c.ListFunctionArgs(api.EvalScope{-1, 0}, normalLoadConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
@ -664,7 +671,7 @@ func TestClientServer_EvalVariable(t *testing.T) {
|
||||
t.Fatalf("Continue(): %v\n", state.Err)
|
||||
}
|
||||
|
||||
var1, err := c.EvalVariable(api.EvalScope{-1, 0}, "a1")
|
||||
var1, err := c.EvalVariable(api.EvalScope{-1, 0}, "a1", normalLoadConfig)
|
||||
assertNoError(err, t, "EvalVariable")
|
||||
|
||||
t.Logf("var1: %s", var1.SinglelineString())
|
||||
@ -685,7 +692,7 @@ func TestClientServer_SetVariable(t *testing.T) {
|
||||
|
||||
assertNoError(c.SetVariable(api.EvalScope{-1, 0}, "a2", "8"), t, "SetVariable()")
|
||||
|
||||
a2, err := c.EvalVariable(api.EvalScope{-1, 0}, "a2")
|
||||
a2, err := c.EvalVariable(api.EvalScope{-1, 0}, "a2", normalLoadConfig)
|
||||
|
||||
t.Logf("a2: %v", a2)
|
||||
|
||||
@ -710,7 +717,7 @@ func TestClientServer_FullStacktrace(t *testing.T) {
|
||||
assertNoError(err, t, "GoroutinesInfo()")
|
||||
found := make([]bool, 10)
|
||||
for _, g := range gs {
|
||||
frames, err := c.Stacktrace(g.ID, 10, true)
|
||||
frames, err := c.Stacktrace(g.ID, 10, &normalLoadConfig)
|
||||
assertNoError(err, t, fmt.Sprintf("Stacktrace(%d)", g.ID))
|
||||
for i, frame := range frames {
|
||||
if frame.Function == nil {
|
||||
@ -744,7 +751,7 @@ func TestClientServer_FullStacktrace(t *testing.T) {
|
||||
t.Fatalf("Continue(): %v\n", state.Err)
|
||||
}
|
||||
|
||||
frames, err := c.Stacktrace(-1, 10, true)
|
||||
frames, err := c.Stacktrace(-1, 10, &normalLoadConfig)
|
||||
assertNoError(err, t, "Stacktrace")
|
||||
|
||||
cur := 3
|
||||
@ -810,15 +817,15 @@ func TestIssue355(t *testing.T) {
|
||||
_, err = c.GetThread(tid)
|
||||
assertError(err, t, "GetThread()")
|
||||
assertError(c.SetVariable(api.EvalScope{gid, 0}, "a", "10"), t, "SetVariable()")
|
||||
_, err = c.ListLocalVariables(api.EvalScope{gid, 0})
|
||||
_, err = c.ListLocalVariables(api.EvalScope{gid, 0}, normalLoadConfig)
|
||||
assertError(err, t, "ListLocalVariables()")
|
||||
_, err = c.ListFunctionArgs(api.EvalScope{gid, 0})
|
||||
_, err = c.ListFunctionArgs(api.EvalScope{gid, 0}, normalLoadConfig)
|
||||
assertError(err, t, "ListFunctionArgs()")
|
||||
_, err = c.ListRegisters()
|
||||
assertError(err, t, "ListRegisters()")
|
||||
_, err = c.ListGoroutines()
|
||||
assertError(err, t, "ListGoroutines()")
|
||||
_, err = c.Stacktrace(gid, 10, false)
|
||||
_, err = c.Stacktrace(gid, 10, &normalLoadConfig)
|
||||
assertError(err, t, "Stacktrace()")
|
||||
_, err = c.FindLocation(api.EvalScope{gid, 0}, "+1")
|
||||
assertError(err, t, "FindLocation()")
|
||||
@ -940,7 +947,7 @@ func TestNegativeStackDepthBug(t *testing.T) {
|
||||
ch := c.Continue()
|
||||
state := <-ch
|
||||
assertNoError(state.Err, t, "Continue()")
|
||||
_, err = c.Stacktrace(-1, -2, true)
|
||||
_, err = c.Stacktrace(-1, -2, &normalLoadConfig)
|
||||
assertError(err, t, "Stacktrace()")
|
||||
})
|
||||
}
|
||||
@ -966,7 +973,7 @@ func TestClientServer_CondBreakpoint(t *testing.T) {
|
||||
state := <-c.Continue()
|
||||
assertNoError(state.Err, t, "Continue()")
|
||||
|
||||
nvar, err := c.EvalVariable(api.EvalScope{-1, 0}, "n")
|
||||
nvar, err := c.EvalVariable(api.EvalScope{-1, 0}, "n", normalLoadConfig)
|
||||
assertNoError(err, t, "EvalVariable()")
|
||||
|
||||
if nvar.SinglelineString() != "7" {
|
||||
@ -1069,9 +1076,27 @@ func TestIssue406(t *testing.T) {
|
||||
ch := c.Continue()
|
||||
state := <-ch
|
||||
assertNoError(state.Err, t, "Continue()")
|
||||
v, err := c.EvalVariable(api.EvalScope{-1, 0}, "cfgtree")
|
||||
v, err := c.EvalVariable(api.EvalScope{-1, 0}, "cfgtree", normalLoadConfig)
|
||||
assertNoError(err, t, "EvalVariable()")
|
||||
vs := v.MultilineString("")
|
||||
t.Logf("cfgtree formats to: %s\n", vs)
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalExprName(t *testing.T) {
|
||||
withTestClient2("testvariables2", t, func(c service.Client) {
|
||||
state := <-c.Continue()
|
||||
assertNoError(state.Err, t, "Continue()")
|
||||
|
||||
var1, err := c.EvalVariable(api.EvalScope{-1, 0}, "i1+1", normalLoadConfig)
|
||||
assertNoError(err, t, "EvalVariable")
|
||||
|
||||
const name = "i1+1"
|
||||
|
||||
t.Logf("i1+1 → %#v", var1)
|
||||
|
||||
if var1.Name != name {
|
||||
t.Fatalf("Wrong variable name %q, expected %q", var1.Name, name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -12,11 +12,14 @@ import (
|
||||
protest "github.com/derekparker/delve/proc/test"
|
||||
)
|
||||
|
||||
var pnormalLoadConfig = proc.LoadConfig{true, 1, 64, 64, -1}
|
||||
var pshortLoadConfig = proc.LoadConfig{false, 0, 64, 0, 3}
|
||||
|
||||
type varTest struct {
|
||||
name string
|
||||
preserveName bool
|
||||
value string
|
||||
setTo string
|
||||
alternate string
|
||||
varType string
|
||||
err error
|
||||
}
|
||||
@ -49,21 +52,17 @@ func assertVariable(t *testing.T, variable *proc.Variable, expected varTest) {
|
||||
}
|
||||
}
|
||||
|
||||
func evalVariable(p *proc.Process, symbol string) (*proc.Variable, error) {
|
||||
func evalVariable(p *proc.Process, symbol string, cfg proc.LoadConfig) (*proc.Variable, error) {
|
||||
scope, err := p.CurrentThread.Scope()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return scope.EvalVariable(symbol)
|
||||
return scope.EvalVariable(symbol, cfg)
|
||||
}
|
||||
|
||||
func (tc *varTest) settable() bool {
|
||||
return tc.setTo != ""
|
||||
}
|
||||
|
||||
func (tc *varTest) afterSet() varTest {
|
||||
func (tc *varTest) alternateVarTest() varTest {
|
||||
r := *tc
|
||||
r.value = r.setTo
|
||||
r.value = r.alternate
|
||||
return r
|
||||
}
|
||||
|
||||
@ -142,7 +141,7 @@ func TestVariableEvaluation(t *testing.T) {
|
||||
assertNoError(err, t, "Continue() returned an error")
|
||||
|
||||
for _, tc := range testcases {
|
||||
variable, err := evalVariable(p, tc.name)
|
||||
variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
|
||||
if tc.err == nil {
|
||||
assertNoError(err, t, "EvalVariable() returned an error")
|
||||
assertVariable(t, variable, tc)
|
||||
@ -155,14 +154,14 @@ func TestVariableEvaluation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
if tc.settable() {
|
||||
assertNoError(setVariable(p, tc.name, tc.setTo), t, "SetVariable()")
|
||||
variable, err = evalVariable(p, tc.name)
|
||||
if tc.alternate != "" {
|
||||
assertNoError(setVariable(p, tc.name, tc.alternate), t, "SetVariable()")
|
||||
variable, err = evalVariable(p, tc.name, pnormalLoadConfig)
|
||||
assertNoError(err, t, "EvalVariable()")
|
||||
assertVariable(t, variable, tc.afterSet())
|
||||
assertVariable(t, variable, tc.alternateVarTest())
|
||||
|
||||
assertNoError(setVariable(p, tc.name, tc.value), t, "SetVariable()")
|
||||
variable, err := evalVariable(p, tc.name)
|
||||
variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
|
||||
assertNoError(err, t, "EvalVariable()")
|
||||
assertVariable(t, variable, tc)
|
||||
}
|
||||
@ -170,6 +169,72 @@ func TestVariableEvaluation(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestVariableEvaluationShort(t *testing.T) {
|
||||
testcases := []varTest{
|
||||
{"a1", true, "\"foofoofoofoofoofoo\"", "", "string", nil},
|
||||
{"a11", true, "[3]main.FooBar [...]", "", "[3]main.FooBar", nil},
|
||||
{"a12", true, "[]main.FooBar len: 2, cap: 2, [...]", "", "[]main.FooBar", nil},
|
||||
{"a13", true, "[]*main.FooBar len: 3, cap: 3, [...]", "", "[]*main.FooBar", nil},
|
||||
{"a2", true, "6", "", "int", nil},
|
||||
{"a3", true, "7.23", "", "float64", nil},
|
||||
{"a4", true, "[2]int [...]", "", "[2]int", nil},
|
||||
{"a5", true, "[]int len: 5, cap: 5, [...]", "", "[]int", nil},
|
||||
{"a6", true, "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
|
||||
{"a7", true, "(*main.FooBar)(0x…", "", "*main.FooBar", nil},
|
||||
{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
|
||||
{"a9", true, "*main.FooBar nil", "", "*main.FooBar", nil},
|
||||
{"baz", true, "\"bazburzum\"", "", "string", nil},
|
||||
{"neg", true, "-1", "", "int", nil},
|
||||
{"f32", true, "1.2", "", "float32", nil},
|
||||
{"c64", true, "(1 + 2i)", "", "complex64", nil},
|
||||
{"c128", true, "(2 + 3i)", "", "complex128", nil},
|
||||
{"a6.Baz", true, "8", "", "int", nil},
|
||||
{"a7.Baz", true, "5", "", "int", nil},
|
||||
{"a8.Baz", true, "\"feh\"", "", "string", nil},
|
||||
{"a9.Baz", true, "nil", "", "int", fmt.Errorf("a9 is nil")},
|
||||
{"a9.NonExistent", true, "nil", "", "int", fmt.Errorf("a9 has no member NonExistent")},
|
||||
{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil}, // reread variable after member
|
||||
{"i32", true, "[2]int32 [...]", "", "[2]int32", nil},
|
||||
{"b1", true, "true", "false", "bool", nil},
|
||||
{"b2", true, "false", "true", "bool", nil},
|
||||
{"i8", true, "1", "2", "int8", nil},
|
||||
{"u16", true, "65535", "0", "uint16", nil},
|
||||
{"u32", true, "4294967295", "1", "uint32", nil},
|
||||
{"u64", true, "18446744073709551615", "2", "uint64", nil},
|
||||
{"u8", true, "255", "3", "uint8", nil},
|
||||
{"up", true, "5", "4", "uintptr", nil},
|
||||
{"f", true, "main.barfoo", "", "func()", nil},
|
||||
{"ba", true, "[]int len: 200, cap: 200, [...]", "", "[]int", nil},
|
||||
{"ms", true, "main.Nest {Level: 0, Nest: (*main.Nest)(0x…", "", "main.Nest", nil},
|
||||
{"ms.Nest.Nest", true, "(*main.Nest)(0x…", "", "*main.Nest", nil},
|
||||
{"ms.Nest.Nest.Nest.Nest.Nest", true, "*main.Nest nil", "", "*main.Nest", nil},
|
||||
{"ms.Nest.Nest.Nest.Nest.Nest.Nest", true, "", "", "*main.Nest", fmt.Errorf("ms.Nest.Nest.Nest.Nest.Nest is nil")},
|
||||
{"main.p1", true, "10", "", "int", nil},
|
||||
{"p1", true, "10", "", "int", nil},
|
||||
{"NonExistent", true, "", "", "", fmt.Errorf("could not find symbol value for NonExistent")},
|
||||
}
|
||||
|
||||
withTestProcess("testvariables", t, func(p *proc.Process, fixture protest.Fixture) {
|
||||
err := p.Continue()
|
||||
assertNoError(err, t, "Continue() returned an error")
|
||||
|
||||
for _, tc := range testcases {
|
||||
variable, err := evalVariable(p, tc.name, pshortLoadConfig)
|
||||
if tc.err == nil {
|
||||
assertNoError(err, t, "EvalVariable() returned an error")
|
||||
assertVariable(t, variable, tc)
|
||||
} else {
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error %s, got no error: %s\n", tc.err.Error(), api.ConvertVar(variable).SinglelineString())
|
||||
}
|
||||
if tc.err.Error() != err.Error() {
|
||||
t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestMultilineVariableEvaluation(t *testing.T) {
|
||||
testcases := []varTest{
|
||||
{"a1", true, "\"foofoofoofoofoofoo\"", "", "string", nil},
|
||||
@ -209,7 +274,7 @@ func TestMultilineVariableEvaluation(t *testing.T) {
|
||||
assertNoError(err, t, "Continue() returned an error")
|
||||
|
||||
for _, tc := range testcases {
|
||||
variable, err := evalVariable(p, tc.name)
|
||||
variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
|
||||
assertNoError(err, t, "EvalVariable() returned an error")
|
||||
if ms := api.ConvertVar(variable).MultilineString(""); !matchStringOrPrefix(ms, tc.value) {
|
||||
t.Fatalf("Expected %s got %s (variable %s)\n", tc.value, ms, variable.Name)
|
||||
@ -237,7 +302,7 @@ func (s varArray) Less(i, j int) bool {
|
||||
|
||||
func TestLocalVariables(t *testing.T) {
|
||||
testcases := []struct {
|
||||
fn func(*proc.EvalScope) ([]*proc.Variable, error)
|
||||
fn func(*proc.EvalScope, proc.LoadConfig) ([]*proc.Variable, error)
|
||||
output []varTest
|
||||
}{
|
||||
{(*proc.EvalScope).LocalVariables,
|
||||
@ -284,7 +349,7 @@ func TestLocalVariables(t *testing.T) {
|
||||
for _, tc := range testcases {
|
||||
scope, err := p.CurrentThread.Scope()
|
||||
assertNoError(err, t, "AsScope()")
|
||||
vars, err := tc.fn(scope)
|
||||
vars, err := tc.fn(scope, pnormalLoadConfig)
|
||||
assertNoError(err, t, "LocalVariables() returned an error")
|
||||
|
||||
sort.Sort(varArray(vars))
|
||||
@ -303,21 +368,24 @@ func TestLocalVariables(t *testing.T) {
|
||||
func TestEmbeddedStruct(t *testing.T) {
|
||||
withTestProcess("testvariables2", t, func(p *proc.Process, fixture protest.Fixture) {
|
||||
testcases := []varTest{
|
||||
{"b.val", true, "-314", "", "int", nil},
|
||||
{"b.A.val", true, "-314", "", "int", nil},
|
||||
{"b.a.val", true, "42", "", "int", nil},
|
||||
{"b.ptr.val", true, "1337", "", "int", nil},
|
||||
{"b.C.s", true, "\"hello\"", "", "string", nil},
|
||||
{"b.s", true, "\"hello\"", "", "string", nil},
|
||||
{"b2", true, "main.B {main.A: struct main.A {val: 42}, *main.C: *struct main.C nil, a: main.A {val: 47}, ptr: *main.A nil}", "", "main.B", nil},
|
||||
{"b.val", true, "-314", "-314", "int", nil},
|
||||
{"b.A.val", true, "-314", "-314", "int", nil},
|
||||
{"b.a.val", true, "42", "42", "int", nil},
|
||||
{"b.ptr.val", true, "1337", "1337", "int", nil},
|
||||
{"b.C.s", true, "\"hello\"", "\"hello\"", "string", nil},
|
||||
{"b.s", true, "\"hello\"", "\"hello\"", "string", nil},
|
||||
{"b2", true, "main.B {main.A: struct main.A {val: 42}, *main.C: *struct main.C nil, a: main.A {val: 47}, ptr: *main.A nil}", "main.B {main.A: (*struct main.A)(0x…", "main.B", nil},
|
||||
}
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
|
||||
for _, tc := range testcases {
|
||||
variable, err := evalVariable(p, tc.name)
|
||||
variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
|
||||
if tc.err == nil {
|
||||
assertNoError(err, t, "EvalVariable() returned an error")
|
||||
assertNoError(err, t, fmt.Sprintf("EvalVariable(%s) returned an error", tc.name))
|
||||
assertVariable(t, variable, tc)
|
||||
variable, err = evalVariable(p, tc.name, pshortLoadConfig)
|
||||
assertNoError(err, t, fmt.Sprintf("EvalVariable(%s, pshortLoadConfig) returned an error", tc.name))
|
||||
assertVariable(t, variable, tc.alternateVarTest())
|
||||
} else {
|
||||
if tc.err.Error() != err.Error() {
|
||||
t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
|
||||
@ -334,7 +402,7 @@ func TestComplexSetting(t *testing.T) {
|
||||
|
||||
h := func(setExpr, value string) {
|
||||
assertNoError(setVariable(p, "c128", setExpr), t, "SetVariable()")
|
||||
variable, err := evalVariable(p, "c128")
|
||||
variable, err := evalVariable(p, "c128", pnormalLoadConfig)
|
||||
assertNoError(err, t, "EvalVariable()")
|
||||
if s := api.ConvertVar(variable).SinglelineString(); s != value {
|
||||
t.Fatalf("Wrong value of c128: \"%s\", expected \"%s\" after setting it to \"%s\"", s, value, setExpr)
|
||||
@ -351,171 +419,171 @@ func TestComplexSetting(t *testing.T) {
|
||||
func TestEvalExpression(t *testing.T) {
|
||||
testcases := []varTest{
|
||||
// slice/array/string subscript
|
||||
{"s1[0]", false, "\"one\"", "", "string", nil},
|
||||
{"s1[1]", false, "\"two\"", "", "string", nil},
|
||||
{"s1[2]", false, "\"three\"", "", "string", nil},
|
||||
{"s1[3]", false, "\"four\"", "", "string", nil},
|
||||
{"s1[4]", false, "\"five\"", "", "string", nil},
|
||||
{"s1[0]", false, "\"one\"", "\"one\"", "string", nil},
|
||||
{"s1[1]", false, "\"two\"", "\"two\"", "string", nil},
|
||||
{"s1[2]", false, "\"three\"", "\"three\"", "string", nil},
|
||||
{"s1[3]", false, "\"four\"", "\"four\"", "string", nil},
|
||||
{"s1[4]", false, "\"five\"", "\"five\"", "string", nil},
|
||||
{"s1[5]", false, "", "", "string", fmt.Errorf("index out of bounds")},
|
||||
{"a1[0]", false, "\"one\"", "", "string", nil},
|
||||
{"a1[1]", false, "\"two\"", "", "string", nil},
|
||||
{"a1[2]", false, "\"three\"", "", "string", nil},
|
||||
{"a1[3]", false, "\"four\"", "", "string", nil},
|
||||
{"a1[4]", false, "\"five\"", "", "string", nil},
|
||||
{"a1[0]", false, "\"one\"", "\"one\"", "string", nil},
|
||||
{"a1[1]", false, "\"two\"", "\"two\"", "string", nil},
|
||||
{"a1[2]", false, "\"three\"", "\"three\"", "string", nil},
|
||||
{"a1[3]", false, "\"four\"", "\"four\"", "string", nil},
|
||||
{"a1[4]", false, "\"five\"", "\"five\"", "string", nil},
|
||||
{"a1[5]", false, "", "", "string", fmt.Errorf("index out of bounds")},
|
||||
{"str1[0]", false, "48", "", "byte", nil},
|
||||
{"str1[1]", false, "49", "", "byte", nil},
|
||||
{"str1[2]", false, "50", "", "byte", nil},
|
||||
{"str1[10]", false, "48", "", "byte", nil},
|
||||
{"str1[0]", false, "48", "48", "byte", nil},
|
||||
{"str1[1]", false, "49", "49", "byte", nil},
|
||||
{"str1[2]", false, "50", "50", "byte", nil},
|
||||
{"str1[10]", false, "48", "48", "byte", nil},
|
||||
{"str1[11]", false, "", "", "byte", fmt.Errorf("index out of bounds")},
|
||||
|
||||
// slice/array/string reslicing
|
||||
{"a1[2:4]", false, "[]string len: 2, cap: 2, [\"three\",\"four\"]", "", "[]string", nil},
|
||||
{"s1[2:4]", false, "[]string len: 2, cap: 2, [\"three\",\"four\"]", "", "[]string", nil},
|
||||
{"str1[2:4]", false, "\"23\"", "", "string", nil},
|
||||
{"str1[0:11]", false, "\"01234567890\"", "", "string", nil},
|
||||
{"str1[:3]", false, "\"012\"", "", "string", nil},
|
||||
{"str1[3:]", false, "\"34567890\"", "", "string", nil},
|
||||
{"a1[2:4]", false, "[]string len: 2, cap: 2, [\"three\",\"four\"]", "[]string len: 2, cap: 2, [...]", "[]string", nil},
|
||||
{"s1[2:4]", false, "[]string len: 2, cap: 2, [\"three\",\"four\"]", "[]string len: 2, cap: 2, [...]", "[]string", nil},
|
||||
{"str1[2:4]", false, "\"23\"", "\"23\"", "string", nil},
|
||||
{"str1[0:11]", false, "\"01234567890\"", "\"01234567890\"", "string", nil},
|
||||
{"str1[:3]", false, "\"012\"", "\"012\"", "string", nil},
|
||||
{"str1[3:]", false, "\"34567890\"", "\"34567890\"", "string", nil},
|
||||
{"str1[0:12]", false, "", "", "string", fmt.Errorf("index out of bounds")},
|
||||
{"str1[5:3]", false, "", "", "string", fmt.Errorf("index out of bounds")},
|
||||
|
||||
// pointers
|
||||
{"*p2", false, "5", "", "int", nil},
|
||||
{"p2", true, "*5", "", "*int", nil},
|
||||
{"p3", true, "*int nil", "", "*int", nil},
|
||||
{"*p2", false, "5", "5", "int", nil},
|
||||
{"p2", true, "*5", "(*int)(0x…", "*int", nil},
|
||||
{"p3", true, "*int nil", "*int nil", "*int", nil},
|
||||
{"*p3", false, "", "", "int", fmt.Errorf("nil pointer dereference")},
|
||||
|
||||
// channels
|
||||
{"ch1", true, "chan int 0/2", "", "chan int", nil},
|
||||
{"chnil", true, "chan int nil", "", "chan int", nil},
|
||||
{"ch1", true, "chan int 0/2", "chan int 0/2", "chan int", nil},
|
||||
{"chnil", true, "chan int nil", "chan int nil", "chan int", nil},
|
||||
{"ch1+1", false, "", "", "", fmt.Errorf("can not convert 1 constant to chan int")},
|
||||
|
||||
// maps
|
||||
{"m1[\"Malone\"]", false, "struct main.astruct {A: 2, B: 3}", "", "struct main.astruct", nil},
|
||||
{"m2[1].B", false, "11", "", "int", nil},
|
||||
{"m2[c1.sa[2].B-4].A", false, "10", "", "int", nil},
|
||||
{"m2[*p1].B", false, "11", "", "int", nil},
|
||||
{"m3[as1]", false, "42", "", "int", nil},
|
||||
{"m1[\"Malone\"]", false, "struct main.astruct {A: 2, B: 3}", "struct main.astruct {A: 2, B: 3}", "struct main.astruct", nil},
|
||||
{"m2[1].B", false, "11", "11", "int", nil},
|
||||
{"m2[c1.sa[2].B-4].A", false, "10", "10", "int", nil},
|
||||
{"m2[*p1].B", false, "11", "11", "int", nil},
|
||||
{"m3[as1]", false, "42", "42", "int", nil},
|
||||
{"mnil[\"Malone\"]", false, "", "", "", fmt.Errorf("key not found")},
|
||||
{"m1[80:]", false, "", "", "", fmt.Errorf("map index out of bounds")},
|
||||
|
||||
// interfaces
|
||||
{"err1", true, "error(*struct main.astruct) *{A: 1, B: 2}", "", "error", nil},
|
||||
{"err2", true, "error(*struct main.bstruct) *{a: main.astruct {A: 1, B: 2}}", "", "error", nil},
|
||||
{"errnil", true, "error nil", "", "error", nil},
|
||||
{"iface1", true, "interface {}(*struct main.astruct) *{A: 1, B: 2}", "", "interface {}", nil},
|
||||
{"iface2", true, "interface {}(*string) *\"test\"", "", "interface {}", nil},
|
||||
{"iface3", true, "interface {}(*map[string]go/constant.Value) *[]", "", "interface {}", nil},
|
||||
{"iface4", true, "interface {}(*[]go/constant.Value) *[*4]", "", "interface {}", nil},
|
||||
{"ifacenil", true, "interface {} nil", "", "interface {}", nil},
|
||||
{"err1 == err2", false, "false", "", "", nil},
|
||||
{"err1", true, "error(*struct main.astruct) *{A: 1, B: 2}", "error(*struct main.astruct) 0x…", "error", nil},
|
||||
{"err2", true, "error(*struct main.bstruct) *{a: main.astruct {A: 1, B: 2}}", "error(*struct main.bstruct) 0x…", "error", nil},
|
||||
{"errnil", true, "error nil", "error nil", "error", nil},
|
||||
{"iface1", true, "interface {}(*struct main.astruct) *{A: 1, B: 2}", "interface {}(*struct main.astruct) 0x…", "interface {}", nil},
|
||||
{"iface2", true, "interface {}(*string) *\"test\"", "interface {}(*string) 0x…", "interface {}", nil},
|
||||
{"iface3", true, "interface {}(*map[string]go/constant.Value) *[]", "interface {}(*map[string]go/constant.Value) 0x…", "interface {}", nil},
|
||||
{"iface4", true, "interface {}(*[]go/constant.Value) *[*4]", "interface {}(*[]go/constant.Value) 0x…", "interface {}", nil},
|
||||
{"ifacenil", true, "interface {} nil", "interface {} nil", "interface {}", nil},
|
||||
{"err1 == err2", false, "false", "false", "", nil},
|
||||
{"err1 == iface1", false, "", "", "", fmt.Errorf("mismatched types \"error\" and \"interface {}\"")},
|
||||
{"errnil == nil", false, "false", "", "", nil},
|
||||
{"nil == errnil", false, "false", "", "", nil},
|
||||
{"err1.(*main.astruct)", false, "*struct main.astruct {A: 1, B: 2}", "", "*struct main.astruct", nil},
|
||||
{"errnil == nil", false, "false", "false", "", nil},
|
||||
{"nil == errnil", false, "false", "false", "", nil},
|
||||
{"err1.(*main.astruct)", false, "*struct main.astruct {A: 1, B: 2}", "(*struct main.astruct)(0x…", "*struct main.astruct", nil},
|
||||
{"err1.(*main.bstruct)", false, "", "", "", fmt.Errorf("interface conversion: error is *struct main.astruct, not *struct main.bstruct")},
|
||||
{"errnil.(*main.astruct)", false, "", "", "", fmt.Errorf("interface conversion: error is nil, not *main.astruct")},
|
||||
{"const1", true, "go/constant.Value(*go/constant.int64Val) *3", "", "go/constant.Value", nil},
|
||||
{"const1", true, "go/constant.Value(*go/constant.int64Val) *3", "go/constant.Value(*go/constant.int64Val) 0x…", "go/constant.Value", nil},
|
||||
|
||||
// combined expressions
|
||||
{"c1.pb.a.A", true, "1", "", "int", nil},
|
||||
{"c1.sa[1].B", false, "3", "", "int", nil},
|
||||
{"s2[5].B", false, "12", "", "int", nil},
|
||||
{"s2[c1.sa[2].B].A", false, "11", "", "int", nil},
|
||||
{"s2[*p2].B", false, "12", "", "int", nil},
|
||||
{"c1.pb.a.A", true, "1", "1", "int", nil},
|
||||
{"c1.sa[1].B", false, "3", "3", "int", nil},
|
||||
{"s2[5].B", false, "12", "12", "int", nil},
|
||||
{"s2[c1.sa[2].B].A", false, "11", "11", "int", nil},
|
||||
{"s2[*p2].B", false, "12", "12", "int", nil},
|
||||
|
||||
// constants
|
||||
{"1.1", false, "1.1", "", "", nil},
|
||||
{"10", false, "10", "", "", nil},
|
||||
{"1 + 2i", false, "(1 + 2i)", "", "", nil},
|
||||
{"true", false, "true", "", "", nil},
|
||||
{"\"test\"", false, "\"test\"", "", "", nil},
|
||||
{"1.1", false, "1.1", "1.1", "", nil},
|
||||
{"10", false, "10", "10", "", nil},
|
||||
{"1 + 2i", false, "(1 + 2i)", "(1 + 2i)", "", nil},
|
||||
{"true", false, "true", "true", "", nil},
|
||||
{"\"test\"", false, "\"test\"", "\"test\"", "", nil},
|
||||
|
||||
// binary operators
|
||||
{"i2 + i3", false, "5", "", "int", nil},
|
||||
{"i2 - i3", false, "-1", "", "int", nil},
|
||||
{"i3 - i2", false, "1", "", "int", nil},
|
||||
{"i2 * i3", false, "6", "", "int", nil},
|
||||
{"i2/i3", false, "0", "", "int", nil},
|
||||
{"f1/2.0", false, "1.5", "", "float64", nil},
|
||||
{"i2 << 2", false, "8", "", "int", nil},
|
||||
{"i2 + i3", false, "5", "5", "int", nil},
|
||||
{"i2 - i3", false, "-1", "-1", "int", nil},
|
||||
{"i3 - i2", false, "1", "1", "int", nil},
|
||||
{"i2 * i3", false, "6", "6", "int", nil},
|
||||
{"i2/i3", false, "0", "0", "int", nil},
|
||||
{"f1/2.0", false, "1.5", "1.5", "float64", nil},
|
||||
{"i2 << 2", false, "8", "8", "int", nil},
|
||||
|
||||
// unary operators
|
||||
{"-i2", false, "-2", "", "int", nil},
|
||||
{"+i2", false, "2", "", "int", nil},
|
||||
{"^i2", false, "-3", "", "int", nil},
|
||||
{"-i2", false, "-2", "-2", "int", nil},
|
||||
{"+i2", false, "2", "2", "int", nil},
|
||||
{"^i2", false, "-3", "-3", "int", nil},
|
||||
|
||||
// comparison operators
|
||||
{"i2 == i3", false, "false", "", "", nil},
|
||||
{"i2 == 2", false, "true", "", "", nil},
|
||||
{"i2 == 2", false, "true", "", "", nil},
|
||||
{"i2 == 3", false, "false", "", "", nil},
|
||||
{"i2 != i3", false, "true", "", "", nil},
|
||||
{"i2 < i3", false, "true", "", "", nil},
|
||||
{"i2 <= i3", false, "true", "", "", nil},
|
||||
{"i2 > i3", false, "false", "", "", nil},
|
||||
{"i2 >= i3", false, "false", "", "", nil},
|
||||
{"i2 >= 2", false, "true", "", "", nil},
|
||||
{"str1 == \"01234567890\"", false, "true", "", "", nil},
|
||||
{"str1 < \"01234567890\"", false, "false", "", "", nil},
|
||||
{"str1 < \"11234567890\"", false, "true", "", "", nil},
|
||||
{"str1 > \"00234567890\"", false, "true", "", "", nil},
|
||||
{"str1 == str1", false, "true", "", "", nil},
|
||||
{"c1.pb.a == *(c1.sa[0])", false, "true", "", "", nil},
|
||||
{"c1.pb.a != *(c1.sa[0])", false, "false", "", "", nil},
|
||||
{"c1.pb.a == *(c1.sa[1])", false, "false", "", "", nil},
|
||||
{"c1.pb.a != *(c1.sa[1])", false, "true", "", "", nil},
|
||||
{"i2 == i3", false, "false", "false", "", nil},
|
||||
{"i2 == 2", false, "true", "true", "", nil},
|
||||
{"i2 == 2", false, "true", "true", "", nil},
|
||||
{"i2 == 3", false, "false", "false", "", nil},
|
||||
{"i2 != i3", false, "true", "true", "", nil},
|
||||
{"i2 < i3", false, "true", "true", "", nil},
|
||||
{"i2 <= i3", false, "true", "true", "", nil},
|
||||
{"i2 > i3", false, "false", "false", "", nil},
|
||||
{"i2 >= i3", false, "false", "false", "", nil},
|
||||
{"i2 >= 2", false, "true", "true", "", nil},
|
||||
{"str1 == \"01234567890\"", false, "true", "true", "", nil},
|
||||
{"str1 < \"01234567890\"", false, "false", "false", "", nil},
|
||||
{"str1 < \"11234567890\"", false, "true", "true", "", nil},
|
||||
{"str1 > \"00234567890\"", false, "true", "true", "", nil},
|
||||
{"str1 == str1", false, "true", "true", "", nil},
|
||||
{"c1.pb.a == *(c1.sa[0])", false, "true", "true", "", nil},
|
||||
{"c1.pb.a != *(c1.sa[0])", false, "false", "false", "", nil},
|
||||
{"c1.pb.a == *(c1.sa[1])", false, "false", "false", "", nil},
|
||||
{"c1.pb.a != *(c1.sa[1])", false, "true", "true", "", nil},
|
||||
|
||||
// builtins
|
||||
{"cap(parr)", false, "4", "", "", nil},
|
||||
{"len(parr)", false, "4", "", "", nil},
|
||||
{"cap(parr)", false, "4", "4", "", nil},
|
||||
{"len(parr)", false, "4", "4", "", nil},
|
||||
{"cap(p1)", false, "", "", "", fmt.Errorf("invalid argument p1 (type *int) for cap")},
|
||||
{"len(p1)", false, "", "", "", fmt.Errorf("invalid argument p1 (type *int) for len")},
|
||||
{"cap(a1)", false, "5", "", "", nil},
|
||||
{"len(a1)", false, "5", "", "", nil},
|
||||
{"cap(s3)", false, "6", "", "", nil},
|
||||
{"len(s3)", false, "0", "", "", nil},
|
||||
{"cap(nilslice)", false, "0", "", "", nil},
|
||||
{"len(nilslice)", false, "0", "", "", nil},
|
||||
{"cap(ch1)", false, "2", "", "", nil},
|
||||
{"len(ch1)", false, "0", "", "", nil},
|
||||
{"cap(chnil)", false, "0", "", "", nil},
|
||||
{"len(chnil)", false, "0", "", "", nil},
|
||||
{"len(m1)", false, "41", "", "", nil},
|
||||
{"len(mnil)", false, "0", "", "", nil},
|
||||
{"imag(cpx1)", false, "2", "", "", nil},
|
||||
{"real(cpx1)", false, "1", "", "", nil},
|
||||
{"imag(3i)", false, "3", "", "", nil},
|
||||
{"real(4)", false, "4", "", "", nil},
|
||||
{"cap(a1)", false, "5", "5", "", nil},
|
||||
{"len(a1)", false, "5", "5", "", nil},
|
||||
{"cap(s3)", false, "6", "6", "", nil},
|
||||
{"len(s3)", false, "0", "0", "", nil},
|
||||
{"cap(nilslice)", false, "0", "0", "", nil},
|
||||
{"len(nilslice)", false, "0", "0", "", nil},
|
||||
{"cap(ch1)", false, "2", "2", "", nil},
|
||||
{"len(ch1)", false, "0", "0", "", nil},
|
||||
{"cap(chnil)", false, "0", "0", "", nil},
|
||||
{"len(chnil)", false, "0", "0", "", nil},
|
||||
{"len(m1)", false, "41", "41", "", nil},
|
||||
{"len(mnil)", false, "0", "0", "", nil},
|
||||
{"imag(cpx1)", false, "2", "2", "", nil},
|
||||
{"real(cpx1)", false, "1", "1", "", nil},
|
||||
{"imag(3i)", false, "3", "3", "", nil},
|
||||
{"real(4)", false, "4", "4", "", nil},
|
||||
|
||||
// nil
|
||||
{"nil", false, "nil", "", "", nil},
|
||||
{"nil", false, "nil", "nil", "", nil},
|
||||
{"nil+1", false, "", "", "", fmt.Errorf("operator + can not be applied to \"nil\"")},
|
||||
{"fn1", false, "main.afunc", "", "main.functype", nil},
|
||||
{"fn2", false, "nil", "", "main.functype", nil},
|
||||
{"nilslice", false, "[]int len: 0, cap: 0, []", "", "[]int", nil},
|
||||
{"fn1", false, "main.afunc", "main.afunc", "main.functype", nil},
|
||||
{"fn2", false, "nil", "nil", "main.functype", nil},
|
||||
{"nilslice", false, "[]int len: 0, cap: 0, []", "[]int len: 0, cap: 0, []", "[]int", nil},
|
||||
{"fn1 == fn2", false, "", "", "", fmt.Errorf("can not compare func variables")},
|
||||
{"fn1 == nil", false, "false", "", "", nil},
|
||||
{"fn1 != nil", false, "true", "", "", nil},
|
||||
{"fn2 == nil", false, "true", "", "", nil},
|
||||
{"fn2 != nil", false, "false", "", "", nil},
|
||||
{"c1.sa == nil", false, "false", "", "", nil},
|
||||
{"c1.sa != nil", false, "true", "", "", nil},
|
||||
{"c1.sa[0] == nil", false, "false", "", "", nil},
|
||||
{"c1.sa[0] != nil", false, "true", "", "", nil},
|
||||
{"nilslice == nil", false, "true", "", "", nil},
|
||||
{"nil == nilslice", false, "true", "", "", nil},
|
||||
{"nilslice != nil", false, "false", "", "", nil},
|
||||
{"nilptr == nil", false, "true", "", "", nil},
|
||||
{"nilptr != nil", false, "false", "", "", nil},
|
||||
{"p1 == nil", false, "false", "", "", nil},
|
||||
{"p1 != nil", false, "true", "", "", nil},
|
||||
{"ch1 == nil", false, "false", "", "", nil},
|
||||
{"chnil == nil", false, "true", "", "", nil},
|
||||
{"fn1 == nil", false, "false", "false", "", nil},
|
||||
{"fn1 != nil", false, "true", "true", "", nil},
|
||||
{"fn2 == nil", false, "true", "true", "", nil},
|
||||
{"fn2 != nil", false, "false", "false", "", nil},
|
||||
{"c1.sa == nil", false, "false", "false", "", nil},
|
||||
{"c1.sa != nil", false, "true", "true", "", nil},
|
||||
{"c1.sa[0] == nil", false, "false", "false", "", nil},
|
||||
{"c1.sa[0] != nil", false, "true", "true", "", nil},
|
||||
{"nilslice == nil", false, "true", "true", "", nil},
|
||||
{"nil == nilslice", false, "true", "true", "", nil},
|
||||
{"nilslice != nil", false, "false", "false", "", nil},
|
||||
{"nilptr == nil", false, "true", "true", "", nil},
|
||||
{"nilptr != nil", false, "false", "false", "", nil},
|
||||
{"p1 == nil", false, "false", "false", "", nil},
|
||||
{"p1 != nil", false, "true", "true", "", nil},
|
||||
{"ch1 == nil", false, "false", "false", "", nil},
|
||||
{"chnil == nil", false, "true", "true", "", nil},
|
||||
{"ch1 == chnil", false, "", "", "", fmt.Errorf("can not compare chan variables")},
|
||||
{"m1 == nil", false, "false", "", "", nil},
|
||||
{"m1 == nil", false, "false", "false", "", nil},
|
||||
{"mnil == m1", false, "", "", "", fmt.Errorf("can not compare map variables")},
|
||||
{"mnil == nil", false, "true", "", "", nil},
|
||||
{"mnil == nil", false, "true", "true", "", nil},
|
||||
{"nil == 2", false, "", "", "", fmt.Errorf("can not compare int to nil")},
|
||||
{"2 == nil", false, "", "", "", fmt.Errorf("can not compare int to nil")},
|
||||
|
||||
@ -536,32 +604,36 @@ func TestEvalExpression(t *testing.T) {
|
||||
{"&nil", false, "", "", "", fmt.Errorf("can not take address of \"nil\"")},
|
||||
{"nil[0]", false, "", "", "", fmt.Errorf("expression \"nil\" (nil) does not support indexing")},
|
||||
{"nil[2:10]", false, "", "", "", fmt.Errorf("can not slice \"nil\" (type nil)")},
|
||||
{"nil.member", false, "", "", "", fmt.Errorf("type nil is not a struct")},
|
||||
{"nil.member", false, "", "", "", fmt.Errorf("nil (type nil) is not a struct")},
|
||||
{"(map[string]main.astruct)(0x4000)", false, "", "", "", fmt.Errorf("can not convert \"0x4000\" to map[string]main.astruct")},
|
||||
|
||||
// typecasts
|
||||
{"uint(i2)", false, "2", "", "uint", nil},
|
||||
{"int8(i2)", false, "2", "", "int8", nil},
|
||||
{"int(f1)", false, "3", "", "int", nil},
|
||||
{"complex128(f1)", false, "(3 + 0i)", "", "complex128", nil},
|
||||
{"uint8(i4)", false, "32", "", "uint8", nil},
|
||||
{"uint8(i5)", false, "253", "", "uint8", nil},
|
||||
{"int8(i5)", false, "-3", "", "int8", nil},
|
||||
{"int8(i6)", false, "12", "", "int8", nil},
|
||||
{"uint(i2)", false, "2", "2", "uint", nil},
|
||||
{"int8(i2)", false, "2", "2", "int8", nil},
|
||||
{"int(f1)", false, "3", "3", "int", nil},
|
||||
{"complex128(f1)", false, "(3 + 0i)", "(3 + 0i)", "complex128", nil},
|
||||
{"uint8(i4)", false, "32", "32", "uint8", nil},
|
||||
{"uint8(i5)", false, "253", "253", "uint8", nil},
|
||||
{"int8(i5)", false, "-3", "-3", "int8", nil},
|
||||
{"int8(i6)", false, "12", "12", "int8", nil},
|
||||
|
||||
// misc
|
||||
{"i1", true, "1", "", "int", nil},
|
||||
{"mainMenu", true, `main.Menu len: 3, cap: 3, [{Name: "home", Route: "/", Active: 1},{Name: "About", Route: "/about", Active: 1},{Name: "Login", Route: "/login", Active: 1}]`, "", "main.Menu", nil},
|
||||
{"mainMenu[0]", false, `main.Item {Name: "home", Route: "/", Active: 1}`, "", "main.Item", nil},
|
||||
{"i1", true, "1", "1", "int", nil},
|
||||
{"mainMenu", true, `main.Menu len: 3, cap: 3, [{Name: "home", Route: "/", Active: 1},{Name: "About", Route: "/about", Active: 1},{Name: "Login", Route: "/login", Active: 1}]`, `main.Menu len: 3, cap: 3, [...]`, "main.Menu", nil},
|
||||
{"mainMenu[0]", false, `main.Item {Name: "home", Route: "/", Active: 1}`, `main.Item {Name: "home", Route: "/", Active: 1}`, "main.Item", nil},
|
||||
{"sd", false, "main.D {u1: 0, u2: 0, u3: 0, u4: 0, u5: 0, u6: 0}", "main.D {u1: 0, u2: 0, u3: 0,...+3 more}", "main.D", nil},
|
||||
}
|
||||
|
||||
withTestProcess("testvariables2", t, func(p *proc.Process, fixture protest.Fixture) {
|
||||
assertNoError(p.Continue(), t, "Continue() returned an error")
|
||||
for _, tc := range testcases {
|
||||
variable, err := evalVariable(p, tc.name)
|
||||
variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
|
||||
if tc.err == nil {
|
||||
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s) returned an error", tc.name))
|
||||
assertVariable(t, variable, tc)
|
||||
variable, err := evalVariable(p, tc.name, pshortLoadConfig)
|
||||
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s, pshortLoadConfig) returned an error", tc.name))
|
||||
assertVariable(t, variable, tc.alternateVarTest())
|
||||
} else {
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error %s, got no error (%s)", tc.err.Error(), tc.name)
|
||||
@ -570,6 +642,7 @@ func TestEvalExpression(t *testing.T) {
|
||||
t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -577,7 +650,7 @@ func TestEvalExpression(t *testing.T) {
|
||||
func TestEvalAddrAndCast(t *testing.T) {
|
||||
withTestProcess("testvariables2", t, func(p *proc.Process, fixture protest.Fixture) {
|
||||
assertNoError(p.Continue(), t, "Continue() returned an error")
|
||||
c1addr, err := evalVariable(p, "&c1")
|
||||
c1addr, err := evalVariable(p, "&c1", pnormalLoadConfig)
|
||||
assertNoError(err, t, "EvalExpression(&c1)")
|
||||
c1addrstr := api.ConvertVar(c1addr).SinglelineString()
|
||||
t.Logf("&c1 → %s", c1addrstr)
|
||||
@ -585,7 +658,7 @@ func TestEvalAddrAndCast(t *testing.T) {
|
||||
t.Fatalf("Invalid value of EvalExpression(&c1) \"%s\"", c1addrstr)
|
||||
}
|
||||
|
||||
aaddr, err := evalVariable(p, "&(c1.pb.a)")
|
||||
aaddr, err := evalVariable(p, "&(c1.pb.a)", pnormalLoadConfig)
|
||||
assertNoError(err, t, "EvalExpression(&(c1.pb.a))")
|
||||
aaddrstr := api.ConvertVar(aaddr).SinglelineString()
|
||||
t.Logf("&(c1.pb.a) → %s", aaddrstr)
|
||||
@ -593,7 +666,7 @@ func TestEvalAddrAndCast(t *testing.T) {
|
||||
t.Fatalf("invalid value of EvalExpression(&(c1.pb.a)) \"%s\"", aaddrstr)
|
||||
}
|
||||
|
||||
a, err := evalVariable(p, "*"+aaddrstr)
|
||||
a, err := evalVariable(p, "*"+aaddrstr, pnormalLoadConfig)
|
||||
assertNoError(err, t, fmt.Sprintf("EvalExpression(*%s)", aaddrstr))
|
||||
t.Logf("*%s → %s", aaddrstr, api.ConvertVar(a).SinglelineString())
|
||||
assertVariable(t, a, varTest{aaddrstr, false, "struct main.astruct {A: 1, B: 2}", "", "struct main.astruct", nil})
|
||||
@ -603,7 +676,7 @@ func TestEvalAddrAndCast(t *testing.T) {
|
||||
func TestMapEvaluation(t *testing.T) {
|
||||
withTestProcess("testvariables2", t, func(p *proc.Process, fixture protest.Fixture) {
|
||||
assertNoError(p.Continue(), t, "Continue() returned an error")
|
||||
m1v, err := evalVariable(p, "m1")
|
||||
m1v, err := evalVariable(p, "m1", pnormalLoadConfig)
|
||||
assertNoError(err, t, "EvalVariable()")
|
||||
m1 := api.ConvertVar(m1v)
|
||||
t.Logf("m1 = %v", m1.MultilineString(""))
|
||||
@ -626,7 +699,7 @@ func TestMapEvaluation(t *testing.T) {
|
||||
t.Fatalf("Could not find Malone")
|
||||
}
|
||||
|
||||
m1sliced, err := evalVariable(p, "m1[10:]")
|
||||
m1sliced, err := evalVariable(p, "m1[10:]", pnormalLoadConfig)
|
||||
assertNoError(err, t, "EvalVariable(m1[10:])")
|
||||
if len(m1sliced.Children)/2 != int(m1.Len-10) {
|
||||
t.Fatalf("Wrong number of children (after slicing): %d", len(m1sliced.Children)/2)
|
||||
@ -637,7 +710,7 @@ func TestMapEvaluation(t *testing.T) {
|
||||
func TestUnsafePointer(t *testing.T) {
|
||||
withTestProcess("testvariables2", t, func(p *proc.Process, fixture protest.Fixture) {
|
||||
assertNoError(p.Continue(), t, "Continue() returned an error")
|
||||
up1v, err := evalVariable(p, "up1")
|
||||
up1v, err := evalVariable(p, "up1", pnormalLoadConfig)
|
||||
assertNoError(err, t, "EvalVariable(up1)")
|
||||
up1 := api.ConvertVar(up1v)
|
||||
if ss := up1.SinglelineString(); !strings.HasPrefix(ss, "unsafe.Pointer(") {
|
||||
|
@ -37,7 +37,6 @@ type callContext struct {
|
||||
}
|
||||
|
||||
type cmdfunc func(t *Term, ctx callContext, args string) error
|
||||
type filteringFunc func(t *Term, ctx callContext, args string) ([]string, error)
|
||||
|
||||
type command struct {
|
||||
aliases []string
|
||||
@ -63,6 +62,11 @@ type Commands struct {
|
||||
client service.Client
|
||||
}
|
||||
|
||||
var (
|
||||
LongLoadConfig = api.LoadConfig{true, 1, 64, 64, -1}
|
||||
ShortLoadConfig = api.LoadConfig{false, 0, 64, 0, 3}
|
||||
)
|
||||
|
||||
// DebugCommands returns a Commands struct with default commands defined.
|
||||
func DebugCommands(client service.Client) *Commands {
|
||||
c := &Commands{client: client}
|
||||
@ -85,12 +89,12 @@ func DebugCommands(client service.Client) *Commands {
|
||||
{aliases: []string{"breakpoints", "bp"}, cmdFn: breakpoints, helpMsg: "Print out info for active breakpoints."},
|
||||
{aliases: []string{"print", "p"}, allowedPrefixes: onPrefix | scopePrefix, cmdFn: printVar, helpMsg: "Evaluate a variable."},
|
||||
{aliases: []string{"set"}, allowedPrefixes: scopePrefix, cmdFn: setVar, helpMsg: "Changes the value of a variable."},
|
||||
{aliases: []string{"sources"}, cmdFn: filterSortAndOutput(sources), helpMsg: "Print list of source files, optionally filtered by a regexp."},
|
||||
{aliases: []string{"funcs"}, cmdFn: filterSortAndOutput(funcs), helpMsg: "Print list of functions, optionally filtered by a regexp."},
|
||||
{aliases: []string{"types"}, cmdFn: filterSortAndOutput(types), helpMsg: "Print list of types, optionally filtered by a regexp."},
|
||||
{aliases: []string{"args"}, allowedPrefixes: scopePrefix, cmdFn: filterSortAndOutput(args), helpMsg: "Print function arguments, optionally filtered by a regexp."},
|
||||
{aliases: []string{"locals"}, allowedPrefixes: scopePrefix, cmdFn: filterSortAndOutput(locals), helpMsg: "Print function locals, optionally filtered by a regexp."},
|
||||
{aliases: []string{"vars"}, cmdFn: filterSortAndOutput(vars), helpMsg: "Print package variables, optionally filtered by a regexp."},
|
||||
{aliases: []string{"sources"}, cmdFn: sources, helpMsg: "Print list of source files, optionally filtered by a regexp."},
|
||||
{aliases: []string{"funcs"}, cmdFn: funcs, helpMsg: "Print list of functions, optionally filtered by a regexp."},
|
||||
{aliases: []string{"types"}, cmdFn: types, helpMsg: "Print list of types, optionally filtered by a regexp."},
|
||||
{aliases: []string{"args"}, allowedPrefixes: scopePrefix | onPrefix, cmdFn: args, helpMsg: "args [-v] <filter>. Print function arguments, optionally filtered by a regexp."},
|
||||
{aliases: []string{"locals"}, allowedPrefixes: scopePrefix | onPrefix, cmdFn: locals, helpMsg: "locals [-v] <filter>. Print function locals, optionally filtered by a regexp."},
|
||||
{aliases: []string{"vars"}, cmdFn: vars, helpMsg: "vars [-v] <filter>. Print package variables, optionally filtered by a regexp."},
|
||||
{aliases: []string{"regs"}, cmdFn: regs, helpMsg: "Print contents of CPU registers."},
|
||||
{aliases: []string{"exit", "quit", "q"}, cmdFn: exitCommand, helpMsg: "Exit the debugger."},
|
||||
{aliases: []string{"list", "ls"}, allowedPrefixes: scopePrefix, cmdFn: listCommand, helpMsg: "list <linespec>. Show source around current point or provided linespec."},
|
||||
@ -552,6 +556,20 @@ func breakpoints(t *Term, ctx callContext, args string) error {
|
||||
if bp.Goroutine {
|
||||
attrs = append(attrs, "\tgoroutine")
|
||||
}
|
||||
if bp.LoadArgs != nil {
|
||||
if *(bp.LoadArgs) == LongLoadConfig {
|
||||
attrs = append(attrs, "\targs -v")
|
||||
} else {
|
||||
attrs = append(attrs, "\targs")
|
||||
}
|
||||
}
|
||||
if bp.LoadLocals != nil {
|
||||
if *(bp.LoadLocals) == LongLoadConfig {
|
||||
attrs = append(attrs, "\tlocals -v")
|
||||
} else {
|
||||
attrs = append(attrs, "\tlocals")
|
||||
}
|
||||
}
|
||||
for i := range bp.Variables {
|
||||
attrs = append(attrs, fmt.Sprintf("\tprint %s", bp.Variables[i]))
|
||||
}
|
||||
@ -624,7 +642,7 @@ func printVar(t *Term, ctx callContext, args string) error {
|
||||
ctx.Breakpoint.Variables = append(ctx.Breakpoint.Variables, args)
|
||||
return nil
|
||||
}
|
||||
val, err := t.client.EvalVariable(ctx.Scope, args)
|
||||
val, err := t.client.EvalVariable(ctx.Scope, args, LongLoadConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -650,55 +668,101 @@ func setVar(t *Term, ctx callContext, args string) error {
|
||||
return t.client.SetVariable(ctx.Scope, lexpr, rexpr)
|
||||
}
|
||||
|
||||
func filterVariables(vars []api.Variable, filter string) []string {
|
||||
func printFilteredVariables(varType string, vars []api.Variable, filter string, cfg api.LoadConfig) error {
|
||||
reg, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, err.Error())
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
data := make([]string, 0, len(vars))
|
||||
match := false
|
||||
for _, v := range vars {
|
||||
if reg == nil || reg.Match([]byte(v.Name)) {
|
||||
data = append(data, fmt.Sprintf("%s = %s", v.Name, v.SinglelineString()))
|
||||
match = true
|
||||
if cfg == ShortLoadConfig {
|
||||
fmt.Printf("%s = %s\n", v.Name, v.SinglelineString())
|
||||
} else {
|
||||
fmt.Printf("%s = %s\n", v.Name, v.MultilineString(""))
|
||||
}
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func sources(t *Term, ctx callContext, filter string) ([]string, error) {
|
||||
return t.client.ListSources(filter)
|
||||
}
|
||||
|
||||
func funcs(t *Term, ctx callContext, filter string) ([]string, error) {
|
||||
return t.client.ListFunctions(filter)
|
||||
}
|
||||
|
||||
func types(t *Term, ctx callContext, filter string) ([]string, error) {
|
||||
return t.client.ListTypes(filter)
|
||||
}
|
||||
|
||||
func args(t *Term, ctx callContext, filter string) ([]string, error) {
|
||||
vars, err := t.client.ListFunctionArgs(ctx.Scope)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if !match {
|
||||
fmt.Printf("(no %s)\n", varType)
|
||||
}
|
||||
return describeNoVars("args", filterVariables(vars, filter)), nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func locals(t *Term, ctx callContext, filter string) ([]string, error) {
|
||||
locals, err := t.client.ListLocalVariables(ctx.Scope)
|
||||
func printSortedStrings(v []string, err error) error {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
return describeNoVars("locals", filterVariables(locals, filter)), nil
|
||||
sort.Strings(v)
|
||||
for _, d := range v {
|
||||
fmt.Println(d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func vars(t *Term, ctx callContext, filter string) ([]string, error) {
|
||||
vars, err := t.client.ListPackageVariables(filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func sources(t *Term, ctx callContext, args string) error {
|
||||
return printSortedStrings(t.client.ListSources(args))
|
||||
}
|
||||
|
||||
func funcs(t *Term, ctx callContext, args string) error {
|
||||
return printSortedStrings(t.client.ListFunctions(args))
|
||||
}
|
||||
|
||||
func types(t *Term, ctx callContext, args string) error {
|
||||
return printSortedStrings(t.client.ListTypes(args))
|
||||
}
|
||||
|
||||
func parseVarArguments(args string) (filter string, cfg api.LoadConfig) {
|
||||
if v := strings.SplitN(args, " ", 2); len(v) >= 1 && v[0] == "-v" {
|
||||
if len(v) == 2 {
|
||||
return v[1], LongLoadConfig
|
||||
} else {
|
||||
return "", LongLoadConfig
|
||||
}
|
||||
}
|
||||
return describeNoVars("vars", filterVariables(vars, filter)), nil
|
||||
return args, ShortLoadConfig
|
||||
}
|
||||
|
||||
func args(t *Term, ctx callContext, args string) error {
|
||||
filter, cfg := parseVarArguments(args)
|
||||
if ctx.Prefix == onPrefix {
|
||||
if filter != "" {
|
||||
return fmt.Errorf("filter not supported on breakpoint")
|
||||
}
|
||||
ctx.Breakpoint.LoadArgs = &cfg
|
||||
return nil
|
||||
}
|
||||
vars, err := t.client.ListFunctionArgs(ctx.Scope, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printFilteredVariables("args", vars, filter, cfg)
|
||||
}
|
||||
|
||||
func locals(t *Term, ctx callContext, args string) error {
|
||||
filter, cfg := parseVarArguments(args)
|
||||
if ctx.Prefix == onPrefix {
|
||||
if filter != "" {
|
||||
return fmt.Errorf("filter not supported on breakpoint")
|
||||
}
|
||||
ctx.Breakpoint.LoadLocals = &cfg
|
||||
return nil
|
||||
}
|
||||
locals, err := t.client.ListLocalVariables(ctx.Scope, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printFilteredVariables("locals", locals, filter, cfg)
|
||||
}
|
||||
|
||||
func vars(t *Term, ctx callContext, args string) error {
|
||||
filter, cfg := parseVarArguments(args)
|
||||
vars, err := t.client.ListPackageVariables(filter, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printFilteredVariables("vars", vars, filter, cfg)
|
||||
}
|
||||
|
||||
func regs(t *Term, ctx callContext, args string) error {
|
||||
@ -710,27 +774,6 @@ func regs(t *Term, ctx callContext, args string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func filterSortAndOutput(fn filteringFunc) cmdfunc {
|
||||
return func(t *Term, ctx callContext, args string) error {
|
||||
var filter string
|
||||
if len(args) > 0 {
|
||||
if _, err := regexp.Compile(args); err != nil {
|
||||
return fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||
}
|
||||
filter = args
|
||||
}
|
||||
data, err := fn(t, ctx, filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sort.Sort(sort.StringSlice(data))
|
||||
for _, d := range data {
|
||||
fmt.Println(d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func stackCommand(t *Term, ctx callContext, args string) error {
|
||||
depth, full, err := parseStackArgs(args)
|
||||
if err != nil {
|
||||
@ -740,7 +783,11 @@ func stackCommand(t *Term, ctx callContext, args string) error {
|
||||
ctx.Breakpoint.Stacktrace = depth
|
||||
return nil
|
||||
}
|
||||
stack, err := t.client.Stacktrace(ctx.Scope.GoroutineID, depth, full)
|
||||
var cfg *api.LoadConfig
|
||||
if full {
|
||||
cfg = &ShortLoadConfig
|
||||
}
|
||||
stack, err := t.client.Stacktrace(ctx.Scope.GoroutineID, depth, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -772,7 +819,7 @@ func parseStackArgs(argstr string) (int, bool, error) {
|
||||
|
||||
func listCommand(t *Term, ctx callContext, args string) error {
|
||||
if ctx.Prefix == scopePrefix {
|
||||
locs, err := t.client.Stacktrace(ctx.Scope.GoroutineID, ctx.Scope.Frame, false)
|
||||
locs, err := t.client.Stacktrace(ctx.Scope.GoroutineID, ctx.Scope.Frame, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -885,7 +932,7 @@ func printStack(stack []api.Stackframe, ind string) {
|
||||
}
|
||||
d := digits(len(stack) - 1)
|
||||
fmtstr := "%s%" + strconv.Itoa(d) + "d 0x%016x in %s\n"
|
||||
s := strings.Repeat(" ", d+2+len(ind))
|
||||
s := ind + strings.Repeat(" ", d+2+len(ind))
|
||||
|
||||
for i := range stack {
|
||||
name := "(nil)"
|
||||
@ -941,7 +988,7 @@ func printcontextThread(t *Term, th *api.Thread) {
|
||||
}
|
||||
|
||||
args := ""
|
||||
if th.Breakpoint.Tracepoint && th.BreakpointInfo != nil {
|
||||
if th.BreakpointInfo != nil && th.Breakpoint.LoadArgs != nil && *th.Breakpoint.LoadArgs == ShortLoadConfig {
|
||||
var arg []string
|
||||
for _, ar := range th.BreakpointInfo.Arguments {
|
||||
arg = append(arg, ar.SinglelineString())
|
||||
@ -977,18 +1024,29 @@ func printcontextThread(t *Term, th *api.Thread) {
|
||||
}
|
||||
|
||||
if th.BreakpointInfo != nil {
|
||||
bp := th.Breakpoint
|
||||
bpi := th.BreakpointInfo
|
||||
|
||||
if bpi.Goroutine != nil {
|
||||
writeGoroutineLong(os.Stdout, bpi.Goroutine, "\t")
|
||||
}
|
||||
|
||||
if len(bpi.Variables) > 0 {
|
||||
ss := make([]string, len(bpi.Variables))
|
||||
for i, v := range bpi.Variables {
|
||||
ss[i] = fmt.Sprintf("%s: %s", v.Name, v.MultilineString(""))
|
||||
for _, v := range bpi.Variables {
|
||||
fmt.Printf("\t%s: %s\n", v.Name, v.MultilineString("\t"))
|
||||
}
|
||||
|
||||
for _, v := range bpi.Locals {
|
||||
if *bp.LoadLocals == LongLoadConfig {
|
||||
fmt.Printf("\t%s: %s\n", v.Name, v.MultilineString("\t"))
|
||||
} else {
|
||||
fmt.Printf("\t%s: %s\n", v.Name, v.SinglelineString())
|
||||
}
|
||||
}
|
||||
|
||||
if bp.LoadArgs != nil && *bp.LoadArgs == LongLoadConfig {
|
||||
for _, v := range bpi.Arguments {
|
||||
fmt.Printf("\t%s: %s\n", v.Name, v.MultilineString("\t"))
|
||||
}
|
||||
fmt.Printf("\t%s\n", strings.Join(ss, ", "))
|
||||
}
|
||||
|
||||
if bpi.Stacktrace != nil {
|
||||
@ -1153,10 +1211,3 @@ func formatBreakpointLocation(bp *api.Breakpoint) string {
|
||||
}
|
||||
return fmt.Sprintf("%#v for %s:%d", bp.Addr, p, bp.Line)
|
||||
}
|
||||
|
||||
func describeNoVars(varType string, data []string) []string {
|
||||
if len(data) == 0 {
|
||||
return []string{fmt.Sprintf("(no %s)", varType)}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
@ -357,3 +357,44 @@ func TestNoVars(t *testing.T) {
|
||||
term.AssertExec("vars filterThatMatchesNothing", "(no vars)\n")
|
||||
})
|
||||
}
|
||||
|
||||
func TestOnPrefixLocals(t *testing.T) {
|
||||
const prefix = "\ti: "
|
||||
withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {
|
||||
term.MustExec("b agobp main.agoroutine")
|
||||
term.MustExec("on agobp args -v")
|
||||
|
||||
seen := make([]bool, 10)
|
||||
|
||||
for {
|
||||
outstr, err := term.Exec("continue")
|
||||
if err != nil {
|
||||
if strings.Index(err.Error(), "exited") < 0 {
|
||||
t.Fatalf("Unexpected error executing 'continue': %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
out := strings.Split(outstr, "\n")
|
||||
|
||||
for i := range out {
|
||||
if !strings.HasPrefix(out[i], "\ti: ") {
|
||||
continue
|
||||
}
|
||||
id, err := strconv.Atoi(out[i][len(prefix):])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if seen[id] {
|
||||
t.Fatalf("Goroutine %d seen twice\n", id)
|
||||
}
|
||||
seen[id] = true
|
||||
}
|
||||
}
|
||||
|
||||
for i := range seen {
|
||||
if !seen[i] {
|
||||
t.Fatalf("Goroutine %d not seen\n", i)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user