mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 934163 - Improve performance of tracer actors; r=past
This commit is contained in:
parent
3a05d32ab8
commit
12f3243085
@ -1700,11 +1700,6 @@ function TraceClient(aClient, aActor) {
|
||||
this._activeTraces = new Set();
|
||||
this._waitingPackets = new Map();
|
||||
this._expectedPacket = 0;
|
||||
|
||||
this.onPacket = this.onPacket.bind(this);
|
||||
this._client.addListener(UnsolicitedNotifications.enteredFrame, this.onPacket);
|
||||
this._client.addListener(UnsolicitedNotifications.exitedFrame, this.onPacket);
|
||||
|
||||
this.request = this._client.request;
|
||||
}
|
||||
|
||||
@ -1777,32 +1772,9 @@ TraceClient.prototype = {
|
||||
return aResponse;
|
||||
},
|
||||
telemetry: "STOPTRACE"
|
||||
}),
|
||||
|
||||
/**
|
||||
* Called when the trace actor notifies that a frame has been
|
||||
* entered or exited.
|
||||
*
|
||||
* @param aEvent string
|
||||
* The type of the unsolicited packet (enteredFrame|exitedFrame).
|
||||
*
|
||||
* @param aPacket object
|
||||
* Packet received over the RDP from the trace actor.
|
||||
*/
|
||||
onPacket: function JSTC_onPacket(aEvent, aPacket) {
|
||||
this._waitingPackets.set(aPacket.sequence, aPacket);
|
||||
|
||||
while (this._waitingPackets.has(this._expectedPacket)) {
|
||||
let packet = this._waitingPackets.get(this._expectedPacket);
|
||||
this._waitingPackets.delete(this._expectedPacket);
|
||||
this.notify(packet.type, packet);
|
||||
this._expectedPacket++;
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
eventSource(TraceClient.prototype);
|
||||
|
||||
/**
|
||||
* Grip clients are used to retrieve information about the relevant object.
|
||||
*
|
||||
|
@ -14,6 +14,39 @@ const { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server
|
||||
Cu.import("resource://gre/modules/jsdebugger.jsm");
|
||||
addDebuggerToGlobal(this);
|
||||
|
||||
const { setTimeout } = require("sdk/timers");
|
||||
|
||||
/**
|
||||
* The number of milliseconds we should buffer frame enter/exit packets before
|
||||
* sending.
|
||||
*/
|
||||
const BUFFER_SEND_DELAY = 50;
|
||||
|
||||
/**
|
||||
* The maximum number of arguments we will send for any single function call.
|
||||
*/
|
||||
const MAX_ARGUMENTS = 5;
|
||||
|
||||
/**
|
||||
* The maximum number of an object's properties we will serialize.
|
||||
*/
|
||||
const MAX_PROPERTIES = 5;
|
||||
|
||||
/**
|
||||
* The complete set of trace types supported.
|
||||
*/
|
||||
const TRACE_TYPES = new Set([
|
||||
"time",
|
||||
"return",
|
||||
"throw",
|
||||
"yield",
|
||||
"name",
|
||||
"location",
|
||||
"callsite",
|
||||
"parameterNames",
|
||||
"arguments"
|
||||
]);
|
||||
|
||||
/**
|
||||
* Creates a TraceActor. TraceActor provides a stream of function
|
||||
* call/return packets to a remote client gathering a full trace.
|
||||
@ -24,11 +57,18 @@ function TraceActor(aConn, aParentActor)
|
||||
this._activeTraces = new MapStack();
|
||||
this._totalTraces = 0;
|
||||
this._startTime = 0;
|
||||
|
||||
// Keep track of how many different trace requests have requested what kind of
|
||||
// tracing info. This way we can minimize the amount of data we are collecting
|
||||
// at any given time.
|
||||
this._requestsForTraceType = Object.create(null);
|
||||
for (let type of TraceTypes.types) {
|
||||
for (let type of TRACE_TYPES) {
|
||||
this._requestsForTraceType[type] = 0;
|
||||
}
|
||||
|
||||
this._sequence = 0;
|
||||
this._bufferSendTimer = null;
|
||||
this._buffer = [];
|
||||
|
||||
this.global = aParentActor.window.wrappedJSObject;
|
||||
}
|
||||
@ -41,24 +81,19 @@ TraceActor.prototype = {
|
||||
get tracing() { return this._attached && this._activeTraces.size > 0; },
|
||||
|
||||
/**
|
||||
* Handle a TraceTypes.Events event by calling each handler which has been
|
||||
* requested by an active trace and adding its result to the packet.
|
||||
*
|
||||
* @param aEvent string
|
||||
* The event to dispatch.
|
||||
*
|
||||
* @param aPacket object
|
||||
* The debugger protocol packet.
|
||||
*
|
||||
* @param aArgs object
|
||||
* The arguments object for the handler.
|
||||
* Buffer traces and only send them every BUFFER_SEND_DELAY milliseconds.
|
||||
*/
|
||||
_handleEvent: function(aEvent, aPacket, aArgs) {
|
||||
let handlersForEvent = TraceTypes.handlers[aEvent];
|
||||
for (let traceType in handlersForEvent) {
|
||||
if (this._requestsForTraceType[traceType]) {
|
||||
aPacket[traceType] = handlersForEvent[traceType].call(null, aArgs);
|
||||
}
|
||||
_send: function(aPacket) {
|
||||
this._buffer.push(aPacket);
|
||||
if (this._bufferSendTimer === null) {
|
||||
this._bufferSendTimer = setTimeout(() => {
|
||||
this.conn.send({
|
||||
from: this.actorID,
|
||||
type: "traces",
|
||||
traces: this._buffer.splice(0, this._buffer.length)
|
||||
});
|
||||
this._bufferSendTimer = null;
|
||||
}, BUFFER_SEND_DELAY);
|
||||
}
|
||||
},
|
||||
|
||||
@ -151,7 +186,11 @@ TraceActor.prototype = {
|
||||
|
||||
this._attached = true;
|
||||
|
||||
return { type: "attached", traceTypes: TraceTypes.types };
|
||||
return {
|
||||
type: "attached",
|
||||
traceTypes: Object.keys(this._requestsForTraceType)
|
||||
.filter(k => !!this._requestsForTraceType[k])
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
@ -168,7 +207,7 @@ TraceActor.prototype = {
|
||||
this.dbg = null;
|
||||
|
||||
this._attached = false;
|
||||
this.conn.send({ from: this.actorID, type: "detached" });
|
||||
return { type: "detached" };
|
||||
},
|
||||
|
||||
/**
|
||||
@ -179,7 +218,7 @@ TraceActor.prototype = {
|
||||
*/
|
||||
onStartTrace: function(aRequest) {
|
||||
for (let traceType of aRequest.trace) {
|
||||
if (TraceTypes.types.indexOf(traceType) < 0) {
|
||||
if (!TRACE_TYPES.has(traceType)) {
|
||||
return {
|
||||
error: "badParameterType",
|
||||
message: "No such trace type: " + traceType
|
||||
@ -190,7 +229,7 @@ TraceActor.prototype = {
|
||||
if (this.idle) {
|
||||
this.dbg.enabled = true;
|
||||
this._sequence = 0;
|
||||
this._startTime = +new Date;
|
||||
this._startTime = Date.now();
|
||||
}
|
||||
|
||||
// Start recording all requested trace types.
|
||||
@ -257,19 +296,61 @@ TraceActor.prototype = {
|
||||
onEnterFrame: function(aFrame) {
|
||||
let callee = aFrame.callee;
|
||||
let packet = {
|
||||
from: this.actorID,
|
||||
type: "enteredFrame",
|
||||
sequence: this._sequence++
|
||||
};
|
||||
|
||||
this._handleEvent(TraceTypes.Events.enterFrame, packet, {
|
||||
frame: aFrame,
|
||||
startTime: this._startTime
|
||||
});
|
||||
if (this._requestsForTraceType.name) {
|
||||
packet.name = aFrame.callee
|
||||
? aFrame.callee.displayName || "(anonymous function)"
|
||||
: "(" + aFrame.type + ")";
|
||||
}
|
||||
|
||||
if (this._requestsForTraceType.location && aFrame.script) {
|
||||
// We should return the location of the start of the script, but
|
||||
// Debugger.Script does not provide complete start locations (bug
|
||||
// 901138). Instead, return the current offset (the location of the first
|
||||
// statement in the function).
|
||||
packet.location = {
|
||||
url: aFrame.script.url,
|
||||
line: aFrame.script.getOffsetLine(aFrame.offset),
|
||||
column: getOffsetColumn(aFrame.offset, aFrame.script)
|
||||
};
|
||||
}
|
||||
|
||||
if (this._requestsForTraceType.callsite
|
||||
&& aFrame.older
|
||||
&& aFrame.older.script) {
|
||||
let older = aFrame.older;
|
||||
packet.callsite = {
|
||||
url: older.script.url,
|
||||
line: older.script.getOffsetLine(older.offset),
|
||||
column: getOffsetColumn(older.offset, older.script)
|
||||
};
|
||||
}
|
||||
|
||||
if (this._requestsForTraceType.time) {
|
||||
packet.time = Date.now() - this._startTime;
|
||||
}
|
||||
|
||||
if (this._requestsForTraceType.parameterNames && aFrame.callee) {
|
||||
packet.parameterNames = aFrame.callee.parameterNames;
|
||||
}
|
||||
|
||||
if (this._requestsForTraceType.arguments && aFrame.arguments) {
|
||||
packet.arguments = [];
|
||||
let i = 0;
|
||||
for (let arg of aFrame.arguments) {
|
||||
if (i++ > MAX_ARGUMENTS) {
|
||||
break;
|
||||
}
|
||||
packet.arguments.push(createValueGrip(arg, true));
|
||||
}
|
||||
}
|
||||
|
||||
aFrame.onPop = this.onExitFrame.bind(this);
|
||||
|
||||
this.conn.send(packet);
|
||||
this._send(packet);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -281,7 +362,6 @@ TraceActor.prototype = {
|
||||
*/
|
||||
onExitFrame: function(aCompletion) {
|
||||
let packet = {
|
||||
from: this.actorID,
|
||||
type: "exitedFrame",
|
||||
sequence: this._sequence++,
|
||||
};
|
||||
@ -296,12 +376,25 @@ TraceActor.prototype = {
|
||||
packet.why = "throw";
|
||||
}
|
||||
|
||||
this._handleEvent(TraceTypes.Events.exitFrame, packet, {
|
||||
value: aCompletion,
|
||||
startTime: this._startTime
|
||||
});
|
||||
if (this._requestsForTraceType.time) {
|
||||
packet.time = Date.now() - this._startTime;
|
||||
}
|
||||
|
||||
this.conn.send(packet);
|
||||
if (aCompletion) {
|
||||
if (this._requestsForTraceType.return) {
|
||||
packet.return = createValueGrip(aCompletion.return, true);
|
||||
}
|
||||
|
||||
if (this._requestsForTraceType.throw) {
|
||||
packet.throw = createValueGrip(aCompletion.throw, true);
|
||||
}
|
||||
|
||||
if (this._requestsForTraceType.yield) {
|
||||
packet.yield = createValueGrip(aCompletion.yield, true);
|
||||
}
|
||||
}
|
||||
|
||||
this._send(packet);
|
||||
}
|
||||
};
|
||||
|
||||
@ -419,91 +512,6 @@ MapStack.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* TraceTypes is a collection of handlers which generate optional trace
|
||||
* information. Handlers are associated with an event (from TraceTypes.Event)
|
||||
* and a trace type, and return a value to be embedded in the packet associated
|
||||
* with that event.
|
||||
*/
|
||||
let TraceTypes = {
|
||||
handlers: {},
|
||||
types: [],
|
||||
|
||||
register: function(aType, aEvent, aHandler) {
|
||||
if (!this.handlers[aEvent]) {
|
||||
this.handlers[aEvent] = {};
|
||||
}
|
||||
this.handlers[aEvent][aType] = aHandler;
|
||||
if (this.types.indexOf(aType) < 0) {
|
||||
this.types.push(aType);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TraceTypes.Events = {
|
||||
"enterFrame": "enterFrame",
|
||||
"exitFrame": "exitFrame"
|
||||
};
|
||||
|
||||
TraceTypes.register("name", TraceTypes.Events.enterFrame, function({ frame }) {
|
||||
return frame.callee
|
||||
? frame.callee.displayName || "(anonymous function)"
|
||||
: "(" + frame.type + ")";
|
||||
});
|
||||
|
||||
TraceTypes.register("location", TraceTypes.Events.enterFrame, function({ frame }) {
|
||||
if (!frame.script) {
|
||||
return undefined;
|
||||
}
|
||||
// We should return the location of the start of the script, but
|
||||
// Debugger.Script does not provide complete start locations
|
||||
// (bug 901138). Instead, return the current offset (the location of
|
||||
// the first statement in the function).
|
||||
return {
|
||||
url: frame.script.url,
|
||||
line: frame.script.getOffsetLine(frame.offset),
|
||||
column: getOffsetColumn(frame.offset, frame.script)
|
||||
};
|
||||
});
|
||||
|
||||
TraceTypes.register("callsite", TraceTypes.Events.enterFrame, function({ frame }) {
|
||||
let older = frame.older;
|
||||
if (!older || !older.script) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
url: older.script.url,
|
||||
line: older.script.getOffsetLine(older.offset),
|
||||
column: getOffsetColumn(older.offset, older.script)
|
||||
};
|
||||
});
|
||||
|
||||
TraceTypes.register("time", TraceTypes.Events.enterFrame, timeSinceTraceStarted);
|
||||
TraceTypes.register("time", TraceTypes.Events.exitFrame, timeSinceTraceStarted);
|
||||
|
||||
TraceTypes.register("parameterNames", TraceTypes.Events.enterFrame, function({ frame }) {
|
||||
return frame.callee ? frame.callee.parameterNames : undefined;
|
||||
});
|
||||
|
||||
TraceTypes.register("arguments", TraceTypes.Events.enterFrame, function({ frame }) {
|
||||
if (!frame.arguments) {
|
||||
return undefined;
|
||||
}
|
||||
let args = Array.prototype.slice.call(frame.arguments);
|
||||
return args.map(arg => createValueGrip(arg, true));
|
||||
});
|
||||
|
||||
TraceTypes.register("return", TraceTypes.Events.exitFrame,
|
||||
serializeCompletionValue.bind(null, "return"));
|
||||
|
||||
TraceTypes.register("throw", TraceTypes.Events.exitFrame,
|
||||
serializeCompletionValue.bind(null, "throw"));
|
||||
|
||||
TraceTypes.register("yield", TraceTypes.Events.exitFrame,
|
||||
serializeCompletionValue.bind(null, "yield"));
|
||||
|
||||
|
||||
// TODO bug 863089: use Debugger.Script.prototype.getOffsetColumn when
|
||||
// it is implemented.
|
||||
function getOffsetColumn(aOffset, aScript) {
|
||||
@ -530,27 +538,6 @@ function getOffsetColumn(aOffset, aScript) {
|
||||
return bestOffsetMapping.columnNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns elapsed time since the given start time.
|
||||
*/
|
||||
function timeSinceTraceStarted({ startTime }) {
|
||||
return +new Date - startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a value grip for the given completion value, to be
|
||||
* serialized by JSON.stringify.
|
||||
*
|
||||
* @param aType string
|
||||
* The type of completion value to serialize (return, throw, or yield).
|
||||
*/
|
||||
function serializeCompletionValue(aType, { value }) {
|
||||
if (!Object.hasOwnProperty.call(value, aType)) {
|
||||
return undefined;
|
||||
}
|
||||
return createValueGrip(value[aType], true);
|
||||
}
|
||||
|
||||
|
||||
// Serialization helper functions. Largely copied from script.js and modified
|
||||
// for use in serialization rather than object actor requests.
|
||||
@ -668,13 +655,19 @@ function objectDescriptor(aObject) {
|
||||
desc.safeGetterValues = Object.create(null);
|
||||
return desc;
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
for (let name of names) {
|
||||
ownProperties[name] = propertyDescriptor(name, aObject);
|
||||
if (i++ > MAX_PROPERTIES) {
|
||||
break;
|
||||
}
|
||||
let desc = propertyDescriptor(name, aObject);
|
||||
if (desc) {
|
||||
ownProperties[name] = desc;
|
||||
}
|
||||
}
|
||||
|
||||
desc.prototype = createValueGrip(aObject.proto);
|
||||
desc.ownProperties = ownProperties;
|
||||
desc.safeGetterValues = findSafeGetterValues(ownProperties, aObject);
|
||||
|
||||
return desc;
|
||||
}
|
||||
@ -708,7 +701,8 @@ function propertyDescriptor(aName, aObject) {
|
||||
};
|
||||
}
|
||||
|
||||
if (!desc) {
|
||||
// Skip objects since we only support shallow objects anyways.
|
||||
if (!desc || typeof desc.value == "object" && desc.value !== null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -730,104 +724,3 @@ function propertyDescriptor(aName, aObject) {
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the safe getter values for the given Debugger.Object.
|
||||
*
|
||||
* @param aOwnProperties object
|
||||
* The object that holds the list of known ownProperties for |aObject|.
|
||||
*
|
||||
* @param Debugger.Object object
|
||||
* The object to find safe getter values for.
|
||||
*
|
||||
* @return object
|
||||
* An object that maps property names to safe getter descriptors.
|
||||
*/
|
||||
function findSafeGetterValues(aOwnProperties, aObject) {
|
||||
let safeGetterValues = Object.create(null);
|
||||
let obj = aObject;
|
||||
let level = 0;
|
||||
|
||||
while (obj) {
|
||||
let getters = findSafeGetters(obj);
|
||||
for (let name of getters) {
|
||||
// Avoid overwriting properties from prototypes closer to this.obj. Also
|
||||
// avoid providing safeGetterValues from prototypes if property |name|
|
||||
// is already defined as an own property.
|
||||
if (name in safeGetterValues ||
|
||||
(obj != aObject && name in aOwnProperties)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let desc = null, getter = null;
|
||||
try {
|
||||
desc = obj.getOwnPropertyDescriptor(name);
|
||||
getter = desc.get;
|
||||
} catch (ex) {
|
||||
// The above can throw if the cache becomes stale.
|
||||
}
|
||||
if (!getter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let result = getter.call(aObject);
|
||||
if (result && !("throw" in result)) {
|
||||
let getterValue = undefined;
|
||||
if ("return" in result) {
|
||||
getterValue = result.return;
|
||||
} else if ("yield" in result) {
|
||||
getterValue = result.yield;
|
||||
}
|
||||
// WebIDL attributes specified with the LenientThis extended attribute
|
||||
// return undefined and should be ignored.
|
||||
if (getterValue !== undefined) {
|
||||
safeGetterValues[name] = {
|
||||
getterValue: createValueGrip(getterValue),
|
||||
getterPrototypeLevel: level,
|
||||
enumerable: desc.enumerable,
|
||||
writable: level == 0 ? desc.writable : true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj = obj.proto;
|
||||
level++;
|
||||
}
|
||||
|
||||
return safeGetterValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the safe getters for a given Debugger.Object. Safe getters are native
|
||||
* getters which are safe to execute.
|
||||
*
|
||||
* @param Debugger.Object aObject
|
||||
* The Debugger.Object where you want to find safe getters.
|
||||
*
|
||||
* @return Set
|
||||
* A Set of names of safe getters.
|
||||
*/
|
||||
function findSafeGetters(aObject) {
|
||||
let getters = new Set();
|
||||
for (let name of aObject.getOwnPropertyNames()) {
|
||||
let desc = null;
|
||||
try {
|
||||
desc = aObject.getOwnPropertyDescriptor(name);
|
||||
} catch (e) {
|
||||
// Calling getOwnPropertyDescriptor on wrapped native prototypes is not
|
||||
// allowed (bug 560072).
|
||||
}
|
||||
if (!desc || desc.value !== undefined || !("get" in desc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let fn = desc.get;
|
||||
if (fn && fn.callable && fn.class == "Function" &&
|
||||
fn.script === undefined) {
|
||||
getters.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
return getters;
|
||||
}
|
||||
|
@ -31,43 +31,50 @@ function run_test()
|
||||
|
||||
function test_enter_exit_frame()
|
||||
{
|
||||
let packetsSeen = 0;
|
||||
let packetNames = [];
|
||||
let tracesSeen = 0;
|
||||
let traceNames = [];
|
||||
let traceStopped = defer();
|
||||
|
||||
gTraceClient.addListener("enteredFrame", function(aEvent, aPacket) {
|
||||
packetsSeen++;
|
||||
do_check_eq(aPacket.type, "enteredFrame",
|
||||
'enteredFrame response should have type "enteredFrame"');
|
||||
do_check_eq(typeof aPacket.sequence, "number",
|
||||
'enteredFrame response should have sequence number');
|
||||
do_check_true(!isNaN(aPacket.sequence),
|
||||
'enteredFrame sequence should be a number');
|
||||
do_check_eq(typeof aPacket.name, "string",
|
||||
'enteredFrame response should have function name');
|
||||
packetNames[aPacket.sequence] = aPacket.name;
|
||||
});
|
||||
gClient.addListener("traces", function onTraces(aEvent, { traces }) {
|
||||
for (let t of traces) {
|
||||
tracesSeen++;
|
||||
|
||||
gTraceClient.addListener("exitedFrame", function(aEvent, aPacket) {
|
||||
packetsSeen++;
|
||||
do_check_eq(aPacket.type, "exitedFrame",
|
||||
'exitedFrame response should have type "exitedFrame"');
|
||||
do_check_eq(typeof aPacket.sequence, "number",
|
||||
'exitedFrame response should have sequence number');
|
||||
do_check_true(!isNaN(aPacket.sequence),
|
||||
'exitedFrame sequence should be a number');
|
||||
if (t.type == "enteredFrame") {
|
||||
do_check_eq(t.type, "enteredFrame",
|
||||
'enteredFrame response should have type "enteredFrame"');
|
||||
do_check_eq(typeof t.sequence, "number",
|
||||
'enteredFrame response should have sequence number');
|
||||
do_check_true(!isNaN(t.sequence),
|
||||
'enteredFrame sequence should be a number');
|
||||
do_check_eq(typeof t.name, "string",
|
||||
'enteredFrame response should have function name');
|
||||
traceNames[t.sequence] = t.name;
|
||||
} else {
|
||||
do_check_eq(t.type, "exitedFrame",
|
||||
'exitedFrame response should have type "exitedFrame"');
|
||||
do_check_eq(typeof t.sequence, "number",
|
||||
'exitedFrame response should have sequence number');
|
||||
do_check_true(!isNaN(t.sequence),
|
||||
'exitedFrame sequence should be a number');
|
||||
}
|
||||
|
||||
if (tracesSeen == 10) {
|
||||
gClient.removeListener("traces", onTraces);
|
||||
traceStopped.resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
start_trace()
|
||||
.then(eval_code)
|
||||
.then(() => traceStopped.promise)
|
||||
.then(stop_trace)
|
||||
.then(function() {
|
||||
do_check_eq(packetsSeen, 10,
|
||||
'Should have seen two packets for each of 5 stack frames');
|
||||
do_check_eq(packetNames[2], "baz",
|
||||
do_check_eq(traceNames[2], "baz",
|
||||
'Should have entered "baz" frame in third packet');
|
||||
do_check_eq(packetNames[3], "bar",
|
||||
do_check_eq(traceNames[3], "bar",
|
||||
'Should have entered "bar" frame in fourth packet');
|
||||
do_check_eq(packetNames[4], "foo",
|
||||
do_check_eq(traceNames[4], "foo",
|
||||
'Should have entered "foo" frame in fifth packet');
|
||||
finishClient(gClient);
|
||||
});
|
||||
|
@ -6,7 +6,7 @@
|
||||
* "arguments", and "return" trace types.
|
||||
*/
|
||||
|
||||
let {defer} = devtools.require("sdk/core/promise");
|
||||
let { defer } = devtools.require("sdk/core/promise");
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
@ -28,106 +28,90 @@ function run_test()
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function check_number(value, name)
|
||||
function check_number(value)
|
||||
{
|
||||
do_check_eq(typeof value, "number", name + ' should be a number');
|
||||
do_check_true(!isNaN(value), name + ' should be a number');
|
||||
do_check_eq(typeof value, "number");
|
||||
do_check_true(!isNaN(value));
|
||||
}
|
||||
|
||||
function check_location(actual, expected, name)
|
||||
function check_location(actual, expected)
|
||||
{
|
||||
do_check_eq(typeof actual, "object",
|
||||
name + ' missing expected source location');
|
||||
do_check_eq(typeof actual, "object");
|
||||
|
||||
check_number(actual.line, name + ' line');
|
||||
check_number(actual.column, name + ' column');
|
||||
check_number(actual.line);
|
||||
check_number(actual.column);
|
||||
|
||||
do_check_eq(actual.url, expected.url,
|
||||
name + ' location should have url ' + expected.url);
|
||||
do_check_eq(actual.line, expected.line,
|
||||
name + ' location should have source line of ' + expected.line);
|
||||
do_check_eq(actual.column, expected.column,
|
||||
name + ' location should have source column of ' + expected.line);
|
||||
do_check_eq(actual.url, expected.url);
|
||||
do_check_eq(actual.line, expected.line);
|
||||
do_check_eq(actual.column, expected.column);
|
||||
|
||||
}
|
||||
|
||||
function test_enter_exit_frame()
|
||||
{
|
||||
let packets = [];
|
||||
let traces = [];
|
||||
let traceStopped = defer();
|
||||
|
||||
gTraceClient.addListener("enteredFrame", function(aEvent, aPacket) {
|
||||
do_check_eq(aPacket.type, "enteredFrame",
|
||||
'enteredFrame response should have type "enteredFrame"');
|
||||
do_check_eq(typeof aPacket.name, "string",
|
||||
'enteredFrame response should have function name');
|
||||
do_check_eq(typeof aPacket.location, "object",
|
||||
'enteredFrame response should have source location');
|
||||
gClient.addListener("traces", function(aEvent, aPacket) {
|
||||
for (let t of aPacket.traces) {
|
||||
if (t.type == "enteredFrame") {
|
||||
do_check_eq(typeof t.name, "string");
|
||||
do_check_eq(typeof t.location, "object");
|
||||
|
||||
check_number(aPacket.sequence, 'enteredFrame sequence');
|
||||
check_number(aPacket.time, 'enteredFrame time');
|
||||
check_number(aPacket.location.line, 'enteredFrame source line');
|
||||
check_number(aPacket.location.column, 'enteredFrame source column');
|
||||
if (aPacket.callsite) {
|
||||
check_number(aPacket.callsite.line, 'enteredFrame callsite line');
|
||||
check_number(aPacket.callsite.column, 'enteredFrame callsite column');
|
||||
check_number(t.sequence);
|
||||
check_number(t.time);
|
||||
check_number(t.location.line);
|
||||
check_number(t.location.column);
|
||||
if (t.callsite) {
|
||||
check_number(t.callsite.line);
|
||||
check_number(t.callsite.column);
|
||||
}
|
||||
} else {
|
||||
check_number(t.sequence);
|
||||
check_number(t.time);
|
||||
}
|
||||
|
||||
traces[t.sequence] = t;
|
||||
if (traces.length === 4) {
|
||||
traceStopped.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
packets[aPacket.sequence] = aPacket;
|
||||
});
|
||||
|
||||
gTraceClient.addListener("exitedFrame", function(aEvent, aPacket) {
|
||||
do_check_eq(aPacket.type, "exitedFrame",
|
||||
'exitedFrame response should have type "exitedFrame"');
|
||||
|
||||
check_number(aPacket.sequence, 'exitedFrame sequence');
|
||||
check_number(aPacket.time, 'exitedFrame time');
|
||||
|
||||
packets[aPacket.sequence] = aPacket;
|
||||
});
|
||||
|
||||
start_trace()
|
||||
.then(eval_code)
|
||||
.then(() => traceStopped.promise)
|
||||
.then(stop_trace)
|
||||
.then(function() {
|
||||
let url = getFileUrl("tracerlocations.js");
|
||||
|
||||
check_location(packets[0].location, { url: url, line: 1, column: 0 },
|
||||
'global entry packet');
|
||||
check_location(traces[0].location, { url: url, line: 1, column: 0 });
|
||||
|
||||
do_check_eq(packets[1].name, "foo",
|
||||
'Second packet in sequence should be entry to "foo" frame');
|
||||
do_check_eq(traces[1].name, "foo");
|
||||
|
||||
// foo's definition is at tracerlocations.js:3:0, but
|
||||
// Debugger.Script does not provide complete definition
|
||||
// locations (bug 901138). tracerlocations.js:4:2 is the first
|
||||
// statement in the function (used as an approximation).
|
||||
check_location(packets[1].location, { url: url, line: 4, column: 2 },
|
||||
'foo source');
|
||||
check_location(packets[1].callsite, { url: url, line: 8, column: 0 },
|
||||
'foo callsite');
|
||||
check_location(traces[1].location, { url: url, line: 4, column: 2 });
|
||||
check_location(traces[1].callsite, { url: url, line: 8, column: 0 });
|
||||
|
||||
do_check_eq(typeof packets[1].parameterNames, "object",
|
||||
'foo entry packet should have parameterNames');
|
||||
do_check_eq(packets[1].parameterNames.length, 1,
|
||||
'foo should have only one formal parameter');
|
||||
do_check_eq(packets[1].parameterNames[0], "x",
|
||||
'foo should have formal parameter "x"');
|
||||
do_check_eq(typeof traces[1].parameterNames, "object");
|
||||
do_check_eq(traces[1].parameterNames.length, 1);
|
||||
do_check_eq(traces[1].parameterNames[0], "x");
|
||||
|
||||
do_check_eq(typeof packets[1].arguments, "object",
|
||||
'foo entry packet should have arguments');
|
||||
do_check_true(Array.isArray(packets[1].arguments),
|
||||
'foo entry packet arguments should be an array');
|
||||
do_check_eq(packets[1].arguments.length, 1,
|
||||
'foo should have only one actual parameter');
|
||||
do_check_eq(packets[1].arguments[0], 42,
|
||||
'foo should have actual parameter 42');
|
||||
do_check_eq(typeof traces[1].arguments, "object");
|
||||
do_check_true(Array.isArray(traces[1].arguments));
|
||||
do_check_eq(traces[1].arguments.length, 1);
|
||||
do_check_eq(traces[1].arguments[0], 42);
|
||||
|
||||
do_check_eq(typeof packets[2].return, "string",
|
||||
'Fourth packet in sequence should be exit from "foo" frame');
|
||||
do_check_eq(packets[2].return, "bar",
|
||||
'foo should return "bar"');
|
||||
do_check_eq(typeof traces[2].return, "string");
|
||||
do_check_eq(traces[2].return, "bar");
|
||||
|
||||
finishClient(gClient);
|
||||
}, error => {
|
||||
DevToolsUtils.reportException("test_trace_actor-05.js", error);
|
||||
do_check_true(false);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* and exitedFrame packets.
|
||||
*/
|
||||
|
||||
let {defer} = devtools.require("sdk/core/promise");
|
||||
let { defer } = devtools.require("sdk/core/promise");
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
@ -30,11 +30,20 @@ function run_test()
|
||||
|
||||
function test_enter_exit_frame()
|
||||
{
|
||||
gTraceClient.addListener("enteredFrame", check_packet);
|
||||
gTraceClient.addListener("exitedFrame", check_packet);
|
||||
const traceStopped = defer();
|
||||
|
||||
gClient.addListener("traces", (aEvent, { traces }) => {
|
||||
for (let t of traces) {
|
||||
check_trace(t);
|
||||
if (t.sequence === 27) {
|
||||
traceStopped.resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
start_trace()
|
||||
.then(eval_code)
|
||||
.then(() => traceStopped.promise)
|
||||
.then(stop_trace)
|
||||
.then(function() {
|
||||
finishClient(gClient);
|
||||
@ -58,17 +67,23 @@ function eval_code()
|
||||
let circular = {};
|
||||
circular.self = circular;
|
||||
|
||||
// Make sure there is only 5 properties per object because that is the value
|
||||
// of MAX_PROPERTIES in the server.
|
||||
let obj = {
|
||||
num: 0,
|
||||
str: "foo",
|
||||
bool: false,
|
||||
undef: undefined,
|
||||
nil: null,
|
||||
nil: null
|
||||
};
|
||||
let obj2 = {
|
||||
inf: Infinity,
|
||||
ninf: -Infinity,
|
||||
nan: NaN,
|
||||
nzero: -0,
|
||||
obj: circular,
|
||||
obj: circular
|
||||
};
|
||||
let obj3 = {
|
||||
arr: [1,2,3,4,5]
|
||||
};
|
||||
|
||||
@ -83,6 +98,8 @@ function eval_code()
|
||||
identity(NaN);
|
||||
identity(-0);
|
||||
identity(obj);
|
||||
identity(obj2);
|
||||
identity(obj3);
|
||||
} + ")()");
|
||||
}
|
||||
|
||||
@ -93,17 +110,15 @@ function stop_trace()
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function check_packet(aEvent, aPacket)
|
||||
function check_trace(aTrace)
|
||||
{
|
||||
let value = (aPacket.type === "enteredFrame" && aPacket.arguments)
|
||||
? aPacket.arguments[0]
|
||||
: aPacket.return;
|
||||
switch(aPacket.sequence) {
|
||||
let value = (aTrace.type === "enteredFrame" && aTrace.arguments)
|
||||
? aTrace.arguments[0]
|
||||
: aTrace.return;
|
||||
switch(aTrace.sequence) {
|
||||
case 2:
|
||||
do_check_eq(typeof aPacket.arguments, "object",
|
||||
"zero-argument function call should send arguments list");
|
||||
do_check_eq(aPacket.arguments.length, 0,
|
||||
"zero-argument function call should send zero-length arguments list");
|
||||
do_check_eq(typeof aTrace.arguments, "object");
|
||||
do_check_eq(aTrace.arguments.length, 0);
|
||||
break;
|
||||
case 3:
|
||||
check_value(value, "object", "undefined");
|
||||
@ -146,8 +161,15 @@ function check_packet(aEvent, aPacket)
|
||||
break;
|
||||
case 22:
|
||||
case 23:
|
||||
check_object(aPacket.type, value);
|
||||
check_obj(aTrace.type, value);
|
||||
break;
|
||||
case 24:
|
||||
case 25:
|
||||
check_obj2(aTrace.type, value);
|
||||
break;
|
||||
case 26:
|
||||
case 27:
|
||||
check_obj3(aTrace.type, value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,67 +179,50 @@ function check_value(aActual, aExpectedType, aExpectedValue)
|
||||
do_check_eq(aExpectedType === "object" ? aActual.type : aActual, aExpectedValue);
|
||||
}
|
||||
|
||||
function check_object(aType, aObj) {
|
||||
do_check_eq(typeof aObj, "object",
|
||||
'serialized object should be present in packet');
|
||||
do_check_eq(typeof aObj.prototype, "object",
|
||||
'serialized object should have prototype');
|
||||
do_check_eq(typeof aObj.ownProperties, "object",
|
||||
'serialized object should have ownProperties list');
|
||||
do_check_eq(typeof aObj.safeGetterValues, "object",
|
||||
'serialized object should have safeGetterValues');
|
||||
function check_obj(aType, aObj) {
|
||||
do_check_eq(typeof aObj, "object");
|
||||
do_check_eq(typeof aObj.ownProperties, "object");
|
||||
|
||||
do_check_eq(typeof aObj.ownProperties.num, "object",
|
||||
'serialized object should have property "num"');
|
||||
do_check_eq(typeof aObj.ownProperties.str, "object",
|
||||
'serialized object should have property "str"');
|
||||
do_check_eq(typeof aObj.ownProperties.bool, "object",
|
||||
'serialized object should have property "bool"');
|
||||
do_check_eq(typeof aObj.ownProperties.undef, "object",
|
||||
'serialized object should have property "undef"');
|
||||
do_check_eq(typeof aObj.ownProperties.undef.value, "object",
|
||||
'serialized object property "undef" should be a grip');
|
||||
do_check_eq(typeof aObj.ownProperties.nil, "object",
|
||||
'serialized object should have property "nil"');
|
||||
do_check_eq(typeof aObj.ownProperties.nil.value, "object",
|
||||
'serialized object property "nil" should be a grip');
|
||||
do_check_eq(typeof aObj.ownProperties.obj, "object",
|
||||
'serialized object should have property "aObj"');
|
||||
do_check_eq(typeof aObj.ownProperties.obj.value, "object",
|
||||
'serialized object property "aObj" should be a grip');
|
||||
do_check_eq(typeof aObj.ownProperties.arr, "object",
|
||||
'serialized object should have property "arr"');
|
||||
do_check_eq(typeof aObj.ownProperties.arr.value, "object",
|
||||
'serialized object property "arr" should be a grip');
|
||||
do_check_eq(typeof aObj.ownProperties.inf, "object",
|
||||
'serialized object should have property "inf"');
|
||||
do_check_eq(typeof aObj.ownProperties.inf.value, "object",
|
||||
'serialized object property "inf" should be a grip');
|
||||
do_check_eq(typeof aObj.ownProperties.ninf, "object",
|
||||
'serialized object should have property "ninf"');
|
||||
do_check_eq(typeof aObj.ownProperties.ninf.value, "object",
|
||||
'serialized object property "ninf" should be a grip');
|
||||
do_check_eq(typeof aObj.ownProperties.nan, "object",
|
||||
'serialized object should have property "nan"');
|
||||
do_check_eq(typeof aObj.ownProperties.nan.value, "object",
|
||||
'serialized object property "nan" should be a grip');
|
||||
do_check_eq(typeof aObj.ownProperties.nzero, "object",
|
||||
'serialized object should have property "nzero"');
|
||||
do_check_eq(typeof aObj.ownProperties.nzero.value, "object",
|
||||
'serialized object property "nzero" should be a grip');
|
||||
|
||||
do_check_eq(aObj.prototype.type, "object");
|
||||
do_check_eq(typeof aObj.ownProperties.num, "object");
|
||||
do_check_eq(aObj.ownProperties.num.value, 0);
|
||||
|
||||
do_check_eq(typeof aObj.ownProperties.str, "object");
|
||||
do_check_eq(aObj.ownProperties.str.value, "foo");
|
||||
|
||||
do_check_eq(typeof aObj.ownProperties.bool, "object");
|
||||
do_check_eq(aObj.ownProperties.bool.value, false);
|
||||
|
||||
do_check_eq(typeof aObj.ownProperties.undef, "object");
|
||||
do_check_eq(typeof aObj.ownProperties.undef.value, "object");
|
||||
do_check_eq(aObj.ownProperties.undef.value.type, "undefined");
|
||||
|
||||
do_check_eq(typeof aObj.ownProperties.nil, "object");
|
||||
do_check_eq(typeof aObj.ownProperties.nil.value, "object");
|
||||
do_check_eq(aObj.ownProperties.nil.value.type, "null");
|
||||
do_check_eq(aObj.ownProperties.obj.value.type, "object");
|
||||
do_check_eq(aObj.ownProperties.obj.value.class, "Object");
|
||||
do_check_eq(aObj.ownProperties.arr.value.type, "object");
|
||||
do_check_eq(aObj.ownProperties.arr.value.class, "Array");
|
||||
do_check_eq(aObj.ownProperties.inf.value.type, "Infinity");
|
||||
do_check_eq(aObj.ownProperties.ninf.value.type, "-Infinity");
|
||||
do_check_eq(aObj.ownProperties.nan.value.type, "NaN");
|
||||
do_check_eq(aObj.ownProperties.nzero.value.type, "-0");
|
||||
}
|
||||
|
||||
function check_obj2(aType, aObj) {
|
||||
do_check_eq(typeof aObj.ownProperties.inf, "object");
|
||||
do_check_eq(typeof aObj.ownProperties.inf.value, "object");
|
||||
do_check_eq(aObj.ownProperties.inf.value.type, "Infinity");
|
||||
|
||||
do_check_eq(typeof aObj.ownProperties.ninf, "object");
|
||||
do_check_eq(typeof aObj.ownProperties.ninf.value, "object");
|
||||
do_check_eq(aObj.ownProperties.ninf.value.type, "-Infinity");
|
||||
|
||||
do_check_eq(typeof aObj.ownProperties.nan, "object");
|
||||
do_check_eq(typeof aObj.ownProperties.nan.value, "object");
|
||||
do_check_eq(aObj.ownProperties.nan.value.type, "NaN");
|
||||
|
||||
do_check_eq(typeof aObj.ownProperties.nzero, "object");
|
||||
do_check_eq(typeof aObj.ownProperties.nzero.value, "object");
|
||||
do_check_eq(aObj.ownProperties.nzero.value.type, "-0");
|
||||
|
||||
// Sub-objects aren't added.
|
||||
do_check_eq(typeof aObj.ownProperties.obj, "undefined");
|
||||
}
|
||||
|
||||
function check_obj3(aType, aObj) {
|
||||
// Sub-objects aren't added.
|
||||
do_check_eq(typeof aObj.ownProperties.arr, "undefined");
|
||||
}
|
||||
|
@ -29,7 +29,13 @@ function run_test()
|
||||
|
||||
function test_exit_frame_whys()
|
||||
{
|
||||
gTraceClient.addListener("exitedFrame", check_packet);
|
||||
gClient.addListener("traces", (aEvent, { traces }) => {
|
||||
for (let t of traces) {
|
||||
if (t.type == "exitedFrame") {
|
||||
check_trace(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
start_trace()
|
||||
.then(eval_code)
|
||||
@ -83,7 +89,7 @@ function stop_trace()
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function check_packet(aEvent, { sequence, why })
|
||||
function check_trace(aEvent, { sequence, why })
|
||||
{
|
||||
switch(sequence) {
|
||||
case 3:
|
||||
|
@ -1,101 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that TraceClient emits enteredFrame and exitedFrame events in
|
||||
* order when receiving packets out of order.
|
||||
*/
|
||||
|
||||
let {defer, resolve} = devtools.require("sdk/core/promise");
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gTraceClient;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
initTestTracerServer();
|
||||
gDebuggee = addTestGlobal("test-tracer-actor");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect(function() {
|
||||
attachTestTab(gClient, "test-tracer-actor", function(aResponse, aTabClient) {
|
||||
gClient.attachTracer(aResponse.traceActor, function(aResponse, aTraceClient) {
|
||||
gTraceClient = aTraceClient;
|
||||
test_packet_order();
|
||||
});
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_packet_order()
|
||||
{
|
||||
let sequence = 0;
|
||||
|
||||
function check_packet(aEvent, aPacket) {
|
||||
do_check_eq(aPacket.sequence, sequence,
|
||||
'packet should have sequence number ' + sequence);
|
||||
sequence++;
|
||||
}
|
||||
|
||||
gTraceClient.addListener("enteredFrame", check_packet);
|
||||
gTraceClient.addListener("exitedFrame", check_packet);
|
||||
|
||||
start_trace()
|
||||
.then(mock_packets)
|
||||
.then(start_trace)
|
||||
.then(mock_packets.bind(null, 14))
|
||||
.then(stop_trace)
|
||||
.then(stop_trace)
|
||||
.then(function() {
|
||||
// All traces were stopped, so the sequence number resets
|
||||
sequence = 0;
|
||||
return resolve();
|
||||
})
|
||||
.then(start_trace)
|
||||
.then(mock_packets)
|
||||
.then(stop_trace)
|
||||
.then(function() {
|
||||
finishClient(gClient);
|
||||
});
|
||||
}
|
||||
|
||||
function start_trace()
|
||||
{
|
||||
let deferred = defer();
|
||||
gTraceClient.startTrace([], null, function() { deferred.resolve(); });
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function stop_trace()
|
||||
{
|
||||
let deferred = defer();
|
||||
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function mock_packets(s = 0)
|
||||
{
|
||||
gTraceClient.onPacket("", { type: "exitedFrame", sequence: s + 5 });
|
||||
gTraceClient.onPacket("", { type: "enteredFrame", sequence: s + 3 });
|
||||
gTraceClient.onPacket("", { type: "exitedFrame", sequence: s + 2 });
|
||||
gTraceClient.onPacket("", { type: "enteredFrame", sequence: s + 4 });
|
||||
gTraceClient.onPacket("", { type: "enteredFrame", sequence: s + 1 });
|
||||
|
||||
gTraceClient.onPacket("", { type: "enteredFrame", sequence: s + 7 });
|
||||
gTraceClient.onPacket("", { type: "enteredFrame", sequence: s + 8 });
|
||||
|
||||
// Triggers 0-5
|
||||
gTraceClient.onPacket("", { type: "enteredFrame", sequence: s + 0 });
|
||||
|
||||
gTraceClient.onPacket("", { type: "exitedFrame", sequence: s + 9 });
|
||||
gTraceClient.onPacket("", { type: "exitedFrame", sequence: s + 10 });
|
||||
|
||||
// Triggers 6-10
|
||||
gTraceClient.onPacket("", { type: "exitedFrame", sequence: s + 6 });
|
||||
|
||||
// Each following packet is expected; event is fired immediately
|
||||
gTraceClient.onPacket("", { type: "enteredFrame", sequence: s + 11 });
|
||||
gTraceClient.onPacket("", { type: "exitedFrame", sequence: s + 12 });
|
||||
gTraceClient.onPacket("", { type: "exitedFrame", sequence: s + 13 });
|
||||
}
|
@ -190,4 +190,3 @@ reason = bug 820380
|
||||
[test_trace_actor-06.js]
|
||||
[test_trace_actor-07.js]
|
||||
[test_ignore_caught_exceptions.js]
|
||||
[test_trace_client-01.js]
|
||||
|
Loading…
Reference in New Issue
Block a user