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
|
||||
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
|
||||
args, err := s.debugger.FunctionArguments(goid, frame, 0, DefaultLoadConfig)
|
||||
if err != nil {
|
||||
s.sendErrorResponse(request.Request, UnableToListArgs, "Unable to list args", err.Error())
|
||||
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
|
||||
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())
|
||||
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)}
|
||||
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
|
||||
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)
|
||||
|
||||
// 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
|
||||
// fully unloaded.
|
||||
func TestVariablesLoading(t *testing.T) {
|
||||
|
@ -1357,6 +1357,18 @@ func (d *Debugger) FunctionArguments(goid, frame, deferredCall int, cfg proc.Loa
|
||||
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'
|
||||
// in the scope provided.
|
||||
func (d *Debugger) EvalVariableInScope(goid, frame, deferredCall int, symbol string, cfg proc.LoadConfig) (*proc.Variable, error) {
|
||||
|
Loading…
Reference in New Issue
Block a user