UNPKG

asmimproved-dbgmits

Version:

Provides the ability to control GDB and LLDB programmatically via GDB/MI.

235 lines (218 loc) 9.09 kB
// Copyright (c) 2015 Vadim Macagon // MIT License, see LICENSE file for full terms. require('source-map-support').install(); import * as chai from 'chai'; import chaiAsPromised = require('chai-as-promised'); import * as bunyan from 'bunyan'; import * as dbgmits from '../lib/index'; import { beforeEachTestWithLogger, logSuite as log, startDebugSession, runToFuncAndStepOut, SourceLineResolver } from './test_utils'; chai.use(chaiAsPromised); // aliases var expect = chai.expect; import DebugSession = dbgmits.DebugSession; // the directory in which Gruntfile.js resides is also Mocha's working directory, // so any relative paths will be relative to that directory var localTargetExe: string = './build/Debug/exec_tests_target'; log(describe("Debug Session", () => { describe("Program Execution", () => { var debugSession: DebugSession; let locationOfCallToPrintNextInt: string; before(() => { const lineResolver = SourceLineResolver.loadSourceFileSync('./test/exec_tests_target.cpp'); const line = lineResolver.getCommentLineNumber('bp: main::printNextInt()'); locationOfCallToPrintNextInt = `exec_tests_target.cpp:${line}`; }); beforeEachTestWithLogger((logger: bunyan.Logger) => { debugSession = startDebugSession(logger); return debugSession.setExecutableFile(localTargetExe); }); afterEach(() => { return debugSession.end(); }); it("starts the target process", () => { return debugSession.startInferior(); }); // FIXME: This test is skipped on GDB because this MI command is not supported even though // it was documented in the GDB/MI spec. it("aborts the target process @skipOnGDB", () => { var verifyTargetExited = () => { // Promises get executed when they're created, wrapping the promise creation in // a function makes it possible to delay execution return new Promise<void>((resolve, reject) => { debugSession.once(dbgmits.EVENT_TARGET_STOPPED, (stopNotify: dbgmits.ITargetStoppedEvent) => { // This event listener function gets invoked outside of the promise, // which means the promise doesn't trap any exception thrown here, // so we have to trap any exceptions manually and then hand them over // to the promise (if we don't an exception here will kill the test runner // instead of just failing this test). try { expect(stopNotify.reason).to.equal(dbgmits.TargetStopReason.ExitedNormally); resolve(); } catch (err) { reject(err); } } ); }); }; // a breakpoint will be set to get to the desired starting point in the target process var onBreakpointAbortTarget = new Promise<void>((resolve, reject) => { debugSession.once(dbgmits.EVENT_BREAKPOINT_HIT, (breakNotify: dbgmits.IBreakpointHitEvent) => { Promise.all([verifyTargetExited(), debugSession.abortInferior()]) .then(() => { resolve(); }, reject); } ); }); // break at the start of main() return debugSession.addBreakpoint('main') .then(() => { return Promise.all([ onBreakpointAbortTarget, debugSession.startInferior() ]); }); }); it("steps into a source line", () => { // when the step is done check we're in printNextInt() var onStepFinishedCheckFrame = new Promise<void>((resolve, reject) => { debugSession.once(dbgmits.EVENT_STEP_FINISHED, (notification: dbgmits.IStepFinishedEvent) => { debugSession.getStackFrame() .then((info: dbgmits.IStackFrameInfo) => { expect(info.func.indexOf('printNextInt')).to.equal(0); }) .then(resolve, reject); } ); }); // a breakpoint will be set to get to the desired starting point in the target process var onBreakpointStepIntoLine = new Promise<void>((resolve, reject) => { debugSession.once(dbgmits.EVENT_BREAKPOINT_HIT, (notify: dbgmits.IBreakpointHitEvent) => { // step into the printNextInt() call in main() resolve(debugSession.stepIntoLine()); } ); }); // break on the line in main() that calls printNextInt() return debugSession.addBreakpoint(locationOfCallToPrintNextInt) .then(() => { return Promise.all([ onBreakpointStepIntoLine, onStepFinishedCheckFrame, debugSession.startInferior() ]); }); }); it("steps into an instruction", () => { // when the step is done check we're in printNextInt() var onStepFinishedCheckFrame = new Promise<void>((resolve, reject) => { debugSession.once(dbgmits.EVENT_STEP_FINISHED, (notification: dbgmits.IStepFinishedEvent) => { debugSession.getStackFrame() .then((info: dbgmits.IStackFrameInfo) => { expect(info.func.indexOf('printNextInt')).to.equal(0); }) .then(resolve, reject); } ); }); // a breakpoint will be set to get to the desired starting point in the target process var onBreakpointStepIntoInstruction = new Promise<void>((resolve, reject) => { debugSession.once(dbgmits.EVENT_BREAKPOINT_HIT, (notify: dbgmits.IBreakpointHitEvent) => { // step into the printNextInt() call in main() resolve(debugSession.stepIntoInstruction()); } ); }); // break on the line in main() that calls printNextInt() return debugSession.addBreakpoint(locationOfCallToPrintNextInt) .then(() => { return Promise.all([ onBreakpointStepIntoInstruction, onStepFinishedCheckFrame, debugSession.startInferior() ]); }); }); it("steps over a source line", () => { // when the step is done check we're still in main() and haven't stepped into printNextInt() var onStepFinishedCheckFrame = new Promise<void>((resolve, reject) => { debugSession.once(dbgmits.EVENT_STEP_FINISHED, (notification: dbgmits.IStepFinishedEvent) => { debugSession.getStackFrame() .then((info: dbgmits.IStackFrameInfo) => { expect(info).to.have.property('func', 'main'); }) .then(resolve, reject); } ); }); // a breakpoint will be set to get to the desired starting point in the target process var onBreakpointStepOverLine = new Promise<void>((resolve, reject) => { debugSession.once(dbgmits.EVENT_BREAKPOINT_HIT, (breakNotify: dbgmits.IBreakpointHitEvent) => { // step over the printNextInt() call in main() resolve(debugSession.stepOverLine()); } ); }); // break on the line in main() that calls printNextInt() return debugSession.addBreakpoint(locationOfCallToPrintNextInt) .then(() => { return Promise.all([ onBreakpointStepOverLine, onStepFinishedCheckFrame, debugSession.startInferior() ]); }); }); it("steps over an instruction", () => { // when the step is done check we're still in main() and haven't stepped into printNextInt() var onStepFinishedCheckFrame = new Promise<void>((resolve, reject) => { debugSession.once(dbgmits.EVENT_STEP_FINISHED, (notification: dbgmits.IStepFinishedEvent) => { debugSession.getStackFrame() .then((info: dbgmits.IStackFrameInfo) => { expect(info).to.have.property('func', 'main'); }) .then(resolve, reject); } ); }); // a breakpoint will be set to get to the desired starting point in the target process var onBreakpointStepOverInstruction = new Promise<void>((resolve, reject) => { debugSession.once(dbgmits.EVENT_BREAKPOINT_HIT, (breakNotify: dbgmits.IBreakpointHitEvent) => { // step over the printNextInt() call in main() resolve(debugSession.stepOverInstruction()); } ); }); // break on the line in main() that calls printNextInt() return debugSession.addBreakpoint(locationOfCallToPrintNextInt) .then(() => { return Promise.all([ onBreakpointStepOverInstruction, onStepFinishedCheckFrame, debugSession.startInferior() ]); }); }); it("steps out of a function", () => { return runToFuncAndStepOut(debugSession, 'printNextInt', () => { return debugSession.getStackFrame() .then((info: dbgmits.IStackFrameInfo) => { // when the step is done check we're back in main() and not still in printNextInt() expect(info).to.have.property('func', 'main'); }); }); }); }); }));