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