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