You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Depending on the language and the type of breakpoint, there are scenarios where creating a single breakpoint from a client can end up with multiple breakpoints being created by a debugger. For example, adding a function breakpoint on a function using a function name that exists in multiple places or on multiple objects (ToString) or on a source line that is compiled into multiple modules or multiple times (a templated c++ function).
Allowing a debug adapter to report multiple breakpoint binds is relatively simple, as it can be done by extending the current Breakpoint object. However, allowing a client to manage the new breakpoint binds is difficult with the current requests, as there is not a clear way to indicate what individual breakpoints should have which state. Using the function breakpoint example, if a client adds a breakpoint on "ToString" and this results in two breakpoints "ClassA.ToString" and "ClassB.ToString", then there is no way for a client to remove or change a condition on one of those breakpoints but not the other. For this reason, this proposal also adds requests that operate on individual breakpoints (using the pre-existing Id field) to do additions, updates, and removals.
Client and DA capabilities
When a debug adapter supports multiple breakpoint binds, some of the semantics around Breakpoint objects changes (see the updates to Breakpoint). That means it's necessary for a client to advertise that it supports handling those semantics via the initialize request. The adapter also needs to be able to advertise the individual breakpoint operations capability, which is a requirement for supporting additional breakpoint binds. These are intended to be used in conjunction, though a client which does not support multiple breakpoint binds could still use the individual breakpoint operations if available via the adapter.
exportinterfaceCapabilities{// .../** * The debug adapter supports operations (add, update, remove) on individual breakpoints. * This is required if the adapter returns multiple breakpoint binds. */supportsIndividualBreakpointOperations?: boolean;}exportinterfaceInitializeRequestArguments{// .../** * The client supports receiving Breakpoints that use the boundBreakpoints property. If an adapter * can provide the BoundBreakpoints but the client does not support it, it should return the first * bound breakpoint as the Breakpoint for the response. */supportsAdditionalBreakpointBinds?: boolean;}
Updates to existing breakpoint request objects
Currently only source breakpoints have a logMessage property, however there does not seem to be a reason why function or data breakpoints couldn't also support a log message. Data breakpoints are more constrained in what would be a valid message since the context in which they are hit may not be consistent, but they could still support some amount of logging.
Adding logMessage to all of the breakpoint objects allows for a more consistent API for individual operations. This same addition would apply to Instruction breakpoints if they are also taken in to the protocol.
exportinterfaceFunctionBreakpoint{// .../** * If this attribute exists and is non-empty, the backend must not 'break' (stop) * but log the message instead. Expressions within {} are interpolated. * The attribute is only honored by a debug adapter if the capability 'supportsLogPoints' is true. */logMessage?: string;}exportinterfaceDataBreakpoint{// .../** * If this attribute exists and is non-empty, the backend must not 'break' (stop) * but log the message instead. Expressions within {} are interpolated. * The attribute is only honored by a debug adapter if the capability 'supportsLogPoints' is true. */logMessage?: string;}
Returning multiple breakpoint binds
If the client and adapter both support/handle additional breakpoint binds then the usage of the Breakpoint in responses changes.
First, the Breakpoint has an additional property "boundBreakpoints" which represents the locations where breakpoints were created. For the ToString example from the beginning of the proposal there would be two elements in the BoundBreakpoints array: one element for ClassA.ToString and one element for ClassB.ToString.
Second, the Breakpoint that is returned in a response always represents the requested breakpoint - not a location that the breakpoint bound. It should always have verified set to true and the id should represent the request.
For a set*BreakpointsResponse or AddBreakpointResponse the boundBreakpoints is the set of breakpoints that were initially able to bind and can be empty. The BreakpointEvent can be used to update the list of boundBreakpoints for a given request - additions, modifications, and removals.
exportinterfaceBreakpoint{// .../** * Optional list used to support a breakpoint that can bind to more than one location. * If client supports additional breakpoint binds and the adapter supports individual breakpoint operations then the parent breakpoint represents the request to set the breakpoint and the boundBreakpoints represent the set of breakpoints that were bound for this parent. * In this case, 'verified' should be true, 'id' should be the id to represent the request, and all other fields are ignored. * This should only be sent if the client set supportsAdditionalBreakpointBinds. */boundBreakpoints?: Breakpoint[];}
Individual Breakpoint Operations
The individual breakpoint operations are used to create, modify, and remove specific breakpoints, rather than in the groups handled by the set*BreakpointsRequest. If individual breakpoint operations are used, the set*BreakpointsRequests should only be used to set up the individual breakpoints as they would invalidate things done by the individual operations.
The update request also supports an "enabled" property in its arguments to support a recoverable remove of the breakpoint. Without it, the only way to bring back an individual breakpoint that was removed would be to recreate all the bound breakpoints by a new AddBreakpointRequest and attempting to reconcile the states of the previously bound breakpoints with the new bound breakpoints.
/** * Creates a new breakpoint. * Clients should only call this request if the capability 'supportsIndividualBreakpointOperations' is true. */exportinterfaceAddBreakpointRequestextendsRequest{// command: 'addBreakpoint'arguments: AddBreakpointArguments;}/** * Arguments for 'addBreakpoint' request* Only one of sourceBreakpoint, dataBreakpoint, or functionBreakpoint should be set.*/exportinterfaceAddBreakpointArguments{/** If provided, the request is to create a data breakpoint and Source must also be provided. */sourceBreakpoint?: SourceBreakpoint;/** The source location of the breakpoints; either 'source.path' or 'source.reference' must be specified. */source?: Source;/** If provided, the request is to create a data breakpoint*/dataBreakpoint?: DataBreakpoint;/** If provided, the request is to create a function breakpoint*/functionBreakpoint?: DataBreakpoint;/** * An optional expression for conditional breakpoints. * It is only honored by a debug adapter if the capability 'supportsConditionalBreakpoints' is true. */condition?: string;/** * An optional expression that controls how many hits of the breakpoint are ignored. * The backend is expected to interpret the expression as needed. * The attribute is only honored by a debug adapter if the capability * 'supportsHitConditionalBreakpoints' is true. */hitCondition?: string;/** * If this attribute exists and is non-empty, the backend must not 'break' (stop) * but log the message instead. Expressions within {} are interpolated. * The attribute is only honored by a debug adapter if the capability 'supportsLogPoints' is true. */logMessage?: string;}/** Response to 'addBreakpoint' request */exportinterfaceAddBreakpointResponseextendsResponse{body: {/** * Information about the breakpoint. */breakpoint: Breakpoint;}}/** * Updates an existing breakpoint. * This can be used to update the enabled/disabled state, condition, hitCondition, or logMessage of an existing Breakpoint. * If additional breakpoint binds are supported, the id can correspond to either the breakpoint request id, or the id of a * a breakpoint from the boundBreakpoints. If it corresponds to a request, the update applies to all bound breakpoints. */exportinterfaceUpdateBreakpointRequestextendsRequest{// command: 'updateBreakpoint'arguments: UpdateBreakpointArguments;}/** Arguments for 'updateBreakpoint' request */exportinterfaceUpdateBreakpointArguments{/** The id of the breakpoint to update. */id: number;/** Whether the breakpoint should be enabled or disabled. */enabled: boolean;/** * An optional expression for conditional breakpoints. * It is only honored by a debug adapter if the capability 'supportsConditionalBreakpoints' is true. */condition?: string;/** * An optional expression that controls how many hits of the breakpoint are ignored. * The backend is expected to interpret the expression as needed. * The attribute is only honored by a debug adapter if the capability * 'supportsHitConditionalBreakpoints' is true. */hitCondition?: string;/** * If this attribute exists and is non-empty, the backend must not 'break' (stop) * but log the message instead. Expressions within {} are interpolated. * The attribute is only honored by a debug adapter if the capability 'supportsLogPoints' is true. */logMessage?: string;}/** Response to 'updateBreakpoint' request. This is just an acknowledgement, so no body field is required. */exportinterfaceUpdateBreakpointResponseextendsResponse{}/** * Removes an existing breakpoint based on the id. * If the is a breakpoint request id then all bound breakpoints are also removed. */exportinterfaceRemoveBreakpointRequestextendsRequest{// command: 'removeBreakpoint'arguments: RemoveBreakpointArguments}/** Arguments for 'removeBreakpoint' request */exportinterfaceRemoveBreakpointArguments{/** The id of the breakpoint to remove. */int: number;}/** Response to 'removeBreakpoint' request. This is just an acknowledgement, so no body field is required. */exportinterfaceRemoveBreakpointResponseextendsResponse{}
Depending on the language and the type of breakpoint, there are scenarios where creating a single breakpoint from a client can end up with multiple breakpoints being created by a debugger. For example, adding a function breakpoint on a function using a function name that exists in multiple places or on multiple objects (ToString) or on a source line that is compiled into multiple modules or multiple times (a templated c++ function).
Allowing a debug adapter to report multiple breakpoint binds is relatively simple, as it can be done by extending the current Breakpoint object. However, allowing a client to manage the new breakpoint binds is difficult with the current requests, as there is not a clear way to indicate what individual breakpoints should have which state. Using the function breakpoint example, if a client adds a breakpoint on "ToString" and this results in two breakpoints "ClassA.ToString" and "ClassB.ToString", then there is no way for a client to remove or change a condition on one of those breakpoints but not the other. For this reason, this proposal also adds requests that operate on individual breakpoints (using the pre-existing Id field) to do additions, updates, and removals.
Client and DA capabilities
When a debug adapter supports multiple breakpoint binds, some of the semantics around Breakpoint objects changes (see the updates to Breakpoint). That means it's necessary for a client to advertise that it supports handling those semantics via the initialize request. The adapter also needs to be able to advertise the individual breakpoint operations capability, which is a requirement for supporting additional breakpoint binds. These are intended to be used in conjunction, though a client which does not support multiple breakpoint binds could still use the individual breakpoint operations if available via the adapter.
Updates to existing breakpoint request objects
Currently only source breakpoints have a logMessage property, however there does not seem to be a reason why function or data breakpoints couldn't also support a log message. Data breakpoints are more constrained in what would be a valid message since the context in which they are hit may not be consistent, but they could still support some amount of logging.
Adding logMessage to all of the breakpoint objects allows for a more consistent API for individual operations. This same addition would apply to Instruction breakpoints if they are also taken in to the protocol.
Returning multiple breakpoint binds
If the client and adapter both support/handle additional breakpoint binds then the usage of the Breakpoint in responses changes.
First, the Breakpoint has an additional property "boundBreakpoints" which represents the locations where breakpoints were created. For the ToString example from the beginning of the proposal there would be two elements in the BoundBreakpoints array: one element for ClassA.ToString and one element for ClassB.ToString.
Second, the Breakpoint that is returned in a response always represents the requested breakpoint - not a location that the breakpoint bound. It should always have verified set to true and the id should represent the request.
For a set*BreakpointsResponse or AddBreakpointResponse the boundBreakpoints is the set of breakpoints that were initially able to bind and can be empty. The BreakpointEvent can be used to update the list of boundBreakpoints for a given request - additions, modifications, and removals.
Individual Breakpoint Operations
The individual breakpoint operations are used to create, modify, and remove specific breakpoints, rather than in the groups handled by the set*BreakpointsRequest. If individual breakpoint operations are used, the set*BreakpointsRequests should only be used to set up the individual breakpoints as they would invalidate things done by the individual operations.
The update request also supports an "enabled" property in its arguments to support a recoverable remove of the breakpoint. Without it, the only way to bring back an individual breakpoint that was removed would be to recreate all the bound breakpoints by a new AddBreakpointRequest and attempting to reconcile the states of the previously bound breakpoints with the new bound breakpoints.
CC: @andrewcrawley, @weinand
The text was updated successfully, but these errors were encountered: