Skip to content

Commit e6a6530

Browse files
Analyzed Read fails with RTX5 lib: Added data logging via "Trace" channel, added list loop detection: (A->B->C->A) (#831)
* added tests for cache and memory-host * Added Data Logging (Channel: Trace), added deeper list loop detection (Cycle A->B->C->A) * changed log to info for timing stats
1 parent 91403b8 commit e6a6530

15 files changed

Lines changed: 3595 additions & 24 deletions

src/views/component-viewer/component-viewer-instance.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ export class ComponentViewerInstance {
161161
this._guiTree.setId(this._fileKey);
162162
this._guiTree.setGuiName('component-viewer-root');
163163

164-
componentViewerLogger.debug(`ComponentViewerInstance readModel stats:\n ${stats.join('\n ')}`);
164+
componentViewerLogger.info(`ComponentViewerInstance readModel stats:\n ${stats.join('\n ')}`);
165165
}
166166

167167
public async update(): Promise<void> {
@@ -173,7 +173,7 @@ export class ComponentViewerInstance {
173173
this._guiTree.clear();
174174
await this.executeStatements(this._guiTree);
175175
stats.push(this.getStats('end'));
176-
componentViewerLogger.debug(`ComponentViewerInstance update stats:\n ${stats.join('\n ')}`);
176+
componentViewerLogger.info(`ComponentViewerInstance update stats:\n ${stats.join('\n ')}`);
177177
}
178178

179179
private async readFileToBuffer(filePath: URI): Promise<Buffer> {

src/views/component-viewer/data-host/memory-host.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,10 @@ export class MemoryHost {
190190
? container.readPartial(byteOff, widthBytes)
191191
: container.readExact(byteOff, widthBytes);
192192
if (!raw) {
193+
componentViewerLogger.trace(`[MemoryHost.readValue] MISS: var="${variableName}" offset=${byteOff} width=${widthBytes}`);
193194
return undefined;
194195
}
196+
componentViewerLogger.trace(`[MemoryHost.readValue] var="${variableName}" offset=${byteOff} width=${widthBytes} data=[${Array.from(raw).map(b => b.toString(16).padStart(2, '0')).join(' ')}]`);
195197

196198
if (this.endianness !== 'little') {
197199
// TOIMPL: add BE support if needed
@@ -216,10 +218,9 @@ export class MemoryHost {
216218
}
217219
}
218220
if (widthBytes <= 4) {
219-
if (ref.valueType?.kind === 'int') {
220-
return leToSignedNumber(raw);
221-
}
222-
return leToNumber(raw);
221+
const value = ref.valueType?.kind === 'int' ? leToSignedNumber(raw) : leToNumber(raw);
222+
componentViewerLogger.trace(`[MemoryHost.readValue] → decoded as ${ref.valueType?.kind === 'int' ? 'int' : 'uint'}: ${value}`);
223+
return value;
223224
}
224225
if (widthBytes === 8) {
225226
let out = 0n;
@@ -228,9 +229,11 @@ export class MemoryHost {
228229
// eslint-disable-next-line security/detect-object-injection
229230
out |= BigInt(raw[i]) << BigInt(8 * i);
230231
}
232+
componentViewerLogger.trace(`[MemoryHost.readValue] → decoded as bigint: ${out}`);
231233
return out;
232234
}
233235
// for larger widths, return a copy of the bytes
236+
componentViewerLogger.trace(`[MemoryHost.readValue] → raw bytes (len=${raw.length})`);
234237
return raw.slice();
235238
}
236239

@@ -321,6 +324,7 @@ export class MemoryHost {
321324
virtualSize?: number, // total logical bytes for this element (>= size)
322325
isConst?: boolean,
323326
): void {
327+
componentViewerLogger.trace(`[MemoryHost.setVariable] var="${name}" offset=${offset} size=${size} virtualSize=${virtualSize ?? size} targetBase=0x${targetBase?.toString(16)} value=${typeof value === 'object' ? `[${value.length}B]` : value}`);
324328
if (!Number.isSafeInteger(offset)) {
325329
componentViewerLogger.error(`setVariable: offset must be a safe integer, got ${offset}`);
326330
return;
@@ -373,6 +377,7 @@ export class MemoryHost {
373377
meta.sizes.push(total);
374378
const normBase = targetBase !== undefined ? this.toAddrNumber(targetBase) : undefined;
375379
meta.bases.push(normBase);
380+
componentViewerLogger.trace(`[MemoryHost.setVariable] → wrote at offset=${appendOff}, element count now=${meta.offsets.length}, targetBase=0x${normBase?.toString(16)}`);
376381

377382
// maintain uniform stride when consistent
378383
if (meta.elementSize === undefined && meta.sizes.length === 1) {
@@ -411,7 +416,9 @@ export class MemoryHost {
411416
public getArrayElementCount(name: string): number {
412417
const m = this.elementMeta.get(name);
413418
const n = m?.offsets.length ?? 0;
414-
return n > 0 ? n : 1;
419+
const count = n > 0 ? n : 1;
420+
componentViewerLogger.trace(`[MemoryHost.getArrayElementCount] var="${name}" → count=${count}`);
421+
return count;
415422
}
416423

417424
// Target base address for element `index` of `name` (number | undefined).
@@ -421,13 +428,16 @@ export class MemoryHost {
421428
componentViewerLogger.error(`getElementTargetBase: unknown symbol "${name}"`);
422429
return undefined;
423430
}
431+
let targetBase: number | undefined;
424432
if (m.bases.length === 1) {
425-
return m.bases[0];
426-
}
427-
if (index < 0 || index >= m.bases.length) {
433+
targetBase = m.bases[0];
434+
} else if (index >= 0 && index < m.bases.length) {
435+
targetBase = m.bases.at(index);
436+
} else {
428437
componentViewerLogger.error(`getElementTargetBase: index ${index} out of range for "${name}"`);
429438
return undefined;
430439
}
431-
return m.bases.at(index);
440+
componentViewerLogger.trace(`[MemoryHost.getElementTargetBase] var="${name}" index=${index} → targetBase=0x${targetBase?.toString(16)}`);
441+
return targetBase;
432442
}
433443
}

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ export class ScvdDebugTarget {
299299
}
300300

301301
public async readMemory(address: number | bigint, size: number): Promise<Uint8Array | undefined> {
302-
componentViewerLogger.debug(`read Memory: addr=${this.formatAddress(address)} size=${size}`);
302+
componentViewerLogger.trace(`[ScvdDebugTarget.readMemory] addr=0x${this.formatAddress(address)} size=${size}`);
303303
const normalized = this.normalizeAddress(address);
304304
const targetReadCache = this.targetReadCache;
305305
if (TARGET_READ_CACHE_ENABLED && normalized !== undefined && targetReadCache) {
@@ -309,16 +309,17 @@ export class ScvdDebugTarget {
309309
const cached = targetReadCache.read(normalized, size);
310310
if (cached) {
311311
perf?.end(hitStart, 'targetReadCacheHitMs', 'targetReadCacheHitCalls');
312-
componentViewerLogger.debug(`read Memory: cache hit for addr=${this.formatAddress(address)} size=${size}`);
312+
componentViewerLogger.trace(`[ScvdDebugTarget.readMemory] CACHE HIT: addr=0x${this.formatAddress(address)} size=${size} data=[${Array.from(cached).map(b => b.toString(16).padStart(2, '0')).join(' ')}]`);
313313
return cached;
314314
}
315+
componentViewerLogger.trace(`[ScvdDebugTarget.readMemory] CACHE MISS: addr=0x${this.formatAddress(address)} size=${size}, fetching from target`);
315316
const missStart = perf?.start() ?? 0;
316317
const missing = targetReadCache.getMissingRanges(normalized, size);
317318
for (const range of missing) {
318319
const data = await this.readMemoryFromTarget(range.start, range.size);
319320
if (!data) {
320321
perf?.end(missStart, 'targetReadCacheMissMs', 'targetReadCacheMissCalls');
321-
componentViewerLogger.debug(`read Memory: read from target failed for addr=${this.formatAddress(address)} size=${size}`);
322+
componentViewerLogger.trace(`[ScvdDebugTarget.readMemory] read from target FAILED for addr=0x${this.formatAddress(address)} size=${size}`);
322323
return undefined;
323324
}
324325
targetReadStats?.recordMissRead();
@@ -332,7 +333,7 @@ export class ScvdDebugTarget {
332333
}
333334

334335
private async readMemoryFromTarget(address: number | bigint, size: number): Promise<Uint8Array | undefined> {
335-
componentViewerLogger.debug(`read Memory From Target: addr=${this.formatAddress(address)} size=${size}`);
336+
componentViewerLogger.trace(`[ScvdDebugTarget.readMemoryFromTarget] TARGET READ: addr=0x${this.formatAddress(address)} size=${size}`);
336337
if (!this.activeSession) {
337338
componentViewerLogger.debug('read Memory From Target: no active session');
338339
return undefined;
@@ -363,13 +364,17 @@ export class ScvdDebugTarget {
363364
}
364365

365366
const result = byteArray.length === size ? byteArray : undefined;
367+
if (result) {
368+
componentViewerLogger.trace(`[ScvdDebugTarget.readMemoryFromTarget] SUCCESS: addr=0x${this.formatAddress(address)} size=${size} data=[${Array.from(result).map(b => b.toString(16).padStart(2, '0')).join(' ')}]`);
369+
} else {
370+
componentViewerLogger.trace(`[ScvdDebugTarget.readMemoryFromTarget] FAILED: addr=0x${this.formatAddress(address)} size=${size} (received ${byteArray.length} bytes)`);
371+
}
366372
if (timingStart !== 0) {
367373
const elapsed = Date.now() - timingStart;
368374
targetReadTimingStats?.recordRead(elapsed, size);
369375
// Aggregate stats are reported after statement execution; avoid per-read logging.
370376
}
371377
perf?.end(perfStartTime, 'targetReadFromTargetMs', 'targetReadFromTargetCalls');
372-
componentViewerLogger.debug(`read Memory From Target: completed for addr=${this.formatAddress(address)} size=${size} success=${result !== undefined}`);
373378
return result;
374379
}
375380

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,12 @@ export class ScvdEvalInterface implements ModelHost, DataAccessHost, IntrinsicPr
6666

6767
public resetPrintfCache(): void {
6868
this._caches.clearPrintf();
69+
componentViewerLogger.trace('[ScvdEvalInterface] Reset printf cache');
6970
}
7071

7172
public resetEvalCaches(): void {
7273
this._caches.clearAll();
74+
componentViewerLogger.trace('[ScvdEvalInterface] Reset all caches');
7375
}
7476

7577
private normalizeScalarType(raw: string | ScalarType | undefined): ScalarType | undefined {
@@ -283,8 +285,13 @@ export class ScvdEvalInterface implements ModelHost, DataAccessHost, IntrinsicPr
283285
/* ---------------- Read/Write via caches ---------------- */
284286
public async readValue(container: RefContainer): Promise<EvalValue> {
285287
const perfStartTime = perf?.start() ?? 0;
288+
const varName = container.anchor?.name ?? '?';
289+
const offset = container.offsetBytes ?? 0;
290+
const width = container.widthBytes ?? 0;
291+
componentViewerLogger.trace(`[ScvdEvalInterface.readValue] container: var="${varName}" offset=${offset} width=${width}`);
286292
try {
287293
const value = await this.memHost.readValue(container);
294+
componentViewerLogger.trace(`[ScvdEvalInterface.readValue] → ${value}`);
288295
return value as EvalValue;
289296
} catch (e) {
290297
componentViewerLogger.error(`ScvdEvalInterface.readValue: exception for container with base=${container.base.getDisplayLabel()}: ${e}`);
@@ -296,6 +303,10 @@ export class ScvdEvalInterface implements ModelHost, DataAccessHost, IntrinsicPr
296303

297304
public async writeValue(container: RefContainer, value: EvalValue): Promise<EvalValue> {
298305
const perfStartTime = perf?.start() ?? 0;
306+
const varName = container.anchor?.name ?? '?';
307+
const offset = container.offsetBytes ?? 0;
308+
const width = container.widthBytes ?? 0;
309+
componentViewerLogger.trace(`[ScvdEvalInterface.writeValue] container: var="${varName}" offset=${offset} width=${width} value=${value}`);
299310
try {
300311
await this.memHost.writeValue(container, value);
301312
return value;
@@ -399,6 +410,7 @@ export class ScvdEvalInterface implements ModelHost, DataAccessHost, IntrinsicPr
399410
const name = base?.name;
400411
if (name !== undefined) {
401412
const count = this.memHost.getArrayElementCount(name); // TOIMPL: this works only for <readlist>, must add for <read>
413+
componentViewerLogger.trace(`[ScvdEvalInterface._count] var="${name}" → ${count}`);
402414
return count;
403415
}
404416
return undefined;
@@ -410,6 +422,7 @@ export class ScvdEvalInterface implements ModelHost, DataAccessHost, IntrinsicPr
410422
const index = container.index ?? 0;
411423
if (name !== undefined) {
412424
const addr = this.memHost.getElementTargetBase(name, index);
425+
componentViewerLogger.trace(`[ScvdEvalInterface._addr] var="${name}" index=${index} → 0x${addr?.toString(16)}`);
413426
return addr;
414427
}
415428
return undefined;

src/views/component-viewer/statement-engine/statement-readList.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,17 @@ export class StatementReadList extends StatementBase {
293293
if (!didBatchRead) {
294294
const loopStart = perf?.start() ?? 0;
295295
let readIdx = 0;
296+
const visitedAddresses = new Set<number | bigint>();
296297
while (nextPtrAddr !== undefined) {
297298
const itemAddress: number | bigint | undefined = typeof nextPtrAddr === 'bigint' ? nextPtrAddr : (nextPtrAddr >>> 0);
298299

300+
// Detect cycles: check if we've visited this address before
301+
if (visitedAddresses.has(itemAddress)) {
302+
componentViewerLogger.error(`${this.scvdItem.getLineNoStr()}: Executing "readlist": ${scvdReadList.name}, symbol: ${symbol?.name}, detected cycle in linked list at address: ${itemAddress.toString(16)}`);
303+
break;
304+
}
305+
visitedAddresses.add(itemAddress);
306+
299307
// Read data from target
300308
const readData = await executionContext.debugTarget.readMemory(itemAddress, readBytes);
301309
if (readData === undefined) {
@@ -345,7 +353,7 @@ export class StatementReadList extends StatementBase {
345353
if (readIdx >= count) {
346354
break;
347355
} else if (readIdx > maxArraySize) {
348-
console.warn(`${this.scvdItem.getLineNoStr()}: Executing "readlist": ${scvdReadList.name}, symbol: ${symbol?.name}, reached maximum array size: ${maxArraySize} for variable: ${itemName}`);
356+
componentViewerLogger.error(`${this.scvdItem.getLineNoStr()}: Executing "readlist": ${scvdReadList.name}, symbol: ${symbol?.name}, reached maximum array size: ${maxArraySize} for variable: ${itemName}`);
349357
break;
350358
}
351359
}
@@ -377,10 +385,10 @@ export class StatementReadList extends StatementBase {
377385

378386
if (this.isInvalidAddress(nextPtrAddr)) { // NULL or invalid pointer, end of list
379387
nextPtrAddr = undefined;
380-
} else if (nextPtrAddr === itemAddress) { // loop detection
381-
console.warn(`${this.scvdItem.getLineNoStr()}: Executing "readlist": ${scvdReadList.name}, symbol: ${symbol?.name}, detected loop in linked list at address: ${itemAddress.toString(16)}`);
382-
break;
383388
}
389+
// Note: Cycle detection is now handled at the start of the loop
390+
// by checking visitedAddresses Set, which catches all cycles (A→B→C→A)
391+
// not just self-loops (A→A)
384392
}
385393
perf?.end(loopStart, 'readListLoopMs', 'readListLoopCalls');
386394
}

src/views/component-viewer/target-read-cache.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
// generated with AI
1717

18+
import { componentViewerLogger } from '../../logger';
1819
import { perf } from './stats-config';
1920
import type { TargetReadStats } from './target-read-stats';
2021

@@ -64,10 +65,13 @@ export class TargetReadCache {
6465
}
6566
const seg = this.findSegmentCovering(this.segments, start, size);
6667
if (!seg) {
68+
componentViewerLogger.trace(`[TargetReadCache.read] MISS: addr=0x${start.toString(16)} size=${size}`);
6769
return undefined;
6870
}
6971
const rel = start - seg.start;
70-
return seg.data.subarray(rel, rel + size);
72+
const data = seg.data.subarray(rel, rel + size);
73+
componentViewerLogger.trace(`[TargetReadCache.read] HIT: addr=0x${start.toString(16)} size=${size} data=[${Array.from(data).map(b => b.toString(16).padStart(2, '0')).join(' ')}]`);
74+
return data;
7175
}
7276

7377
public getMissingRanges(start: number, size: number): Array<{ start: number; size: number }> {
@@ -103,6 +107,7 @@ export class TargetReadCache {
103107
if (data.length === 0) {
104108
return;
105109
}
110+
componentViewerLogger.trace(`[TargetReadCache.write] addr=0x${start.toString(16)} size=${data.length} data=[${Array.from(data).map(b => b.toString(16).padStart(2, '0')).join(' ')}]`);
106111
this.mergeSegments(this.segments, start, data);
107112
}
108113

0 commit comments

Comments
 (0)