memory/replace/dmd/DMD.h
author Francois Marier <francois@mozilla.com>
Tue, 18 Nov 2014 01:12:00 +0100
changeset 242695 199fcbb1b0187dc5ea98a4b701553bce14bd2b8e
parent 242643 3c6bd176246d277649e7a73dd00f201cfa194fc6
child 243462 c3ecdd9ea544eb0407fbeda412acfb6793e7c9a2
permissions -rw-r--r--
Bug 529697 - (CSP 1.1) Implement form-action directive [1/4], r=geekboy Teach CSP about the form-action directive from CSP Level 2.

/* -*- 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 <string.h>
#include <stdarg.h>

#include "mozilla/DebugOnly.h"
#include "mozilla/Move.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 mBlockTable;

  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 AnalyzeReports(UniquePtr<JSONWriteFunc>);

  virtual void SizeOf(Sizes*);

  virtual void StatusMsg(const char*, va_list);

  virtual void SetSampleBelowSize(size_t);

  virtual void ClearBlocks();

#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
// 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
// - AnalyzeReports()
// 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": 1,
//
//   // Information about how DMD was invoked. A mandatory object.
//   "invocation": {
//     // The contents of the $DMD environment variable. A mandatory string.
//     "dmdEnvVar": "1",
//
//     // 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.
//       "alloc": "A"
//     },
//
//     // An example of a sampled heap block.
//     {
//       "alloc": "B",
//
//       // One or more stack traces at which this heap block was reported by a
//       // memory reporter. An optional array. The elements are strings that
//       // index into the "traceTable" object.
//       "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"]
//   },
//
//   // 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)"
//   }
// }
// Implementation note: normally, this 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
AnalyzeReports(UniquePtr<JSONWriteFunc> aWriteFunc)
{
  DMDFuncs* funcs = DMDFuncs::Get();
  if (funcs) {
    funcs->AnalyzeReports(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.
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();
}

// Sets the sample-below size. Only used for testing purposes.
inline void
SetSampleBelowSize(size_t aSize)
{
  DMDFuncs* funcs = DMDFuncs::Get();
  if (funcs) {
    funcs->SetSampleBelowSize(aSize);
  }
}

// Clears all records of live allocations. Only used for testing purposes.
inline void
ClearBlocks()
{
  DMDFuncs* funcs = DMDFuncs::Get();
  if (funcs) {
    funcs->ClearBlocks();
  }
}
#endif

} // namespace dmd
} // namespace mozilla

#endif /* DMD_h___ */