summaryrefslogtreecommitdiffstats
path: root/devtools/docs/memory-panel.md
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/docs/memory-panel.md')
-rw-r--r--devtools/docs/memory-panel.md226
1 files changed, 226 insertions, 0 deletions
diff --git a/devtools/docs/memory-panel.md b/devtools/docs/memory-panel.md
new file mode 100644
index 000000000..25b86f066
--- /dev/null
+++ b/devtools/docs/memory-panel.md
@@ -0,0 +1,226 @@
+# Memory Tool Architecture
+
+The memory tool is built of three main elements:
+
+1. The live heap graph exists in memory, and is managed by the C++ allocator and
+ garbage collector. In order to get access to the structure of this graph, a
+ specialized interface is created to represent its state. The `JS::ubi::Node`
+ is the basis for this representation. This interface can be created from the
+ live heap graph, or a serialized, offline snapshot from a previous moment in
+ time. Our various heap analyses (census, dominator trees, shortest paths,
+ etc) run on top of `JS::ubi::Node` graphs. The `ubi` in the name stands for
+ "ubiquitous" and provides a namespace for memory analyses in C++ code.
+
+2. The `HeapAnalysesWorker` runs in a worker thread, performing analyses on
+ snapshots and translating the results into something the frontend can render
+ simply and quickly. The `HeapAnalysesClient` is used to communicate between
+ the worker and the main thread.
+
+3. Finally, the last element is the frontend that renders data received from the
+ `HeapAnalysesClient` to the DOM and translates user input into requests for
+ new data with the `HeapAnalysesClient`.
+
+Unlike other tools (such as the JavaScript debugger), the memory tool makes very
+little use of the Remote Debugger Server and the actors that reside in it. Use
+of the [`MemoryActor`](devtools/server/actors/memory.js) is limited to toggling
+allocation stack recording on and off, and transferring heap snapshots from the
+debuggee (which is on the server) to the `HeapAnalysesWorker` (which is on the
+client). A nice benefit that naturally emerges, is that supporting "legacy"
+servers (eg, using Firefox Developer Edition as a client to remote debug a
+release Firefox for Android server) is a no-op. As we add new analyses, we can
+run them on snapshots taken on old servers no problem. The only requirement is
+that changes to the snapshot format itself remain backwards compatible.
+
+## `JS::ubi::Node`
+
+`JS::ubi::Node` is a lightweight serializable interface that can represent the
+current state of the heap graph. For a deeper dive into the particulars of how
+it works, it is very well documented in the `js/public/UbiNode.h`
+
+A "heap snapshot" is a representation of the heap graph at some particular past
+instance in time.
+
+A "heap analysis" is an algorithm that runs on a `JS::ubi::Node` heap graph.
+Generally, analyses can run on either the live heap graph or a deserialized
+snapshot. Example analyses include "census", which aggregates and counts nodes
+into various user-specified buckets; "dominator trees", which compute the
+[dominates](https://en.wikipedia.org/wiki/Dominator_%28graph_theory%29) relation
+and retained size for all nodes in the heap graph; and "shortest paths" which
+finds the shortest paths from the GC roots to some subset of nodes.
+
+### Saving Heap Snapshots
+
+Saving a heap snapshot has a few requirements:
+
+1. The binary format must remain backwards compatible and future extensible.
+
+2. The live heap graph must not mutate while we are in the process of
+ serializing it.
+
+3. The act of saving a heap snapshot should impose as little memory overhead as
+ possible. If we are taking a snapshot to debug frequent out-of-memory errors,
+ we don't want to trigger an OOM ourselves!
+
+To solve (1), we use the [protobuf](https://developers.google.com/protocol-buffers/)
+message format. The message definitions themselves are in
+`devtools/shared/heapsnapshot/CoreDump.proto`. We always use `optional` fields
+so we can change our mind about what fields are required sometime in the future.
+Deserialization checks the semantic integrity of deserialized protobuf messages.
+
+For (2), we rely on SpiderMonkey's GC rooting hazard static analysis and the
+`AutoCheckCannotGC` dynamic analysis to ensure that neither JS nor GC runs and
+modifies objects or moves them from one address in memory to another. There is
+no equivalent suppression and static analysis technique for the
+[cycle collector](https://developer.mozilla.org/en/docs/Interfacing_with_the_XPCOM_cycle_collector),
+so care must be taken not to invoke methods that could start cycle collection or
+mutate the heap graph from the cycle collector's perspective. At the time of
+writing, we don't yet support saving the cycle collector's portion of the heap
+graph in snapshots, but that work is deemed Very Important and Very High
+Priority.
+
+Finally, (3) imposes upon us that we do not build the serialized heap snapshot
+binary blob in memory, but instead stream it out to disk while generating it.
+
+Once all of that is accounted for, saving snapshots becomes pretty straight
+forward. We traverse the live heap graph with `JS::ubi::Node` and
+`JS::ubi::BreadthFirst`, create a protobuf message for each node and each node's
+edges, and write these messages to disk before continuing the traversal to the
+next node.
+
+This functionality is exposed to chrome JavaScript as the
+`[ThreadSafe]ChromeUtils.saveHeapSnapshot` function. See
+`dom/webidl/ThreadSafeChromeUtils.webidl` for API documentation.
+
+### Reading Heap Snapshots
+
+Reading heap snapshots has less restrictions than saving heap snapshots. The
+protobuf messages that make up the core dump are deserialized one by one, stored
+as a set of `DeserializedNode`s and a set of `DeserializedEdge`s, and the result
+is a `HeapSnapshot` instance.
+
+The `DeserializedNode` and `DeserializedEdge` classes implement the
+`JS::ubi::Node` interface. Analyses running on offline heap snapshots rather
+than the live heap graph operate on these classes (unknowingly, of course).
+
+For more details, see the
+[`mozilla::devtools::HeapSnapshot`](devtools/shared/heapsnapshot/HeapSnapshot.cpp)
+and
+[`mozilla::devtools::Deserialized{Node,Edge}`](devtools/shared/heapsnapshot/DeserializedNode.h)
+classes.
+
+### Heap Analyses
+
+Heap analyses operate on `JS::ubi::Node` graphs without knowledge of whether
+that graph is backed by the live heap graph or an offline heap snapshot. They
+must make sure never to allocate GC things or modify the live heap graph.
+
+In general, analyses are implemented in their own `js/public/Ubi{AnalysisName}.h`
+header (eg `js/public/UbiCensus.h`), and are exposed to chrome JavaScript code
+via a method on the [`HeapSnapshot`](dom/webidl/HeapSnapshot.webidl) webidl
+interface.
+
+For each analysis we expose to chrome JavaScript on the `HeapSnapshot` webidl
+interface, there is a small amount of glue code in Gecko. The
+[`mozilla::devtools::HeapSnapshot`](devtools/shared/heapsnapshot/HeapSnapshot.h)
+C++ class implements the webidl interface. The analyses methods (eg
+`ComputeDominatorTree`) take the deserialized nodes and edges from the heap
+snapshot, create `JS::ubi::Node`s from them, call the analyses from
+`js/public/Ubi*.h`, and wrap the results in something that can be represented in
+JavaScript.
+
+For API documentation on running specific analyses, see the
+[`HeapSnapshot`](dom/webidl/HeapSnapshot.webidl) webidl interface.
+
+### Testing `JS::ubi::Node`, Snapshots, and Analyses
+
+The majority of the tests reside within `devtools/shared/heapsnapshot/tests/**`.
+For reading and saving heap snapshots, most tests are gtests. The gtests can be
+run with the `mach gtest DevTools.*` command. The rest are integration sanity
+tests to make sure we can read and save snapshots in various environments, such
+as xpcshell or workers. These can be run with the usual `mach test $PATH`
+commands.
+
+There are also `JS::ubi::Node` related unit tests in
+`js/src/jit-test/tests/heap-analysis/*`, `js/src/jit-test/tests/debug/Memory-*`,
+and `js/src/jsapi-tests/testUbiNode.cpp`. See
+https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Running_Automated_JavaScript_Tests#Running_jit-tests
+for running the JIT tests.
+
+## `HeapAnalysesWorker`
+
+The `HeapAnalysesWorker` orchestrates running specific analyses on snapshots and
+transforming the results into something that can simply and quickly be rendered
+by the frontend. The analyses can take some time to run (sometimes on the order
+of seconds), so doing them in a worker thread allows the interface to stay
+responsive. The `HeapAnalysisClient` provides the main thread's interface to the
+worker.
+
+The `HeapAnalysesWorker` doesn't actually do much itself; mostly just shuffling
+data and transforming it from one representation to another or calling C++
+utility functions exposed by webidl that do those things. Most of these are
+implemented as traversals of the resulting census or dominator trees.
+
+See the following files for details on the various data transformations and
+shuffling that the `HeapAnalysesWorker` delegates to.
+
+* `devtools/shared/heapsnapshot/CensusUtils.js`
+* `devtools/shared/heapsnapshot/CensusTreeNode.js`
+* `devtools/shared/heapsnapshot/DominatorTreeNode.js`
+
+### Testing the `HeapAnalysesWorker` and `HeapAnalysesClient`
+
+Tests for the `HeapAnalysesWorker` and `HeapAnalysesClient` reside in
+`devtools/shared/heapsnapshot/tests/**` and can be run with the usual `mach test
+$PATH` command.
+
+## Frontend
+
+The frontend of the memory tool is built with React and Redux.
+
+[React has thorough documentation.](https://facebook.github.io/react/)
+
+[Redux has thorough documentation.](http://rackt.org/redux/index.html)
+
+We have React components in `devtools/client/memory/components/*`.
+
+We have Redux reducers in `devtools/client/memory/reducers/*`.
+
+We have Redux actions and action-creating tasks in
+`devtools/client/memory/actions/*`.
+
+React components should be pure functions from their props to the rendered
+(virtual) DOM. Redux reducers should also be observably pure.
+
+Impurity within the frontend is confined to the tasks that are creating and
+dispatching actions. All communication with the outside world (such as the
+`HeapAnalysesWorker`, the Remote Debugger Server, or the file system) is
+restricted to within these tasks.
+
+### Snapshots State
+
+On the JavaScript side, the snapshots represent a reference to the underlying
+heap dump and the various analyses. The following diagram represents a finite
+state machine describing the snapshot states. Any of these states may go to the
+ERROR state, from which they can never leave.
+
+```
+SAVING → SAVED → READING → READ
+ ↗
+ IMPORTING
+```
+
+Each of the report types (census, diffing, tree maps, dominators) have their own states as well, and are documented at `devtools/client/memory/constants.js`.
+These report states are updated as the various filtering and selecting options
+are updated in the UI.
+
+### Testing the Frontend
+
+Unit tests for React components are in `devtools/client/memory/test/chrome/*`.
+
+Unit tests for actions, reducers, and state changes are in
+`devtools/client/memory/test/unit/*`.
+
+Holistic integration tests for the frontend and the whole memory tool are in
+`devtools/client/memory/test/browser/*`.
+
+All tests can be run with the usual `mach test $PATH` command.