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