2012-12-11 04:05:07 +04:00
|
|
|
/* -*- 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___
|
|
|
|
|
2012-12-12 01:54:11 +04:00
|
|
|
#include <string.h>
|
2014-11-18 13:21:06 +03:00
|
|
|
#include <stdarg.h>
|
2012-12-11 04:05:07 +04:00
|
|
|
|
2014-11-18 13:21:06 +03:00
|
|
|
#include "mozilla/DebugOnly.h"
|
|
|
|
#include "mozilla/Move.h"
|
2012-12-11 04:05:07 +04:00
|
|
|
#include "mozilla/Types.h"
|
2014-11-13 03:24:28 +03:00
|
|
|
#include "mozilla/UniquePtr.h"
|
2012-12-11 04:05:07 +04:00
|
|
|
|
2014-11-18 13:21:06 +03:00
|
|
|
#include "replace_malloc_bridge.h"
|
|
|
|
|
2012-12-11 04:05:07 +04:00
|
|
|
namespace mozilla {
|
2014-09-03 07:34:58 +04:00
|
|
|
|
2014-11-13 03:24:28 +03:00
|
|
|
class JSONWriteFunc;
|
2014-09-03 07:34:58 +04:00
|
|
|
|
2012-12-11 04:05:07 +04:00
|
|
|
namespace dmd {
|
|
|
|
|
2014-11-18 13:21:06 +03:00
|
|
|
struct Sizes
|
|
|
|
{
|
|
|
|
size_t mStackTracesUsed;
|
|
|
|
size_t mStackTracesUnused;
|
|
|
|
size_t mStackTraceTable;
|
2014-11-19 05:57:20 +03:00
|
|
|
size_t mLiveBlockTable;
|
2015-01-16 07:38:38 +03:00
|
|
|
size_t mDeadBlockTable;
|
2014-11-18 13:21:06 +03:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2014-12-09 01:45:13 +03:00
|
|
|
virtual void Analyze(UniquePtr<JSONWriteFunc>);
|
2014-11-18 13:21:06 +03:00
|
|
|
|
|
|
|
virtual void SizeOf(Sizes*);
|
|
|
|
|
|
|
|
virtual void StatusMsg(const char*, va_list);
|
|
|
|
|
2014-12-09 01:45:14 +03:00
|
|
|
virtual void ResetEverything(const char*);
|
2014-11-18 13:21:06 +03:00
|
|
|
|
|
|
|
#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()), mInitialized(true) {}
|
|
|
|
|
|
|
|
DMDFuncs* Get()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mInitialized);
|
|
|
|
return mValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
DMDFuncs* mValue;
|
|
|
|
DebugOnly<bool> mInitialized;
|
|
|
|
};
|
|
|
|
|
|
|
|
// 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
|
2012-12-11 04:05:07 +04:00
|
|
|
// Mark a heap block as reported by a memory reporter.
|
2014-11-18 13:21:06 +03:00
|
|
|
inline void
|
|
|
|
Report(const void* aPtr)
|
|
|
|
{
|
|
|
|
DMDFuncs* funcs = DMDFuncs::Get();
|
|
|
|
if (funcs) {
|
|
|
|
funcs->Report(aPtr);
|
|
|
|
}
|
|
|
|
}
|
2012-12-11 04:05:07 +04:00
|
|
|
|
|
|
|
// Mark a heap block as reported immediately on allocation.
|
2014-11-18 13:21:06 +03:00
|
|
|
inline void
|
|
|
|
ReportOnAlloc(const void* aPtr)
|
|
|
|
{
|
|
|
|
DMDFuncs* funcs = DMDFuncs::Get();
|
|
|
|
if (funcs) {
|
|
|
|
funcs->ReportOnAlloc(aPtr);
|
|
|
|
}
|
|
|
|
}
|
2012-12-11 04:05:07 +04:00
|
|
|
|
2013-01-07 01:34:39 +04:00
|
|
|
// Clears existing reportedness data from any prior runs of the memory
|
|
|
|
// reporters. The following sequence should be used.
|
|
|
|
// - ClearReports()
|
|
|
|
// - run the memory reporters
|
2014-12-09 01:45:13 +03:00
|
|
|
// - Analyze()
|
2013-01-07 01:34:39 +04:00
|
|
|
// This sequence avoids spurious twice-reported warnings.
|
2014-11-18 13:21:06 +03:00
|
|
|
inline void
|
|
|
|
ClearReports()
|
|
|
|
{
|
|
|
|
DMDFuncs* funcs = DMDFuncs::Get();
|
|
|
|
if (funcs) {
|
|
|
|
funcs->ClearReports();
|
|
|
|
}
|
|
|
|
}
|
2013-01-07 01:34:39 +04:00
|
|
|
|
2014-09-03 07:34:58 +04:00
|
|
|
// 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.
|
2014-12-09 01:45:14 +03:00
|
|
|
// //
|
|
|
|
// // Version history:
|
2015-01-16 07:38:38 +03:00
|
|
|
// // - 1: Bug 1044709. The original format.
|
|
|
|
// // - 2: Bug 1094552. Added the "mode" property under "invocation".
|
|
|
|
// // - 3: Bug 1100851. The "dmdEnvVar" property under "invocation" can now
|
|
|
|
// // be |null| if the |DMD| environment variable is not defined.
|
|
|
|
// // - 4: Bug 1121830. Added the "num" property in "blockList" object.
|
|
|
|
// //
|
|
|
|
// "version": 4,
|
2014-09-03 07:34:58 +04:00
|
|
|
//
|
|
|
|
// // Information about how DMD was invoked. A mandatory object.
|
|
|
|
// "invocation": {
|
2014-11-28 08:04:46 +03:00
|
|
|
// // The contents of the $DMD environment variable. A string, or |null| is
|
|
|
|
// // $DMD is undefined.
|
|
|
|
// "dmdEnvVar": "--mode=dark-matter",
|
2014-09-03 07:34:58 +04:00
|
|
|
//
|
2014-12-09 01:45:14 +03:00
|
|
|
// // The profiling mode. A mandatory string taking one of the following
|
2014-10-31 06:22:47 +03:00
|
|
|
// // values: "live", "dark-matter", "cumulative".
|
2014-12-09 01:45:14 +03:00
|
|
|
// "mode": "dark-matter",
|
|
|
|
//
|
2014-09-03 07:34:58 +04:00
|
|
|
// // The value of the --sample-below-size option. A mandatory integer.
|
|
|
|
// "sampleBelowSize": 4093
|
|
|
|
// },
|
|
|
|
//
|
|
|
|
// // Details of all analyzed heap blocks. A mandatory array.
|
|
|
|
// "blockList": [
|
|
|
|
// // An example of a non-sampled heap block.
|
|
|
|
// {
|
|
|
|
// // Requested size, in bytes. In non-sampled blocks this is a
|
|
|
|
// // mandatory integer. In sampled blocks this is not present, and the
|
|
|
|
// // requested size is equal to the "sampleBelowSize" value. Therefore,
|
|
|
|
// // the block is sampled if and only if this property is absent.
|
|
|
|
// "req": 3584,
|
|
|
|
//
|
|
|
|
// // Requested slop size, in bytes. This is mandatory if it is non-zero,
|
|
|
|
// // but omitted otherwise. Because sampled blocks never have slop, this
|
|
|
|
// // property is never present for non-sampled blocks.
|
|
|
|
// "slop": 512,
|
|
|
|
//
|
|
|
|
// // The stack trace at which the block was allocated. A mandatory
|
|
|
|
// // string which indexes into the "traceTable" object.
|
2015-01-16 07:38:38 +03:00
|
|
|
// "alloc": "A",
|
|
|
|
//
|
|
|
|
// // 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
|
2014-09-03 07:34:58 +04:00
|
|
|
// },
|
|
|
|
//
|
|
|
|
// // An example of a sampled heap block.
|
|
|
|
// {
|
|
|
|
// "alloc": "B",
|
|
|
|
//
|
|
|
|
// // One or more stack traces at which this heap block was reported by a
|
2014-12-09 01:45:14 +03:00
|
|
|
// // memory reporter. An optional array that will only be present in
|
|
|
|
// // "dark-matter" mode. The elements are strings that index into
|
|
|
|
// // the "traceTable" object.
|
2014-09-03 07:34:58 +04:00
|
|
|
// "reps": ["C"]
|
|
|
|
// }
|
|
|
|
// ],
|
|
|
|
//
|
|
|
|
// // 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": ["D", "F"],
|
|
|
|
// "C": ["G", "H"]
|
|
|
|
// },
|
2014-04-08 21:07:46 +04:00
|
|
|
//
|
2014-09-03 07:34:58 +04:00
|
|
|
// // 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 the stack-fixing
|
|
|
|
// // scripts (e.g. fix_linux_stack.py), which require 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)",
|
|
|
|
// "H": "#00: quuux (Quux.cpp:567)"
|
|
|
|
// }
|
|
|
|
// }
|
2014-12-09 01:45:14 +03:00
|
|
|
//
|
2015-01-16 07:38:38 +03:00
|
|
|
// 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.
|
2014-12-09 01:45:14 +03:00
|
|
|
//
|
2014-11-18 13:21:06 +03:00
|
|
|
template <typename JSONWriteFunc>
|
|
|
|
inline void
|
2014-12-09 01:45:13 +03:00
|
|
|
Analyze(UniquePtr<JSONWriteFunc> aWriteFunc)
|
2012-12-12 01:54:11 +04:00
|
|
|
{
|
2014-11-18 13:21:06 +03:00
|
|
|
DMDFuncs* funcs = DMDFuncs::Get();
|
|
|
|
if (funcs) {
|
2014-12-09 01:45:13 +03:00
|
|
|
funcs->Analyze(Move(aWriteFunc));
|
2014-11-18 13:21:06 +03:00
|
|
|
}
|
|
|
|
}
|
2012-12-12 01:54:11 +04:00
|
|
|
|
|
|
|
// Gets the size of various data structures. Used to implement a memory
|
|
|
|
// reporter for DMD.
|
2014-11-18 13:21:06 +03:00
|
|
|
inline void
|
|
|
|
SizeOf(Sizes* aSizes)
|
|
|
|
{
|
|
|
|
DMDFuncs* funcs = DMDFuncs::Get();
|
|
|
|
if (funcs) {
|
|
|
|
funcs->SizeOf(aSizes);
|
|
|
|
}
|
|
|
|
}
|
2012-12-12 01:54:11 +04:00
|
|
|
|
2014-09-05 07:45:22 +04:00
|
|
|
// Prints a status message prefixed with "DMD[<pid>]". Use sparingly.
|
2014-11-18 13:21:06 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2014-09-05 07:45:22 +04:00
|
|
|
|
2014-05-23 06:45:14 +04:00
|
|
|
// Indicates whether or not DMD is running.
|
2014-11-18 13:21:06 +03:00
|
|
|
inline bool
|
|
|
|
IsRunning()
|
|
|
|
{
|
|
|
|
return !!DMDFuncs::Get();
|
|
|
|
}
|
2014-04-24 23:43:11 +04:00
|
|
|
|
2014-12-09 01:45:14 +03:00
|
|
|
// 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.
|
2014-11-18 13:21:06 +03:00
|
|
|
inline void
|
2014-12-09 01:45:14 +03:00
|
|
|
ResetEverything(const char* aOptions)
|
2014-11-18 13:21:06 +03:00
|
|
|
{
|
|
|
|
DMDFuncs* funcs = DMDFuncs::Get();
|
|
|
|
if (funcs) {
|
2014-12-09 01:45:14 +03:00
|
|
|
funcs->ResetEverything(aOptions);
|
2014-11-18 13:21:06 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2014-10-10 06:28:33 +04:00
|
|
|
|
2012-12-11 04:05:07 +04:00
|
|
|
} // namespace dmd
|
2014-11-18 13:21:06 +03:00
|
|
|
} // namespace mozilla
|
2012-12-11 04:05:07 +04:00
|
|
|
|
|
|
|
#endif /* DMD_h___ */
|