Created
July 12, 2018 16:16
-
-
Save ochameau/d7f4dc3a469eac08e80a5077f97f5703 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, 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