Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to set data breakpoints #139

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 59 additions & 6 deletions media/memory-table.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,53 @@
outline-offset: -1px;
}

.memory-inspector-table .data-breakpoint {
outline: 1px solid var(--vscode-debugIcon-breakpointForeground);
outline-offset: 1px;
}

.memory-inspector-table .data-breakpoint.data-breakpoint-external {
outline-style: dashed;
}

.memory-inspector-table tbody .column-address {
position: relative;
}

.memory-inspector-table tbody .address-status {
position: absolute;
left: -1px;
align-items: center;
display: flex;
justify-content: center;
}

.memory-inspector-table tbody .address-status.codicon {
font-size: 12px;
}

.memory-inspector-table tbody .address-status.codicon-debug-breakpoint {
color: var(--vscode-debugIcon-breakpointForeground);
}

.memory-inspector-table tbody .address-status.codicon-debug-stackframe {
color: var(--vscode-debugIcon-breakpointCurrentStackframeForeground);
}

.memory-inspector-table
tbody
.address-status.codicon-debug-breakpoint.codicon-debug-stackframe:after {
content: "\ea71";
position: absolute;
left: 3px;
font-size: 6px;
color: var(--vscode-debugIcon-breakpointForeground);
}

.memory-inspector-table tbody .debug-hit {
outline-color: var(--vscode-debugIcon-breakpointCurrentStackframeForeground);
}

/* == MoreMemorySelect == */

