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