asmimproved-dbgmits
Version:
Provides the ability to control GDB and LLDB programmatically via GDB/MI.
430 lines • 15.3 kB
JavaScript
// Copyright (c) 2015 Vadim Macagon
// MIT License, see LICENSE file for full terms.
"use strict";
var types_1 = require('./types');
var extractors_1 = require('./extractors');
/**
* Emitted when a thread group is added by the debugger, it's possible the thread group
* hasn't yet been associated with a running program.
*
* Listener function should have the signature:
* ~~~
* (e: [[IThreadGroupAddedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_THREAD_GROUP_ADDED = 'thdgrpadd';
/**
* Emitted when a thread group is removed by the debugger.
*
* Listener function should have the signature:
* ~~~
* (e: [[IThreadGroupRemovedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_THREAD_GROUP_REMOVED = 'thdgrprem';
/**
* Emitted when a thread group is associated with a running program,
* either because the program was started or the debugger was attached to it.
*
* Listener function should have the signature:
* ~~~
* (e: [[IThreadGroupStartedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_THREAD_GROUP_STARTED = 'thdgrpstart';
/**
* Emitted when a thread group ceases to be associated with a running program,
* either because the program terminated or the debugger was dettached from it.
*
* Listener function should have the signature:
* ~~~
* (e: [[IThreadGroupExitedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_THREAD_GROUP_EXITED = 'thdgrpexit';
/**
* Emitted when a thread is created.
*
* Listener function should have the signature:
* ~~~
* (e: [[IThreadCreatedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_THREAD_CREATED = 'thdcreate';
/**
* Emitted when a thread exits.
*
* Listener function should have the signature:
* ~~~
* (e: [[IThreadExitedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_THREAD_EXITED = 'thdexit';
/**
* Emitted when the debugger changes the current thread selection.
*
* Listener function should have the signature:
* ~~~
* (e: [[IThreadSelectedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_THREAD_SELECTED = 'thdselect';
/**
* Emitted when a new library is loaded by the program being debugged.
*
* Listener function should have the signature:
* ~~~
* (e: [[ILibLoadedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_LIB_LOADED = 'libload';
/**
* Emitted when a library is unloaded by the program being debugged.
*
* Listener function should have the signature:
* ~~~
* (e: [[ILibUnloadedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_LIB_UNLOADED = 'libunload';
/**
* Emitted when some console output from the debugger becomes available,
* usually in response to a CLI command.
*
* Listener function should have the signature:
* ~~~
* (output: string) => void
* ~~~
* @event
*/
exports.EVENT_DBG_CONSOLE_OUTPUT = 'conout';
/**
* Emitted when some console output from the target becomes available.
*
* Listener function should have the signature:
* ~~~
* (output: string) => void
* ~~~
* @event
*/
exports.EVENT_TARGET_OUTPUT = 'targetout';
/**
* Emitted when log output from the debugger becomes available.
*
* Listener function should have the signature:
* ~~~
* (output: string) => void
* ~~~
* @event
*/
exports.EVENT_DBG_LOG_OUTPUT = 'dbgout';
/**
* Emitted when the target starts running.
*
* The `threadId` passed to the listener indicates which specific thread is now running,
* a value of **"all"** indicates all threads are running. According to the GDB/MI spec.
* no interaction with a running thread is possible after this notification is produced until
* it is stopped again.
*
* Listener function should have the signature:
* ~~~
* (threadId: string) => void
* ~~~
* @event
*/
exports.EVENT_TARGET_RUNNING = 'targetrun';
/**
* Emitted when the target stops running.
*
* Listener function should have the signature:
* ~~~
* (e: [[ITargetStoppedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_TARGET_STOPPED = 'targetstop';
/**
* Emitted when the target stops running because a breakpoint was hit.
*
* Listener function should have the signature:
* ~~~
* (e: [[IBreakpointHitEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_BREAKPOINT_HIT = 'brkpthit';
/**
* Emitted when the target stops running because a breakpoint was hit.
*
* Listener function should have the signature:
* ~~~
* (e: [[IBreakpointHitEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_WATCHPOINT_TRIGGERED = 'watchpointtriggered';
/**
* Emitted when the target stops due to a stepping operation finishing.
*
* Listener function should have the signature:
* ~~~
* (e: [[IStepFinishedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_STEP_FINISHED = 'endstep';
/**
* Emitted when the target stops due to a step-out operation finishing.
*
* NOTE: Currently this event will not be emitted by LLDB-MI, it will only be emitted by GDB-MI,
* so for the time being use [[EVENT_STEP_FINISHED]] with LLDB-MI.
*
* Listener function should have the signature:
* ~~~
* (e: [[IStepOutFinishedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_FUNCTION_FINISHED = 'endfunc';
/**
* Emitted when the target stops running because it received a signal.
*
* Listener function should have the signature:
* ~~~
* (e: [[ISignalReceivedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_SIGNAL_RECEIVED = 'signal';
/**
* Emitted when the target stops running due to an exception.
*
* Listener function should have the signature:
* ~~~
* (e: [[IExceptionReceivedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_EXCEPTION_RECEIVED = 'exception';
/**
* Emitted when a breakpoint is modified by the debugger.
*
* Listener function should have the signature:
* ~~~
* (e: [[IBreakpointModifiedEvent]]) => void
* ~~~
* @event
*/
exports.EVENT_BREAKPOINT_MODIFIED = 'breakpoint-modified';
function createEventsForExecNotification(notification, data) {
switch (notification) {
case 'running':
return [{ name: exports.EVENT_TARGET_RUNNING, data: data['thread-id'] }];
case 'stopped':
var stopEvent = {
reason: parseTargetStopReason(data.reason),
threadId: parseInt(data['thread-id'], 10),
stoppedThreads: parseStoppedThreadsList(data['stopped-threads']),
processorCore: data.core
};
var events = [{ name: exports.EVENT_TARGET_STOPPED, data: stopEvent }];
// emit a more specialized event for notifications that contain additional info
switch (stopEvent.reason) {
case types_1.TargetStopReason.WatchpointTriggered:
var watchpointTriggeredEvent = {
reason: stopEvent.reason,
threadId: stopEvent.threadId,
stoppedThreads: stopEvent.stoppedThreads,
processorCore: stopEvent.processorCore,
watchpointId: parseInt(data.wpt.number, 10),
frame: extractFrameInfo(data.frame)
};
events.push({ name: exports.EVENT_WATCHPOINT_TRIGGERED, data: watchpointTriggeredEvent });
break;
case types_1.TargetStopReason.BreakpointHit:
var breakpointHitEvent = {
reason: stopEvent.reason,
threadId: stopEvent.threadId,
stoppedThreads: stopEvent.stoppedThreads,
processorCore: stopEvent.processorCore,
breakpointId: parseInt(data.bkptno, 10),
frame: extractFrameInfo(data.frame)
};
events.push({ name: exports.EVENT_BREAKPOINT_HIT, data: breakpointHitEvent });
break;
case types_1.TargetStopReason.EndSteppingRange:
var stepFinishedEvent = {
reason: stopEvent.reason,
threadId: stopEvent.threadId,
stoppedThreads: stopEvent.stoppedThreads,
processorCore: stopEvent.processorCore,
frame: extractFrameInfo(data.frame)
};
events.push({ name: exports.EVENT_STEP_FINISHED, data: stepFinishedEvent });
break;
case types_1.TargetStopReason.FunctionFinished:
var stepOutEvent = {
reason: stopEvent.reason,
threadId: stopEvent.threadId,
stoppedThreads: stopEvent.stoppedThreads,
processorCore: stopEvent.processorCore,
frame: extractFrameInfo(data.frame),
resultVar: data['gdb-result-var'],
returnValue: data['return-value']
};
events.push({ name: exports.EVENT_FUNCTION_FINISHED, data: stepOutEvent });
break;
case types_1.TargetStopReason.SignalReceived:
var signalEvent = {
reason: stopEvent.reason,
threadId: stopEvent.threadId,
stoppedThreads: stopEvent.stoppedThreads,
processorCore: stopEvent.processorCore,
signalCode: data.signal,
signalName: data['signal-name'],
signalMeaning: data['signal-meaning']
};
events.push({ name: exports.EVENT_SIGNAL_RECEIVED, data: signalEvent });
break;
case types_1.TargetStopReason.ExceptionReceived:
var exceptionEvent = {
reason: stopEvent.reason,
threadId: stopEvent.threadId,
stoppedThreads: stopEvent.stoppedThreads,
processorCore: stopEvent.processorCore,
exception: data.exception
};
events.push({ name: exports.EVENT_EXCEPTION_RECEIVED, data: exceptionEvent });
break;
}
return events;
default:
// TODO: log and keep on going
return [];
}
}
exports.createEventsForExecNotification = createEventsForExecNotification;
function createEventForAsyncNotification(notification, data) {
switch (notification) {
case 'thread-group-added':
return { name: exports.EVENT_THREAD_GROUP_ADDED, data: data };
case 'thread-group-removed':
return { name: exports.EVENT_THREAD_GROUP_REMOVED, data: data };
case 'thread-group-started':
return { name: exports.EVENT_THREAD_GROUP_STARTED, data: data };
case 'thread-group-exited':
var groupExitedEvent = {
id: data.id,
exitCode: data['exit-code']
};
return { name: exports.EVENT_THREAD_GROUP_EXITED, data: groupExitedEvent };
case 'thread-created':
var threadCreatedEvent = {
id: data.id ? parseInt(data.id, 10) : undefined,
groupId: data['group-id']
};
return { name: exports.EVENT_THREAD_CREATED, data: threadCreatedEvent };
case 'thread-exited':
var threadExitedEvent = {
id: data.id ? parseInt(data.id, 10) : undefined,
groupId: data['group-id']
};
return { name: exports.EVENT_THREAD_EXITED, data: threadExitedEvent };
case 'thread-selected':
var threadSelectedEvent = {
id: data.id ? parseInt(data.id, 10) : undefined
};
return { name: exports.EVENT_THREAD_SELECTED, data: threadSelectedEvent };
case 'library-loaded':
var libLoadedEvent = {
id: data.id,
targetName: data['target-name'],
hostName: data['host-name'],
threadGroup: data['thread-group'],
symbolsPath: data['symbols-path'],
loadAddress: data.loaded_addr
};
return { name: exports.EVENT_LIB_LOADED, data: libLoadedEvent };
case 'library-unloaded':
var libUnloadedEvent = {
id: data.id,
targetName: data['target-name'],
hostName: data['host-name'],
threadGroup: data['thread-group'],
symbolsPath: data['symbols-path'],
loadAddress: data.loaded_addr
};
return { name: exports.EVENT_LIB_UNLOADED, data: libUnloadedEvent };
case 'breakpoint-modified':
return {
name: exports.EVENT_BREAKPOINT_MODIFIED,
data: {
breakpoint: extractors_1.extractBreakpointInfo(data)
}
};
default:
// TODO: log and keep on going
return undefined;
}
;
}
exports.createEventForAsyncNotification = createEventForAsyncNotification;
/**
* Creates an object that conforms to the IFrameInfo interface from the output of the
* MI Output parser.
*/
function extractFrameInfo(data) {
return {
func: data.func,
args: data.args,
address: data.addr,
filename: data.file,
fullname: data.fullname,
line: data.line ? parseInt(data.line, 10) : undefined,
};
}
// There are more reasons listed in the GDB/MI spec., the ones here are just the subset that's
// actually used by LLDB MI at this time (11-Apr-2015).
var targetStopReasonMap = new Map()
.set('breakpoint-hit', types_1.TargetStopReason.BreakpointHit)
.set('watchpoint-trigger', types_1.TargetStopReason.WatchpointTriggered)
.set('end-stepping-range', types_1.TargetStopReason.EndSteppingRange)
.set('function-finished', types_1.TargetStopReason.FunctionFinished)
.set('exited-normally', types_1.TargetStopReason.ExitedNormally)
.set('exited-signalled', types_1.TargetStopReason.ExitedSignalled)
.set('exited', types_1.TargetStopReason.Exited)
.set('signal-received', types_1.TargetStopReason.SignalReceived)
.set('exception-received', types_1.TargetStopReason.ExceptionReceived);
function parseTargetStopReason(reasonString) {
var reasonCode = targetStopReasonMap.get(reasonString);
if (reasonCode !== undefined) {
return reasonCode;
}
// TODO: log and keep on running
return types_1.TargetStopReason.Unrecognized;
}
/**
* Parses a list of stopped threads from a GDB/MI 'stopped' async notification.
* @return An array of thread identifiers, an empty array is used to indicate that all threads
* were stopped.
*/
function parseStoppedThreadsList(stoppedThreads) {
if (stoppedThreads === 'all') {
return [];
}
else {
// FIXME: GDB/MI spec. fails to specify what the format of the list is, need to experiment
// to figure out what is actually produced by the debugger.
return [parseInt(stoppedThreads, 10)];
}
}
//# sourceMappingURL=events.js.map