js/src/devtools/rootAnalysis/loadCallgraph.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: Javascript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2
michael@0 3 "use strict";
michael@0 4
michael@0 5 loadRelativeToScript('utility.js');
michael@0 6
michael@0 7 // Functions come out of sixgill in the form "mangled|readable". The mangled
michael@0 8 // name is Truth. One mangled name might correspond to multiple readable names,
michael@0 9 // for multiple reasons, including (1) sixgill/gcc doesn't always qualify types
michael@0 10 // the same way or de-typedef the same amount; (2) sixgill's output treats
michael@0 11 // references and pointers the same, and so doesn't distinguish them, but C++
michael@0 12 // treats them as separate for overloading and linking; (3) (identical)
michael@0 13 // destructors sometimes have an int32 parameter, sometimes not.
michael@0 14 //
michael@0 15 // The readable names are useful because they're far more meaningful to the
michael@0 16 // user, and are what should show up in reports and questions to mrgiggles. At
michael@0 17 // least in most cases, it's fine to have the extra mangled name tacked onto
michael@0 18 // the beginning for these.
michael@0 19 //
michael@0 20 // The strategy used is to separate out the pieces whenever they are read in,
michael@0 21 // create a table mapping mangled names to (one of the) readable names, and
michael@0 22 // use the mangled names in all computation.
michael@0 23 //
michael@0 24 // Note that callgraph.txt uses a compressed representation -- each name is
michael@0 25 // mapped to an integer, and those integers are what is recorded in the edges.
michael@0 26 // But the integers depend on the full name, whereas the true edge should only
michael@0 27 // consider the mangled name. And some of the names encoded in callgraph.txt
michael@0 28 // are FieldCalls, not just function names.
michael@0 29
michael@0 30 var readableNames = {}; // map from mangled name => list of readable names
michael@0 31 var mangledName = {}; // map from demangled names => mangled names. Could be eliminated.
michael@0 32 var calleeGraph = {}; // map from mangled => list of tuples of {'callee':mangled, 'suppressed':bool}
michael@0 33 var callerGraph = {}; // map from mangled => list of tuples of {'caller':mangled, 'suppressed':bool}
michael@0 34 var gcFunctions = {}; // map from mangled callee => reason
michael@0 35 var suppressedFunctions = {}; // set of mangled names (map from mangled name => true)
michael@0 36 var gcEdges = {};
michael@0 37
michael@0 38 function addGCFunction(caller, reason)
michael@0 39 {
michael@0 40 if (caller in suppressedFunctions)
michael@0 41 return false;
michael@0 42
michael@0 43 if (ignoreGCFunction(caller))
michael@0 44 return false;
michael@0 45
michael@0 46 if (!(caller in gcFunctions)) {
michael@0 47 gcFunctions[caller] = reason;
michael@0 48 return true;
michael@0 49 }
michael@0 50
michael@0 51 return false;
michael@0 52 }
michael@0 53
michael@0 54 function addCallEdge(caller, callee, suppressed)
michael@0 55 {
michael@0 56 if (!(caller in calleeGraph))
michael@0 57 calleeGraph[caller] = [];
michael@0 58 calleeGraph[caller].push({callee:callee, suppressed:suppressed});
michael@0 59
michael@0 60 if (!(callee in callerGraph))
michael@0 61 callerGraph[callee] = [];
michael@0 62 callerGraph[callee].push({caller:caller, suppressed:suppressed});
michael@0 63 }
michael@0 64
michael@0 65 // Map from identifier to full "mangled|readable" name. Or sometimes to a
michael@0 66 // Class.Field name.
michael@0 67 var functionNames = [""];
michael@0 68
michael@0 69 // Map from identifier to mangled name (or to a Class.Field)
michael@0 70 var idToMangled = [""];
michael@0 71
michael@0 72 function loadCallgraph(file)
michael@0 73 {
michael@0 74 var suppressedFieldCalls = {};
michael@0 75 var resolvedFunctions = {};
michael@0 76
michael@0 77 var textLines = snarf(file).split('\n');
michael@0 78 for (var line of textLines) {
michael@0 79 var match;
michael@0 80 if (match = line.charAt(0) == "#" && /^\#(\d+) (.*)/.exec(line)) {
michael@0 81 assert(functionNames.length == match[1]);
michael@0 82 functionNames.push(match[2]);
michael@0 83 var [ mangled, readable ] = splitFunction(match[2]);
michael@0 84 if (mangled in readableNames)
michael@0 85 readableNames[mangled].push(readable);
michael@0 86 else
michael@0 87 readableNames[mangled] = [ readable ];
michael@0 88 mangledName[readable] = mangled;
michael@0 89 idToMangled.push(mangled);
michael@0 90 continue;
michael@0 91 }
michael@0 92 var suppressed = false;
michael@0 93 if (line.indexOf("SUPPRESS_GC") != -1) {
michael@0 94 match = /^(..)SUPPRESS_GC (.*)/.exec(line);
michael@0 95 line = match[1] + match[2];
michael@0 96 suppressed = true;
michael@0 97 }
michael@0 98 var tag = line.charAt(0);
michael@0 99 if (match = tag == 'I' && /^I (\d+) VARIABLE ([^\,]*)/.exec(line)) {
michael@0 100 var mangledCaller = idToMangled[match[1]];
michael@0 101 var name = match[2];
michael@0 102 if (!indirectCallCannotGC(functionNames[match[1]], name) && !suppressed)
michael@0 103 addGCFunction(mangledCaller, "IndirectCall: " + name);
michael@0 104 } else if (match = tag == 'F' && /^F (\d+) CLASS (.*?) FIELD (.*)/.exec(line)) {
michael@0 105 var caller = idToMangled[match[1]];
michael@0 106 var csu = match[2];
michael@0 107 var fullfield = csu + "." + match[3];
michael@0 108 if (suppressed)
michael@0 109 suppressedFieldCalls[fullfield] = true;
michael@0 110 else if (!fieldCallCannotGC(csu, fullfield))
michael@0 111 addGCFunction(caller, "FieldCall: " + fullfield);
michael@0 112 } else if (match = tag == 'D' && /^D (\d+) (\d+)/.exec(line)) {
michael@0 113 var caller = idToMangled[match[1]];
michael@0 114 var callee = idToMangled[match[2]];
michael@0 115 addCallEdge(caller, callee, suppressed);
michael@0 116 } else if (match = tag == 'R' && /^R (\d+) (\d+)/.exec(line)) {
michael@0 117 var callerField = idToMangled[match[1]];
michael@0 118 var callee = idToMangled[match[2]];
michael@0 119 addCallEdge(callerField, callee, false);
michael@0 120 resolvedFunctions[callerField] = true;
michael@0 121 }
michael@0 122 }
michael@0 123
michael@0 124 // Initialize suppressedFunctions to the set of all functions, and the
michael@0 125 // worklist to all toplevel callers.
michael@0 126 var worklist = [];
michael@0 127 for (var callee in callerGraph)
michael@0 128 suppressedFunctions[callee] = true;
michael@0 129 for (var caller in calleeGraph) {
michael@0 130 if (!(caller in callerGraph)) {
michael@0 131 suppressedFunctions[caller] = true;
michael@0 132 worklist.push(caller);
michael@0 133 }
michael@0 134 }
michael@0 135
michael@0 136 // Find all functions reachable via an unsuppressed call chain, and remove
michael@0 137 // them from the suppressedFunctions set. Everything remaining is only
michael@0 138 // reachable when GC is suppressed.
michael@0 139 var top = worklist.length;
michael@0 140 while (top > 0) {
michael@0 141 name = worklist[--top];
michael@0 142 if (!(name in suppressedFunctions))
michael@0 143 continue;
michael@0 144 delete suppressedFunctions[name];
michael@0 145 if (!(name in calleeGraph))
michael@0 146 continue;
michael@0 147 for (var entry of calleeGraph[name]) {
michael@0 148 if (!entry.suppressed)
michael@0 149 worklist[top++] = entry.callee;
michael@0 150 }
michael@0 151 }
michael@0 152
michael@0 153 // Such functions are known to not GC.
michael@0 154 for (var name in gcFunctions) {
michael@0 155 if (name in suppressedFunctions)
michael@0 156 delete gcFunctions[name];
michael@0 157 }
michael@0 158
michael@0 159 for (var name in suppressedFieldCalls) {
michael@0 160 suppressedFunctions[name] = true;
michael@0 161 }
michael@0 162
michael@0 163 for (var gcName of [ 'jsgc.cpp:void Collect(JSRuntime*, uint8, int64, uint32, uint32)',
michael@0 164 'void js::MinorGC(JSRuntime*, uint32)' ])
michael@0 165 {
michael@0 166 assert(gcName in mangledName);
michael@0 167 addGCFunction(mangledName[gcName], "GC");
michael@0 168 }
michael@0 169
michael@0 170 // Initialize the worklist to all known gcFunctions.
michael@0 171 var worklist = [];
michael@0 172 for (var name in gcFunctions)
michael@0 173 worklist.push(name);
michael@0 174
michael@0 175 // Recursively find all callers and add them to the set of gcFunctions.
michael@0 176 while (worklist.length) {
michael@0 177 name = worklist.shift();
michael@0 178 assert(name in gcFunctions);
michael@0 179 if (!(name in callerGraph))
michael@0 180 continue;
michael@0 181 for (var entry of callerGraph[name]) {
michael@0 182 if (!entry.suppressed && addGCFunction(entry.caller, name))
michael@0 183 worklist.push(entry.caller);
michael@0 184 }
michael@0 185 }
michael@0 186
michael@0 187 // Any field call that has been resolved to all possible callees can be
michael@0 188 // trusted to not GC if all of those callees are known to not GC.
michael@0 189 for (var name in resolvedFunctions) {
michael@0 190 if (!(name in gcFunctions))
michael@0 191 suppressedFunctions[name] = true;
michael@0 192 }
michael@0 193 }

mercurial