Skip to content

Commit fe7ae6d

Browse files
committed
Allow to create data breakpoints
1 parent 866d068 commit fe7ae6d

22 files changed

+795
-40
lines changed

media/memory-table.css

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,53 @@
3131
border-bottom: 1px dotted var(--vscode-editorHoverWidget-border);
3232
}
3333

34+
.memory-inspector-table .data-breakpoint {
35+
outline: 1px solid var(--vscode-debugIcon-breakpointForeground);
36+
outline-offset: 1px;
37+
}
38+
39+
.memory-inspector-table .data-breakpoint.data-breakpoint-external {
40+
outline-style: dashed;
41+
}
42+
43+
.memory-inspector-table tbody .column-address {
44+
position: relative;
45+
}
46+
47+
.memory-inspector-table tbody .address-status {
48+
position: absolute;
49+
left: -1px;
50+
align-items: center;
51+
display: flex;
52+
justify-content: center;
53+
}
54+
55+
.memory-inspector-table tbody .address-status.codicon {
56+
font-size: 12px;
57+
}
58+
59+
.memory-inspector-table tbody .address-status.codicon-debug-breakpoint {
60+
color: var(--vscode-debugIcon-breakpointForeground);
61+
}
62+
63+
.memory-inspector-table tbody .address-status.codicon-debug-stackframe {
64+
color: var(--vscode-debugIcon-breakpointCurrentStackframeForeground);
65+
}
66+
67+
.memory-inspector-table
68+
tbody
69+
.address-status.codicon-debug-breakpoint.codicon-debug-stackframe:after {
70+
content: "\ea71";
71+
position: absolute;
72+
left: 3px;
73+
font-size: 6px;
74+
color: var(--vscode-debugIcon-breakpointForeground);
75+
}
76+
77+
.memory-inspector-table tbody .debug-hit {
78+
outline-color: var(--vscode-debugIcon-breakpointCurrentStackframeForeground);
79+
}
80+
3481
/* == MoreMemorySelect == */
3582

