Created
July 12, 2018 16:17
-
-
Save ochameau/9ebf1edab02399fd9ca350d30b9a37f8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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