blob: 7601867aab0730a36ae20330f9e0380e41f51f7a [file] [log] [blame] [view]
hjd0304f112016-11-14 22:36:531# Heap Profiling with MemoryInfra
2
3As of Chrome 48, MemoryInfra supports heap profiling. The core principle is
4a solution that JustWorks on all platforms without patching or rebuilding,
qyearsleyc0dc6f42016-12-02 22:13:395integrated with the chrome://tracing ecosystem.
hjd0304f112016-11-14 22:36:536
7[TOC]
8
9## How to Use
10
11 1. Start Chrome with the `--enable-heap-profiling` switch. This will make
12 Chrome keep track of all allocations.
13
14 2. Grab a [MemoryInfra][memory-infra] trace. For best results, start tracing
15 first, and _then_ open a new tab that you want to trace. Furthermore,
16 enabling more categories (besides memory-infra) will yield more detailed
17 information in the heap profiler backtraces.
18
19 3. When the trace has been collected, select a heavy memory dump indicated by
20 a purple ![M][m-purple] dot. Heap dumps are only included in heavy memory
21 dumps.
22
23 4. In the analysis view, cells marked with a triple bar icon (☰) contain heap
24 dumps. Select such a cell.
25
26 ![Cells containing a heap dump][cells-heap-dump]
27
28 5. Scroll down all the way to _Heap Details_.
29
30 6. Pinpoint the memory bug and live happily ever after.
31
32[memory-infra]: README.md
33[m-purple]: https://storage.googleapis.com/chromium-docs.appspot.com/d7bdf4d16204c293688be2e5a0bcb2bf463dbbc3
34[cells-heap-dump]: https://storage.googleapis.com/chromium-docs.appspot.com/a24d80d6a08da088e2e9c8b2b64daa215be4dacb
35
36### Native stack traces
37
38By default heap profiling collects pseudo allocation traces, which are based
39on trace events. I.e. frames in allocation traces correspond to trace events
40that were active at the time of allocations, and are not real function names.
erikchenffe16492017-06-23 00:43:2741It's also possible to use heap profiling with native, symbolized stack traces.
42
43#### Native stack traces (Chrome - macOS/Windows)
44
451. Using any officially distributed build of Chrome, navigate to chrome://flags,
46 and set "enable-heap-profiling" to Enabled (native mode).
47
482. Use the [TraceOnTap][extension-link] extension to grab a trace.
49
503. Run the following script to symbolize the trace.
51
52 third_party/catapult/tracing/bin/symbolize_trace <trace file>
53
544. Load the trace file in `chrome://tracing`. Locate a purple ![M][m-purple]
55 dot, and continue from step *3* from the instructions above. Native stack
56 traces will be shown in the _Heap Details_ pane.
57
58[extension-link]: https://cs.chromium.org/chromium/src/third_party/catapult/experimental/trace_on_tap/?q=traceontap+package:%5Echromium$&dr=CSs
59
60#### Native stack traces (Chromium - all OSes)
61
62On Linux / Android, you need to build Chromium with special flags to use native
63heap profiling. On macOS / Windows, it's also possible to use native heap
64profiling with Chromium.
hjd0304f112016-11-14 22:36:5365
66 1. Build with the following GN flags:
67
erikchenffe16492017-06-23 00:43:2768 macOS / Windows
69
70 symbol_level = 1
71
72 Linux
hjd0304f112016-11-14 22:36:5373
74 enable_profiling = true
erikchenffe16492017-06-23 00:43:2775 symbol_level = 1
hjd0304f112016-11-14 22:36:5376
77 Android
78
79 arm_use_thumb = false
80 enable_profiling = true
erikchenffe16492017-06-23 00:43:2781 symbol_level = 1
hjd0304f112016-11-14 22:36:5382
83 2. Start Chrome with `--enable-heap-profiling=native` switch (notice
84 `=native` part).
85
86 On Android use the command line tool before starting the app:
87
88 build/android/adb_chrome_public_command_line --enable-heap-profiling=native
89
90 (run the tool with an empty argument `''` to clear the command line)
91
92 3. Grab a [MemoryInfra][memory-infra] trace. You don't need any other
93 categories besides `memory-infra`.
94
95 4. Save the grabbed trace file. This step is needed because freshly
96 taken trace file contains raw addresses (which look like `pc:dcf5dbf8`)
97 instead of function names, and needs to be symbolized.
98
99 4. Symbolize the trace file. During symbolization addresses are resolved to
100 the corresponding function names and trace file is rewritten (but a backup
101 is saved with `.BACKUP` extension).
102
103 Linux
104
105 third_party/catapult/tracing/bin/symbolize_trace <trace file>
106
107 Android
108
109 third_party/catapult/tracing/bin/symbolize_trace --output-directory out/Release <trace file>
110
111 (note `--output-directory` and make sure it's right for your setup)
112
113 5. Load the trace file in `chrome://tracing`. Locate a purple ![M][m-purple]
114 dot, and continue from step *3* from the instructions above. Native stack
115 traces will be shown in the _Heap Details_ pane.
116
117## Heap Details
118
119The heap details view contains a tree that represents the heap. The size of the
120root node corresponds to the selected allocator cell.
121
122*** aside
123The size value in the heap details view will not match the value in the selected
124analysis view cell exactly. There are three reasons for this. First, the heap
125profiler reports the memory that _the program requested_, whereas the allocator
126reports the memory that it _actually allocated_ plus its own bookkeeping
127overhead. Second, allocations that happen early --- before Chrome knows that
128heap profiling is enabled --- are not captured by the heap profiler, but they
129are reported by the allocator. Third, tracing overhead is not discounted by the
130heap profiler.
131***
132
133The heap can be broken down in two ways: by _backtrace_ (marked with an ƒ), and
134by _type_ (marked with a Ⓣ). When tracing is enabled, Chrome records trace
135events, most of which appear in the flame chart in timeline view. At every
136point in time these trace events form a pseudo stack, and a vertical slice
137through the flame chart is like a backtrace. This corresponds to the ƒ nodes in
138the heap details view. Hence enabling more tracing categories will give a more
139detailed breakdown of the heap.
140
141The other way to break down the heap is by object type. At the moment this is
142only supported for PartitionAlloc.
143
144*** aside
145In official builds, only the most common type names are included due to binary
146size concerns. Development builds have full type information.
147***
148
149To keep the trace log small, uninteresting information is omitted from heap
150dumps. The long tail of small nodes is not dumped, but grouped in an `<other>`
qyearsleyc0dc6f42016-12-02 22:13:39151node instead. Note that although these small nodes are insignificant on their
hjd0304f112016-11-14 22:36:53152own, together they can be responsible for a significant portion of the heap. The
153`<other>` node is large in that case.
154
155## Example
156
157In the trace below, `ParseAuthorStyleSheet` is called at some point.
158
159![ParseAuthorStyleSheet pseudo stack][pseudo-stack]
160
161The pseudo stack of trace events corresponds to the tree of ƒ nodes below. Of
162the 23.5 MiB of memory allocated with PartitionAlloc, 1.9 MiB was allocated
163inside `ParseAuthorStyleSheet`, either directly, or at a deeper level (like
164`CSSParserImpl::parseStyleSheet`).
165
166![Memory Allocated in ParseAuthorStyleSheet][break-down-by-backtrace]
167
168By expanding `ParseAuthorStyleSheet`, we can see which types were allocated
169there. Of the 1.9 MiB, 371 KiB was spent on `ImmutableStylePropertySet`s, and
170238 KiB was spent on `StringImpl`s.
171
172![ParseAuthorStyleSheet broken down by type][break-down-by-type]
173
174It is also possible to break down by type first, and then by backtrace. Below
175we see that of the 23.5 MiB allocated with PartitionAlloc, 1 MiB is spent on
176`Node`s, and about half of the memory spent on nodes was allocated in
177`HTMLDocumentParser`.
178
179![The PartitionAlloc heap broken down by type first and then by backtrace][type-then-backtrace]
180
181Heap dump diffs are fully supported by trace viewer. Select a heavy memory dump
182(a purple dot), then with the control key select a heavy memory dump earlier in
183time. Below is a diff of theverge.com before and in the middle of loading ads.
184We can see that 4 MiB were allocated when parsing the documents in all those
185iframes, almost a megabyte of which was due to JavaScript. (Note that this is
186memory allocated by PartitionAlloc alone, the total renderer memory increase was
187around 72 MiB.)
188
189![Diff of The Verge before and after loading ads][diff]
190
191[pseudo-stack]: https://storage.googleapis.com/chromium-docs.appspot.com/058e50350836f55724e100d4dbbddf4b9803f550
192[break-down-by-backtrace]: https://storage.googleapis.com/chromium-docs.appspot.com/ec61c5f15705f5bcf3ca83a155ed647a0538bbe1
193[break-down-by-type]: https://storage.googleapis.com/chromium-docs.appspot.com/2236e61021922c0813908c6745136953fa20a37b
194[type-then-backtrace]: https://storage.googleapis.com/chromium-docs.appspot.com/c5367dde11476bdbf2d5a1c51674148915573d11
195[diff]: https://storage.googleapis.com/chromium-docs.appspot.com/802141906869cd533bb613da5f91bd0b071ceb24