Bug 916677 - Split up the output files and append GC function stack to hazards. Also check against an expected hazard count, r=terrence
authorSteve Fink <sfink@mozilla.com>
Mon, 28 Oct 2013 14:21:46 -0700
changeset 168105 98cb3022ae1094deec33a0f3c68a5e331c5aef4a
parent 168104 58ca27d61309b208feddb36052a7676ba2413df3
child 168106 81ef1e1afd5ea988819546bbbc0294e50a8a64bd
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs916677
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 916677 - Split up the output files and append GC function stack to hazards. Also check against an expected hazard count, r=terrence DONTBUILD because NPOTB
js/src/devtools/rootAnalysis/analyze.py
js/src/devtools/rootAnalysis/expect.json
js/src/devtools/rootAnalysis/explain.py
--- a/js/src/devtools/rootAnalysis/analyze.py
+++ b/js/src/devtools/rootAnalysis/analyze.py
@@ -119,27 +119,32 @@ JOBS = { 'dbs':
              (('%(js)s', '%(analysis_scriptdir)s/computeGCTypes.js',),
               'gcTypes.txt'),
 
          'allFunctions':
              (('%(sixgill_bin)s/xdbkeys', 'src_body.xdb',),
               'allFunctions.txt'),
 
          'hazards':
-             (generate_hazards, 'rootingHazards.txt')
+             (generate_hazards, 'rootingHazards.txt'),
+
+         'explain':
+             (('python', '%(analysis_scriptdir)s/explain.py',
+               '--expect-file=%(analysis_scriptdir)s/expect.json',
+               '%(hazards)s', '%(gcFunctions)s',
+               '[explained_hazards]', '[unnecessary]', '[refs]'),
+              ('hazards.txt', 'unnecessary.txt', 'refs.txt'))
          }
 
-
 def out_indexes(command):
     for i in range(len(command)):
         m = re.match(r'^\[(.*)\]$', command[i])
         if m:
             yield (i, m.group(1))
 
-
 def run_job(name, config):
     cmdspec, outfiles = JOBS[name]
     print("Running " + name + " to generate " + str(outfiles))
     if hasattr(cmdspec, '__call__'):
         cmdspec(config, outfiles)
     else:
         temp_map = {}
         cmdspec = fill(cmdspec, config)
@@ -227,17 +232,18 @@ if 'ANALYZED_OBJDIR' in os.environ:
 if 'SOURCE' in os.environ:
     data['source'] = os.environ['SOURCE']
 
 steps = [ 'dbs',
           'callgraph',
           'gcTypes',
           'gcFunctions',
           'allFunctions',
-          'hazards' ]
+          'hazards',
+          'explain' ]
 
 if args.list:
     for step in steps:
         command, outfilename = JOBS[step]
         if outfilename:
             print("%s -> %s" % (step, outfilename))
         else:
             print(step)
@@ -247,17 +253,17 @@ for step in steps:
     command, outfiles = JOBS[step]
     if isinstance(outfiles, basestring):
         data[step] = outfiles
     else:
         outfile = 0
         for (i, name) in out_indexes(command):
             data[name] = outfiles[outfile]
             outfile += 1
-        assert len(outfiles) == outfile, 'step %s: mismatched number of output files and params' % step
+        assert len(outfiles) == outfile, 'step \'%s\': mismatched number of output files and params' % step
 
 if args.step:
     steps = steps[steps.index(args.step):]
 
 if args.upto:
     steps = steps[:steps.index(args.upto)+1]
 
 for step in steps:
