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