summaryrefslogtreecommitdiffstats
path: root/memory/replace/dmd/DMD.h
blob: c057047800eb14b32f1e45215bab8a3a63c95c8c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef DMD_h___
#define DMD_h___

#include <stdarg.h>
#include <string.h>

#include <utility>

#include "mozilla/DebugOnly.h"
#include "mozilla/Types.h"
#include "mozilla/UniquePtr.h"
#include "replace_malloc_bridge.h"

namespace mozilla {

class JSONWriteFunc;

namespace dmd {

struct Sizes {
  size_t mStackTracesUsed;
  size_t mStackTracesUnused;
  size_t mStackTraceTable;
  size_t mLiveBlockTable;
  size_t mDeadBlockTable;

  Sizes() { Clear(); }
  void Clear() { memset(this, 0, sizeof(Sizes)); }
};

// See further below for a description of each method. The DMDFuncs class
// should contain a virtual method for each of them (except IsRunning,
// which can be inferred from the DMDFuncs singleton existing).
struct DMDFuncs {
  virtual void Report(const void*);

  virtual void ReportOnAlloc(const void*);

  virtual void ClearReports();

  virtual void Analyze(UniquePtr<JSONWriteFunc>);

  virtual void SizeOf(Sizes*);

  virtual void StatusMsg(const char*, va_list) MOZ_FORMAT_PRINTF(2, 0);

  virtual void ResetEverything(const char*);

#ifndef REPLACE_MALLOC_IMPL
  // We deliberately don't use ReplaceMalloc::GetDMDFuncs here, because if we
  // did, the following would happen.
  // - The code footprint of each call to Get() larger as GetDMDFuncs ends
  //   up inlined.
  // - When no replace-malloc library is loaded, the number of instructions
  //   executed is equivalent, but don't necessarily fit in the same cache
  //   line.
  // - When a non-DMD replace-malloc library is loaded, the overhead is
  //   higher because there is first a check for the replace malloc bridge
  //   and then for the DMDFuncs singleton.
  // Initializing the DMDFuncs singleton on the first access makes the
  // overhead even worse. Either Get() is inlined and massive, or it isn't
  // and a simple value check becomes a function call.
  static DMDFuncs* Get() { return sSingleton.Get(); }

 private:
  // Wrapper class keeping a pointer to the DMD functions. It is statically
  // initialized because it needs to be set early enough.
  // Debug builds also check that it's never accessed before the static
  // initialization actually occured, which could be the case if some other
  // static initializer ended up calling into DMD.
  class Singleton {
   public:
    Singleton()
        : mValue(ReplaceMalloc::GetDMDFuncs())
#  ifdef DEBUG
          ,
          mInitialized(true)
#  endif
    {
    }

    DMDFuncs* Get() {
      MOZ_ASSERT(mInitialized);
      return mValue;
    }

   private:
    DMDFuncs* mValue;
#  ifdef DEBUG
    bool mInitialized;
#  endif
  };