new file mode 100644
--- /dev/null
+++ b/js/src/devtools/rootAnalysis/expect.json
@@ -0,0 +1,3 @@
+{
+  "expect-hazards": 13
+}
new file mode 100755
--- /dev/null
+++ b/js/src/devtools/rootAnalysis/explain.py
@@ -0,0 +1,120 @@
+#!/usr/bin/python
+
+import re
+import argparse
+import sys
+
+parser = argparse.ArgumentParser(description='Process some integers.')
+parser.add_argument('rootingHazards', nargs='?', default='rootingHazards.txt')
+parser.add_argument('gcFunctions', nargs='?', default='gcFunctions.txt')
+parser.add_argument('hazards', nargs='?', default='hazards.txt')
+parser.add_argument('extra', nargs='?', default='unnecessary.txt')
+parser.add_argument('refs', nargs='?', default='refs.txt')
+parser.add_argument('--expect-hazards', type=int, default=None)
+parser.add_argument('--expect-refs', type=int, default=None)
+parser.add_argument('--expect-file', type=str, default=None)
+args = parser.parse_args()
+
+if args.expect_file:
+    import json
+    data = json.load(file(args.expect_file, 'r'))
+    args.expect_hazards = args.expect_hazards or data.get('expect-hazards')
+    args.expect_refs = args.expect_refs or data.get('expect-refs')
+
+num_hazards = 0
+num_refs = 0
+try:
+    with open(args.rootingHazards) as rootingHazards, \
+        open(args.hazards, 'w') as hazards, \
+        open(args.extra, 'w') as extra, \
+        open(args.refs, 'w') as refs:
+        current_gcFunction = None
+
+        # Map from a GC function name to the list of hazards resulting from
+        # that GC function
+        hazardousGCFunctions = {}
+
+        # List of tuples (gcFunction, index of hazard) used to maintain the
+        # ordering of the hazards
+        hazardOrder = []
+
+        for line in rootingHazards:
+            m = re.match(r'^Time: (.*)', line)
+            mm = re.match(r'^Run on:', line)
+            if m or mm:
+                print >>hazards, line
+                print >>extra, line
+                print >>refs, line
+                continue
+
+            m = re.match(r'^Function.*has unnecessary root', line)
+            if m:
+                print >>extra, line
+                continue
+
+            m = re.match(r'^Function.*takes unsafe address of unrooted', line)
+            if m:
+                num_refs += 1
+                print >>refs, line
+                continue
+
+            m = re.match(r"^Function.*has unrooted.*of type.*live across GC call '(.*?)'", line)
+            if m:
+                current_gcFunction = m.group(1)
+                hazardousGCFunctions.setdefault(current_gcFunction, []).append(line);
+                hazardOrder.append((current_gcFunction, len(hazardousGCFunctions[current_gcFunction]) - 1))
+                num_hazards += 1
+                continue
+
+            if current_gcFunction:
+                if not line.strip():
+                    # Blank line => end of this hazard
+                    current_gcFunction = None
+                else:
+                    hazardousGCFunctions[current_gcFunction][-1] += line
+
+        with open(args.gcFunctions) as gcFunctions:
+            gcExplanations = {} # gcFunction => stack showing why it can GC
+
+            current_func = None
+            for line in gcFunctions:
+                m = re.match(r'^GC Function: (.*)', line)
+                if m:
+                    if current_func:
+                        gcExplanations[current_func] = explanation
+                    current_func = None
+                    if m.group(1) in hazardousGCFunctions:
+                        current_func = m.group(1)
+                        explanation = line
+                elif current_func:
+                    explanation += line
+            if current_func:
+                gcExplanations[current_func] = explanation
+
+            for gcFunction, index in hazardOrder:
+                gcHazards = hazardousGCFunctions[gcFunction]
+                print >>hazards, (gcHazards[index] + gcExplanations[gcFunction])
+
+except IOError as e:
+    print 'Failed: %s' % str(e)
+
+print("Wrote %s" % args.hazards)
+print("Wrote %s" % args.extra)
+print("Wrote %s" % args.refs)
+print("Found %d hazards and %d unsafe references" % (num_hazards, num_refs))
+
+if args.expect_hazards is not None and args.expect_hazards != num_hazards:
+    if args.expect_hazards < num_hazards:
+        print("TEST-UNEXPECTED-FAILURE: %d more hazards than expected (expected %d, saw %d)" % (num_hazards - args.expect_hazards, args.expect_hazards, num_hazards))
+        sys.exit(1)
+    else:
+        print("%d fewer hazards than expected! (expected %d, saw %d)" % (args.expect_hazards - num_hazards, args.expect_hazards, num_hazards))
+
+if args.expect_refs is not None and args.expect_refs != num_refs:
+    if args.expect_refs < num_refs:
+        print("TEST-UNEXPECTED-FAILURE: %d more unsafe refs than expected (expected %d, saw %d)" % (num_refs - args.expect_refs, args.expect_refs, num_refs))
+        sys.exit(1)
+    else:
+        print("%d fewer unsafe refs than expected! (expected %d, saw %d)" % (args.expect_refs - num_refs, args.expect_refs, num_refs))
+
+sys.exit(0)