diff --git a/src/frontend/disassembly_content_provider.ts b/src/frontend/disassembly_content_provider.ts index 51565a88..8b154187 100644 --- a/src/frontend/disassembly_content_provider.ts +++ b/src/frontend/disassembly_content_provider.ts @@ -5,10 +5,21 @@ import { DisassemblyInstruction } from "../common"; export class DisassemblyContentProvider implements vscode.TextDocumentContentProvider { provideTextDocumentContent(uri: vscode.Uri, token: vscode.CancellationToken): Thenable { return new Promise((resolve, reject) => { - let query = this.parseQuery(uri.query); - let funcname = query['function']; + let funcName: string; + let file: string; + let path = uri.path; + let pathParts = path.substring(1, path.length - 6).split('::'); - vscode.debug.activeDebugSession.customRequest('disassemble', { function: funcname }).then((data) => { + if (pathParts.length === 1) { + file = null; + funcName = pathParts[0]; + } + else { + file = pathParts[0]; + funcName = pathParts[1]; + } + + vscode.debug.activeDebugSession.customRequest('disassemble', { function: funcName, file: file }).then((data) => { let instructions: DisassemblyInstruction[] = data.instructions; let output = ''; diff --git a/src/frontend/extension.ts b/src/frontend/extension.ts index d88ffc8c..0fe19cf1 100644 --- a/src/frontend/extension.ts +++ b/src/frontend/extension.ts @@ -20,6 +20,7 @@ import { FifoSWOSource } from "./swo/sources/fifo"; import { FileSWOSource } from "./swo/sources/file"; import { SerialSWOSource } from "./swo/sources/serial"; import { DisassemblyContentProvider } from "./disassembly_content_provider"; +import { SymbolInformation, SymbolScope } from "../symbols"; interface SVDInfo { expression: RegExp; @@ -99,6 +100,7 @@ class CortexDebugExtension { private registerProvider: RegisterTreeProvider; private SVDDirectory: SVDInfo[] = []; + private functionSymbols: SymbolInformation[] = null; constructor(private context: vscode.ExtensionContext) { this.peripheralProvider = new PeripheralTreeProvider(); @@ -138,6 +140,7 @@ class CortexDebugExtension { context.subscriptions.push(vscode.debug.onDidReceiveDebugSessionCustomEvent(this.receivedCustomEvent.bind(this))); context.subscriptions.push(vscode.debug.onDidStartDebugSession(this.debugSessionStarted.bind(this))); context.subscriptions.push(vscode.debug.onDidTerminateDebugSession(this.debugSessionTerminated.bind(this))); + context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(this.activeEditorChanged.bind(this))); context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('jlink-gdb', new DeprecatedDebugConfigurationProvider(context, 'jlink'))); context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('openocd-gdb', new DeprecatedDebugConfigurationProvider(context, 'openocd'))); @@ -151,12 +154,34 @@ class CortexDebugExtension { return entry ? entry.path : null; } + activeEditorChanged(editor: vscode.TextEditor) { + if (editor !== undefined && vscode.debug.activeDebugSession) { + let uri = editor.document.uri; + if (uri.scheme == 'file') { + vscode.debug.activeDebugSession.customRequest('set-active-editor', { path: uri.path }); + } + else { + vscode.debug.activeDebugSession.customRequest('set-active-editor', { path: `${uri.scheme}://${uri.authority}${uri.path}` }); + } + } + } + async showDisassembly() { if (!vscode.debug.activeDebugSession) { vscode.window.showErrorMessage('No debugging session available'); return; } + if (!this.functionSymbols) { + try { + let resp = await vscode.debug.activeDebugSession.customRequest('load-function-symbols'); + this.functionSymbols = resp.functionSymbols; + } + catch (e) { + vscode.window.showErrorMessage('Unable to load symbol table. Disassembly view unavailable.'); + } + } + try { let funcname: string = await vscode.window.showInputBox({ placeHolder: 'main', @@ -164,10 +189,46 @@ class CortexDebugExtension { prompt: 'Function Name to Disassemble' }); - vscode.window.showTextDocument(vscode.Uri.parse(`disassembly:///${funcname}.cdasm?function=${funcname}`)); + let functions = this.functionSymbols.filter(s => s.name == funcname); + + let url: string; + + if (functions.length === 0) { + vscode.window.showErrorMessage(`No function with name ${funcname} found.`); + } + else if (functions.length === 1) { + if (functions[0].scope == SymbolScope.Global) { + url = `disassembly:///${functions[0].name}.cdasm`; + } + else { + url = `disassembly:///${functions[0].file}::${functions[0].name}.cdasm`; + } + } + else { + let selected = await vscode.window.showQuickPick(functions.map(f => { + return { + label: f.name, + name: f.name, + file: f.file, + scope: f.scope, + description: f.scope == SymbolScope.Global ? 'Global Scope' : `Static in ${f.file}` + }; + }), { + ignoreFocusOut: true + }); + + if (selected.scope == SymbolScope.Global) { + url = `disassembly:///${selected.name}.cdasm`; + } + else { + url = `disassembly:///${selected.file}::${selected.name}.cdasm`; + } + } + + vscode.window.showTextDocument(vscode.Uri.parse(url)); } catch (e) { - vscode.window.showErrorMessage('Unable to get function name'); + vscode.window.showErrorMessage('Unable to show disassembly.'); } } @@ -289,6 +350,8 @@ class CortexDebugExtension { this.swo = null; } + this.functionSymbols = null; + session.customRequest('get-arguments').then(args => { let svdfile = args.svdFile; if (!svdfile) { diff --git a/src/gdb.ts b/src/gdb.ts index 89ff13de..d37983ee 100644 --- a/src/gdb.ts +++ b/src/gdb.ts @@ -84,6 +84,10 @@ export class GDBDebugSession extends DebugSession { protected threadID: number = 1; protected commandServer: net.Server; protected forceDisassembly: boolean = false; + protected activeEditorPath: string = null; + + private stopped: boolean = false; + private stoppedReason: string = ''; protected breakpointMap: Map = new Map(); protected fileExistsCache: Map = new Map(); @@ -151,6 +155,7 @@ export class GDBDebugSession extends DebugSession { this.started = false; this.crashed = false; this.debugReady = false; + this.stopped = false; portastic.find({ min: 50000, max: 52000, retrieve: this.serverController.portsNeeded.length }).then(ports => { this.ports = {}; @@ -256,12 +261,25 @@ export class GDBDebugSession extends DebugSession { case 'set-force-disassembly': response.body = { success: true }; this.forceDisassembly = args.force; + if (this.stopped) { + this.activeEditorPath = null; + this.sendEvent(new ContinuedEvent(this.threadID, true)); + this.sendEvent(new StoppedEvent(this.stoppedReason, this.threadID)); + } this.sendResponse(response); break; case 'load-function-symbols': response.body = { functionSymbols: this.symbolTable.getFunctionSymbols() }; this.sendResponse(response); break; + case 'set-active-editor': + this.activeEditorPath = args.path; + response.body = {}; + if (this.stopped) { + this.sendEvent(new StoppedEvent(this.stoppedReason, this.threadID)); + } + this.sendResponse(response); + break; case 'get-arguments': response.body = this.args; this.sendResponse(response); @@ -303,7 +321,7 @@ export class GDBDebugSession extends DebugSession { protected async disassembleRequest(response: DebugProtocol.Response, args: any): Promise { if (args.function) { try { - let funcInfo: SymbolInformation = await this.getDisassemblyForFunction(args.function); + let funcInfo: SymbolInformation = await this.getDisassemblyForFunction(args.function, args.file); response.body = { instructions: funcInfo.instructions, name: funcInfo.name, @@ -526,22 +544,28 @@ export class GDBDebugSession extends DebugSession { } protected handleRunning(info: MINode) { + this.stopped = false; this.sendEvent(new ContinuedEvent(this.threadID, true)); this.sendEvent(new CustomContinuedEvent(this.threadID, true)); } protected handleBreakpoint(info: MINode) { - + this.stopped = true; + this.stoppedReason = 'breakpoint'; this.sendEvent(new StoppedEvent("breakpoint", this.threadID)); this.sendEvent(new CustomStoppedEvent("breakpoint", this.threadID)); } protected handleBreak(info: MINode) { + this.stopped = true; + this.stoppedReason = 'step'; this.sendEvent(new StoppedEvent("step", this.threadID)); this.sendEvent(new CustomStoppedEvent("step", this.threadID)); } protected handlePause(info: MINode) { + this.stopped = true; + this.stoppedReason = 'user request'; this.sendEvent(new StoppedEvent("user request", this.threadID)); this.sendEvent(new CustomStoppedEvent("user request", this.threadID)); } @@ -550,6 +574,8 @@ export class GDBDebugSession extends DebugSession { if (!this.started) this.crashed = true; if (!this.quit) { + this.stopped = true; + this.stoppedReason = 'exception'; this.sendEvent(new StoppedEvent("exception", this.threadID)); this.sendEvent(new CustomStoppedEvent("exception", this.threadID)); } @@ -626,10 +652,8 @@ export class GDBDebugSession extends DebugSession { if (sourcepath.startsWith('disassembly:/')) { let sidx = 13; - if (args.source.path.startsWith('disassembly:///')) { sidx = 15; } - - let eidx = sourcepath.indexOf('?'); - let path = sourcepath.substring(sidx, eidx - 6); // Account for protocol and extension + if (sourcepath.startsWith('disassembly:///')) { sidx = 15; } + let path = sourcepath.substring(sidx, sourcepath.length - 6); // Account for protocol and extension let parts = path.split('::'); let func: string; let file: string; @@ -701,8 +725,18 @@ export class GDBDebugSession extends DebugSession { for (let element of stack) { let file = element.file; let disassemble = this.forceDisassembly || !file; - if (!disassemble) { disassemble = !(await this.checkFileExists(file)); } + if (!disassemble && this.activeEditorPath && this.activeEditorPath.startsWith('disassembly:///')) { + let symbolInfo = this.symbolTable.getFunctionByName(element.function, element.fileName); + let url: string; + if (symbolInfo.scope !== SymbolScope.Global) { + url = `disassembly:///${symbolInfo.file}::${symbolInfo.name}.cdasm`; + } + else { + url = `disassembly:///${symbolInfo.name}.cdasm`; + } + if (url == this.activeEditorPath) { disassemble = true; } + } try { if (disassemble) { @@ -714,13 +748,14 @@ export class GDBDebugSession extends DebugSession { if (line !== -1) { let fname: string; - if (element.fileName) { - fname = `${element.fileName}::${element.function}`; + let url: string; + if (symbolInfo.scope !== SymbolScope.Global) { + url = `disassembly:///${symbolInfo.file}::${symbolInfo.name}.cdasm`; } else { - fname = element.function; + url = `disassembly:///${symbolInfo.name}.cdasm`; } - let url = `disassembly:///${fname}.cdasm?function=${element.function}&file=${element.fileName}`; + ret.push(new StackFrame(element.level, `${element.function}@${element.address}`, new Source(fname, url), line, 0)); } else { @@ -1099,6 +1134,18 @@ export class GDBDebugSession extends DebugSession { if (!assemblyMode) { let frame = await this.miDebugger.getFrame(this.threadID, 0); assemblyMode = !(await this.checkFileExists(frame.file)); + + if (this.activeEditorPath && this.activeEditorPath.startsWith('disassembly:///')) { + let symbolInfo = this.symbolTable.getFunctionByName(frame.function, frame.fileName); + let url: string; + if (symbolInfo.scope !== SymbolScope.Global) { + url = `disassembly:///${symbolInfo.file}::${symbolInfo.name}.cdasm`; + } + else { + url = `disassembly:///${symbolInfo.name}.cdasm`; + } + if (url == this.activeEditorPath) { assemblyMode = true; } + } } let done = await this.miDebugger.step(assemblyMode); @@ -1123,6 +1170,18 @@ export class GDBDebugSession extends DebugSession { if (!assemblyMode) { let frame = await this.miDebugger.getFrame(this.threadID, 0); assemblyMode = !(await this.checkFileExists(frame.file)); + + if (this.activeEditorPath && this.activeEditorPath.startsWith('disassembly:///')) { + let symbolInfo = this.symbolTable.getFunctionByName(frame.function, frame.fileName); + let url: string; + if (symbolInfo.scope !== SymbolScope.Global) { + url = `disassembly:///${symbolInfo.file}::${symbolInfo.name}.cdasm`; + } + else { + url = `disassembly:///${symbolInfo.name}.cdasm`; + } + if (url == this.activeEditorPath) { assemblyMode = true; } + } } let done = await this.miDebugger.next(assemblyMode);