Bug 1923344 - r=smaug
[gecko.git] / memory / replace / dmd / DMD.h
blobc057047800eb14b32f1e45215bab8a3a63c95c8c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef DMD_h___
8 #define DMD_h___
10 #include <stdarg.h>
11 #include <string.h>
13 #include <utility>
15 #include "mozilla/DebugOnly.h"
16 #include "mozilla/Types.h"
17 #include "mozilla/UniquePtr.h"
18 #include "replace_malloc_bridge.h"
20 namespace mozilla {
22 class JSONWriteFunc;
24 namespace dmd {
26 struct Sizes {
27 size_t mStackTracesUsed;
28 size_t mStackTracesUnused;
29 size_t mStackTraceTable;
30 size_t mLiveBlockTable;
31 size_t mDeadBlockTable;
33 Sizes() { Clear(); }
34 void Clear() { memset(this, 0, sizeof(Sizes)); }
37 // See further below for a description of each method. The DMDFuncs class
38 // should contain a virtual method for each of them (except IsRunning,
39 // which can be inferred from the DMDFuncs singleton existing).
40 struct DMDFuncs {
41 virtual void Report(const void*);
43 virtual void ReportOnAlloc(const void*);
45 virtual void ClearReports();
47 virtual void Analyze(UniquePtr<JSONWriteFunc>);
49 virtual void SizeOf(Sizes*);
51 virtual void StatusMsg(const char*, va_list) MOZ_FORMAT_PRINTF(2, 0);
53 virtual void ResetEverything(const char*);
55 #ifndef REPLACE_MALLOC_IMPL
56 // We deliberately don't use ReplaceMalloc::GetDMDFuncs here, because if we
57 // did, the following would happen.
58 // - The code footprint of each call to Get() larger as GetDMDFuncs ends
59 // up inlined.
60 // - When no replace-malloc library is loaded, the number of instructions
61 // executed is equivalent, but don't necessarily fit in the same cache
62 // line.
63 // - When a non-DMD replace-malloc library is loaded, the overhead is
64 // higher because there is first a check for the replace malloc bridge
65 // and then for the DMDFuncs singleton.
66 // Initializing the DMDFuncs singleton on the first access makes the
67 // overhead even worse. Either Get() is inlined and massive, or it isn't
68 // and a simple value check becomes a function call.
69 static DMDFuncs* Get() { return sSingleton.Get(); }
71 private:
72 // Wrapper class keeping a pointer to the DMD functions. It is statically
73 // initialized because it needs to be set early enough.
74 // Debug builds also check that it's never accessed before the static
75 // initialization actually occured, which could be the case if some other
76 // static initializer ended up calling into DMD.
77 class Singleton {
78 public:
79 Singleton()
80 : mValue(ReplaceMalloc::GetDMDFuncs())
81 # ifdef DEBUG
83 mInitialized(true)
84 # endif
88 DMDFuncs* Get() {
89 MOZ_ASSERT(mInitialized);
90 return mValue;
93 private:
94 DMDFuncs* mValue;
95 # ifdef DEBUG
96 bool mInitialized;
97 # endif
100 // This singleton pointer must be defined on the program side. In Gecko,
101 // this is done in xpcom/base/nsMemoryInfoDumper.cpp.
102 static /* DMDFuncs:: */ Singleton sSingleton;
103 #endif
106 #ifndef REPLACE_MALLOC_IMPL
107 // Mark a heap block as reported by a memory reporter.
108 inline void Report(const void* aPtr) {
109 DMDFuncs* funcs = DMDFuncs::Get();
110 if (funcs) {
111 funcs->Report(aPtr);
115 // Mark a heap block as reported immediately on allocation.
116 inline void ReportOnAlloc(const void* aPtr) {
117 DMDFuncs* funcs = DMDFuncs::Get();
118 if (funcs) {
119 funcs->ReportOnAlloc(aPtr);
123 // Clears existing reportedness data from any prior runs of the memory
124 // reporters. The following sequence should be used.
125 // - ClearReports()
126 // - run the memory reporters
127 // - Analyze()
128 // This sequence avoids spurious twice-reported warnings.
129 inline void ClearReports() {
130 DMDFuncs* funcs = DMDFuncs::Get();
131 if (funcs) {
132 funcs->ClearReports();
136 // Determines which heap blocks have been reported, and dumps JSON output
137 // (via |aWriter|) describing the heap.
139 // The following sample output contains comments that explain the format and
140 // design choices. The output files can be quite large, so a number of
141 // decisions were made to minimize size, such as using short property names and
142 // omitting properties whenever possible.
144 // {
145 // // The version number of the format, which will be incremented each time
146 // // backwards-incompatible changes are made. A mandatory integer.
147 // //
148 // // Version history:
149 // // - 1: Bug 1044709
150 // // - 2: Bug 1094552
151 // // - 3: Bug 1100851
152 // // - 4: Bug 1121830
153 // // - 5: Bug 1253512
154 // "version": 5,
156 // // Information about how DMD was invoked. A mandatory object.
157 // "invocation": {
158 // // The contents of the $DMD environment variable. A string, or |null| if
159 // // $DMD is undefined.
160 // "dmdEnvVar": "--mode=dark-matter",
162 // // The profiling mode. A mandatory string taking one of the following
163 // // values: "live", "dark-matter", "cumulative", "scan".
164 // "mode": "dark-matter",
165 // },
167 // // Details of all analyzed heap blocks. A mandatory array.
168 // "blockList": [
169 // // An example of a heap block.
170 // {
171 // // Requested size, in bytes. This is a mandatory integer.
172 // "req": 3584,
174 // // Requested slop size, in bytes. This is mandatory if it is non-zero,
175 // // but omitted otherwise.
176 // "slop": 512,
178 // // The stack trace at which the block was allocated. An optional
179 // // string that indexes into the "traceTable" object. If omitted, no
180 // // allocation stack trace was recorded for the block.
181 // "alloc": "A",
183 // // One or more stack traces at which this heap block was reported by a
184 // // memory reporter. An optional array that will only be present in
185 // // "dark-matter" mode. The elements are strings that index into
186 // // the "traceTable" object.
187 // "reps": ["B"]
189 // // The number of heap blocks with exactly the above properties. This
190 // // is mandatory if it is greater than one, but omitted otherwise.
191 // // (Blocks with identical properties don't have to be aggregated via
192 // // this property, but it can greatly reduce output file size.)
193 // "num": 5,
195 // // The address of the block. This is mandatory in "scan" mode, but
196 // // omitted otherwise.
197 // "addr": "4e4e4e4e",
199 // // The contents of the block, read one word at a time. This is
200 // // mandatory in "scan" mode for blocks at least one word long, but
201 // // omitted otherwise.
202 // "contents": ["0", "6", "7f7f7f7f", "0"]
203 // }
204 // ],
206 // // The stack traces referenced by elements of the "blockList" array. This
207 // // could be an array, but making it an object makes it easier to see
208 // // which stacks correspond to which references in the "blockList" array.
209 // "traceTable": {
210 // // Each property corresponds to a stack trace mentioned in the "blocks"
211 // // object. Each element is an index into the "frameTable" object.
212 // "A": ["D", "E"],
213 // "B": ["F", "G"]
214 // },
216 // // The stack frames referenced by the "traceTable" object. The
217 // // descriptions can be quite long, so they are stored separately from the
218 // // "traceTable" object so that each one only has to be written once.
219 // // This could also be an array, but again, making it an object makes it
220 // // easier to see which frames correspond to which references in the
221 // // "traceTable" object.
222 // "frameTable": {
223 // // Each property key is a frame key mentioned in the "traceTable" object.
224 // // Each property value is a string containing a frame description. Each
225 // // frame description must be in a format recognized by `fix_stacks.py`,
226 // // which requires a frame number at the start. Because each stack frame
227 // // description in this table can be shared between multiple stack
228 // // traces, we use a dummy value of #00. The proper frame number can be
229 // // reconstructed later by scripts that output stack traces in a
230 // // conventional non-shared format.
231 // "D": "#00: foo (Foo.cpp:123)",
232 // "E": "#00: bar (Bar.cpp:234)",
233 // "F": "#00: baz (Baz.cpp:345)",
234 // "G": "#00: quux (Quux.cpp:456)"
235 // }
236 // }
238 // Implementation note: normally, this function wouldn't be templated, but in
239 // that case, the function is compiled, which makes the destructor for the
240 // UniquePtr fire up, and that needs JSONWriteFunc to be fully defined. That,
241 // in turn, requires to include JSONWriter.h, which includes
242 // double-conversion.h, which ends up breaking various things built with
243 // -Werror for various reasons.
245 template <typename JSONWriteFunc>
246 inline void Analyze(UniquePtr<JSONWriteFunc> aWriteFunc) {
247 DMDFuncs* funcs = DMDFuncs::Get();
248 if (funcs) {
249 funcs->Analyze(std::move(aWriteFunc));
253 // Gets the size of various data structures. Used to implement a memory
254 // reporter for DMD.
255 inline void SizeOf(Sizes* aSizes) {
256 DMDFuncs* funcs = DMDFuncs::Get();
257 if (funcs) {
258 funcs->SizeOf(aSizes);
262 // Prints a status message prefixed with "DMD[<pid>]". Use sparingly.
263 MOZ_FORMAT_PRINTF(1, 2)
264 inline void StatusMsg(const char* aFmt, ...) {
265 DMDFuncs* funcs = DMDFuncs::Get();
266 if (funcs) {
267 va_list ap;
268 va_start(ap, aFmt);
269 funcs->StatusMsg(aFmt, ap);
270 va_end(ap);
274 // Indicates whether or not DMD is running.
275 inline bool IsRunning() { return !!DMDFuncs::Get(); }
277 // Resets all DMD options and then sets new ones according to those specified
278 // in |aOptions|. Also clears all recorded data about allocations. Only used
279 // for testing purposes.
280 inline void ResetEverything(const char* aOptions) {
281 DMDFuncs* funcs = DMDFuncs::Get();
282 if (funcs) {
283 funcs->ResetEverything(aOptions);
286 #endif
288 } // namespace dmd
289 } // namespace mozilla
291 #endif /* DMD_h___ */