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