tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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___ */