Skip to content

Commit 47fb8b9

Browse files
add disassembly view updates (#1518)
1 parent 5c102ea commit 47fb8b9

File tree

6 files changed

+369
-104
lines changed

6 files changed

+369
-104
lines changed

src/cdtDebugAdapter/adapter/GDBDebugSession.ts

Lines changed: 40 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@ import { GDBBackend } from "./GDBBackend";
2929
import * as mi from "./mi";
3030
import {
3131
sendDataReadMemoryBytes,
32-
sendDataDisassemble,
3332
sendDataWriteMemoryBytes,
3433
} from "./mi/data";
3534
import { StoppedEvent } from "./stoppedEvent";
3635
import { VarObjType } from "./varManager";
37-
import { createEnvValues, getGdbCwd } from "./util";
36+
import { getGdbCwd } from "./util";
37+
import { calculateMemoryOffset } from "./util/calculateMemoryOffset";
38+
import { getInstructions } from "./util/disassembly";
3839

3940
export interface RequestArguments extends DebugProtocol.LaunchRequestArguments {
4041
gdb?: string;
@@ -1438,112 +1439,51 @@ export class GDBDebugSession extends LoggingDebugSession {
14381439
args: CDTDisassembleArguments
14391440
) {
14401441
try {
1441-
const meanSizeOfInstruction = 4;
1442-
let startOffset = 0;
1443-
let lastStartOffset = -1;
1442+
if (!args.memoryReference) {
1443+
throw new Error('Target memory reference is not specified!');
1444+
}
1445+
const instructionStartOffset = args.instructionOffset ?? 0;
1446+
const instructionEndOffset =
1447+
args.instructionCount + instructionStartOffset;
14441448
const instructions: DebugProtocol.DisassembledInstruction[] = [];
1445-
let oneIterationOnly = false;
1446-
outer_loop: while (
1447-
instructions.length < args.instructionCount &&
1448-
!oneIterationOnly
1449-
) {
1450-
if (startOffset === lastStartOffset) {
1451-
// We have stopped getting new instructions, give up
1452-
break outer_loop;
1453-
}
1454-
lastStartOffset = startOffset;
1455-
1456-
const fetchSize =
1457-
(args.instructionCount - instructions.length) * meanSizeOfInstruction;
1458-
1459-
// args.memoryReference is an arbitrary expression, so let GDB do the
1460-
// math on resolving value rather than doing the addition in the adapter
1461-
try {
1462-
const stepStartAddress = `(${args.memoryReference})+${startOffset}`;
1463-
let stepEndAddress = `(${args.memoryReference})+${startOffset}+${fetchSize}`;
1464-
if (args.endMemoryReference && instructions.length === 0) {
1465-
// On the first call, if we have an end memory address use it instead of
1466-
// the approx size
1467-
stepEndAddress = args.endMemoryReference;
1468-
oneIterationOnly = true;
1469-
}
1470-
const result = await sendDataDisassemble(
1471-
this.gdb,
1472-
stepStartAddress,
1473-
stepEndAddress
1449+
const memoryReference =
1450+
args.offset === undefined
1451+
? args.memoryReference
1452+
: calculateMemoryOffset(args.memoryReference, args.offset);
1453+
1454+
if (instructionStartOffset < 0) {
1455+
// Getting lower memory area
1456+
const list = await getInstructions(
1457+
this.gdb,
1458+
memoryReference,
1459+
instructionStartOffset
14741460
);
1475-
for (const asmInsn of result.asm_insns) {
1476-
const line: number | undefined = asmInsn.line
1477-
? parseInt(asmInsn.line, 10)
1478-
: undefined;
1479-
const source = {
1480-
name: asmInsn.file,
1481-
path: asmInsn.fullname,
1482-
} as DebugProtocol.Source;
1483-
for (const asmLine of asmInsn.line_asm_insn) {
1484-
let funcAndOffset: string | undefined;
1485-
if (asmLine["func-name"] && asmLine.offset) {
1486-
funcAndOffset = `${asmLine["func-name"]}+${asmLine.offset}`;
1487-
} else if (asmLine["func-name"]) {
1488-
funcAndOffset = asmLine["func-name"];
1489-
} else {
1490-
funcAndOffset = undefined;
1491-
}
1492-
const disInsn = {
1493-
address: asmLine.address,
1494-
instructionBytes: asmLine.opcodes,
1495-
instruction: asmLine.inst,
1496-
symbol: funcAndOffset,
1497-
location: source,
1498-
line,
1499-
} as DebugProtocol.DisassembledInstruction;
1500-
instructions.push(disInsn);
1501-
if (instructions.length === args.instructionCount) {
1502-
break outer_loop;
1503-
}
15041461

1505-
const bytes = asmLine.opcodes.replace(/\s/g, "");
1506-
startOffset += bytes.length;
1507-
}
1508-
}
1509-
} catch (err) {
1510-
// Failed to read instruction -- what best to do here?
1511-
// in other words, whose responsibility (adapter or client)
1512-
// to reissue reads in smaller chunks to find good memory
1513-
while (instructions.length < args.instructionCount) {
1514-
const badDisInsn = {
1515-
// TODO this should start at byte after last retrieved address
1516-
address: `0x${startOffset.toString(16)}`,
1517-
instruction: err instanceof Error ? err.message : String(err),
1518-
} as DebugProtocol.DisassembledInstruction;
1519-
instructions.push(badDisInsn);
1520-
startOffset += 2;
1521-
}
1522-
break outer_loop;
1523-
}
1462+
// Add them to instruction list
1463+
instructions.push(...list);
15241464
}
15251465

1526-
if (!args.endMemoryReference) {
1527-
while (instructions.length < args.instructionCount) {
1528-
const badDisInsn = {
1529-
// TODO this should start at byte after last retrieved address
1530-
address: `0x${startOffset.toString(16)}`,
1531-
instruction: "failed to retrieve instruction",
1532-
} as DebugProtocol.DisassembledInstruction;
1533-
instructions.push(badDisInsn);
1534-
startOffset += 2;
1535-
}
1466+
if (instructionEndOffset > 0) {
1467+
// Getting higher memory area
1468+
const list = await getInstructions(
1469+
this.gdb,
1470+
memoryReference,
1471+
instructionEndOffset
1472+
);
1473+
1474+
// Add them to instruction list
1475+
instructions.push(...list);
15361476
}
15371477

1538-
response.body = { instructions };
1478+
response.body = {
1479+
instructions,
1480+
};
15391481
this.sendResponse(response);
1540-
} catch (err) {
1541-
this.sendErrorResponse(
1542-
response,
1543-
1,
1544-
err instanceof Error ? err.message : String(err)
1545-
);
1546-
}
1482+
} catch (err) {
1483+
const message = err instanceof Error ? err.message : String(err);
1484+
this.sendEvent(new OutputEvent(`Error: ${message}`));
1485+
this.sendErrorResponse(response, 1, message);
1486+
}
15471487
}
15481488

15491489
protected async readMemoryRequest(

src/cdtDebugAdapter/adapter/mi/data.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@
1111
import { GDBBackend } from '../GDBBackend';
1212
import { MIResponse, MIRegisterValueInfo } from './base';
1313

14-
interface MIDataReadMemoryBytesResponse {
14+
export interface MIDataReadMemoryBytesResponse {
1515
memory: Array<{
1616
begin: string;
1717
end: string;
1818
offset: string;
1919
contents: string;
2020
}>;
2121
}
22-
interface MIDataDisassembleAsmInsn {
22+
export interface MIDataDisassembleAsmInsn {
2323
address: string;
2424
// func-name in MI
2525
'func-name': string;
@@ -28,13 +28,13 @@ interface MIDataDisassembleAsmInsn {
2828
inst: string;
2929
}
3030

31-
interface MIDataDisassembleSrcAndAsmLine {
31+
export interface MIDataDisassembleSrcAndAsmLine {
3232
line: string;
3333
file: string;
3434
fullname: string;
3535
line_asm_insn: MIDataDisassembleAsmInsn[];
3636
}
37-
interface MIDataDisassembleResponse {
37+
export interface MIDataDisassembleResponse {
3838
asm_insns: MIDataDisassembleSrcAndAsmLine[];
3939
}
4040

src/cdtDebugAdapter/adapter/util.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { platform } from 'os';
1212
import { promisify } from 'util';
1313
import { dirname } from 'path';
1414
import { existsSync } from 'fs';
15+
import { DebugProtocol } from '@vscode/debugprotocol';
16+
import { MIDataDisassembleAsmInsn } from '../adapter/mi/data';
1517

1618
/**
1719
* This method actually launches 'gdb --version' to determine the version of
@@ -43,6 +45,33 @@ export async function getGdbVersion(
4345
return gdbVersion;
4446
}
4547

48+
/**
49+
* Converts the MIDataDisassembleAsmInsn object to DebugProtocol.DisassembledInstruction
50+
*
51+
* @param asmInstruction
52+
* MI instruction object
53+
* @return
54+
* Returns the DebugProtocol.DisassembledInstruction object
55+
*/
56+
export const getDisassembledInstruction = (
57+
asmInstruction: MIDataDisassembleAsmInsn
58+
): DebugProtocol.DisassembledInstruction => {
59+
let symbol: string | undefined;
60+
if (asmInstruction['func-name'] && asmInstruction.offset) {
61+
symbol = `${asmInstruction['func-name']}+${asmInstruction.offset}`;
62+
} else if (asmInstruction['func-name']) {
63+
symbol = asmInstruction['func-name'];
64+
} else {
65+
symbol = undefined;
66+
}
67+
return {
68+
address: asmInstruction.address,
69+
instructionBytes: asmInstruction.opcodes,
70+
instruction: asmInstruction.inst,
71+
...(symbol ? { symbol } : {}),
72+
} as DebugProtocol.DisassembledInstruction;
73+
};
74+
4675
/**
4776
* Find gdb version info from a string object which is supposed to
4877
* contain output text of "gdb --version" command.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*********************************************************************
2+
* Copyright (c) 2024 Renesas Electronics Corporation and others
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*********************************************************************/
10+
11+
import { isHexString } from "./isHexString";
12+
13+
/**
14+
* This method calculates the memory offset arithmetics on string hexadecimal address value
15+
*
16+
* @param address
17+
* Reference address to perform the operation for example '0x0000FF00', 'main', 'main+200'
18+
* @param offset
19+
* Offset (in bytes) to be applied to the reference location before disassembling. Can be negative.
20+
* @return
21+
* Returns the calculated address. Keeping the address length same.
22+
*/
23+
export const calculateMemoryOffset = (
24+
address: string,
25+
offset: string | number | bigint
26+
): string => {
27+
if (isHexString(address)) {
28+
const addressLength = address.length - 2;
29+
const newAddress = BigInt(address) + BigInt(offset);
30+
if (newAddress < 0) {
31+
return `(0x${"0".padStart(addressLength, "0")})${newAddress}`;
32+
}
33+
return `0x${newAddress.toString(16).padStart(addressLength, "0")}`;
34+
} else {
35+
const addrParts = /^([^+-]*)([+-]\d+)?$/g.exec(address);
36+
const addrReference = addrParts?.[1];
37+
const addrOffset = BigInt(addrParts?.[2] ?? 0);
38+
const calcOffset = BigInt(offset) + addrOffset;
39+
return `${addrReference}${calcOffset < 0 ? "-" : "+"}${
40+
calcOffset < 0 ? -calcOffset : calcOffset
41+
}`;
42+
}
43+
};

0 commit comments

Comments
 (0)