.bytes-select {
Expand Down Expand Up @@ -91,7 +138,7 @@

.memory-inspector-table span.p-column-resizer {
border-right: 2px solid var(--vscode-editor-lineHighlightBorder);
transition: border-right .1s ease-out;
transition: border-right 0.1s ease-out;
}

.memory-inspector-table span.p-column-resizer:hover {
Expand All @@ -112,7 +159,7 @@
/* Basic hover formatting (copied from Monaco hovers) */
.memory-hover {
min-width: fit-content;
max-width: var(--vscode-hover-maxWidth,500px);
max-width: var(--vscode-hover-maxWidth, 500px);
border: 1px solid var(--vscode-editorHoverWidget-border);
border-radius: 3px;

Expand All @@ -128,34 +175,40 @@
border-collapse: collapse;
border-style: hidden;
}

.memory-hover table caption {
padding: 4px;
border-bottom: 1px solid var(--vscode-editorHoverWidget-border);
}

.memory-hover td {
border: 1px solid var(--vscode-editorHoverWidget-border);
padding: 2px 8px;
}

.memory-hover td:first-child {
text-align: right;
}

/* Colors for the hover fields */
.memory-hover .label-value-pair>.label {
color: var(--vscode-debugTokenExpression-string);
white-space: nowrap;
.memory-hover .label-value-pair > .label {
color: var(--vscode-debugTokenExpression-string);
white-space: nowrap;
}
.memory-hover .label-value-pair>.value {

.memory-hover .label-value-pair > .value {
color: var(--vscode-debugTokenExpression-number);
}

/* Colors for specific hover fields */
.memory-hover .address-hover .primary {
background-color: var(--vscode-list-hoverBackground);
}

.memory-hover table caption {
color: var(--vscode-symbolIcon-variableForeground);
}

.memory-hover .address-hover .value.utf8,
.memory-hover .data-hover .value.utf8,
.memory-hover .variable-hover .value.type {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -446,4 +446,4 @@
"extensionKind": [
"ui"
]
}
}
51 changes: 51 additions & 0 deletions src/common/breakpoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/********************************************************************************
* Copyright (C) 2024 EclipseSource.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { DebugProtocol } from '@vscode/debugprotocol';
import { DebugRequestTypes } from './debug-requests';

export interface TrackedDataBreakpoint {
type: TrackedBreakpointType;
breakpoint: DebugProtocol.DataBreakpoint;
/**
* The respective response for the breakpoint.
*/
response: DebugProtocol.Breakpoint;
}

/**
* Temp. workaround till we have a proper API for this within VSCode.
*/
export interface TrackedDataBreakpoints {
/**
* Breakpoints set from external contributors.
*/
external: TrackedDataBreakpoint[],
/**
* Breakpoints set from us.
*/
internal: TrackedDataBreakpoint[]
}

/**
* Temp. workaround till we have a proper API for this within VSCode.
*/
export type TrackedBreakpointType = 'internal' | 'external';

export type DataBreakpointInfoArguments = DebugRequestTypes['dataBreakpointInfo'][0];
export type DataBreakpointInfoResult = DebugRequestTypes['dataBreakpointInfo'][1];
export type SetDataBreakpointsArguments = DebugRequestTypes['setDataBreakpoints'][0];
export type SetDataBreakpointsResult = DebugRequestTypes['setDataBreakpoints'][1];
24 changes: 19 additions & 5 deletions src/common/debug-requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export interface DebugRequestTypes {
'scopes': [DebugProtocol.ScopesArguments, DebugProtocol.ScopesResponse['body']]
'variables': [DebugProtocol.VariablesArguments, DebugProtocol.VariablesResponse['body']]
'writeMemory': [DebugProtocol.WriteMemoryArguments, DebugProtocol.WriteMemoryResponse['body']]
'dataBreakpointInfo': [DebugProtocol.DataBreakpointInfoArguments, DebugProtocol.DataBreakpointInfoResponse['body']]
'setDataBreakpoints': [DebugProtocol.SetDataBreakpointsArguments, DebugProtocol.SetDataBreakpointsResponse['body']]
}

export interface DebugEvents {
Expand Down Expand Up @@ -59,16 +61,28 @@ export function isDebugEvaluateArguments(args: DebugProtocol.EvaluateArguments |
}

export function isDebugRequest<K extends keyof DebugRequestTypes>(command: K, message: unknown): message is DebugRequest<K, DebugRequestTypes[K][0]> {
const assumed = message ? message as DebugProtocol.Request : undefined;
return !!assumed && assumed.type === 'request' && assumed.command === command;
return isDebugRequestType(message) && message.command === command;
}

export function isDebugResponse<K extends keyof DebugRequestTypes>(command: K, message: unknown): message is DebugResponse<K, DebugRequestTypes[K][1]> {
const assumed = message ? message as DebugProtocol.Response : undefined;
return !!assumed && assumed.type === 'response' && assumed.command === command;
return isDebugResponseType(message) && message.command === command;
}

export function isDebugEvent<K extends keyof DebugEvents>(event: K, message: unknown): message is DebugEvents[K] {
return isDebugEventType(message) && message.event === event;
}

export function isDebugRequestType(message: unknown): message is DebugProtocol.Request {
const assumed = message ? message as DebugProtocol.Request : undefined;
return !!assumed && assumed.type === 'request';
}

export function isDebugResponseType(message: unknown): message is DebugProtocol.Response {
const assumed = message ? message as DebugProtocol.Response : undefined;
return !!assumed && assumed.type === 'response';
}

export function isDebugEventType(message: unknown): message is DebugProtocol.Event {
const assumed = message ? message as DebugProtocol.Event : undefined;
return !!assumed && assumed.type === 'event' && assumed.event === event;
return !!assumed && assumed.type === 'event';
}
1 change: 1 addition & 0 deletions src/common/memory-range.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export interface VariableMetadata {
type?: string;
/** If applicable, a string representation of the variable's value */
value?: string;
parentVariablesReference?: number;
isPointer?: boolean;
}

Expand Down
9 changes: 8 additions & 1 deletion src/common/messaging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
import type { DebugProtocol } from '@vscode/debugprotocol';
import type { NotificationType, RequestType } from 'vscode-messenger-common';
import { URI } from 'vscode-uri';
import { DebugRequestTypes } from './debug-requests';
import type { TrackedDataBreakpoints } from './breakpoint';
import { DebugEvents, DebugRequestTypes } from './debug-requests';
import { VariablesView } from './external-views';
import type { VariableRange, WrittenMemory } from './memory-range';
import { MemoryViewSettings } from './webview-configuration';
Expand All @@ -32,6 +33,9 @@ export type ReadMemoryResult = DebugRequestTypes['readMemory'][1];
export type WriteMemoryArguments = DebugRequestTypes['writeMemory'][0];
export type WriteMemoryResult = DebugRequestTypes['writeMemory'][1];

export type StoppedEvent = DebugEvents['stopped'];
export type ContinuedEvent = DebugEvents['continued'];

export type StoreMemoryArguments = MemoryOptions & { proposedOutputName?: string } | VariablesView.IVariablesContext | WebviewContext;
export type StoreMemoryResult = void;

Expand All @@ -51,6 +55,9 @@ export const setMemoryViewSettingsType: NotificationType<Partial<MemoryViewSetti
export const setTitleType: NotificationType<string> = { method: 'setTitle' };
export const memoryWrittenType: NotificationType<WrittenMemory> = { method: 'memoryWritten' };
export const sessionContextChangedType: NotificationType<SessionContext> = { method: 'sessionContextChanged' };
export const setTrackedBreakpointType: NotificationType<TrackedDataBreakpoints> = { method: 'setTrackedBreakpoints' };
export const notifyStoppedType: NotificationType<StoppedEvent> = { method: 'notifyStoppedType' };
export const notifyContinuedType: NotificationType<ContinuedEvent> = { method: 'notifyContinuedType' };

// Requests
export const setOptionsType: RequestType<MemoryOptions, void> = { method: 'setOptions' };
Expand Down
17 changes: 17 additions & 0 deletions src/common/webview-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ export interface WebviewVariableContext extends WebviewCellContext {
variable?: VariableMetadata
}

export interface WebviewGroupContext extends WebviewCellContext {
memoryData?: {
group: {
startAddress: string;
length: number;
}
}
}

/**
* Retrieves the currently visible (configurable) columns from the given {@link WebviewContext}.
* @returns A string array containing the visible columns ids.
Expand All @@ -64,6 +73,14 @@ export function isWebviewContext(args: WebviewContext | unknown): args is Webvie
&& typeof assumed.activeReadArguments?.memoryReference === 'string';
}

export function isWebviewGroupContext(args: WebviewVariableContext | unknown): args is Required<WebviewGroupContext> {
const assumed = args ? args as WebviewGroupContext : undefined;
return !!assumed && isWebviewContext(args)
&& !!assumed.memoryData
&& (typeof assumed.memoryData.group.startAddress === 'string')
&& (typeof assumed.memoryData.group.length === 'number');
}

export function isWebviewVariableContext(args: WebviewVariableContext | unknown): args is Required<WebviewVariableContext> {
const assumed = args ? args as WebviewVariableContext : undefined;
return !!assumed && isWebviewContext(args)
Expand Down
6 changes: 5 additions & 1 deletion src/entry-points/browser/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import * as vscode from 'vscode';
import { AdapterRegistry } from '../../plugin/adapter-registry/adapter-registry';
import { CAdapter } from '../../plugin/adapter-registry/c-adapter';
import { BreakpointProvider } from '../../plugin/breakpoints/breakpoint-provider';
import { BreakpointTracker } from '../../plugin/breakpoints/breakpoint-tracker';
import { ContextTracker } from '../../plugin/context-tracker';
import { MemoryProvider } from '../../plugin/memory-provider';
import { MemoryStorage } from '../../plugin/memory-storage';
Expand All @@ -27,8 +29,10 @@ export const activate = async (context: vscode.ExtensionContext): Promise<Adapte
const registry = new AdapterRegistry();
const sessionTracker = new SessionTracker();
new ContextTracker(sessionTracker);
const breakpointTracker = new BreakpointTracker(sessionTracker);
const breakpointProvider = new BreakpointProvider(sessionTracker, breakpointTracker);
const memoryProvider = new MemoryProvider(registry, sessionTracker);
const memoryView = new MemoryWebview(context.extensionUri, memoryProvider, sessionTracker);
const memoryView = new MemoryWebview(context.extensionUri, memoryProvider, sessionTracker, breakpointTracker, breakpointProvider);
const memoryStorage = new MemoryStorage(memoryProvider);
const cAdapter = new CAdapter(registry);

Expand Down
6 changes: 5 additions & 1 deletion src/entry-points/desktop/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import * as vscode from 'vscode';
import { AdapterRegistry } from '../../plugin/adapter-registry/adapter-registry';
import { CAdapter } from '../../plugin/adapter-registry/c-adapter';
import { BreakpointProvider } from '../../plugin/breakpoints/breakpoint-provider';
import { BreakpointTracker } from '../../plugin/breakpoints/breakpoint-tracker';
import { ContextTracker } from '../../plugin/context-tracker';
import { MemoryProvider } from '../../plugin/memory-provider';
import { MemoryStorage } from '../../plugin/memory-storage';
Expand All @@ -27,8 +29,10 @@ export const activate = async (context: vscode.ExtensionContext): Promise<Adapte
const registry = new AdapterRegistry();
const sessionTracker = new SessionTracker();
new ContextTracker(sessionTracker);
const breakpointTracker = new BreakpointTracker(sessionTracker);
const breakpointProvider = new BreakpointProvider(sessionTracker, breakpointTracker);
const memoryProvider = new MemoryProvider(registry, sessionTracker);
const memoryView = new MemoryWebview(context.extensionUri, memoryProvider, sessionTracker);
const memoryView = new MemoryWebview(context.extensionUri, memoryProvider, sessionTracker, breakpointTracker, breakpointProvider);
const memoryStorage = new MemoryStorage(memoryProvider);
const cAdapter = new CAdapter(registry);

Expand Down
7 changes: 5 additions & 2 deletions src/plugin/adapter-registry/adapter-capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export class AdapterVariableTracker implements vscode.DebugAdapterTracker {
if (this.isDesiredVariable(parent) && parent.children?.length) {
this.logger.debug('Resolving children of', parent.name);
parent.children.forEach(child => {
previous.push(this.variableToVariableRange(child, session));
previous.push(this.variableToVariableRange(child, session, parent));
});
} else {
this.logger.debug('Ignoring', parent.name);
Expand All @@ -126,7 +126,10 @@ export class AdapterVariableTracker implements vscode.DebugAdapterTracker {
return candidate.presentationHint !== 'registers' && candidate.name !== 'Registers';
}

protected variableToVariableRange(_variable: DebugProtocol.Variable, _session: vscode.DebugSession): Promise<VariableRange | undefined> {
protected variableToVariableRange(
_variable: DebugProtocol.Variable,
_session: vscode.DebugSession,
_parent: WithChildren<DebugProtocol.Scope | DebugProtocol.Variable>): Promise<VariableRange | undefined> {
throw new Error('To be implemented by derived classes!');
}

Expand Down
8 changes: 6 additions & 2 deletions src/plugin/adapter-registry/c-tracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { DebugProtocol } from '@vscode/debugprotocol';
import * as vscode from 'vscode';
import { sendRequest } from '../../common/debug-requests';
import { toHexStringWithRadixMarker, VariableRange } from '../../common/memory-range';
import { AdapterVariableTracker, decimalAddress, extractAddress, hexAddress, notADigit } from './adapter-capabilities';
import { AdapterVariableTracker, decimalAddress, extractAddress, hexAddress, notADigit, WithChildren } from './adapter-capabilities';

export namespace CEvaluateExpression {
export function sizeOf(expression: string): string {
Expand All @@ -35,7 +35,10 @@ export class CTracker extends AdapterVariableTracker {
* Resolves memory location and size using evaluate requests for `$(variable.name)` and `sizeof(variable.name)`
* Ignores the presence or absence of variable.memoryReference.
*/
protected override async variableToVariableRange(variable: DebugProtocol.Variable, session: vscode.DebugSession): Promise<VariableRange | undefined> {
protected override async variableToVariableRange(
variable: DebugProtocol.Variable,
session: vscode.DebugSession,
parent: WithChildren<DebugProtocol.Scope | DebugProtocol.Variable>): Promise<VariableRange | undefined> {
if (this.currentFrame === undefined || !variable.name) {
this.logger.debug('Unable to resolve', variable.name,
{ noName: !variable.name, noFrame: this.currentFrame === undefined });
Expand Down Expand Up @@ -66,6 +69,7 @@ export class CTracker extends AdapterVariableTracker {
endAddress: variableSize === undefined ? undefined : toHexStringWithRadixMarker(address + variableSize),
value: variable.value,
type: variable.type,
parentVariablesReference: parent.variablesReference,
isPointer,
};
return variableRange;
Expand Down
Loading
Loading