1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved.
2a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
3a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)# found in the LICENSE file.
4a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)"""This module handles the JSON de/serialization of the core classes.
6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)This is needed for both long term storage (e.g., loading/storing traces to local
8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)files) and for short term data exchange (AJAX with the HTML UI).
9a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)The rationale of these serializers is to store data in an efficient (i.e. avoid
11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)to store redundant information) and intelligible (i.e. flatten the classes
12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)hierarchy keeping only the meaningful bits) format.
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)"""
14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import json
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
17effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfrom memory_inspector.classification import results
18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from memory_inspector.core import backends
19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from memory_inspector.core import memory_map
20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from memory_inspector.core import native_heap
21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from memory_inspector.core import stacktrace
22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from memory_inspector.core import symbol
23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class Encoder(json.JSONEncoder):
26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  def default(self, obj):  # pylint: disable=E0202
27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if isinstance(obj, memory_map.Map):
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return [entry.__dict__ for entry in obj.entries]
29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if isinstance(obj, symbol.Symbols):
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return obj.symbols
32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if isinstance(obj, (symbol.Symbol, symbol.SourceInfo)):
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return obj.__dict__
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if isinstance(obj, native_heap.NativeHeap):
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      # Just keep the list of (distinct) stack frames from the index. Encoding
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      # it as a JSON dictionary would be redundant.
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return {'stack_frames': obj.stack_frames.values(),
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              'allocations': obj.allocations}
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if isinstance(obj, native_heap.Allocation):
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return obj.__dict__
44a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if isinstance(obj, stacktrace.Stacktrace):
46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      # Keep just absolute addrs of stack frames. The full frame details will be
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      # kept in (and rebuilt from) |native_heap.NativeHeap.stack_frames|. See
48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      # NativeHeapDecoder below.
49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return [frame.address for frame in obj.frames]
50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if isinstance(obj, stacktrace.Frame):
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      # Strip out the symbol information from stack frames. Symbols are stored
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      # (and will be loaded) separately. Rationale: different heap snapshots can
54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      # share the same symbol db. Serializing the symbol information for each
55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      # stack frame for each heap snapshot is a waste.
56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return {'address': obj.address,
57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              'exec_file_rel_path': obj.exec_file_rel_path,
58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              'offset': obj.offset}
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if isinstance(obj, (backends.DeviceStats, backends.ProcessStats)):
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return obj.__dict__
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
63effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if isinstance(obj, results.AggreatedResults):
64effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      return {'keys': obj.keys, 'buckets': obj.total}
65effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
66effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if isinstance(obj, results.Bucket):
67effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      return {obj.rule.name : {'values': obj.values, 'children': obj.children}}
68effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return json.JSONEncoder.default(self, obj)
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class MmapDecoder(json.JSONDecoder):
73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  def decode(self, json_str):  # pylint: disable=W0221
74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    d = super(MmapDecoder, self).decode(json_str)
75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    mmap = memory_map.Map()
76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    for entry_dict in d:
77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      entry = memory_map.MapEntry(**entry_dict)
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      mmap.Add(entry)
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return mmap
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class SymbolsDecoder(json.JSONDecoder):
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  def decode(self, json_str):  # pylint: disable=W0221
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    d = super(SymbolsDecoder, self).decode(json_str)
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    symbols = symbol.Symbols()
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    for sym_key, sym_dict in d.iteritems():
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      sym = symbol.Symbol(sym_dict['name'])
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      for source_info in sym_dict['source_info']:
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        sym.AddSourceLineInfo(**source_info)
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      symbols.symbols[sym_key] = sym
91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return symbols
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class NativeHeapDecoder(json.JSONDecoder):
95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  def decode(self, json_str):  # pylint: disable=W0221
96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    d = super(NativeHeapDecoder, self).decode(json_str)
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    nh = native_heap.NativeHeap()
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    # First load and rebuild the stack_frame index.
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    for frame_dict in d['stack_frames']:
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      frame = nh.GetStackFrame(frame_dict['address'])
101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      frame.SetExecFileInfo(frame_dict['exec_file_rel_path'],
102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                            frame_dict['offset'])
103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    # Then load backtraces (reusing stack frames from the index above).
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    for alloc_dict in d['allocations']:
105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      stack_trace = stacktrace.Stacktrace()
106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      for absolute_addr in alloc_dict['stack_trace']:
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        stack_trace.Add(nh.GetStackFrame(absolute_addr))
108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      allocation = native_heap.Allocation(alloc_dict['size'],
109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                          alloc_dict['count'],
110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                          stack_trace)
111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      nh.Add(allocation)
112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return nh