Skip to content

Instantly share code, notes, and snippets.

@ochameau
Created July 12, 2018 16:17
Show Gist options
  • Save ochameau/9ebf1edab02399fd9ca350d30b9a37f8 to your computer and use it in GitHub Desktop.
Save ochameau/9ebf1edab02399fd9ca350d30b9a37f8 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, marker) {
let strIndex = thread.stringTable.indexOf(marker);
if (strIndex == -1)
throw "marker not found";
let nameCol = thread.markers.schema.name;
let timeCol = thread.markers.schema.time;
for (let marker of thread.markers.data) {
if (marker[nameCol] == strIndex)
return marker[timeCol];
}
}
(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;
}
let decoder = new TextDecoder();
let minPath, minTime = Infinity, maxPath, maxTime = 0;
while (paths.length) {
let iterator;
try {
iterator = new OS.File.DirectoryIterator(paths.pop());
await iterator.forEach(async function(child) {
child = await child;
let path = child.path;
let relativePath = path.slice(currentDirectory.length + 1);
if (child.isDir) {
paths.push(path);
return;
}
if (!path.endsWith(".profile"))
return;
let file = decoder.decode(await OS.File.read(path));
print("parsed " + path);
let json = JSON.parse(file);
for (let thread of json.threads) {
if (thread.name != "GeckoMain")
continue;
let start = getMarkerTime(thread, "sessionRestoreInit");
let end = getMarkerTime(thread, "sessionRestored");
let time = end - start;
if (time > maxTime) {
maxTime = time;
maxPath = path;
maxJSON = json;
}
if (time < minTime) {
minTime = time;
minPath = path;
minJSON = json;
}
}
// if (file)
// await OS.File.writeAtomic(path, (new TextEncoder()).encode(file));
});
} catch (ex) {
// Ignore StopIteration to prevent exiting the loop.
dump(ex + "\n");
}
iterator.close();
}
print("min time: " + minTime);
print("max time: " + maxTime);
let json = maxJSON;
json.threads = json.threads.filter(t => t.name == "GeckoMain");
minJSON.threads = minJSON.threads.filter(t => t.name == "GeckoMain");
json.threads.push(minJSON.threads[0]);
for (let thread of json.threads) {
let start = getMarkerTime(thread, "sessionRestoreInit");
let end = getMarkerTime(thread, "sessionRestored");
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 = "max";
// threads[1].name = "min";
threads.push(JSON.parse(JSON.stringify(threads[0])));
threads[2].name = "only in slowest";
threads.push(JSON.parse(JSON.stringify(threads[1])));
threads[3].name = "only in fastest";
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(maxPath + "-filtered.json", (new TextEncoder()).encode(JSON.stringify(json)));
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