3683
.bytes-select {
@@ -90,7 +137,7 @@
90137

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

96143
.memory-inspector-table span.p-column-resizer:hover {
@@ -111,7 +158,7 @@
111158
/* Basic hover formatting (copied from Monaco hovers) */
112159
.memory-hover {
113160
min-width: fit-content;
114-
max-width: var(--vscode-hover-maxWidth,500px);
161+
max-width: var(--vscode-hover-maxWidth, 500px);
115162
border: 1px solid var(--vscode-editorHoverWidget-border);
116163
border-radius: 3px;
117164

@@ -127,34 +174,40 @@
127174
border-collapse: collapse;
128175
border-style: hidden;
129176
}
177+
130178
.memory-hover table caption {
131179
padding: 4px;
132180
border-bottom: 1px solid var(--vscode-editorHoverWidget-border);
133181
}
182+
134183
.memory-hover td {
135184
border: 1px solid var(--vscode-editorHoverWidget-border);
136185
padding: 2px 8px;
137186
}
187+
138188
.memory-hover td:first-child {
139189
text-align: right;
140190
}
141191

142192
/* Colors for the hover fields */
143-
.memory-hover .label-value-pair>.label {
144-
color: var(--vscode-debugTokenExpression-string);
145-
white-space: nowrap;
193+
.memory-hover .label-value-pair > .label {
194+
color: var(--vscode-debugTokenExpression-string);
195+
white-space: nowrap;
146196
}
147-
.memory-hover .label-value-pair>.value {
197+
198+
.memory-hover .label-value-pair > .value {
148199
color: var(--vscode-debugTokenExpression-number);
149200
}
150201

151202
/* Colors for specific hover fields */
152203
.memory-hover .address-hover .primary {
153204
background-color: var(--vscode-list-hoverBackground);
154205
}
206+
155207
.memory-hover table caption {
156208
color: var(--vscode-symbolIcon-variableForeground);
157209
}
210+
158211
.memory-hover .address-hover .value.utf8,
159212
.memory-hover .data-hover .value.utf8,
160213
.memory-hover .variable-hover .value.type {

package.json

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,36 @@
9191
"title": "Go to value in Memory Inspector",
9292
"category": "Memory"
9393
},
94+
{
95+
"command": "memory-inspector.data-breakpoint.set.read",
96+
"title": "Break on Value Read",
97+
"enablement": "memory-inspector.canWrite",
98+
"category": "Memory"
99+
},
100+
{
101+
"command": "memory-inspector.data-breakpoint.set.readWrite",
102+
"title": "Break on Value Access",
103+
"enablement": "memory-inspector.canWrite",
104+
"category": "Memory"
105+
},
106+
{
107+
"command": "memory-inspector.data-breakpoint.set.write",
108+
"title": "Break on Value Change",
109+
"enablement": "memory-inspector.canWrite",
110+
"category": "Memory"
111+
},
112+
{
113+
"command": "memory-inspector.data-breakpoint.remove",
114+
"title": "Remove Breakpoint",
115+
"enablement": "memory-inspector.canWrite",
116+
"category": "Memory"
117+
},
118+
{
119+
"command": "memory-inspector.data-breakpoint.remove-all",
120+
"title": "Remove All Breakpoints",
121+
"enablement": "memory-inspector.canWrite",
122+
"category": "Memory"
123+
},
94124
{
95125
"command": "memory-inspector.toggle-variables-column",
96126
"title": "Toggle Variables Column",
@@ -214,6 +244,31 @@
214244
"command": "memory-inspector.go-to-value",
215245
"group": "display@7",
216246
"when": "webviewId === memory-inspector.memory && memory-inspector.variable.isPointer"
247+
},
248+
{
249+
"command": "memory-inspector.data-breakpoint.set.read",
250+
"group": "breakpoints@1",
251+
"when": "webviewId === memory-inspector.memory && memory-inspector.breakpoint.isBreakable"
252+
},
253+
{
254+
"command": "memory-inspector.data-breakpoint.set.write",
255+
"group": "breakpoints@2",
256+
"when": "webviewId === memory-inspector.memory && memory-inspector.breakpoint.isBreakable"
257+
},
258+
{
259+
"command": "memory-inspector.data-breakpoint.set.readWrite",
260+
"group": "breakpoints@3",
261+
"when": "webviewId === memory-inspector.memory && memory-inspector.breakpoint.isBreakable"
262+
},
263+
{
264+
"command": "memory-inspector.data-breakpoint.remove",
265+
"group": "breakpoints@4",
266+
"when": "webviewId === memory-inspector.memory && memory-inspector.breakpoint.type === 'internal'"
267+
},
268+
{
269+
"command": "memory-inspector.data-breakpoint.remove-all",
270+
"group": "breakpoints@5",
271+
"when": "webviewId === memory-inspector.memory && memory-inspector.breakpoint.type === 'internal'"
217272
}
218273
]
219274
},

src/common/breakpoint.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/********************************************************************************
2+
* Copyright (C) 2024 EclipseSource.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the Eclipse
10+
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
11+
* with the GNU Classpath Exception which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
********************************************************************************/
16+
17+
import { DebugProtocol } from '@vscode/debugprotocol';
18+
import { DebugRequestTypes } from './debug-requests';
19+
20+
export interface TrackedDataBreakpoint {
21+
type: TrackedBreakpointType;
22+
breakpoint: DebugProtocol.DataBreakpoint;
23+
/**
24+
* The respective response for the breakpoint.
25+
*/
26+
response: DebugProtocol.SetDataBreakpointsResponse['body']['breakpoints'][0]
27+
}
28+
29+
export interface TrackedDataBreakpoints {
30+
/**
31+
* Breakpoints set from external contributors.
32+
*/
33+
external: TrackedDataBreakpoint[],
34+
/**
35+
* Breakpoints set from us.
36+
*/
37+
internal: TrackedDataBreakpoint[]
38+
}
39+
40+
export type TrackedBreakpointType = 'internal' | 'external';
41+
42+
export type DataBreakpointInfoArguments = DebugRequestTypes['dataBreakpointInfo'][0];
43+
export type DataBreakpointInfoResult = DebugRequestTypes['dataBreakpointInfo'][1];
44+
export type SetDataBreakpointsArguments = DebugRequestTypes['setDataBreakpoints'][0];
45+
export type SetDataBreakpointsResult = DebugRequestTypes['setDataBreakpoints'][1];

src/common/debug-requests.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ export interface DebugRequestTypes {
2525
'scopes': [DebugProtocol.ScopesArguments, DebugProtocol.ScopesResponse['body']]
2626
'variables': [DebugProtocol.VariablesArguments, DebugProtocol.VariablesResponse['body']]
2727
'writeMemory': [DebugProtocol.WriteMemoryArguments, DebugProtocol.WriteMemoryResponse['body']]
28+
'dataBreakpointInfo': [DebugProtocol.DataBreakpointInfoArguments, DebugProtocol.DataBreakpointInfoResponse['body']]
29+
'setDataBreakpoints': [DebugProtocol.SetDataBreakpointsArguments, DebugProtocol.SetDataBreakpointsResponse['body']]
2830
}
2931

3032
export interface DebugEvents {
@@ -59,16 +61,28 @@ export function isDebugEvaluateArguments(args: DebugProtocol.EvaluateArguments |
5961
}
6062

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

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

7171
export function isDebugEvent<K extends keyof DebugEvents>(event: K, message: unknown): message is DebugEvents[K] {
72+
return isDebugEventType(message) && message.event === event;
73+
}
74+
75+
export function isDebugRequestType(message: unknown): message is DebugProtocol.Request {
76+
const assumed = message ? message as DebugProtocol.Request : undefined;
77+
return !!assumed && assumed.type === 'request';
78+
}
79+
80+
export function isDebugResponseType(message: unknown): message is DebugProtocol.Response {
81+
const assumed = message ? message as DebugProtocol.Response : undefined;
82+
return !!assumed && assumed.type === 'response';
83+
}
84+
85+
export function isDebugEventType(message: unknown): message is DebugProtocol.Event {
7286
const assumed = message ? message as DebugProtocol.Event : undefined;
73-
return !!assumed && assumed.type === 'event' && assumed.event === event;
87+
return !!assumed && assumed.type === 'event';
7488
}

src/common/memory-range.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ export interface VariableMetadata {
108108
type?: string;
109109
/** If applicable, a string representation of the variable's value */
110110
value?: string;
111+
parentVariablesReference?: number;
111112
isPointer?: boolean;
112113
}
113114

src/common/messaging.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import type { DebugProtocol } from '@vscode/debugprotocol';
1818
import type { NotificationType, RequestType } from 'vscode-messenger-common';
1919
import { URI } from 'vscode-uri';
2020
import { VariablesView } from '../plugin/external-views';
21-
import { DebugRequestTypes } from './debug-requests';
21+
import type { TrackedDataBreakpoints } from './breakpoint';
22+
import { DebugEvents, DebugRequestTypes } from './debug-requests';
2223
import type { VariableRange, WrittenMemory } from './memory-range';
2324
import { MemoryViewSettings } from './webview-configuration';
2425
import { WebviewContext } from './webview-context';
@@ -32,6 +33,9 @@ export type ReadMemoryResult = DebugRequestTypes['readMemory'][1];
3233
export type WriteMemoryArguments = DebugRequestTypes['writeMemory'][0];
3334
export type WriteMemoryResult = DebugRequestTypes['writeMemory'][1];
3435

36+
export type StoppedEvent = DebugEvents['stopped'];
37+
export type ContinuedEvent = DebugEvents['continued'];
38+
3539
export type StoreMemoryArguments = MemoryOptions & { proposedOutputName?: string } | VariablesView.IVariablesContext | WebviewContext;
3640
export type StoreMemoryResult = void;
3741

@@ -52,6 +56,9 @@ export const resetMemoryViewSettingsType: NotificationType<void> = { method: 're
5256
export const setTitleType: NotificationType<string> = { method: 'setTitle' };
5357
export const memoryWrittenType: NotificationType<WrittenMemory> = { method: 'memoryWritten' };
5458
export const sessionContextChangedType: NotificationType<SessionContext> = { method: 'sessionContextChanged' };
59+
export const setTrackedBreakpointType: NotificationType<TrackedDataBreakpoints> = { method: 'setTrackedBreakpoints' };
60+
export const notifyStoppedType: NotificationType<StoppedEvent> = { method: 'notifyStoppedType' };
61+
export const notifyContinuedType: NotificationType<ContinuedEvent> = { method: 'notifyContinuedType' };
5562

5663
// Requests
5764
export const setOptionsType: RequestType<MemoryOptions, void> = { method: 'setOptions' };

src/common/webview-context.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ export interface WebviewVariableContext extends WebviewCellContext {
3939
variable?: VariableMetadata
4040
}
4141

42+
export interface WebviewGroupContext extends WebviewCellContext {
43+
memoryData?: {
44+
group: {
45+
startAddress: string;
46+
length: number;
47+
}
48+
}
49+
}
50+
4251
/**
4352
* Retrieves the currently visible (configurable) columns from the given {@link WebviewContext}.
4453
* @returns A string array containing the visible columns ids.
@@ -62,6 +71,14 @@ export function isWebviewContext(args: WebviewContext | unknown): args is Webvie
6271
&& typeof assumed.activeReadArguments?.memoryReference === 'string';
6372
}
6473

74+
export function isWebviewGroupContext(args: WebviewVariableContext | unknown): args is Required<WebviewGroupContext> {
75+
const assumed = args ? args as WebviewGroupContext : undefined;
76+
return !!assumed && isWebviewContext(args)
77+
&& !!assumed.memoryData
78+
&& (typeof assumed.memoryData.group.startAddress === 'string')
79+
&& (typeof assumed.memoryData.group.length === 'number');
80+
}
81+
6582
export function isWebviewVariableContext(args: WebviewVariableContext | unknown): args is Required<WebviewVariableContext> {
6683
const assumed = args ? args as WebviewVariableContext : undefined;
6784
return !!assumed && isWebviewContext(args)

src/entry-points/browser/extension.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import * as vscode from 'vscode';
1818
import { AdapterRegistry } from '../../plugin/adapter-registry/adapter-registry';
1919
import { CAdapter } from '../../plugin/adapter-registry/c-adapter';
20+
import { BreakpointProvider } from '../../plugin/breakpoints/breakpoint-provider';
21+
import { BreakpointTracker } from '../../plugin/breakpoints/breakpoint-tracker';
2022
import { ContextTracker } from '../../plugin/context-tracker';
2123
import { MemoryProvider } from '../../plugin/memory-provider';
2224
import { MemoryStorage } from '../../plugin/memory-storage';
@@ -27,8 +29,10 @@ export const activate = async (context: vscode.ExtensionContext): Promise<Adapte
2729
const registry = new AdapterRegistry();
2830
const sessionTracker = new SessionTracker();
2931
new ContextTracker(sessionTracker);
32+
const breakpointTracker = new BreakpointTracker(sessionTracker);
33+
const breakpointProvider = new BreakpointProvider(sessionTracker, breakpointTracker);
3034
const memoryProvider = new MemoryProvider(registry, sessionTracker);
31-
const memoryView = new MemoryWebview(context.extensionUri, memoryProvider, sessionTracker);
35+
const memoryView = new MemoryWebview(context.extensionUri, memoryProvider, sessionTracker, breakpointTracker, breakpointProvider);
3236
const memoryStorage = new MemoryStorage(memoryProvider);
3337
const cAdapter = new CAdapter(registry);
3438

src/entry-points/desktop/extension.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import * as vscode from 'vscode';
1818
import { AdapterRegistry } from '../../plugin/adapter-registry/adapter-registry';
1919
import { CAdapter } from '../../plugin/adapter-registry/c-adapter';
20+
import { BreakpointProvider } from '../../plugin/breakpoints/breakpoint-provider';
21+
import { BreakpointTracker } from '../../plugin/breakpoints/breakpoint-tracker';
2022
import { ContextTracker } from '../../plugin/context-tracker';
2123
import { MemoryProvider } from '../../plugin/memory-provider';
2224
import { MemoryStorage } from '../../plugin/memory-storage';
@@ -27,8 +29,10 @@ export const activate = async (context: vscode.ExtensionContext): Promise<Adapte
2729
const registry = new AdapterRegistry();
2830
const sessionTracker = new SessionTracker();
2931
new ContextTracker(sessionTracker);
32+
const breakpointTracker = new BreakpointTracker(sessionTracker);
33+
const breakpointProvider = new BreakpointProvider(sessionTracker, breakpointTracker);
3034
const memoryProvider = new MemoryProvider(registry, sessionTracker);
31-
const memoryView = new MemoryWebview(context.extensionUri, memoryProvider, sessionTracker);
35+
const memoryView = new MemoryWebview(context.extensionUri, memoryProvider, sessionTracker, breakpointTracker, breakpointProvider);
3236
const memoryStorage = new MemoryStorage(memoryProvider);
3337
const cAdapter = new CAdapter(registry);
3438

0 commit comments

Comments
 (0)