service/dap: warn users of debugging optimized functions (#2475)
* service/dap: warn users of debugging optimized functions * Add test for optimized scopes * service/dap: warn users of debugging optimized functions * rename functionscope * update warning message
This commit is contained in:
parent
11cf6e689f
commit
1e9c5c3b07
@ -1338,13 +1338,23 @@ func (s *Server) onScopesRequest(request *dap.ScopesRequest) {
|
|||||||
goid := sf.(stackFrame).goroutineID
|
goid := sf.(stackFrame).goroutineID
|
||||||
frame := sf.(stackFrame).frameIndex
|
frame := sf.(stackFrame).frameIndex
|
||||||
|
|
||||||
|
// Check if the function is optimized.
|
||||||
|
fn, err := s.debugger.Function(goid, frame, 0, DefaultLoadConfig)
|
||||||
|
if fn == nil || err != nil {
|
||||||
|
s.sendErrorResponse(request.Request, UnableToListArgs, "Unable to find enclosing function", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
suffix := ""
|
||||||
|
if fn.Optimized() {
|
||||||
|
suffix = " (warning: optimized function)"
|
||||||
|
}
|
||||||
// Retrieve arguments
|
// Retrieve arguments
|
||||||
args, err := s.debugger.FunctionArguments(goid, frame, 0, DefaultLoadConfig)
|
args, err := s.debugger.FunctionArguments(goid, frame, 0, DefaultLoadConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.sendErrorResponse(request.Request, UnableToListArgs, "Unable to list args", err.Error())
|
s.sendErrorResponse(request.Request, UnableToListArgs, "Unable to list args", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
argScope := &fullyQualifiedVariable{&proc.Variable{Name: "Arguments", Children: slicePtrVarToSliceVar(args)}, "", true}
|
argScope := &fullyQualifiedVariable{&proc.Variable{Name: fmt.Sprintf("Arguments%s", suffix), Children: slicePtrVarToSliceVar(args)}, "", true}
|
||||||
|
|
||||||
// Retrieve local variables
|
// Retrieve local variables
|
||||||
locals, err := s.debugger.LocalVariables(goid, frame, 0, DefaultLoadConfig)
|
locals, err := s.debugger.LocalVariables(goid, frame, 0, DefaultLoadConfig)
|
||||||
@ -1352,7 +1362,7 @@ func (s *Server) onScopesRequest(request *dap.ScopesRequest) {
|
|||||||
s.sendErrorResponse(request.Request, UnableToListLocals, "Unable to list locals", err.Error())
|
s.sendErrorResponse(request.Request, UnableToListLocals, "Unable to list locals", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
locScope := &fullyQualifiedVariable{&proc.Variable{Name: "Locals", Children: slicePtrVarToSliceVar(locals)}, "", true}
|
locScope := &fullyQualifiedVariable{&proc.Variable{Name: fmt.Sprintf("Locals%s", suffix), Children: slicePtrVarToSliceVar(locals)}, "", true}
|
||||||
|
|
||||||
scopeArgs := dap.Scope{Name: argScope.Name, VariablesReference: s.variableHandles.create(argScope)}
|
scopeArgs := dap.Scope{Name: argScope.Name, VariablesReference: s.variableHandles.create(argScope)}
|
||||||
scopeLocals := dap.Scope{Name: locScope.Name, VariablesReference: s.variableHandles.create(locScope)}
|
scopeLocals := dap.Scope{Name: locScope.Name, VariablesReference: s.variableHandles.create(locScope)}
|
||||||
|
|||||||
@ -47,7 +47,11 @@ func TestMain(m *testing.M) {
|
|||||||
|
|
||||||
// name is for _fixtures/<name>.go
|
// name is for _fixtures/<name>.go
|
||||||
func runTest(t *testing.T, name string, test func(c *daptest.Client, f protest.Fixture)) {
|
func runTest(t *testing.T, name string, test func(c *daptest.Client, f protest.Fixture)) {
|
||||||
var buildFlags protest.BuildFlags = protest.AllNonOptimized
|
runTestBuildFlags(t, name, test, protest.AllNonOptimized)
|
||||||
|
}
|
||||||
|
|
||||||
|
// name is for _fixtures/<name>.go
|
||||||
|
func runTestBuildFlags(t *testing.T, name string, test func(c *daptest.Client, f protest.Fixture), buildFlags protest.BuildFlags) {
|
||||||
fixture := protest.BuildFixture(name, buildFlags)
|
fixture := protest.BuildFixture(name, buildFlags)
|
||||||
|
|
||||||
// Start the DAP server.
|
// Start the DAP server.
|
||||||
@ -1383,6 +1387,63 @@ func TestScopesAndVariablesRequests2(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestScopesRequestsOptimized executes to a breakpoint and tests different
|
||||||
|
// that the names of the "Locals" and "Arguments" scopes are correctly annotated with
|
||||||
|
// a warning about debugging an optimized function.
|
||||||
|
func TestScopesRequestsOptimized(t *testing.T) {
|
||||||
|
runTestBuildFlags(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {
|
||||||
|
runDebugSessionWithBPs(t, client, "launch",
|
||||||
|
// Launch
|
||||||
|
func() {
|
||||||
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
|
"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// Breakpoints are set within the program
|
||||||
|
fixture.Source, []int{},
|
||||||
|
[]onBreakpoint{{
|
||||||
|
// Stop at first breakpoint
|
||||||
|
execute: func() {
|
||||||
|
client.StackTraceRequest(1, 0, 20)
|
||||||
|
stack := client.ExpectStackTraceResponse(t)
|
||||||
|
|
||||||
|
startLineno := 66
|
||||||
|
if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {
|
||||||
|
// Go1.15 on windows inserts a NOP after the call to
|
||||||
|
// runtime.Breakpoint and marks it same line as the
|
||||||
|
// runtime.Breakpoint call, making this flaky, so skip the line check.
|
||||||
|
startLineno = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
expectStackFrames(t, stack, "main.foobar", startLineno, 1000, 4, 4)
|
||||||
|
|
||||||
|
client.ScopesRequest(1000)
|
||||||
|
scopes := client.ExpectScopesResponse(t)
|
||||||
|
expectScope(t, scopes, 0, "Arguments (warning: optimized function)", 1000)
|
||||||
|
expectScope(t, scopes, 1, "Locals (warning: optimized function)", 1001)
|
||||||
|
expectScope(t, scopes, 2, "Globals (package main)", 1002)
|
||||||
|
},
|
||||||
|
disconnect: false,
|
||||||
|
}, {
|
||||||
|
// Stop at second breakpoint
|
||||||
|
execute: func() {
|
||||||
|
// Frame ids get reset at each breakpoint.
|
||||||
|
client.StackTraceRequest(1, 0, 20)
|
||||||
|
stack := client.ExpectStackTraceResponse(t)
|
||||||
|
expectStackFrames(t, stack, "main.barfoo", 27, 1000, 5, 5)
|
||||||
|
|
||||||
|
client.ScopesRequest(1000)
|
||||||
|
scopes := client.ExpectScopesResponse(t)
|
||||||
|
expectScope(t, scopes, 0, "Arguments (warning: optimized function)", 1000)
|
||||||
|
expectScope(t, scopes, 1, "Locals (warning: optimized function)", 1001)
|
||||||
|
expectScope(t, scopes, 2, "Globals (package main)", 1002)
|
||||||
|
},
|
||||||
|
disconnect: false,
|
||||||
|
}})
|
||||||
|
},
|
||||||
|
protest.EnableOptimization)
|
||||||
|
}
|
||||||
|
|
||||||
// TestVariablesLoading exposes test cases where variables might be partiall or
|
// TestVariablesLoading exposes test cases where variables might be partiall or
|
||||||
// fully unloaded.
|
// fully unloaded.
|
||||||
func TestVariablesLoading(t *testing.T) {
|
func TestVariablesLoading(t *testing.T) {
|
||||||
|
|||||||
@ -1357,6 +1357,18 @@ func (d *Debugger) FunctionArguments(goid, frame, deferredCall int, cfg proc.Loa
|
|||||||
return s.FunctionArguments(cfg)
|
return s.FunctionArguments(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function returns the current function.
|
||||||
|
func (d *Debugger) Function(goid, frame, deferredCall int, cfg proc.LoadConfig) (*proc.Function, error) {
|
||||||
|
d.targetMutex.Lock()
|
||||||
|
defer d.targetMutex.Unlock()
|
||||||
|
|
||||||
|
s, err := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s.Fn, nil
|
||||||
|
}
|
||||||
|
|
||||||
// EvalVariableInScope will attempt to evaluate the variable represented by 'symbol'
|
// EvalVariableInScope will attempt to evaluate the variable represented by 'symbol'
|
||||||
// in the scope provided.
|
// in the scope provided.
|
||||||
func (d *Debugger) EvalVariableInScope(goid, frame, deferredCall int, symbol string, cfg proc.LoadConfig) (*proc.Variable, error) {
|
func (d *Debugger) EvalVariableInScope(goid, frame, deferredCall int, symbol string, cfg proc.LoadConfig) (*proc.Variable, error) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user