1#!/usr/bin/env python
2# Copyright 2014 the V8 project authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6# for py2/py3 compatibility
7from __future__ import print_function
8
9import sys
10
11
12action = sys.argv[1]
13
14if action in ["help", "-h", "--help"] or len(sys.argv) != 3:
15  print("Usage: %s <action> <inputfile>, where action can be: \n"
16        "help    Print this message\n"
17        "plain   Print ASCII tree to stdout\n"
18        "dot     Print dot file to stdout\n"
19        "count   Count most frequent transition reasons\n" % sys.argv[0])
20  sys.exit(0)
21
22
23filename = sys.argv[2]
24maps = {}
25root_maps = []
26transitions = {}
27annotations = {}
28
29
30class Map(object):
31
32  def __init__(self, pointer, origin):
33    self.pointer = pointer
34    self.origin = origin
35
36  def __str__(self):
37    return "%s (%s)" % (self.pointer, self.origin)
38
39
40class Transition(object):
41
42  def __init__(self, from_map, to_map, reason):
43    self.from_map = from_map
44    self.to_map = to_map
45    self.reason = reason
46
47
48def RegisterNewMap(raw_map):
49  if raw_map in annotations:
50    annotations[raw_map] += 1
51  else:
52    annotations[raw_map] = 0
53  return AnnotateExistingMap(raw_map)
54
55
56def AnnotateExistingMap(raw_map):
57  return "%s_%d" % (raw_map, annotations[raw_map])
58
59
60def AddMap(pointer, origin):
61  pointer = RegisterNewMap(pointer)
62  maps[pointer] = Map(pointer, origin)
63  return pointer
64
65
66def AddTransition(from_map, to_map, reason):
67  from_map = AnnotateExistingMap(from_map)
68  to_map = AnnotateExistingMap(to_map)
69  if from_map not in transitions:
70    transitions[from_map] = {}
71  targets = transitions[from_map]
72  if to_map in targets:
73    # Some events get printed twice, that's OK. In some cases, ignore the
74    # second output...
75    old_reason = targets[to_map].reason
76    if old_reason.startswith("ReplaceDescriptors"):
77      return
78    # ...and in others use it for additional detail.
79    if reason in []:
80      targets[to_map].reason = reason
81      return
82    # Unexpected duplicate events? Warn.
83    print("// warning: already have a transition from %s to %s, reason: %s" %
84            (from_map, to_map, targets[to_map].reason))
85    return
86  targets[to_map] = Transition(from_map, to_map, reason)
87
88
89with open(filename, "r") as f:
90  last_to_map = ""
91  for line in f:
92    if not line.startswith("[TraceMaps: "): continue
93    words = line.split(" ")
94    event = words[1]
95    if event == "InitialMap":
96      assert words[2] == "map="
97      assert words[4] == "SFI="
98      new_map = AddMap(words[3], "SFI#%s" % words[5])
99      root_maps.append(new_map)
100      continue
101    if words[2] == "from=" and words[4] == "to=":
102      from_map = words[3]
103      to_map = words[5]
104      if from_map not in annotations:
105        print("// warning: unknown from_map %s" % from_map)
106        new_map = AddMap(from_map, "<unknown>")
107        root_maps.append(new_map)
108      if to_map != last_to_map:
109        AddMap(to_map, "<transition> (%s)" % event)
110      last_to_map = to_map
111      if event in ["Transition", "NoTransition"]:
112        assert words[6] == "name=", line
113        reason = "%s: %s" % (event, words[7])
114      elif event in ["Normalize", "ReplaceDescriptors", "SlowToFast"]:
115        assert words[6] == "reason=", line
116        reason = "%s: %s" % (event, words[7])
117        if words[8].strip() != "]":
118          reason = "%s_%s" % (reason, words[8])
119      else:
120        reason = event
121      AddTransition(from_map, to_map, reason)
122      continue
123
124
125def PlainPrint(m, indent, label):
126  print("%s%s (%s)" % (indent, m, label))
127  if m in transitions:
128    for t in transitions[m]:
129      PlainPrint(t, indent + "  ", transitions[m][t].reason)
130
131
132def CountTransitions(m):
133  if m not in transitions: return 0
134  return len(transitions[m])
135
136
137def DotPrint(m, label):
138  print("m%s [label=\"%s\"]" % (m[2:], label))
139  if m in transitions:
140    for t in transitions[m]:
141      # GraphViz doesn't like node labels looking like numbers, so use
142      # "m..." instead of "0x...".
143      print("m%s -> m%s" % (m[2:], t[2:]))
144      reason = transitions[m][t].reason
145      reason = reason.replace("\\", "BACKSLASH")
146      reason = reason.replace("\"", "\\\"")
147      DotPrint(t, reason)
148
149
150if action == "plain":
151  root_maps = sorted(root_maps, key=CountTransitions, reverse=True)
152  for m in root_maps:
153    PlainPrint(m, "", maps[m].origin)
154
155elif action == "dot":
156  print("digraph g {")
157  for m in root_maps:
158    DotPrint(m, maps[m].origin)
159  print("}")
160
161elif action == "count":
162  reasons = {}
163  for s in transitions:
164    for t in transitions[s]:
165      reason = transitions[s][t].reason
166      if reason not in reasons:
167        reasons[reason] = 1
168      else:
169        reasons[reason] += 1
170  reasons_list = []
171  for r in reasons:
172    reasons_list.append("%8d %s" % (reasons[r], r))
173  reasons_list.sort(reverse=True)
174  for r in reasons_list[:20]:
175    print(r)
176