  // This singleton pointer must be defined on the program side. In Gecko,
  // this is done in xpcom/base/nsMemoryInfoDumper.cpp.
  static /* DMDFuncs:: */ Singleton sSingleton;
#endif
};

#ifndef REPLACE_MALLOC_IMPL
// Mark a heap block as reported by a memory reporter.
inline void Report(const void* aPtr) {
  DMDFuncs* funcs = DMDFuncs::Get();
  if (funcs) {
    funcs->Report(aPtr);
  }
}

// Mark a heap block as reported immediately on allocation.
inline void ReportOnAlloc(const void* aPtr) {
  DMDFuncs* funcs = DMDFuncs::Get();
  if (funcs) {
    funcs->ReportOnAlloc(aPtr);
  }
}

// Clears existing reportedness data from any prior runs of the memory
// reporters.  The following sequence should be used.
// - ClearReports()
// - run the memory reporters
// - Analyze()
// This sequence avoids spurious twice-reported warnings.
inline void ClearReports() {
  DMDFuncs* funcs = DMDFuncs::Get();
  if (funcs) {
    funcs->ClearReports();
  }
}

// Determines which heap blocks have been reported, and dumps JSON output
// (via |aWriter|) describing the heap.
//
// The following sample output contains comments that explain the format and
// design choices. The output files can be quite large, so a number of
// decisions were made to minimize size, such as using short property names and
// omitting properties whenever possible.
//
// {
//   // The version number of the format, which will be incremented each time
//   // backwards-incompatible changes are made. A mandatory integer.
//   //
//   // Version history:
//   // - 1: Bug 1044709
//   // - 2: Bug 1094552
//   // - 3: Bug 1100851
//   // - 4: Bug 1121830
//   // - 5: Bug 1253512
//   "version": 5,
//
//   // Information about how DMD was invoked. A mandatory object.
//   "invocation": {
//     // The contents of the $DMD environment variable. A string, or |null| if
//     // $DMD is undefined.
//     "dmdEnvVar": "--mode=dark-matter",
//
//     // The profiling mode. A mandatory string taking one of the following
//     // values: "live", "dark-matter", "cumulative", "scan".
//     "mode": "dark-matter",
//   },
//
//   // Details of all analyzed heap blocks. A mandatory array.
//   "blockList": [
//     // An example of a heap block.
//     {
//       // Requested size, in bytes. This is a mandatory integer.
//       "req": 3584,
//
//       // Requested slop size, in bytes. This is mandatory if it is non-zero,
//       // but omitted otherwise.
//       "slop": 512,
//
//       // The stack trace at which the block was allocated. An optional
//       // string that indexes into the "traceTable" object. If omitted, no
//       // allocation stack trace was recorded for the block.
//       "alloc": "A",
//
//       // One or more stack traces at which this heap block was reported by a
//       // memory reporter. An optional array that will only be present in
//       // "dark-matter" mode. The elements are strings that index into
//       // the "traceTable" object.
//       "reps": ["B"]
//
//       // The number of heap blocks with exactly the above properties. This
//       // is mandatory if it is greater than one, but omitted otherwise.
//       // (Blocks with identical properties don't have to be aggregated via
//       // this property, but it can greatly reduce output file size.)
//       "num": 5,
//
//       // The address of the block. This is mandatory in "scan" mode, but
//       // omitted otherwise.
//       "addr": "4e4e4e4e",
//
//       // The contents of the block, read one word at a time. This is
//       // mandatory in "scan" mode for blocks at least one word long, but
//       // omitted otherwise.
//       "contents": ["0", "6", "7f7f7f7f", "0"]
//     }
//   ],
//
//   // The stack traces referenced by elements of the "blockList" array. This
//   // could be an array, but making it an object makes it easier to see
//   // which stacks correspond to which references in the "blockList" array.
//   "traceTable": {
//     // Each property corresponds to a stack trace mentioned in the "blocks"
//     // object. Each element is an index into the "frameTable" object.
//     "A": ["D", "E"],
//     "B": ["F", "G"]
//   },
//
//   // The stack frames referenced by the "traceTable" object. The
//   // descriptions can be quite long, so they are stored separately from the
//   // "traceTable" object so that each one only has to be written once.
//   // This could also be an array, but again, making it an object makes it
//   // easier to see which frames correspond to which references in the
//   // "traceTable" object.
//   "frameTable": {
//     // Each property key is a frame key mentioned in the "traceTable" object.
//     // Each property value is a string containing a frame description. Each
//     // frame description must be in a format recognized by `fix_stacks.py`,
//     // which requires a frame number at the start. Because each stack frame
//     // description in this table can be shared between multiple stack
//     // traces, we use a dummy value of #00. The proper frame number can be
//     // reconstructed later by scripts that output stack traces in a
//     // conventional non-shared format.
//     "D": "#00: foo (Foo.cpp:123)",
//     "E": "#00: bar (Bar.cpp:234)",
//     "F": "#00: baz (Baz.cpp:345)",
//     "G": "#00: quux (Quux.cpp:456)"
//   }
// }
//
// Implementation note: normally, this function wouldn't be templated, but in
// that case, the function is compiled, which makes the destructor for the
// UniquePtr fire up, and that needs JSONWriteFunc to be fully defined. That,
// in turn, requires to include JSONWriter.h, which includes
// double-conversion.h, which ends up breaking various things built with
// -Werror for various reasons.
//
template <typename JSONWriteFunc>
inline void Analyze(UniquePtr<JSONWriteFunc> aWriteFunc) {
  DMDFuncs* funcs = DMDFuncs::Get();
  if (funcs) {
    funcs->Analyze(std::move(aWriteFunc));
  }
}

// Gets the size of various data structures.  Used to implement a memory
// reporter for DMD.
inline void SizeOf(Sizes* aSizes) {
  DMDFuncs* funcs = DMDFuncs::Get();
  if (funcs) {
    funcs->SizeOf(aSizes);
  }
}

// Prints a status message prefixed with "DMD[<pid>]". Use sparingly.
MOZ_FORMAT_PRINTF(1, 2)
inline void StatusMsg(const char* aFmt, ...) {
  DMDFuncs* funcs = DMDFuncs::Get();
  if (funcs) {
    va_list ap;
    va_start(ap, aFmt);
    funcs->StatusMsg(aFmt, ap);
    va_end(ap);
  }
}

// Indicates whether or not DMD is running.
inline bool IsRunning() { return !!DMDFuncs::Get(); }

// Resets all DMD options and then sets new ones according to those specified
// in |aOptions|. Also clears all recorded data about allocations. Only used
// for testing purposes.
inline void ResetEverything(const char* aOptions) {
  DMDFuncs* funcs = DMDFuncs::Get();
  if (funcs) {
    funcs->ResetEverything(aOptions);
  }
}
#endif

}  // namespace dmd
}  // namespace mozilla

#endif /* DMD_h___ */