Skip to content

Commit f4711cc

Browse files
enabled "while running" features, silently "fail" on __Symbol_exists() (#835)
* enabled "while running" features * silently "fail" on __Symbol_exists() if symbol does not exist * added tests
1 parent dfdc8c2 commit f4711cc

5 files changed

Lines changed: 75 additions & 49 deletions

File tree

src/views/component-viewer/component-viewer-target-access.ts

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,9 @@ export class ComponentViewerTargetAccess {
3030
this._activeSession = session;
3131
}
3232

33-
public async evaluateSymbolAddress(address: string, context = 'hover'): Promise<string | undefined> {
33+
public async evaluateSymbolAddress(address: string, context = 'hover', existCheck: boolean = false): Promise<string | undefined> {
3434
try {
3535
const frameId = (vscode.debug.activeStackItem as vscode.DebugStackFrame)?.frameId;
36-
// if FrameId is undefined, evaluation is not possible as cdt-adapter doesn't accept it
37-
if (frameId === undefined) {
38-
return undefined;
39-
}
4036
const args: DebugProtocol.EvaluateArguments = {
4137
expression: `&${address}`,
4238
frameId, // Currently required by CDT GDB Adapter
@@ -49,6 +45,10 @@ export class ComponentViewerTargetAccess {
4945
}
5046
return response.result.split(' ')[0]; // Return only the address part
5147
} catch (error: unknown) {
48+
if(existCheck) {
49+
// If this evaluation is just to check existence, we can treat any error as symbol not existing
50+
return undefined;
51+
}
5252
const errorMessage = (error as Error)?.message;
5353
componentViewerLogger.debug(`Session '${this._activeSession?.session.name}': Failed to evaluate address '${address}' - '${errorMessage}'`);
5454
return undefined;
@@ -76,10 +76,6 @@ export class ComponentViewerTargetAccess {
7676
public async evaluateSymbolName(address: string | number | bigint, context = 'hover'): Promise<string | undefined> {
7777
try {
7878
const frameId = (vscode.debug.activeStackItem as vscode.DebugStackFrame)?.frameId;
79-
// if FrameId is undefined, evaluation is not possible as cdt-adapter doesn't accept it
80-
if (frameId === undefined) {
81-
return undefined;
82-
}
8379
const formattedAddress = this.formatAddress(address);
8480
const args: DebugProtocol.EvaluateArguments = {
8581
expression: `(unsigned int*)${formattedAddress}`,
@@ -103,10 +99,6 @@ export class ComponentViewerTargetAccess {
10399
public async evaluateSymbolContext(address: string, context = 'hover'): Promise<string | undefined> {
104100
try {
105101
const frameId = (vscode.debug.activeStackItem as vscode.DebugStackFrame)?.frameId;
106-
// if FrameId is undefined, evaluation is not possible as cdt-adapter doesn't accept it
107-
if (frameId === undefined) {
108-
return undefined;
109-
}
110102
const formattedAddress = this.formatAddress(address);
111103
// Ask GDB for file/line context of the address.
112104
const args: DebugProtocol.EvaluateArguments = {
@@ -130,10 +122,6 @@ export class ComponentViewerTargetAccess {
130122
public async evaluateSymbolSize(symbol: string, context = 'hover'): Promise<number | undefined> {
131123
try {
132124
const frameId = (vscode.debug.activeStackItem as vscode.DebugStackFrame)?.frameId;
133-
// if FrameId is undefined, evaluation is not possible as cdt-adapter doesn't accept it
134-
if (frameId === undefined) {
135-
return undefined;
136-
}
137125
const args: DebugProtocol.EvaluateArguments = {
138126
expression: `sizeof(${symbol})`,
139127
frameId,
@@ -174,10 +162,6 @@ export class ComponentViewerTargetAccess {
174162
public async evaluateNumberOfArrayElements(symbol: string): Promise<number | undefined> {
175163
try {
176164
const frameId = (vscode.debug.activeStackItem as vscode.DebugStackFrame)?.frameId;
177-
// if FrameId is undefined, evaluation is not possible as cdt-adapter doesn't accept it
178-
if (frameId === undefined) {
179-
return undefined;
180-
}
181165
const args: DebugProtocol.EvaluateArguments = {
182166
expression: `sizeof(${symbol})/sizeof(${symbol}[0])`,
183167
frameId, // Currently required by CDT GDB Adapter

src/views/component-viewer/scvd-debug-target.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ export class ScvdDebugTarget {
145145
});
146146
}
147147

148-
public async getSymbolInfo(symbol: string): Promise<SymbolInfo | undefined> {
148+
public async getSymbolInfo(symbol: string, existCheck: boolean = false): Promise<SymbolInfo | undefined> {
149149
componentViewerLogger.debug(`get Symbol Info: resolving ${symbol}`);
150150
if (symbol === undefined) {
151151
componentViewerLogger.debug('get Symbol Info: no symbol provided');
@@ -157,7 +157,7 @@ export class ScvdDebugTarget {
157157
}
158158

159159
const addressInfo = await this.symbolCaches.getAddressWithName(symbol, async (symbolName) => {
160-
const symbolAddressStr = await this.targetAccess.evaluateSymbolAddress(symbolName);
160+
const symbolAddressStr = await this.targetAccess.evaluateSymbolAddress(symbolName, 'hover', existCheck);
161161
if (symbolAddressStr !== undefined) {
162162
const parsed = parseInt(symbolAddressStr as unknown as string, 16);
163163
if (Number.isFinite(parsed)) {
@@ -242,9 +242,9 @@ export class ScvdDebugTarget {
242242
return this.isTargetRunning;
243243
}
244244

245-
public async findSymbolAddress(symbol: string): Promise<number | undefined> {
245+
public async findSymbolAddress(symbol: string, existCheck: boolean = false): Promise<number | undefined> {
246246
componentViewerLogger.debug(`find Symbol Address: ${symbol}`);
247-
const symbolInfo = await this.getSymbolInfo(symbol);
247+
const symbolInfo = await this.getSymbolInfo(symbol, existCheck);
248248
return symbolInfo?.address;
249249
}
250250

src/views/component-viewer/scvd-eval-interface.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,12 +169,12 @@ export class ScvdEvalInterface implements ModelHost, DataAccessHost, IntrinsicPr
169169
return trimmed && trimmed.length > 0 ? trimmed : undefined;
170170
}
171171

172-
private async findSymbolAddressNormalized(name: string | undefined): Promise<number | undefined> {
172+
private async findSymbolAddressNormalized(name: string | undefined, existCheck: boolean = false): Promise<number | undefined> {
173173
const normalized = this.normalizeName(name);
174174
if (!normalized) {
175175
return undefined;
176176
}
177-
return this.debugTarget.findSymbolAddress(normalized);
177+
return this.debugTarget.findSymbolAddress(normalized, existCheck);
178178
}
179179

180180
// ---------------- Host Interface: model + data access ----------------
@@ -347,7 +347,7 @@ export class ScvdEvalInterface implements ModelHost, DataAccessHost, IntrinsicPr
347347
}
348348

349349
public async __Symbol_exists(symbol: string): Promise<number | undefined> {
350-
const found = await this.findSymbolAddressNormalized(symbol);
350+
const found = await this.findSymbolAddressNormalized(symbol, true);
351351
return found !== undefined ? 1 : 0;
352352
}
353353

src/views/component-viewer/test/integration/component-viewer-target-access.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ describe('ComponentViewerTargetAccess', () => {
8686
(vscode.debug.activeStackItem as unknown) = undefined;
8787
});
8888

89-
it('should return undefined when no active stack frame exists', async () => {
89+
it('should evaluate symbol address when no active stack frame exists', async () => {
9090
(debugSession.customRequest as jest.Mock).mockResolvedValueOnce({
9191
result: '0x30000000',
9292
variablesReference: 0
@@ -95,8 +95,12 @@ describe('ComponentViewerTargetAccess', () => {
9595

9696
const result = await targetAccess.evaluateSymbolAddress('globalVar');
9797

98-
expect(result).toBeUndefined();
99-
expect(debugSession.customRequest).not.toHaveBeenCalled();
98+
expect(result).toBe('0x30000000');
99+
expect(debugSession.customRequest).toHaveBeenCalledWith('evaluate', {
100+
expression: '&globalVar',
101+
frameId: undefined,
102+
context: 'hover'
103+
});
100104
});
101105

102106
it('should log when evaluation fails', async () => {

src/views/component-viewer/test/unit/component-viewer-target-access.test.ts

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,21 @@ import { debugSessionFactory } from '../../../../__test__/vscode.factory';
2525
import { GDBTargetDebugSession } from '../../../../debug-session';
2626
import { componentViewerLogger } from '../../../../logger';
2727

28-
type DebugWithSession = {
29-
activeDebugSession: vscode.DebugSession | undefined;
30-
activeStackItem: vscode.DebugStackFrame | undefined;
31-
};
32-
3328
function setActiveStackItem(session: vscode.DebugSession | undefined, frameId: number | undefined) {
34-
(vscode.debug as unknown as DebugWithSession).activeStackItem = session
35-
? ({ session, threadId: 1, frameId } as unknown as vscode.DebugStackFrame)
36-
: undefined;
29+
const value = session ? { session, threadId: 1, frameId } : undefined;
30+
Object.defineProperty(vscode.debug, 'activeStackItem', {
31+
value,
32+
configurable: true,
33+
writable: true,
34+
});
35+
}
36+
37+
function setActiveDebugSession(session: vscode.DebugSession | undefined) {
38+
Object.defineProperty(vscode.debug, 'activeDebugSession', {
39+
value: session,
40+
configurable: true,
41+
writable: true,
42+
});
3743
}
3844

3945
describe('ComponentViewerTargetAccess', () => {
@@ -53,24 +59,42 @@ describe('ComponentViewerTargetAccess', () => {
5359
targetAccess = new ComponentViewerTargetAccess();
5460
targetAccess.setActiveSession(gdbTargetSession);
5561
setActiveStackItem(debugSession, 1);
62+
setActiveDebugSession(debugSession);
5663
});
5764

5865
afterEach(() => {
5966
setActiveStackItem(undefined, undefined);
60-
(vscode.debug as unknown as DebugWithSession).activeDebugSession = undefined;
67+
setActiveDebugSession(undefined);
6168
jest.restoreAllMocks();
6269
});
6370

64-
it('formats addresses consistently', () => {
65-
const formatAddress = (targetAccess as unknown as { formatAddress: (addr: string | number | bigint) => string })
66-
.formatAddress;
71+
it('formats addresses consistently for symbol lookups', async () => {
72+
setActiveStackItem(debugSession, 7);
73+
74+
const cases = [
75+
{ input: '', expected: '(unsigned int*)' },
76+
{ input: ' 0x1A ', expected: '(unsigned int*)0x1A' },
77+
{ input: '15', expected: '(unsigned int*)0xf' },
78+
{ input: 16, expected: '(unsigned int*)0x10' },
79+
{ input: 0x20n, expected: '(unsigned int*)0x20' },
80+
{ input: 'not-a-number', expected: '(unsigned int*)not-a-number' },
81+
];
82+
83+
const expectedExpressions = cases.map(({ expected }) => expected);
84+
(debugSession.customRequest as jest.Mock).mockImplementation(async (_command, args) => {
85+
const expectedExpression = expectedExpressions.shift();
86+
expect(args).toEqual({
87+
expression: expectedExpression ?? '',
88+
frameId: 7,
89+
context: 'hover',
90+
});
91+
return { result: '0x0 <Sym>' };
92+
});
6793

68-
expect(formatAddress('')).toBe('');
69-
expect(formatAddress(' 0x1A ')).toBe('0x1A');
70-
expect(formatAddress('15')).toBe('0xf');
71-
expect(formatAddress(16)).toBe('0x10');
72-
expect(formatAddress(0x20n)).toBe('0x20');
73-
expect(formatAddress('not-a-number')).toBe('not-a-number');
94+
for (const { input } of cases) {
95+
await expect(targetAccess.evaluateSymbolName(input)).resolves.toBe('Sym');
96+
}
97+
expect(expectedExpressions).toHaveLength(0);
7498
});
7599

76100
it('evaluates symbol address and handles failures', async () => {
@@ -91,6 +115,14 @@ describe('ComponentViewerTargetAccess', () => {
91115
);
92116
});
93117

118+
it('returns undefined without logging when exist check fails', async () => {
119+
const debugSpy = jest.spyOn(componentViewerLogger, 'debug');
120+
(debugSession.customRequest as jest.Mock).mockRejectedValueOnce(new Error('probe failed'));
121+
122+
await expect(targetAccess.evaluateSymbolAddress('missing', 'hover', true)).resolves.toBeUndefined();
123+
expect(debugSpy).not.toHaveBeenCalled();
124+
});
125+
94126
it('evaluates symbol name with valid and missing results', async () => {
95127
(debugSession.customRequest as jest.Mock).mockResolvedValueOnce({ result: '0x20000000 <MySymbol>' });
96128
setActiveStackItem(debugSession, 1);
@@ -109,6 +141,9 @@ describe('ComponentViewerTargetAccess', () => {
109141
(debugSession.customRequest as jest.Mock).mockResolvedValueOnce({ result: 'No symbol matches' });
110142
await expect(targetAccess.evaluateSymbolName('0x0')).resolves.toBeUndefined();
111143

144+
(debugSession.customRequest as jest.Mock).mockResolvedValueOnce({ result: '0x20000000 Symbol' });
145+
await expect(targetAccess.evaluateSymbolName('0x20000000')).resolves.toBeUndefined();
146+
112147
const debugSpy = jest.spyOn(componentViewerLogger, 'debug');
113148
(debugSession.customRequest as jest.Mock).mockRejectedValueOnce(new Error('oops'));
114149
await expect(targetAccess.evaluateSymbolName('0x1')).resolves.toBeUndefined();
@@ -124,6 +159,9 @@ describe('ComponentViewerTargetAccess', () => {
124159
(debugSession.customRequest as jest.Mock).mockResolvedValueOnce({ result: 'No line information' });
125160
await expect(targetAccess.evaluateSymbolContext('0x100')).resolves.toBeUndefined();
126161

162+
(debugSession.customRequest as jest.Mock).mockResolvedValueOnce({ result: '' });
163+
await expect(targetAccess.evaluateSymbolContext('0x100')).resolves.toBeUndefined();
164+
127165
const debugSpy = jest.spyOn(componentViewerLogger, 'debug');
128166
(debugSession.customRequest as jest.Mock).mockRejectedValueOnce(new Error('context fail'));
129167
await expect(targetAccess.evaluateSymbolContext('0x100')).resolves.toBeUndefined();

0 commit comments

Comments
 (0)