summaryrefslogtreecommitdiffstats
path: root/js/src/gdb/mozilla/IonGraph.py
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/gdb/mozilla/IonGraph.py')
-rw-r--r--js/src/gdb/mozilla/IonGraph.py199
1 files changed, 199 insertions, 0 deletions
diff --git a/js/src/gdb/mozilla/IonGraph.py b/js/src/gdb/mozilla/IonGraph.py
new file mode 100644
index 000000000..edd9ef8e4
--- /dev/null
+++ b/js/src/gdb/mozilla/IonGraph.py
@@ -0,0 +1,199 @@
+"""
+Debugging JIT Compilations can be obscure without large context. This python
+script provide commands to let GDB open an image viewer to display the graph of
+any compilation, as they are executed within GDB.
+
+This python script should be sourced within GDB after loading the python scripts
+provided with SpiderMonkey.
+"""
+
+import gdb
+import os
+import io
+import subprocess
+import tempfile
+import time
+import mozilla.prettyprinters
+from mozilla.prettyprinters import pretty_printer
+
+# Forget any printers from previous loads of this module.
+mozilla.prettyprinters.clear_module_printers(__name__)
+
+# Cache information about the JSString type for this objfile.
+class jsvmPrinterCache(object):
+ def __init__(self):
+ self.d = None
+
+ def __getattr__(self, name):
+ if self.d is None:
+ self.initialize()
+ return self.d[name]
+
+ def initialize(self):
+ self.d = {}
+ self.d['char'] = gdb.lookup_type('char')
+
+# Dummy class used to store the content of the type cache in the context of the
+# iongraph command, which uses the jsvmLSprinter.
+class ModuleCache(object):
+ def __init__(self):
+ self.mod_IonGraph = None
+
+@pretty_printer("js::vm::LSprinter")
+class jsvmLSprinter(object):
+ def __init__(self, value, cache):
+ self.value = value
+ if not cache.mod_IonGraph:
+ cache.mod_IonGraph = jsvmPrinterCache()
+ self.cache = cache.mod_IonGraph
+
+ def to_string(self):
+ next = self.value['head_']
+ tail = self.value['tail_']
+ if next == 0:
+ return ""
+ res = ""
+ while next != tail:
+ chars = (next + 1).cast(self.cache.char.pointer())
+ res = res + chars.string('ascii', 'ignore', next['length'])
+ next = next['next']
+ length = next['length'] - self.value['unused_']
+ chars = (next + 1).cast(self.cache.char.pointer())
+ res = res + chars.string('ascii', 'ignore', length)
+ return res
+
+def search_in_path(bin):
+ paths = os.getenv("PATH", "")
+ if paths == "":
+ return ""
+ for d in paths.split(":"):
+ f = os.path.join(d, bin)
+ if os.access(f, os.X_OK):
+ return f
+ return ""
+
+class IonGraphBinParameter(gdb.Parameter):
+ set_doc = "Set the path to iongraph binary, used by iongraph command."
+ show_doc = "Show the path to iongraph binary, used by iongraph command."
+ def get_set_string(self):
+ return "Path to iongraph binary changed to: %s" % self.value
+ def get_show_string(self, value):
+ return "Path to iongraph binary set to: %s" % value
+ def __init__(self):
+ super (IonGraphBinParameter, self).__init__ ("iongraph-bin", gdb.COMMAND_SUPPORT, gdb.PARAM_FILENAME)
+ self.value = os.getenv("GDB_IONGRAPH", "")
+ if self.value == "":
+ self.value = search_in_path("iongraph")
+
+
+class DotBinParameter(gdb.Parameter):
+ set_doc = "Set the path to dot binary, used by iongraph command."
+ show_doc = "Show the path to dot binary, used by iongraph command."
+ def get_set_string(self):
+ return "Path to dot binary changed to: %s" % self.value
+ def get_show_string(self, value):
+ return "Path to dot binary set to: %s" % value
+ def __init__(self):
+ super (DotBinParameter, self).__init__ ("dot-bin", gdb.COMMAND_SUPPORT, gdb.PARAM_FILENAME)
+ self.value = os.getenv("GDB_DOT", "")
+ if self.value == "":
+ self.value = search_in_path("dot")
+
+class PngViewerBinParameter(gdb.Parameter):
+ set_doc = "Set the path to a png viewer binary, used by iongraph command."
+ show_doc = "Show the path to a png viewer binary, used by iongraph command."
+ def get_set_string(self):
+ return "Path to a png viewer binary changed to: %s" % self.value
+ def get_show_string(self):
+ return "Path to a png viewer binary set to: %s" % value
+ def __init__(self):
+ super (PngViewerBinParameter, self).__init__ ("pngviewer-bin", gdb.COMMAND_SUPPORT, gdb.PARAM_FILENAME)
+ self.value = os.getenv("GDB_PNGVIEWER", "")
+ if self.value == "":
+ self.value = search_in_path("xdg-open")
+
+iongraph = IonGraphBinParameter()
+dot = DotBinParameter()
+pngviewer = PngViewerBinParameter()
+
+class IonGraphCommand(gdb.Command):
+ """Command used to display the current state of the MIR graph in a png
+ viewer by providing an expression to access the MIRGenerator.
+ """
+
+ def __init__(self):
+ super (IonGraphCommand, self).__init__ ("iongraph", gdb.COMMAND_DATA, gdb.COMPLETE_EXPRESSION)
+ self.typeCache = ModuleCache()
+
+ def invoke(self, mirGenExpr, from_tty):
+ """Call function from the graph spewer to populate the json printer with
+ the content generated by the jsonSpewer. Then we read the json content
+ from the jsonPrinter internal data, and gives that as input of iongraph
+ command."""
+
+ # From the MIRGenerator, find the graph spewer which contains both the
+ # jsonPrinter (containing the result of the output), and the jsonSpewer
+ # (continaining methods for spewing the graph).
+ mirGen = gdb.parse_and_eval(mirGenExpr)
+ jsonPrinter = mirGen['gs_']['jsonPrinter_']
+ jsonSpewer = mirGen['gs_']['jsonSpewer_']
+ graph = mirGen['graph_']
+
+ # These commands are doing side-effects which are saving the state of
+ # the compiled code on the LSprinter dedicated for logging. Fortunately,
+ # if you are using these gdb command, this probably means that other
+ # ways of getting this content failed you already, so making a mess in
+ # these logging strings should not cause much issues.
+ gdb.parse_and_eval('(*(%s*)(%s)).clear()' % (jsonPrinter.type, jsonPrinter.address,))
+ gdb.parse_and_eval('(*(%s*)(%s)).beginFunction((JSScript*)0)' % (jsonSpewer.type, jsonSpewer.address,))
+ gdb.parse_and_eval('(*(%s*)(%s)).beginPass("gdb")' % (jsonSpewer.type, jsonSpewer.address,))
+ gdb.parse_and_eval('(*(%s*)(%s)).spewMIR((%s)%s)' % (jsonSpewer.type, jsonSpewer.address, graph.type, graph,))
+ gdb.parse_and_eval('(*(%s*)(%s)).spewLIR((%s)%s)' % (jsonSpewer.type, jsonSpewer.address, graph.type, graph,))
+ gdb.parse_and_eval('(*(%s*)(%s)).endPass()' % (jsonSpewer.type, jsonSpewer.address,))
+ gdb.parse_and_eval('(*(%s*)(%s)).endFunction()' % (jsonSpewer.type, jsonSpewer.address,))
+
+ # Dump the content of the LSprinter containing the JSON view of the
+ # graph into a python string.
+ json = jsvmLSprinter(jsonPrinter, self.typeCache).to_string()
+
+ # We are in the middle of the program execution and are messing up with
+ # the state of the logging data. As this might not be the first time we
+ # call beginFunction, we might have an extra comma at the beginning of
+ # the output, just strip it.
+ if json[0] == ",":
+ json = json[1:]
+
+ # Usually this is added by the IonSpewer.
+ json = '{ "functions": [%s] }' % json
+
+ # Display the content of the json with iongraph and other tools.
+ self.displayMIRGraph(json)
+
+ def displayMIRGraph(self, jsonStr):
+ png = tempfile.NamedTemporaryFile()
+
+ # start all processes in a shell-like equivalent of:
+ # iongraph < json | dot > tmp.png; xdg-open tmp.png
+ i = subprocess.Popen([iongraph.value, '--funcnum', '0', '--passnum', '0', '--out-mir', '-', '-'], stdin = subprocess.PIPE, stdout = subprocess.PIPE)
+ d = subprocess.Popen([dot.value, '-Tpng'], stdin = i.stdout, stdout = png)
+
+ # Write the json file as the input of the iongraph command.
+ i.stdin.write(jsonStr)
+ i.stdin.close()
+ i.stdout.close()
+
+ # Wait for iongraph and dot, such that the png file contains all the
+ # bits needed to by the png viewer.
+ i.wait()
+ output = d.communicate()[0]
+
+ # Spawn & detach the png viewer, to which we give the name of the
+ # temporary file. Note, as we do not want to wait on the image viewer,
+ # there is a minor race between the removal of the temporary file, which
+ # would happen at the next garbage collection cycle, and the start of
+ # the png viewer. We could use a pipe, but unfortunately, this does not
+ # seems to be supported by xdg-open.
+ v = subprocess.Popen([pngviewer.value, png.name], stdin = None, stdout = None)
+ time.sleep(1)
+
+iongraph_cmd = IonGraphCommand()