dap: fill in breakpoint reason for being unverified
When a breakpoint fails to be verified, it will switch the reason to "failed". It starts off the reason as "pending". The `reason` field for a breakpoint was added some time after the last release of `go-dap` which has only been updated once in the last year so this uses the `main` branch version which contains the field. Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
This commit is contained in:
@@ -589,6 +589,7 @@ func (b *breakpointMap) Set(fname string, sbps []dap.SourceBreakpoint) (breakpoi
|
||||
EndLine: sbp.Line,
|
||||
Column: sbp.Column,
|
||||
EndColumn: sbp.Column,
|
||||
Reason: "pending",
|
||||
}
|
||||
}
|
||||
breakpoints = append(breakpoints, bp)
|
||||
@@ -608,6 +609,27 @@ func (b *breakpointMap) Intersect(ctx Context, src *pb.Source, ws string) map[di
|
||||
digests[digest.Digest(dgst)] = id
|
||||
}
|
||||
}
|
||||
|
||||
// Mark unverified breakpoints as failed at this point since we couldn't find an area
|
||||
// in the source where they applied.
|
||||
for _, info := range src.Infos {
|
||||
fname := filepath.Join(ws, info.Filename)
|
||||
|
||||
bps := b.byPath[fname]
|
||||
for _, bp := range bps {
|
||||
if !bp.Verified && bp.Reason != "failed" {
|
||||
bp.Reason = "failed"
|
||||
|
||||
ctx.C() <- &dap.BreakpointEvent{
|
||||
Event: dap.Event{Event: "breakpoint"},
|
||||
Body: dap.BreakpointEventBody{
|
||||
Reason: "changed",
|
||||
Breakpoint: bp,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return digests
|
||||
}
|
||||
|
||||
@@ -651,6 +673,7 @@ func (b *breakpointMap) intersect(ctx Context, src *pb.Source, locs *pb.Location
|
||||
bp.Column = int(r.Start.Character)
|
||||
bp.EndColumn = int(r.End.Character)
|
||||
bp.Verified = true
|
||||
bp.Reason = ""
|
||||
|
||||
ctx.C() <- &dap.BreakpointEvent{
|
||||
Event: dap.Event{Event: "breakpoint"},
|
||||
|
||||
@@ -21,7 +21,7 @@ require (
|
||||
github.com/docker/docker v28.5.2+incompatible
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/gofrs/flock v0.13.0
|
||||
github.com/google/go-dap v0.12.0
|
||||
github.com/google/go-dap v0.12.1-0.20250904181021-d7a2259b058b
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/hashicorp/go-cty-funcs v0.0.0-20250818135842-6aab67130928
|
||||
|
||||
@@ -155,8 +155,8 @@ github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnL
|
||||
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-dap v0.12.0 h1:rVcjv3SyMIrpaOoTAdFDyHs99CwVOItIJGKLQFQhNeM=
|
||||
github.com/google/go-dap v0.12.0/go.mod h1:tNjCASCm5cqePi/RVXXWEVqtnNLV1KTWtYOqu6rZNzc=
|
||||
github.com/google/go-dap v0.12.1-0.20250904181021-d7a2259b058b h1:m+yLjBIoXaMKk6pwd1IJYYgM76+mwcy7J9+cuY7LqmQ=
|
||||
github.com/google/go-dap v0.12.1-0.20250904181021-d7a2259b058b/go.mod h1:tNjCASCm5cqePi/RVXXWEVqtnNLV1KTWtYOqu6rZNzc=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY=
|
||||
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
|
||||
|
||||
@@ -77,6 +77,7 @@ var dapBuildTests = []func(t *testing.T, sb integration.Sandbox){
|
||||
testDapBuild,
|
||||
testDapBuildStopOnEntry,
|
||||
testDapBuildSetBreakpoints,
|
||||
testDapBuildVerifiedBreakpoints,
|
||||
testDapBuildStepIn,
|
||||
testDapBuildStepNext,
|
||||
testDapBuildStepOut,
|
||||
@@ -199,6 +200,56 @@ func testDapBuildSetBreakpoints(t *testing.T, sb integration.Sandbox) {
|
||||
require.NoError(t, done(false))
|
||||
}
|
||||
|
||||
func testDapBuildVerifiedBreakpoints(t *testing.T, sb integration.Sandbox) {
|
||||
dir := createTestProject(t)
|
||||
client, done, err := dapBuildCmd(t, sb, withArgs(dir))
|
||||
require.NoError(t, err)
|
||||
|
||||
interruptCh := pollInterruptEvents(client)
|
||||
|
||||
var actual []dap.BreakpointEventBody
|
||||
client.RegisterEvent("breakpoint", func(em dap.EventMessage) {
|
||||
e := em.(*dap.BreakpointEvent)
|
||||
actual = append(actual, e.Body)
|
||||
})
|
||||
|
||||
doLaunch(t, client, commands.LaunchConfig{
|
||||
Dockerfile: path.Join(dir, "Dockerfile"),
|
||||
ContextPath: dir,
|
||||
},
|
||||
dap.SourceBreakpoint{Line: 2},
|
||||
dap.SourceBreakpoint{Line: 10},
|
||||
)
|
||||
|
||||
stopped := waitForInterrupt[*dap.StoppedEvent](t, interruptCh)
|
||||
require.NotNil(t, stopped)
|
||||
|
||||
assert.Equal(t, []dap.BreakpointEventBody{
|
||||
{
|
||||
Reason: "changed",
|
||||
Breakpoint: dap.Breakpoint{
|
||||
Id: 1,
|
||||
Line: 2,
|
||||
EndLine: 2,
|
||||
Verified: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Reason: "changed",
|
||||
Breakpoint: dap.Breakpoint{
|
||||
Id: 2,
|
||||
Line: 10,
|
||||
EndLine: 10,
|
||||
Verified: false,
|
||||
Reason: "failed",
|
||||
},
|
||||
},
|
||||
}, actual)
|
||||
|
||||
var exitErr *exec.ExitError
|
||||
require.ErrorAs(t, done(true), &exitErr)
|
||||
}
|
||||
|
||||
func testDapBuildStepIn(t *testing.T, sb integration.Sandbox) {
|
||||
dir := createTestProject(t)
|
||||
client, done, err := dapBuildCmd(t, sb, withArgs(dir))
|
||||
|
||||
+68
-11
@@ -112,7 +112,8 @@ type ErrorResponseBody struct {
|
||||
|
||||
// CancelRequest: The `cancel` request is used by the client in two situations:
|
||||
// - to indicate that it is no longer interested in the result produced by a specific request issued earlier
|
||||
// - to cancel a progress sequence. Clients should only call this request if the corresponding capability `supportsCancelRequest` is true.
|
||||
// - to cancel a progress sequence.
|
||||
// Clients should only call this request if the corresponding capability `supportsCancelRequest` is true.
|
||||
// This request has a hint characteristic: a debug adapter can only be expected to make a 'best effort' in honoring this request but there are no guarantees.
|
||||
// The `cancel` request may return an error if it could not cancel an operation but a client should refrain from presenting this error to end users.
|
||||
// The request that got cancelled still needs to send a response back. This can either be a normal result (`success` attribute true) or an error response (`success` attribute false and the `message` set to `cancelled`).
|
||||
@@ -232,6 +233,7 @@ type OutputEventBody struct {
|
||||
Line int `json:"line,omitempty"`
|
||||
Column int `json:"column,omitempty"`
|
||||
Data json.RawMessage `json:"data,omitempty"`
|
||||
LocationReference int `json:"locationReference,omitempty"`
|
||||
}
|
||||
|
||||
// BreakpointEvent: The event indicates that some information about a breakpoint has changed.
|
||||
@@ -456,6 +458,7 @@ type InitializeRequestArguments struct {
|
||||
SupportsMemoryEvent bool `json:"supportsMemoryEvent,omitempty"`
|
||||
SupportsArgsCanBeInterpretedByShell bool `json:"supportsArgsCanBeInterpretedByShell,omitempty"`
|
||||
SupportsStartDebuggingRequest bool `json:"supportsStartDebuggingRequest,omitempty"`
|
||||
SupportsANSIStyling bool `json:"supportsANSIStyling,omitempty"`
|
||||
}
|
||||
|
||||
// InitializeResponse: Response to `initialize` request.
|
||||
@@ -655,8 +658,7 @@ type SetFunctionBreakpointsResponseBody struct {
|
||||
Breakpoints []Breakpoint `json:"breakpoints"`
|
||||
}
|
||||
|
||||
// SetExceptionBreakpointsRequest: The request configures the debugger's response to thrown exceptions.
|
||||
// If an exception is configured to break, a `stopped` event is fired (with reason `exception`).
|
||||
// SetExceptionBreakpointsRequest: The request configures the debugger's response to thrown exceptions. Each of the `filters`, `filterOptions`, and `exceptionOptions` in the request are independent configurations to a debug adapter indicating a kind of exception to catch. An exception thrown in a program should result in a `stopped` event from the debug adapter (with reason `exception`) if any of the configured filters match.
|
||||
// Clients should only call this request if the corresponding capability `exceptionBreakpointFilters` returns one or more filters.
|
||||
type SetExceptionBreakpointsRequest struct {
|
||||
Request
|
||||
@@ -673,7 +675,7 @@ type SetExceptionBreakpointsArguments struct {
|
||||
|
||||
// SetExceptionBreakpointsResponse: Response to `setExceptionBreakpoints` request.
|
||||
// The response contains an array of `Breakpoint` objects with information about each exception breakpoint or filter. The `Breakpoint` objects are in the same order as the elements of the `filters`, `filterOptions`, `exceptionOptions` arrays given as arguments. If both `filters` and `filterOptions` are given, the returned array must start with `filters` information first, followed by `filterOptions` information.
|
||||
// The `verified` property of a `Breakpoint` object signals whether the exception breakpoint or filter could be successfully created and whether the condition or hit count expressions are valid. In case of an error the `message` property explains the problem. The `id` property can be used to introduce a unique ID for the exception breakpoint or filter so that it can be updated subsequently by sending breakpoint events.
|
||||
// The `verified` property of a `Breakpoint` object signals whether the exception breakpoint or filter could be successfully created and whether the condition is valid. In case of an error the `message` property explains the problem. The `id` property can be used to introduce a unique ID for the exception breakpoint or filter so that it can be updated subsequently by sending breakpoint events.
|
||||
// For backward compatibility both the `breakpoints` array and the enclosing `body` are optional. If these elements are missing a client is not able to show problems for individual exception breakpoints or filters.
|
||||
type SetExceptionBreakpointsResponse struct {
|
||||
Response
|
||||
@@ -698,6 +700,9 @@ type DataBreakpointInfoArguments struct {
|
||||
VariablesReference int `json:"variablesReference,omitempty"`
|
||||
Name string `json:"name"`
|
||||
FrameId int `json:"frameId,omitempty"`
|
||||
Bytes int `json:"bytes,omitempty"`
|
||||
AsAddress bool `json:"asAddress,omitempty"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
}
|
||||
|
||||
// DataBreakpointInfoResponse: Response to `dataBreakpointInfo` request.
|
||||
@@ -1067,6 +1072,8 @@ type SetVariableResponseBody struct {
|
||||
VariablesReference int `json:"variablesReference,omitempty"`
|
||||
NamedVariables int `json:"namedVariables,omitempty"`
|
||||
IndexedVariables int `json:"indexedVariables,omitempty"`
|
||||
MemoryReference string `json:"memoryReference,omitempty"`
|
||||
ValueLocationReference int `json:"valueLocationReference,omitempty"`
|
||||
}
|
||||
|
||||
// SourceRequest: The request retrieves the source code for a given source reference.
|
||||
@@ -1177,7 +1184,7 @@ type LoadedSourcesResponseBody struct {
|
||||
Sources []Source `json:"sources"`
|
||||
}
|
||||
|
||||
// EvaluateRequest: Evaluates the given expression in the context of the topmost stack frame.
|
||||
// EvaluateRequest: Evaluates the given expression in the context of a stack frame.
|
||||
// The expression has access to any variables and arguments that are in scope.
|
||||
type EvaluateRequest struct {
|
||||
Request
|
||||
@@ -1189,6 +1196,9 @@ type EvaluateRequest struct {
|
||||
type EvaluateArguments struct {
|
||||
Expression string `json:"expression"`
|
||||
FrameId int `json:"frameId,omitempty"`
|
||||
Line int `json:"line,omitempty"`
|
||||
Column int `json:"column,omitempty"`
|
||||
Source *Source `json:"source,omitempty"`
|
||||
Context string `json:"context,omitempty"`
|
||||
Format *ValueFormat `json:"format,omitempty"`
|
||||
}
|
||||
@@ -1208,6 +1218,7 @@ type EvaluateResponseBody struct {
|
||||
NamedVariables int `json:"namedVariables,omitempty"`
|
||||
IndexedVariables int `json:"indexedVariables,omitempty"`
|
||||
MemoryReference string `json:"memoryReference,omitempty"`
|
||||
ValueLocationReference int `json:"valueLocationReference,omitempty"`
|
||||
}
|
||||
|
||||
// SetExpressionRequest: Evaluates the given `value` expression and assigns it to the `expression` which must be a modifiable l-value.
|
||||
@@ -1242,6 +1253,8 @@ type SetExpressionResponseBody struct {
|
||||
VariablesReference int `json:"variablesReference,omitempty"`
|
||||
NamedVariables int `json:"namedVariables,omitempty"`
|
||||
IndexedVariables int `json:"indexedVariables,omitempty"`
|
||||
MemoryReference string `json:"memoryReference,omitempty"`
|
||||
ValueLocationReference int `json:"valueLocationReference,omitempty"`
|
||||
}
|
||||
|
||||
// StepInTargetsRequest: This request retrieves the possible step-in targets for the specified stack frame.
|
||||
@@ -1434,6 +1447,33 @@ type DisassembleResponseBody struct {
|
||||
Instructions []DisassembledInstruction `json:"instructions"`
|
||||
}
|
||||
|
||||
// LocationsRequest: Looks up information about a location reference previously returned by the debug adapter.
|
||||
type LocationsRequest struct {
|
||||
Request
|
||||
|
||||
Arguments LocationsArguments `json:"arguments"`
|
||||
}
|
||||
|
||||
// LocationsArguments: Arguments for `locations` request.
|
||||
type LocationsArguments struct {
|
||||
LocationReference int `json:"locationReference"`
|
||||
}
|
||||
|
||||
// LocationsResponse: Response to `locations` request.
|
||||
type LocationsResponse struct {
|
||||
Response
|
||||
|
||||
Body LocationsResponseBody `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
type LocationsResponseBody struct {
|
||||
Source Source `json:"source"`
|
||||
Line int `json:"line"`
|
||||
Column int `json:"column,omitempty"`
|
||||
EndLine int `json:"endLine,omitempty"`
|
||||
EndColumn int `json:"endColumn,omitempty"`
|
||||
}
|
||||
|
||||
// Capabilities: Information about the capabilities of a debug adapter.
|
||||
type Capabilities struct {
|
||||
SupportsConfigurationDoneRequest bool `json:"supportsConfigurationDoneRequest,omitempty"`
|
||||
@@ -1475,6 +1515,9 @@ type Capabilities struct {
|
||||
SupportsInstructionBreakpoints bool `json:"supportsInstructionBreakpoints,omitempty"`
|
||||
SupportsExceptionFilterOptions bool `json:"supportsExceptionFilterOptions,omitempty"`
|
||||
SupportsSingleThreadExecutionRequests bool `json:"supportsSingleThreadExecutionRequests,omitempty"`
|
||||
SupportsDataBreakpointBytes bool `json:"supportsDataBreakpointBytes,omitempty"`
|
||||
BreakpointModes []BreakpointMode `json:"breakpointModes,omitempty"`
|
||||
SupportsANSIStyling bool `json:"supportsANSIStyling,omitempty"`
|
||||
}
|
||||
|
||||
// ExceptionBreakpointsFilter: An `ExceptionBreakpointsFilter` is shown in the UI as an filter option for configuring how exceptions are dealt with.
|
||||
@@ -1529,12 +1572,6 @@ type ColumnDescriptor struct {
|
||||
Width int `json:"width,omitempty"`
|
||||
}
|
||||
|
||||
// ModulesViewDescriptor: The ModulesViewDescriptor is the container for all declarative configuration options of a module view.
|
||||
// For now it only specifies the columns to be shown in the modules view.
|
||||
type ModulesViewDescriptor struct {
|
||||
Columns []ColumnDescriptor `json:"columns"`
|
||||
}
|
||||
|
||||
// Thread: A Thread
|
||||
type Thread struct {
|
||||
Id int `json:"id"`
|
||||
@@ -1600,6 +1637,8 @@ type Variable struct {
|
||||
NamedVariables int `json:"namedVariables,omitempty"`
|
||||
IndexedVariables int `json:"indexedVariables,omitempty"`
|
||||
MemoryReference string `json:"memoryReference,omitempty"`
|
||||
DeclarationLocationReference int `json:"declarationLocationReference,omitempty"`
|
||||
ValueLocationReference int `json:"valueLocationReference,omitempty"`
|
||||
}
|
||||
|
||||
// VariablePresentationHint: Properties of a variable that can be used to determine how to render the variable in the UI.
|
||||
@@ -1625,6 +1664,7 @@ type SourceBreakpoint struct {
|
||||
Condition string `json:"condition,omitempty"`
|
||||
HitCondition string `json:"hitCondition,omitempty"`
|
||||
LogMessage string `json:"logMessage,omitempty"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
}
|
||||
|
||||
// FunctionBreakpoint: Properties of a breakpoint passed to the `setFunctionBreakpoints` request.
|
||||
@@ -1651,6 +1691,7 @@ type InstructionBreakpoint struct {
|
||||
Offset int `json:"offset,omitempty"`
|
||||
Condition string `json:"condition,omitempty"`
|
||||
HitCondition string `json:"hitCondition,omitempty"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
}
|
||||
|
||||
// Breakpoint: Information about a breakpoint created in `setBreakpoints`, `setFunctionBreakpoints`, `setInstructionBreakpoints`, or `setDataBreakpoints` requests.
|
||||
@@ -1665,6 +1706,7 @@ type Breakpoint struct {
|
||||
EndColumn int `json:"endColumn,omitempty"`
|
||||
InstructionReference string `json:"instructionReference,omitempty"`
|
||||
Offset int `json:"offset,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
// SteppingGranularity: The granularity of one 'step' in the stepping requests `next`, `stepIn`, `stepOut`, and `stepBack`.
|
||||
@@ -1739,6 +1781,7 @@ type StackFrameFormat struct {
|
||||
type ExceptionFilterOptions struct {
|
||||
FilterId string `json:"filterId"`
|
||||
Condition string `json:"condition,omitempty"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
}
|
||||
|
||||
// ExceptionOptions: An `ExceptionOptions` assigns configuration options to a set of exceptions.
|
||||
@@ -1782,11 +1825,23 @@ type DisassembledInstruction struct {
|
||||
Column int `json:"column,omitempty"`
|
||||
EndLine int `json:"endLine,omitempty"`
|
||||
EndColumn int `json:"endColumn,omitempty"`
|
||||
PresentationHint string `json:"presentationHint,omitempty"`
|
||||
}
|
||||
|
||||
// InvalidatedAreas: Logical areas that can be invalidated by the `invalidated` event.
|
||||
type InvalidatedAreas string
|
||||
|
||||
// BreakpointMode: A `BreakpointMode` is provided as a option when setting breakpoints on sources or instructions.
|
||||
type BreakpointMode struct {
|
||||
Mode string `json:"mode"`
|
||||
Label string `json:"label"`
|
||||
Description string `json:"description,omitempty"`
|
||||
AppliesTo []BreakpointModeApplicability `json:"appliesTo"`
|
||||
}
|
||||
|
||||
// BreakpointModeApplicability: Describes one or more type of breakpoint a `BreakpointMode` applies to. This is a non-exhaustive enumeration and may expand as future breakpoint types are added.
|
||||
type BreakpointModeApplicability string
|
||||
|
||||
// Mapping of request commands and corresponding struct constructors that
|
||||
// can be passed to json.Unmarshal.
|
||||
var requestCtor = map[string]messageCtor{
|
||||
@@ -1843,6 +1898,7 @@ var requestCtor = map[string]messageCtor{
|
||||
"readMemory": func() Message { return &ReadMemoryRequest{} },
|
||||
"writeMemory": func() Message { return &WriteMemoryRequest{} },
|
||||
"disassemble": func() Message { return &DisassembleRequest{} },
|
||||
"locations": func() Message { return &LocationsRequest{} },
|
||||
}
|
||||
|
||||
// Mapping of response commands and corresponding struct constructors that
|
||||
@@ -1892,6 +1948,7 @@ var responseCtor = map[string]messageCtor{
|
||||
"readMemory": func() Message { return &ReadMemoryResponse{} },
|
||||
"writeMemory": func() Message { return &WriteMemoryResponse{} },
|
||||
"disassemble": func() Message { return &DisassembleResponse{} },
|
||||
"locations": func() Message { return &LocationsResponse{} },
|
||||
}
|
||||
|
||||
// Mapping of event ids and corresponding struct constructors that
|
||||
|
||||
Vendored
+1
-1
@@ -333,7 +333,7 @@ github.com/google/go-cmp/cmp/internal/diff
|
||||
github.com/google/go-cmp/cmp/internal/flags
|
||||
github.com/google/go-cmp/cmp/internal/function
|
||||
github.com/google/go-cmp/cmp/internal/value
|
||||
# github.com/google/go-dap v0.12.0
|
||||
# github.com/google/go-dap v0.12.1-0.20250904181021-d7a2259b058b
|
||||
## explicit; go 1.18
|
||||
github.com/google/go-dap
|
||||
# github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
|
||||
Reference in New Issue
Block a user