1# Heap Profiling with MemoryInfra
2
3As of Chrome 48, MemoryInfra supports heap profiling. Chrome will track all live
4allocations (calls to new or malloc without a subsequent call to delete or free)
5along with sufficient metadata to identify the code that made the allocation.
6
7By default, MemoryInfra traces will not contain heap dumps. Heap profiling must
8be enabled via chrome://memory-internals or about://flags.
9
10[TOC]
11
12## How to obtain a heap dump (M66+, Linux, macOS, Windows)
13
14 1. Navigate to chrome://memory-internals.
15    * There will be an error message at the top if heap-profiling is not
16      supported on the current configuration
17 2. Enable heap profiling for the relevant processes. Future allocations will be
18    tracked. Refresh the page to view tracked processes.
19    * To enable tracking at process start, navigate to chrome://flags and search
20      for `memlog`.
21 3. To take a heap dump, click `save dump`. This is stored as a
22    [MemoryInfra][memory-infra] trace.
23 4. To symbolize the trace:
24   * Windows only: build `addr2line-pdb` from the chromium repository. For subsequent commands, add the flag `--addr2line-executable=<path_to_addr2lin-pdb>`
25   * If this is a local build, run the command `./third_party/catapult/tracing/bin/symbolize_trace --is-local-build <path_to_trace>`
26   * If this is an official Chrome build,  run `./third_party/catapult/tracing/bin/symbolize_trace <path_to_trace>`. This will request authentication with google cloud storage to obtain symbol files [googlers only].
27   * If this is an official macOS or Linux Chrome build, add the flag `--use-breakpad-symbols`.
28   * If the trace is from a different device, add the flag `--only-symbolize-chrome-symbols`.
29 5. Load the (now symbolized) trace in chrome://tracing.
30
31## How to obtain a heap dump (M66+, Android)
32
33To obtain native heap dumps, you will need a custom build of Chrome with the GN
34arguments `enable_profiling = true`, `arm_use_thumb = false` and
35`symbol_level=1`. All other steps are the same.
36
37Alternatively, if you want to use an Official build of Chrome, navigate to
38chrome://flags and set `memlog-stack-mode` to `pseudo`. This will provide
39less-detailed stacks. The stacks also don't require symbolization.
40
41## How to obtain a heap dump (M65 and older)
42
43For the most part, the setting `enable-heap-profiling` in `chrome://flags` has a
44similar effect to the various `memlog` flags.
45
46
47## How to manually browse a heap dump
48
49 1. Select a heavy memory dump indicated by a purple ![M][m-purple] dot.
50
51 2. In the analysis view, cells marked with a triple bar icon (☰) contain heap
52    dumps. Select such a cell.
53
54      ![Cells containing a heap dump][cells-heap-dump]
55
56 3. Scroll down all the way to _Heap Details_.
57
58 4. To navigate allocations, select a frame in the right-side pane and press
59    Enter/Return. To pop up the stack, press Backspace/Delete.
60
61[memory-infra]:    README.md
62[m-purple]:        https://storage.googleapis.com/chromium-docs.appspot.com/d7bdf4d16204c293688be2e5a0bcb2bf463dbbc3
63[cells-heap-dump]: https://storage.googleapis.com/chromium-docs.appspot.com/a24d80d6a08da088e2e9c8b2b64daa215be4dacb
64
65## How to automatically extract large allocations from a heap dump
66
67 1. Run `python ./third_party/catapult/experimental/tracing/bin/diff_heap_profiler.py
68    <path_to_trace>`
69
70 2. This produces a directory `output`, which contains a JSON file.
71
72 3. Load the contents of the JSON file in any JSON viewer, e.g.
73    [jsonviewer](http://jsonviewer.stack.hu/).
74
75 4. The JSON files shows allocations segmented by stacktrace, sorted by largest
76    first.
77
78## Heap Details
79
80The heap details view contains a tree that represents the heap. The size of the
81root node corresponds to the selected allocator cell.
82
83*** aside
84The size value in the heap details view will not match the value in the selected
85analysis view cell exactly. There are three reasons for this. First, the heap
86profiler reports the memory that _the program requested_, whereas the allocator
87reports the memory that it _actually allocated_ plus its own bookkeeping
88overhead. Second, allocations that happen early --- before Chrome knows that
89heap profiling is enabled --- are not captured by the heap profiler, but they
90are reported by the allocator. Third, tracing overhead is not discounted by the
91heap profiler.
92***
93
94The heap can be broken down in two ways: by _backtrace_ (marked with an ƒ), and
95by _type_ (marked with a Ⓣ). When tracing is enabled, Chrome records trace
96events, most of which appear in the flame chart in timeline view. At every
97point in time these trace events form a pseudo stack, and a vertical slice
98through the flame chart is like a backtrace. This corresponds to the ƒ nodes in
99the heap details view.  Hence enabling more tracing categories will give a more
100detailed breakdown of the heap.
101
102The other way to break down the heap is by object type. At the moment this is
103only supported for PartitionAlloc.
104
105*** aside
106In official builds, only the most common type names are included due to binary
107size concerns. Development builds have full type information.
108***
109
110To keep the trace log small, uninteresting information is omitted from heap
111dumps. The long tail of small nodes is not dumped, but grouped in an `<other>`
112node instead. Note that although these small nodes are insignificant on their
113own, together they can be responsible for a significant portion of the heap. The
114`<other>` node is large in that case.
115
116## Example
117
118In the trace below, `ParseAuthorStyleSheet` is called at some point.
119
120![ParseAuthorStyleSheet pseudo stack][pseudo-stack]
121
122The pseudo stack of trace events corresponds to the tree of ƒ nodes below. Of
123the 23.5 MiB of memory allocated with PartitionAlloc, 1.9 MiB was allocated
124inside `ParseAuthorStyleSheet`, either directly, or at a deeper level (like
125`CSSParserImpl::parseStyleSheet`).
126
127![Memory Allocated in ParseAuthorStyleSheet][break-down-by-backtrace]
128
129By expanding `ParseAuthorStyleSheet`, we can see which types were allocated
130there. Of the 1.9 MiB, 371 KiB was spent on `ImmutableStylePropertySet`s, and
131238 KiB was spent on `StringImpl`s.
132
133![ParseAuthorStyleSheet broken down by type][break-down-by-type]
134
135It is also possible to break down by type first, and then by backtrace. Below
136we see that of the 23.5 MiB allocated with PartitionAlloc, 1 MiB is spent on
137`Node`s, and about half of the memory spent on nodes was allocated in
138`HTMLDocumentParser`.
139
140![The PartitionAlloc heap broken down by type first and then by backtrace][type-then-backtrace]
141
142Heap dump diffs are fully supported by trace viewer. Select a heavy memory dump
143(a purple dot), then with the control key select a heavy memory dump earlier in
144time. Below is a diff of theverge.com before and in the middle of loading ads.
145We can see that 4 MiB were allocated when parsing the documents in all those
146iframes, almost a megabyte of which was due to JavaScript. (Note that this is
147memory allocated by PartitionAlloc alone, the total renderer memory increase was
148around 72 MiB.)
149
150![Diff of The Verge before and after loading ads][diff]
151
152[pseudo-stack]:            https://storage.googleapis.com/chromium-docs.appspot.com/058e50350836f55724e100d4dbbddf4b9803f550
153[break-down-by-backtrace]: https://storage.googleapis.com/chromium-docs.appspot.com/ec61c5f15705f5bcf3ca83a155ed647a0538bbe1
154[break-down-by-type]:      https://storage.googleapis.com/chromium-docs.appspot.com/2236e61021922c0813908c6745136953fa20a37b
155[type-then-backtrace]:     https://storage.googleapis.com/chromium-docs.appspot.com/c5367dde11476bdbf2d5a1c51674148915573d11
156[diff]:                    https://storage.googleapis.com/chromium-docs.appspot.com/802141906869cd533bb613da5f91bd0b071ceb24
157