Skip to content

Instantly share code, notes, and snippets.

@ochameau
Created July 12, 2018 16:16
Show Gist options
  • Save ochameau/d7f4dc3a469eac08e80a5077f97f5703 to your computer and use it in GitHub Desktop.
Save ochameau/d7f4dc3a469eac08e80a5077f97f5703 to your computer and use it in GitHub Desktop.
var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Components.utils.import("resource://gre/modules/osfile.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
var done = false;
let gPaths = arguments.filter(a => !a.startsWith("-"));
if (!gPaths.length) {
gPaths = [];
}
function getMarkerTime(thread, name) {
for (let [,,marker] of thread.markers.data) {
if (!marker || !marker.name) continue;
if (marker.name == name)
return {
start: marker.startTime,
end: marker.endTime,
};
}
throw "marker not found: "+marker;
}
const decoder = new TextDecoder();
async function readProfile(path) {
const data = decoder.decode(await OS.File.read(path));
print("parsed " + path);
return JSON.parse(data);
}
const markerName = "server.protocoljs.DAMP";
(async function() {
let currentDirectory = await OS.File.getCurrentDirectory();
let paths = gPaths.map(a => currentDirectory + "/" + a);
if (!paths.length) {
print("error, no path provided");
done = true;
return;
}
if (paths.length != 2) {
print("need exactly two profiles, got:" + paths.length);
done = true
return;
}
let json = await readProfile(paths[0]);
let json2 = await readProfile(paths[1]);
json.threads = json.threads.filter(t => t.name == "GeckoMain");
json2.threads = json2.threads.filter(t => t.name == "GeckoMain");
json.threads.push(json2.threads[0]);
for (let thread of json.threads) {
let { start, end } = getMarkerTime(thread, markerName);
let samples = thread.samples;
let timeCol = samples.schema.time;
samples.data = samples.data.filter(s => {
let t = s[timeCol];
return t >= start && t <= end;
});
for (let d of samples.data)
d[timeCol] -= start;
thread.registerTime = 0;
let markers = thread.markers;
timeCol = markers.schema.time;
markers.data = markers.data.filter(m => m[timeCol] >= start);
for (let m of markers.data)
m[timeCol] -= start;
}
json.processes = [];
let threads = json.threads;
threads[0].name = "First profile";
threads[1].name = "Second profile";
threads.push(JSON.parse(JSON.stringify(threads[0])));
threads[2].name = "Only first";
threads.push(JSON.parse(JSON.stringify(threads[1])));
threads[3].name = "Only second";
let onlyMax = threads[2];
let onlyMin = threads[3];
function getJSStack(thread, sample) {
let result = [];
let stackCol = thread.samples.schema.stack;
let stackPrefixCol = thread.stackTable.schema.prefix;
let stackFrameCol = thread.stackTable.schema.frame;
let frameLocationCol = thread.frameTable.schema.location;
let frameImplementationCol = thread.frameTable.schema.implementation;
let stackId = sample[stackCol];
while (stackId !== null) {
let stack = thread.stackTable.data[stackId];
let frame = thread.frameTable.data[stack[stackFrameCol]];
if (typeof frame[frameImplementationCol] == "number")
result.push(thread.stringTable[frame[frameLocationCol]]);
stackId = stack[stackPrefixCol];
}
if (result.length > 10)
result = result.slice(0, 10);
return JSON.stringify(result);
}
function getStack(thread, sample) {
let result = [];
let jsresult = [];
let stackCol = thread.samples.schema.stack;
let stackPrefixCol = thread.stackTable.schema.prefix;
let stackFrameCol = thread.stackTable.schema.frame;
let frameLocationCol = thread.frameTable.schema.location;
let frameImplementationCol = thread.frameTable.schema.implementation;
let stackId = sample[stackCol];
while (stackId !== null) {
let stack = thread.stackTable.data[stackId];
let frame = thread.frameTable.data[stack[stackFrameCol]];
let line = thread.stringTable[frame[frameLocationCol]];
if (line.includes(".js") || line.includes(".xml"))
jsresult.push(line);
result.push(line);
stackId = stack[stackPrefixCol];
}
if (jsresult.length > 2) {
result = jsresult;
// print(jsresult.toSource());
}
if (result.length > 10)
result = result.slice(0, 10);
return JSON.stringify(result).replace(/0x[0-9a-f]{8}/g, "address");
}
function getStackMap(thread) {
let stackMap = new Map();
for (let sample of thread.samples.data) {
let stack = getStack(thread, sample);
stackMap.set(stack, (stackMap.get(stack) || 0) + 1);
}
return stackMap;
}
let maxMap = getStackMap(onlyMax);
let minMap = getStackMap(onlyMin);
print("maxMap.size = " + maxMap.size);
print("minMap.size = " + minMap.size);
print("onlyMax samples before: " + onlyMax.samples.data.length);
onlyMax.samples.data = onlyMax.samples.data.filter(sample => {
let stack = getStack(onlyMax, sample);
let count = minMap.get(stack);
if (count) {
if (count == 1)
minMap.delete(stack);
else
minMap.set(stack, count - 1);
}
return !count;
});
print("onlyMax samples after: " + onlyMax.samples.data.length);
print("onlyMin samples before: " + onlyMin.samples.data.length);
onlyMin.samples.data = onlyMin.samples.data.filter(sample => {
let stack = getStack(onlyMin, sample);
let count = maxMap.get(stack);
if (count) {
if (count == 1)
maxMap.delete(stack);
else
maxMap.set(stack, count - 1);
}
return !count;
});
print("onlyMin samples after: " + onlyMin.samples.data.length);
/*
for (let [stack, count] of maxMap) {
print(stack);
}
*/
await OS.File.writeAtomic("merged-profile.json", (new TextEncoder()).encode(JSON.stringify(json)));
done = true;
})().catch(e => {
dump(e + "\n");
done = true;
});
// Spin an event loop.
(() => {
var thread = Components.classes["@mozilla.org/thread-manager;1"]
.getService().currentThread;
while (!done)
thread.processNextEvent(true);
// get rid of any pending event
while (thread.hasPendingEvents())
thread.processNextEvent(true);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment