зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1072910 - TraceLogger: Create hooks for the debugger, r=bbouvier
This commit is contained in:
Родитель
0b6b4a1006
Коммит
f60f8b9d68
|
@ -45,7 +45,6 @@ using js::frontend::IsIdentifier;
|
||||||
using mozilla::ArrayLength;
|
using mozilla::ArrayLength;
|
||||||
using mozilla::Maybe;
|
using mozilla::Maybe;
|
||||||
|
|
||||||
|
|
||||||
/*** Forward declarations ************************************************************************/
|
/*** Forward declarations ************************************************************************/
|
||||||
|
|
||||||
extern const Class DebuggerFrame_class;
|
extern const Class DebuggerFrame_class;
|
||||||
|
@ -360,7 +359,13 @@ Debugger::Debugger(JSContext *cx, NativeObject *dbg)
|
||||||
scripts(cx),
|
scripts(cx),
|
||||||
sources(cx),
|
sources(cx),
|
||||||
objects(cx),
|
objects(cx),
|
||||||
environments(cx)
|
environments(cx),
|
||||||
|
#ifdef NIGHTLY_BUILD
|
||||||
|
traceLoggerLastDrainedId(0),
|
||||||
|
traceLoggerLastDrainedIteration(0),
|
||||||
|
#endif
|
||||||
|
traceLoggerScriptedCallsLastDrainedId(0),
|
||||||
|
traceLoggerScriptedCallsLastDrainedIteration(0)
|
||||||
{
|
{
|
||||||
assertSameCompartment(cx, dbg);
|
assertSameCompartment(cx, dbg);
|
||||||
|
|
||||||
|
@ -3771,6 +3776,18 @@ Debugger::makeGlobalObjectReference(JSContext *cx, unsigned argc, Value *vp)
|
||||||
return dbg->wrapDebuggeeValue(cx, args.rval());
|
return dbg->wrapDebuggeeValue(cx, args.rval());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
DefineProperty(JSContext *cx, HandleObject obj, HandleId id, const char *value, size_t n)
|
||||||
|
{
|
||||||
|
JSString *text = JS_NewStringCopyN(cx, value, n);
|
||||||
|
if (!text)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RootedValue str(cx, StringValue(text));
|
||||||
|
return JS_DefinePropertyById(cx, obj, id, str, JSPROP_ENUMERATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef NIGHTLY_BUILD
|
||||||
bool
|
bool
|
||||||
Debugger::setupTraceLogger(JSContext *cx, unsigned argc, Value *vp)
|
Debugger::setupTraceLogger(JSContext *cx, unsigned argc, Value *vp)
|
||||||
{
|
{
|
||||||
|
@ -3839,6 +3856,169 @@ Debugger::setupTraceLogger(JSContext *cx, unsigned argc, Value *vp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Debugger::drainTraceLogger(JSContext *cx, unsigned argc, Value *vp)
|
||||||
|
{
|
||||||
|
THIS_DEBUGGER(cx, argc, vp, "drainTraceLogger", args, dbg);
|
||||||
|
if (!args.requireAtLeast(cx, "Debugger.drainTraceLogger", 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t num;
|
||||||
|
TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
|
||||||
|
bool lostEvents = logger->lostEvents(dbg->traceLoggerLastDrainedIteration,
|
||||||
|
dbg->traceLoggerLastDrainedId);
|
||||||
|
EventEntry *events = logger->getEventsStartingAt(&dbg->traceLoggerLastDrainedIteration,
|
||||||
|
&dbg->traceLoggerLastDrainedId,
|
||||||
|
&num);
|
||||||
|
|
||||||
|
RootedObject array(cx, NewDenseEmptyArray(cx));
|
||||||
|
JSAtom *dataAtom = Atomize(cx, "data", strlen("data"));
|
||||||
|
if (!dataAtom)
|
||||||
|
return false;
|
||||||
|
RootedId dataId(cx, AtomToId(dataAtom));
|
||||||
|
|
||||||
|
/* Add all events to the array. */
|
||||||
|
uint32_t index = 0;
|
||||||
|
for (EventEntry *eventItem = events; eventItem < events + num; eventItem++, index++) {
|
||||||
|
RootedObject item(cx, NewObjectWithGivenProto(cx, &JSObject::class_, nullptr, cx->global()));
|
||||||
|
if (!item)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const char *eventText = logger->eventText(eventItem->textId);
|
||||||
|
if (!DefineProperty(cx, item, dataId, eventText, strlen(eventText)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RootedValue obj(cx, ObjectValue(*item));
|
||||||
|
if (!JS_DefineElement(cx, array, index, obj, JSPROP_ENUMERATE))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add "lostEvents" indicating if there are events that were lost. */
|
||||||
|
RootedValue lost(cx, BooleanValue(lostEvents));
|
||||||
|
if (!JS_DefineProperty(cx, array, "lostEvents", lost, JSPROP_ENUMERATE))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
args.rval().setObject(*array);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool
|
||||||
|
Debugger::setupTraceLoggerScriptCalls(JSContext *cx, unsigned argc, Value *vp)
|
||||||
|
{
|
||||||
|
THIS_DEBUGGER(cx, argc, vp, "setupTraceLoggerScriptCalls", args, dbg);
|
||||||
|
if (!args.requireAtLeast(cx, "Debugger.setupTraceLoggerScriptCalls", 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TraceLogEnableTextId(cx, TraceLogger_Scripts);
|
||||||
|
|
||||||
|
args.rval().setBoolean(true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Debugger::startTraceLogger(JSContext *cx, unsigned argc, Value *vp)
|
||||||
|
{
|
||||||
|
THIS_DEBUGGER(cx, argc, vp, "startTraceLogger", args, dbg);
|
||||||
|
if (!args.requireAtLeast(cx, "Debugger.startTraceLogger", 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
|
||||||
|
TraceLoggerEnable(logger, cx);
|
||||||
|
|
||||||
|
args.rval().setUndefined();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Debugger::endTraceLogger(JSContext *cx, unsigned argc, Value *vp)
|
||||||
|
{
|
||||||
|
THIS_DEBUGGER(cx, argc, vp, "endTraceLogger", args, dbg);
|
||||||
|
if (!args.requireAtLeast(cx, "Debugger.endTraceLogger", 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
|
||||||
|
TraceLoggerDisable(logger);
|
||||||
|
|
||||||
|
args.rval().setUndefined();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Debugger::drainTraceLoggerScriptCalls(JSContext *cx, unsigned argc, Value *vp)
|
||||||
|
{
|
||||||
|
THIS_DEBUGGER(cx, argc, vp, "drainTraceLoggerScriptCalls", args, dbg);
|
||||||
|
if (!args.requireAtLeast(cx, "Debugger.drainTraceLoggerScriptCalls", 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t num;
|
||||||
|
TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
|
||||||
|
bool lostEvents = logger->lostEvents(dbg->traceLoggerScriptedCallsLastDrainedIteration,
|
||||||
|
dbg->traceLoggerScriptedCallsLastDrainedId);
|
||||||
|
EventEntry *events = logger->getEventsStartingAt(
|
||||||
|
&dbg->traceLoggerScriptedCallsLastDrainedIteration,
|
||||||
|
&dbg->traceLoggerScriptedCallsLastDrainedId,
|
||||||
|
&num);
|
||||||
|
|
||||||
|
RootedObject array(cx, NewDenseEmptyArray(cx));
|
||||||
|
RootedId fileNameId(cx, AtomToId(cx->names().fileName));
|
||||||
|
RootedId lineNumberId(cx, AtomToId(cx->names().lineNumber));
|
||||||
|
RootedId columnNumberId(cx, AtomToId(cx->names().columnNumber));
|
||||||
|
JSAtom *logTypeAtom = Atomize(cx, "logType", strlen("logType"));
|
||||||
|
if (!logTypeAtom)
|
||||||
|
return false;
|
||||||
|
RootedId logTypeId(cx, AtomToId(logTypeAtom));
|
||||||
|
|
||||||
|
/* Add all events to the array. */
|
||||||
|
uint32_t index = 0;
|
||||||
|
for (EventEntry *eventItem = events; eventItem < events + num; eventItem++) {
|
||||||
|
RootedObject item(cx, NewObjectWithGivenProto(cx, &JSObject::class_, nullptr, cx->global()));
|
||||||
|
if (!item)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint32_t textId = eventItem->textId;
|
||||||
|
if (textId != TraceLogger_Stop && !logger->textIdIsScriptEvent(textId))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const char *type = (textId == TraceLogger_Stop) ? "Stop" : "Script";
|
||||||
|
if (!DefineProperty(cx, item, logTypeId, type, strlen(type)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (textId != TraceLogger_Stop) {
|
||||||
|
const char *filename, *lineno, *colno;
|
||||||
|
size_t filename_len, lineno_len, colno_len;
|
||||||
|
logger->extractScriptDetails(textId, &filename, &filename_len, &lineno, &lineno_len,
|
||||||
|
&colno, &colno_len);
|
||||||
|
|
||||||
|
if (!DefineProperty(cx, item, fileNameId, filename, filename_len))
|
||||||
|
return false;
|
||||||
|
if (!DefineProperty(cx, item, lineNumberId, lineno, lineno_len))
|
||||||
|
return false;
|
||||||
|
if (!DefineProperty(cx, item, columnNumberId, colno, colno_len))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RootedValue obj(cx, ObjectValue(*item));
|
||||||
|
if (!JS_DefineElement(cx, array, index, obj, JSPROP_ENUMERATE))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add "lostEvents" indicating if there are events that were lost. */
|
||||||
|
RootedValue lost(cx, BooleanValue(lostEvents));
|
||||||
|
if (!JS_DefineProperty(cx, array, "lostEvents", lost, JSPROP_ENUMERATE))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
args.rval().setObject(*array);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const JSPropertySpec Debugger::properties[] = {
|
const JSPropertySpec Debugger::properties[] = {
|
||||||
JS_PSGS("enabled", Debugger::getEnabled, Debugger::setEnabled, 0),
|
JS_PSGS("enabled", Debugger::getEnabled, Debugger::setEnabled, 0),
|
||||||
JS_PSGS("onDebuggerStatement", Debugger::getOnDebuggerStatement,
|
JS_PSGS("onDebuggerStatement", Debugger::getOnDebuggerStatement,
|
||||||
|
@ -3868,7 +4048,14 @@ const JSFunctionSpec Debugger::methods[] = {
|
||||||
JS_FN("findObjects", Debugger::findObjects, 1, 0),
|
JS_FN("findObjects", Debugger::findObjects, 1, 0),
|
||||||
JS_FN("findAllGlobals", Debugger::findAllGlobals, 0, 0),
|
JS_FN("findAllGlobals", Debugger::findAllGlobals, 0, 0),
|
||||||
JS_FN("makeGlobalObjectReference", Debugger::makeGlobalObjectReference, 1, 0),
|
JS_FN("makeGlobalObjectReference", Debugger::makeGlobalObjectReference, 1, 0),
|
||||||
|
JS_FN("setupTraceLoggerScriptCalls", Debugger::setupTraceLoggerScriptCalls, 0, 0),
|
||||||
|
JS_FN("drainTraceLoggerScriptCalls", Debugger::drainTraceLoggerScriptCalls, 0, 0),
|
||||||
|
JS_FN("startTraceLogger", Debugger::startTraceLogger, 0, 0),
|
||||||
|
JS_FN("endTraceLogger", Debugger::endTraceLogger, 0, 0),
|
||||||
|
#ifdef NIGHTLY_BUILD
|
||||||
JS_FN("setupTraceLogger", Debugger::setupTraceLogger, 1, 0),
|
JS_FN("setupTraceLogger", Debugger::setupTraceLogger, 1, 0),
|
||||||
|
JS_FN("drainTraceLogger", Debugger::drainTraceLogger, 0, 0),
|
||||||
|
#endif
|
||||||
JS_FS_END
|
JS_FS_END
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -296,6 +296,17 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||||
/* The map from debuggee Envs to Debugger.Environment instances. */
|
/* The map from debuggee Envs to Debugger.Environment instances. */
|
||||||
ObjectWeakMap environments;
|
ObjectWeakMap environments;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Keep track of tracelogger last drained identifiers to know if there are
|
||||||
|
* lost events.
|
||||||
|
*/
|
||||||
|
#ifdef NIGHTLY_BUILD
|
||||||
|
uint32_t traceLoggerLastDrainedId;
|
||||||
|
uint32_t traceLoggerLastDrainedIteration;
|
||||||
|
#endif
|
||||||
|
uint32_t traceLoggerScriptedCallsLastDrainedId;
|
||||||
|
uint32_t traceLoggerScriptedCallsLastDrainedIteration;
|
||||||
|
|
||||||
class FrameRange;
|
class FrameRange;
|
||||||
class ScriptQuery;
|
class ScriptQuery;
|
||||||
class ObjectQuery;
|
class ObjectQuery;
|
||||||
|
@ -394,7 +405,14 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||||
static bool findObjects(JSContext *cx, unsigned argc, Value *vp);
|
static bool findObjects(JSContext *cx, unsigned argc, Value *vp);
|
||||||
static bool findAllGlobals(JSContext *cx, unsigned argc, Value *vp);
|
static bool findAllGlobals(JSContext *cx, unsigned argc, Value *vp);
|
||||||
static bool makeGlobalObjectReference(JSContext *cx, unsigned argc, Value *vp);
|
static bool makeGlobalObjectReference(JSContext *cx, unsigned argc, Value *vp);
|
||||||
|
static bool setupTraceLoggerScriptCalls(JSContext *cx, unsigned argc, Value *vp);
|
||||||
|
static bool drainTraceLoggerScriptCalls(JSContext *cx, unsigned argc, Value *vp);
|
||||||
|
static bool startTraceLogger(JSContext *cx, unsigned argc, Value *vp);
|
||||||
|
static bool endTraceLogger(JSContext *cx, unsigned argc, Value *vp);
|
||||||
|
#ifdef NIGHTLY_BUILD
|
||||||
static bool setupTraceLogger(JSContext *cx, unsigned argc, Value *vp);
|
static bool setupTraceLogger(JSContext *cx, unsigned argc, Value *vp);
|
||||||
|
static bool drainTraceLogger(JSContext *cx, unsigned argc, Value *vp);
|
||||||
|
#endif
|
||||||
static bool construct(JSContext *cx, unsigned argc, Value *vp);
|
static bool construct(JSContext *cx, unsigned argc, Value *vp);
|
||||||
static const JSPropertySpec properties[];
|
static const JSPropertySpec properties[];
|
||||||
static const JSFunctionSpec methods[];
|
static const JSFunctionSpec methods[];
|
||||||
|
|
|
@ -97,6 +97,7 @@ TraceLoggerThread::TraceLoggerThread()
|
||||||
: enabled(0),
|
: enabled(0),
|
||||||
failed(false),
|
failed(false),
|
||||||
graph(),
|
graph(),
|
||||||
|
iteration_(0),
|
||||||
top(nullptr)
|
top(nullptr)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
@ -252,12 +253,58 @@ TraceLoggerThread::disable()
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
TraceLoggerThread::eventText(uint32_t id) {
|
TraceLoggerThread::eventText(uint32_t id)
|
||||||
|
{
|
||||||
if (id < TraceLogger_Last)
|
if (id < TraceLogger_Last)
|
||||||
return TLTextIdString(static_cast<TraceLoggerTextId>(id));
|
return TLTextIdString(static_cast<TraceLoggerTextId>(id));
|
||||||
return extraTextId[id - TraceLogger_Last];
|
return extraTextId[id - TraceLogger_Last];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
TraceLoggerThread::textIdIsScriptEvent(uint32_t id)
|
||||||
|
{
|
||||||
|
if (id < TraceLogger_Last)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Currently this works by checking if text begins with "script".
|
||||||
|
const char *str = eventText(id);
|
||||||
|
return EqualChars(str, "script", 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TraceLoggerThread::extractScriptDetails(uint32_t textId, const char **filename, size_t *filename_len,
|
||||||
|
const char **lineno, size_t *lineno_len, const char **colno,
|
||||||
|
size_t *colno_len)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(textIdIsScriptEvent(textId));
|
||||||
|
|
||||||
|
const char *script = eventText(textId);
|
||||||
|
|
||||||
|
// Get the start of filename (remove 'script ' at the start).
|
||||||
|
MOZ_ASSERT(EqualChars(script, "script ", 7));
|
||||||
|
*filename = script + 7;
|
||||||
|
|
||||||
|
// Get the start of lineno and colno.
|
||||||
|
*lineno = script;
|
||||||
|
*colno = script;
|
||||||
|
const char *next = script - 1;
|
||||||
|
while ((next = strchr(next + 1, ':'))) {
|
||||||
|
*lineno = *colno;
|
||||||
|
*colno = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(*lineno && *lineno != script);
|
||||||
|
MOZ_ASSERT(*colno && *colno != script);
|
||||||
|
|
||||||
|
// Remove the ':' at the front.
|
||||||
|
*lineno = *lineno + 1;
|
||||||
|
*colno = *colno + 1;
|
||||||
|
|
||||||
|
*filename_len = *lineno - *filename - 1;
|
||||||
|
*lineno_len = *colno - *lineno - 1;
|
||||||
|
*colno_len = strlen(*colno);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
TraceLoggerThread::createTextId(const char *text)
|
TraceLoggerThread::createTextId(const char *text)
|
||||||
{
|
{
|
||||||
|
@ -381,6 +428,7 @@ TraceLoggerThread::logTimestamp(uint32_t id)
|
||||||
if (graph.get())
|
if (graph.get())
|
||||||
graph->log(events);
|
graph->log(events);
|
||||||
|
|
||||||
|
iteration_++;
|
||||||
events.clear();
|
events.clear();
|
||||||
|
|
||||||
// Log the time it took to flush the events as being from the
|
// Log the time it took to flush the events as being from the
|
||||||
|
|
|
@ -85,6 +85,10 @@ class TraceLoggerThread
|
||||||
|
|
||||||
ContinuousSpace<EventEntry> events;
|
ContinuousSpace<EventEntry> events;
|
||||||
|
|
||||||
|
// Every time the events get flushed, this count is increased by one.
|
||||||
|
// This together with events.lastEntryId(), gives an unique position.
|
||||||
|
uint32_t iteration_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AutoTraceLog *top;
|
AutoTraceLog *top;
|
||||||
|
|
||||||
|
@ -99,7 +103,48 @@ class TraceLoggerThread
|
||||||
bool enable(JSContext *cx);
|
bool enable(JSContext *cx);
|
||||||
bool disable();
|
bool disable();
|
||||||
|
|
||||||
|
// Given the previous iteration and lastEntryId, return an array of events
|
||||||
|
// (there could be lost events). At the same time update the iteration and
|
||||||
|
// lastEntry and gives back how many events there are.
|
||||||
|
EventEntry *getEventsStartingAt(uint32_t *lastIteration, uint32_t *lastEntryId, size_t *num) {
|
||||||
|
EventEntry *start;
|
||||||
|
if (iteration_ == *lastIteration) {
|
||||||
|
MOZ_ASSERT(events.lastEntryId() >= *lastEntryId);
|
||||||
|
*num = events.lastEntryId() - *lastEntryId;
|
||||||
|
start = events.data() + *lastEntryId + 1;
|
||||||
|
} else {
|
||||||
|
*num = events.lastEntryId() + 1;
|
||||||
|
start = events.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
*lastIteration = iteration_;
|
||||||
|
*lastEntryId = events.lastEntryId();
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the details filename, lineNumber and columnNumber out of a event
|
||||||
|
// containing script information.
|
||||||
|
void extractScriptDetails(uint32_t textId, const char **filename, size_t *filename_len,
|
||||||
|
const char **lineno, size_t *lineno_len, const char **colno,
|
||||||
|
size_t *colno_len);
|
||||||
|
|
||||||
|
bool lostEvents(uint32_t lastIteration, uint32_t lastEntryId) {
|
||||||
|
// If still logging in the same iteration, there are no lost events.
|
||||||
|
if (lastIteration == iteration_) {
|
||||||
|
MOZ_ASSERT(lastEntryId <= events.lastEntryId());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When proceeded to the next iteration and lastEntryId points to
|
||||||
|
// the maximum capacity there are no logs that are lost.
|
||||||
|
if (lastIteration + 1 == iteration_ && lastEntryId == events.capacity())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const char *eventText(uint32_t id);
|
const char *eventText(uint32_t id);
|
||||||
|
bool textIdIsScriptEvent(uint32_t id);
|
||||||
|
|
||||||
// The createTextId functions map a unique input to a logger ID.
|
// The createTextId functions map a unique input to a logger ID.
|
||||||
// This can be used to give start and stop events. Calls to these functions should be
|
// This can be used to give start and stop events. Calls to these functions should be
|
||||||
|
|
Загрузка…
Ссылка в новой задаче