1d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved.
2d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
3d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)# found in the LICENSE file.
4d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
5d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)import json
6d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
7d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)from telemetry.core.heap import live_heap_object
8d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)from telemetry.core.heap import retaining_edge
9d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
11d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)class ChromeJsHeapSnapshotParser(object):
12d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  """ Parser for the heap snapshot.
13d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
14d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  The heap snapshot JSON format is defined by HeapSnapshotJSONSerializer in V8.
15d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
16d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  The snapshot contains a list of integers describing nodes (types, names, etc.)
17d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  and a list of integers describing edges (types, the node the edge points to,
18d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  etc.) and a string table. All strings are expressed as indices to the string
19d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  table.
20d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
21d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  In addition, the snapshot contains meta information describing the data fields
22d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  for nodes and the data fields for edges.
23d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
24d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  Attributes:
25d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _node_dict: {int -> LiveHeapObject}, maps integer ids to LiveHeapObject
26d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        objects.
27d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _node_list: [int], the raw node data of the heap snapshot.
28d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _edge_list: [int], the raw edge data of the heap snapshot.
29d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _node_types: [str], the possible node types in the heap snapshot.
30d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _edge_types: [str], the possible edge types in the heap snapshot.
31d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _node_fields: [str], the fields present in the heap snapshot for each node.
32d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _edge_fields: [str], the fields present in the heap snapshot for each node.
33d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _node_type_ix: int, index of the node type field.
34d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _node_name_ix: int, index of the node name field.
35d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _node_id_ix: int, index of the node id field.
36d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _node_edge_count_ix: int, index of the node edge count field.
37d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _node_field_count: int, number of node fields.
38d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _edge_type_ix: int, index of the edge type field.
39d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _edge_name_or_ix_ix: int, index of the "edge name or index" field.
40d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _edge_to_node_ix: int, index of the "to node for an edge" field.
41d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    _edge_field_count: int, number of edge fields.
42d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  """
43d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
44d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  def __init__(self, raw_data):
45d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    heap = json.loads(raw_data)
46d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._node_dict = {}
47d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
48d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    # Read the snapshot components (nodes, edges, strings, metadata).
49d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._node_list = heap['nodes']
50d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._edge_list = heap['edges']
51d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._strings = heap['strings']
52d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
53d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._node_types = heap['snapshot']['meta']['node_types'][0]
54d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._edge_types = heap['snapshot']['meta']['edge_types'][0]
55d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    node_fields = heap['snapshot']['meta']['node_fields']
56d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    edge_fields = heap['snapshot']['meta']['edge_fields']
57d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
58d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    # Find the indices of the required node and edge fields based on the
59d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    # metadata.
60d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._node_type_ix = node_fields.index('type')
61d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._node_name_ix = node_fields.index('name')
62d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._node_id_ix = node_fields.index('id')
63d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._node_edge_count_ix = node_fields.index('edge_count')
64d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._node_field_count = len(node_fields)
65d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
66d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._edge_type_ix = edge_fields.index('type')
67d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._edge_name_or_ix_ix = edge_fields.index('name_or_index')
68d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._edge_to_node_ix = edge_fields.index('to_node')
69d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._edge_field_count = len(edge_fields)
70d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
71d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._ParseSnapshot()
72d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
73d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  @staticmethod
74d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  def CanImport(raw_data):
75d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    heap = json.loads(raw_data)
76d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if ('nodes' not in heap or 'edges' not in heap or 'strings' not in heap or
77d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        'snapshot' not in heap or 'meta' not in heap['snapshot']):
78d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return False
79d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    meta = heap['snapshot']['meta']
80d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if ('node_types' not in meta or 'edge_types' not in meta or
81d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        'node_fields' not in meta or 'edge_fields' not in meta):
82d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return False
83d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    node_fields = meta['node_fields']
84d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    edge_fields = meta['edge_fields']
85d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if ('type' not in node_fields or 'name' not in node_fields or
86d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        'id' not in node_fields or 'edge_count' not in node_fields):
87d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return False
88d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if ('type' not in edge_fields or 'name_or_index' not in edge_fields or
89d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        'to_node' not in edge_fields):
90d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return False
91d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return True
92d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
93d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  def GetAllLiveHeapObjects(self):
94d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return self._node_dict.values()
95d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
96d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  @staticmethod
97d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  def LiveHeapObjectToJavaScript(heap_object):
98d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return heap_object.name or str(heap_object)
99d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
100d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  @staticmethod
101d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  def RetainingEdgeToJavaScript(edge):
102d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if edge.type_string == 'property':
103d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return '.' + edge.name_string
104d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if edge.type_string == 'element':
105d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return '[' + edge.name_string + ']'
106d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return str(edge)
107d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
108d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  def _ParseSnapshot(self):
109d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    """Parses the stored JSON snapshot data.
110d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
111d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    Fills in self._node_dict with LiveHeapObject objects constructed based on
112d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    the heap snapshot. The LiveHeapObject objects contain the associated
113d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    RetainingEdge objects.
114d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    """
115d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    edge_start_ix = 0
116d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    for ix in xrange(0, len(self._node_list), self._node_field_count):
117d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      edge_start_ix = self._ReadNodeFromIndex(ix, edge_start_ix)
118d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
119d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    # Add pointers to the endpoints to the edges, and associate the edges with
120d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    # the "to" nodes.
121d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    for node_id in self._node_dict:
122d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      n = self._node_dict[node_id]
123d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      for e in n.edges_from:
124d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        self._node_dict[e.to_object_id].AddEdgeTo(e)
125d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        e.SetFromObject(n)
126d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        e.SetToObject(self._node_dict[e.to_object_id])
127d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
128d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  def _ReadNodeFromIndex(self, ix, edges_start):
129d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    """Reads the data for a node from the heap snapshot.
130d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
131d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    If the index contains an interesting node, constructs a Node object and adds
132d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    it to self._node_dict.
133d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
134d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    Args:
135d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      ix: int, index into the self._node_list array.
136d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      edges_start: int, the index of the edge array where the edges for the node
137d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          start.
138d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    Returns:
139d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      int, the edge start index for the next node.
140d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
141d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    Raises:
142d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      Exception: The node list of the snapshot is malformed.
143d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    """
144d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if ix + self._node_field_count > len(self._node_list):
145d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      raise Exception('Snapshot node list too short')
146d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
147d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    type_ix = self._node_list[ix + self._node_type_ix]
148d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    type_string = self._node_types[int(type_ix)]
149d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
150d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    # edges_end is noninclusive (the index of the first edge that is not part of
151d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    # this node).
152d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    edge_count = self._node_list[ix + self._node_edge_count_ix]
153d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    edges_end = edges_start + edge_count * self._edge_field_count
154d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
155d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if ChromeJsHeapSnapshotParser._IsNodeTypeUninteresting(type_string):
156d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return edges_end
157d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
158d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    name_ix = self._node_list[ix + self._node_name_ix]
159d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    node_id = self._node_list[ix + self._node_id_ix]
160d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
161d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    def ConstructorName(type_string, node_name_ix):
162d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      if type_string == 'object':
163d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        return self._strings[int(node_name_ix)]
164d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return '(%s)' % type_string
165d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
166d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    ctor_name = ConstructorName(type_string, name_ix)
167d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    n = live_heap_object.LiveHeapObject(node_id, type_string, ctor_name)
168d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if type_string == 'string':
169d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      n.string = self._strings[int(name_ix)]
170d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
171d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    for edge_ix in xrange(edges_start, edges_end, self._edge_field_count):
172d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      edge = self._ReadEdgeFromIndex(node_id, edge_ix)
173d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      if edge:
174d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        # The edge will be associated with the other endpoint when all the data
175d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        # has been read.
176d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        n.AddEdgeFrom(edge)
177d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
178d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    self._node_dict[node_id] = n
179d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return edges_end
180d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
181d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  @staticmethod
182d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  def _IsNodeTypeUninteresting(type_string):
183d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    """Helper function for filtering out nodes from the heap snapshot.
184d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
185d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    Args:
186d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      type_string: str, type of the node.
187d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    Returns:
188d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      bool, True if the node is of an uninteresting type and shouldn't be
189d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          included in the heap snapshot analysis.
190d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    """
191d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    uninteresting_types = ('hidden', 'code', 'number', 'native', 'synthetic')
192d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return type_string in uninteresting_types
193d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
194d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  @staticmethod
195d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  def _IsEdgeTypeUninteresting(edge_type_string):
196d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    """Helper function for filtering out edges from the heap snapshot.
197d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
198d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    Args:
199d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      edge_type_string: str, type of the edge.
200d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    Returns:
201d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      bool, True if the edge is of an uninteresting type and shouldn't be
202d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          included in the heap snapshot analysis.
203d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    """
204d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    uninteresting_types = ('weak', 'hidden', 'internal')
205d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return edge_type_string in uninteresting_types
206d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
207d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  def _ReadEdgeFromIndex(self, node_id, edge_ix):
208d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    """Reads the data for an edge from the heap snapshot.
209d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
210d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    Args:
211d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      node_id: int, id of the node which is the starting point of the edge.
212d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      edge_ix: int, index into the self._edge_list array.
213d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    Returns:
214d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      Edge, if the index contains an interesting edge, otherwise None.
215d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    Raises:
216d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      Exception: The node list of the snapshot is malformed.
217d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    """
218d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if edge_ix + self._edge_field_count > len(self._edge_list):
219d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      raise Exception('Snapshot edge list too short')
220d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
221d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    edge_type_ix = self._edge_list[edge_ix + self._edge_type_ix]
222d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    edge_type_string = self._edge_types[int(edge_type_ix)]
223d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
224d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if ChromeJsHeapSnapshotParser._IsEdgeTypeUninteresting(edge_type_string):
225d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return None
226d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
227d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    child_name_or_ix = self._edge_list[edge_ix + self._edge_name_or_ix_ix]
228d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    child_node_ix = self._edge_list[edge_ix + self._edge_to_node_ix]
229d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
230d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    # The child_node_ix is an index into the node list. Read the actual
231d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    # node information.
232d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    child_node_type_ix = self._node_list[child_node_ix + self._node_type_ix]
233d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    child_node_type_string = self._node_types[int(child_node_type_ix)]
234d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    child_node_id = self._node_list[child_node_ix + self._node_id_ix]
235d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
236d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if ChromeJsHeapSnapshotParser._IsNodeTypeUninteresting(
237d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        child_node_type_string):
238d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return None
239d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
240d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    child_name_string = ''
241d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    # For element nodes, the child has no name (only an index).
242d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (edge_type_string == 'element' or
243d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        int(child_name_or_ix) >= len(self._strings)):
244d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      child_name_string = str(child_name_or_ix)
245d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    else:
246d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      child_name_string = self._strings[int(child_name_or_ix)]
247d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return retaining_edge.RetainingEdge(node_id, child_node_id,
248d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                        edge_type_string, child_name_string)
249