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.

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

mercurial