summaryrefslogtreecommitdiffstats
path: root/js/src/devtools/rootAnalysis/explain.py
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/devtools/rootAnalysis/explain.py')
-rwxr-xr-xjs/src/devtools/rootAnalysis/explain.py129
1 files changed, 129 insertions, 0 deletions
diff --git a/js/src/devtools/rootAnalysis/explain.py b/js/src/devtools/rootAnalysis/explain.py
new file mode 100755
index 0000000000..993725273c
--- /dev/null
+++ b/js/src/devtools/rootAnalysis/explain.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python3
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+from __future__ import print_function
+
+import argparse
+import re
+
+from collections import defaultdict
+
+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")
+args = parser.parse_args()
+
+num_hazards = 0
+num_refs = 0
+num_missing = 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 = defaultdict(list)
+
+ # List of tuples (gcFunction, index of hazard) used to maintain the
+ # ordering of the hazards
+ hazardOrder = []
+
+ # Map from a hazardous GC function to the filename containing it.
+ fileOfFunction = {}
+
+ for line in rootingHazards:
+ m = re.match(r"^Time: (.*)", line)
+ mm = re.match(r"^Run on:", line)
+ if m or mm:
+ print(line, file=hazards)
+ print(line, file=extra)
+ print(line, file=refs)
+ continue
+
+ m = re.match(r"^Function.*has unnecessary root", line)
+ if m:
+ print(line, file=extra)
+ continue
+
+ m = re.match(r"^Function.*takes unsafe address of unrooted", line)
+ if m:
+ num_refs += 1
+ print(line, file=refs)
+ continue
+
+ m = re.match(
+ r"^Function.*has unrooted.*of type.*live across GC call '(.*?)' at (\S+):\d+$",
+ line,
+ ) # NOQA: E501
+ if m:
+ current_gcFunction = m.group(1)
+ hazardousGCFunctions[current_gcFunction].append(line)
+ hazardOrder.append(
+ (
+ current_gcFunction,
+ len(hazardousGCFunctions[current_gcFunction]) - 1,
+ )
+ )
+ num_hazards += 1
+ fileOfFunction[current_gcFunction] = m.group(2)
+ continue
+
+ m = re.match(r"Function.*expected hazard.*but none were found", line)
+ if m:
+ num_missing += 1
+ print(line + "\n", file=hazards)
+ 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
+ explanation = 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]
+
+ if gcFunction in gcExplanations:
+ print(gcHazards[index] + gcExplanations[gcFunction], file=hazards)
+ else:
+ print(gcHazards[index], file=hazards)
+
+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 %d unsafe references %d missing"
+ % (num_hazards, num_refs, num_missing)
+)