Skip to content
Merged
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
140 changes: 40 additions & 100 deletions src/cdtDebugAdapter/adapter/GDBDebugSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ import { GDBBackend } from "./GDBBackend";
import * as mi from "./mi";
import {
sendDataReadMemoryBytes,
sendDataDisassemble,
sendDataWriteMemoryBytes,
} from "./mi/data";
import { StoppedEvent } from "./stoppedEvent";
import { VarObjType } from "./varManager";
import { createEnvValues, getGdbCwd } from "./util";
import { getGdbCwd } from "./util";
import { calculateMemoryOffset } from "./util/calculateMemoryOffset";
import { getInstructions } from "./util/disassembly";

export interface RequestArguments extends DebugProtocol.LaunchRequestArguments {
gdb?: string;
Expand Down Expand Up @@ -1438,112 +1439,51 @@ export class GDBDebugSession extends LoggingDebugSession {
args: CDTDisassembleArguments
) {
try {
const meanSizeOfInstruction = 4;
let startOffset = 0;
let lastStartOffset = -1;
if (!args.memoryReference) {
throw new Error('Target memory reference is not specified!');
}
const instructionStartOffset = args.instructionOffset ?? 0;
const instructionEndOffset =
args.instructionCount + instructionStartOffset;
const instructions: DebugProtocol.DisassembledInstruction[] = [];
let oneIterationOnly = false;
outer_loop: while (
instructions.length < args.instructionCount &&
!oneIterationOnly
) {
if (startOffset === lastStartOffset) {
// We have stopped getting new instructions, give up
break outer_loop;
}
lastStartOffset = startOffset;

const fetchSize =
(args.instructionCount - instructions.length) * meanSizeOfInstruction;

// args.memoryReference is an arbitrary expression, so let GDB do the
// math on resolving value rather than doing the addition in the adapter
try {
const stepStartAddress = `(${args.memoryReference})+${startOffset}`;
let stepEndAddress = `(${args.memoryReference})+${startOffset}+${fetchSize}`;
if (args.endMemoryReference && instructions.length === 0) {
// On the first call, if we have an end memory address use it instead of
// the approx size
stepEndAddress = args.endMemoryReference;
oneIterationOnly = true;
}
const result = await sendDataDisassemble(
this.gdb,
stepStartAddress,
stepEndAddress
const memoryReference =
args.offset === undefined
? args.memoryReference
: calculateMemoryOffset(args.memoryReference, args.offset);

if (instructionStartOffset < 0) {
// Getting lower memory area
const list = await getInstructions(
this.gdb,
memoryReference,
instructionStartOffset
);
for (const asmInsn of result.asm_insns) {
const line: number | undefined = asmInsn.line
? parseInt(asmInsn.line, 10)
: undefined;
const source = {
name: asmInsn.file,
path: asmInsn.fullname,
} as DebugProtocol.Source;
for (const asmLine of asmInsn.line_asm_insn) {
let funcAndOffset: string | undefined;
if (asmLine["func-name"] && asmLine.offset) {
funcAndOffset = `${asmLine["func-name"]}+${asmLine.offset}`;
} else if (asmLine["func-name"]) {
funcAndOffset = asmLine["func-name"];
} else {
funcAndOffset = undefined;
}
const disInsn = {
address: asmLine.address,
instructionBytes: asmLine.opcodes,
instruction: asmLine.inst,
symbol: funcAndOffset,
location: source,
line,
} as DebugProtocol.DisassembledInstruction;
instructions.push(disInsn);
if (instructions.length === args.instructionCount) {
break outer_loop;
}

const bytes = asmLine.opcodes.replace(/\s/g, "");
startOffset += bytes.length;
}
}
} catch (err) {
// Failed to read instruction -- what best to do here?
// in other words, whose responsibility (adapter or client)
// to reissue reads in smaller chunks to find good memory
while (instructions.length < args.instructionCount) {
const badDisInsn = {
// TODO this should start at byte after last retrieved address
address: `0x${startOffset.toString(16)}`,
instruction: err instanceof Error ? err.message : String(err),
} as DebugProtocol.DisassembledInstruction;
instructions.push(badDisInsn);
startOffset += 2;
}
break outer_loop;
}
// Add them to instruction list
instructions.push(...list);
}

if (!args.endMemoryReference) {
while (instructions.length < args.instructionCount) {
const badDisInsn = {
// TODO this should start at byte after last retrieved address
address: `0x${startOffset.toString(16)}`,
instruction: "failed to retrieve instruction",
} as DebugProtocol.DisassembledInstruction;
instructions.push(badDisInsn);
startOffset += 2;
}
if (instructionEndOffset > 0) {
// Getting higher memory area
const list = await getInstructions(
this.gdb,
memoryReference,
instructionEndOffset
);

// Add them to instruction list
instructions.push(...list);
}

response.body = { instructions };
response.body = {
instructions,
};
this.sendResponse(response);
} catch (err) {
this.sendErrorResponse(
response,
1,
err instanceof Error ? err.message : String(err)
);
}
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
this.sendEvent(new OutputEvent(`Error: ${message}`));
this.sendErrorResponse(response, 1, message);
}
}

protected async readMemoryRequest(
Expand Down
8 changes: 4 additions & 4 deletions src/cdtDebugAdapter/adapter/mi/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
import { GDBBackend } from '../GDBBackend';
import { MIResponse, MIRegisterValueInfo } from './base';

interface MIDataReadMemoryBytesResponse {
export interface MIDataReadMemoryBytesResponse {
memory: Array<{
begin: string;
end: string;
offset: string;
contents: string;
}>;
}
interface MIDataDisassembleAsmInsn {
export interface MIDataDisassembleAsmInsn {
address: string;
// func-name in MI
'func-name': string;
Expand All @@ -28,13 +28,13 @@ interface MIDataDisassembleAsmInsn {
inst: string;
}

interface MIDataDisassembleSrcAndAsmLine {
export interface MIDataDisassembleSrcAndAsmLine {
line: string;
file: string;
fullname: string;
line_asm_insn: MIDataDisassembleAsmInsn[];
}
interface MIDataDisassembleResponse {
export interface MIDataDisassembleResponse {
asm_insns: MIDataDisassembleSrcAndAsmLine[];
}

Expand Down
29 changes: 29 additions & 0 deletions src/cdtDebugAdapter/adapter/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { platform } from 'os';
import { promisify } from 'util';
import { dirname } from 'path';
import { existsSync } from 'fs';
import { DebugProtocol } from '@vscode/debugprotocol';
import { MIDataDisassembleAsmInsn } from '../adapter/mi/data';

/**
* This method actually launches 'gdb --version' to determine the version of
Expand Down Expand Up @@ -43,6 +45,33 @@ export async function getGdbVersion(
return gdbVersion;
}

/**
* Converts the MIDataDisassembleAsmInsn object to DebugProtocol.DisassembledInstruction
*
* @param asmInstruction
* MI instruction object
* @return
* Returns the DebugProtocol.DisassembledInstruction object
*/
export const getDisassembledInstruction = (
asmInstruction: MIDataDisassembleAsmInsn
): DebugProtocol.DisassembledInstruction => {
let symbol: string | undefined;
if (asmInstruction['func-name'] && asmInstruction.offset) {
symbol = `${asmInstruction['func-name']}+${asmInstruction.offset}`;
} else if (asmInstruction['func-name']) {
symbol = asmInstruction['func-name'];
} else {
symbol = undefined;
}
return {
address: asmInstruction.address,
instructionBytes: asmInstruction.opcodes,
instruction: asmInstruction.inst,
...(symbol ? { symbol } : {}),
} as DebugProtocol.DisassembledInstruction;
};

/**
* Find gdb version info from a string object which is supposed to
* contain output text of "gdb --version" command.
Expand Down
43 changes: 43 additions & 0 deletions src/cdtDebugAdapter/adapter/util/calculateMemoryOffset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*********************************************************************
* Copyright (c) 2024 Renesas Electronics Corporation and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*********************************************************************/

import { isHexString } from "./isHexString";

/**
* This method calculates the memory offset arithmetics on string hexadecimal address value
*
* @param address
* Reference address to perform the operation for example '0x0000FF00', 'main', 'main+200'
* @param offset
* Offset (in bytes) to be applied to the reference location before disassembling. Can be negative.
* @return
* Returns the calculated address. Keeping the address length same.
*/
export const calculateMemoryOffset = (
address: string,
offset: string | number | bigint
): string => {
if (isHexString(address)) {
const addressLength = address.length - 2;
const newAddress = BigInt(address) + BigInt(offset);
if (newAddress < 0) {
return `(0x${"0".padStart(addressLength, "0")})${newAddress}`;
}
return `0x${newAddress.toString(16).padStart(addressLength, "0")}`;
} else {
const addrParts = /^([^+-]*)([+-]\d+)?$/g.exec(address);
const addrReference = addrParts?.[1];
const addrOffset = BigInt(addrParts?.[2] ?? 0);
const calcOffset = BigInt(offset) + addrOffset;
return `${addrReference}${calcOffset < 0 ? "-" : "+"}${
calcOffset < 0 ? -calcOffset : calcOffset
}`;
}
};
Loading