mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-01 12:57:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
116
tools/memory-profiler/CompactTraceTable.h
Normal file
116
tools/memory-profiler/CompactTraceTable.h
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/* -*- 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 memory_profiler_CompactTraceTable_h
|
||||
#define memory_profiler_CompactTraceTable_h
|
||||
|
||||
#include "mozilla/HashFunctions.h"
|
||||
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct TrieNode final
|
||||
{
|
||||
uint32_t parentIdx;
|
||||
uint32_t nameIdx;
|
||||
bool operator==(const TrieNode t) const
|
||||
{
|
||||
return parentIdx == t.parentIdx && nameIdx == t.nameIdx;
|
||||
}
|
||||
uint32_t Hash() const
|
||||
{
|
||||
return HashGeneric(parentIdx, nameIdx);
|
||||
}
|
||||
};
|
||||
|
||||
// This class maps a Node of type T to its parent's index in the
|
||||
// map. When serializing, the map is traversed and put into an ordered
|
||||
// array of Nodes.
|
||||
template<typename KeyClass, typename T>
|
||||
class NodeIndexMap final
|
||||
{
|
||||
public:
|
||||
uint32_t Insert(const T& e)
|
||||
{
|
||||
uint32_t index = mMap.Count();
|
||||
if (!mMap.Get(e, &index)) {
|
||||
mMap.Put(e, index);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
nsTArray<T> Serialize() const
|
||||
{
|
||||
nsTArray<T> v;
|
||||
v.SetLength(mMap.Count());
|
||||
for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
v[iter.Data()] = iter.Key();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
uint32_t Size() const
|
||||
{
|
||||
return mMap.Count();
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
mMap.Clear();
|
||||
}
|
||||
private:
|
||||
nsDataHashtable<KeyClass, uint32_t> mMap;
|
||||
};
|
||||
|
||||
// Backtraces are stored in a trie to save spaces.
|
||||
// Function names are stored in an unique table and TrieNodes contain indexes
|
||||
// into that table.
|
||||
// The trie is implemented with a hash table; children are stored in
|
||||
// traces[TrieNode{parent node index, branch/function name index}].
|
||||
class CompactTraceTable final
|
||||
{
|
||||
public:
|
||||
CompactTraceTable()
|
||||
{
|
||||
mNames.Insert(nsAutoCString("(unknown)"));
|
||||
mTraces.Insert(TrieNode{0, 0});
|
||||
}
|
||||
|
||||
nsTArray<nsCString> GetNames() const
|
||||
{
|
||||
return mNames.Serialize();
|
||||
}
|
||||
|
||||
nsTArray<TrieNode> GetTraces() const
|
||||
{
|
||||
return mTraces.Serialize();
|
||||
}
|
||||
|
||||
// Returns an ID to a stacktrace.
|
||||
uint32_t Insert(const nsTArray<nsCString>& aRawStacktrace)
|
||||
{
|
||||
uint32_t parent = 0;
|
||||
for (auto& frame: aRawStacktrace) {
|
||||
parent = mTraces.Insert(TrieNode{parent, mNames.Insert(frame)});
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
mNames.Clear();
|
||||
mTraces.Clear();
|
||||
}
|
||||
private:
|
||||
NodeIndexMap<nsCStringHashKey, nsCString> mNames;
|
||||
NodeIndexMap<nsGenericHashKey<TrieNode>, TrieNode> mTraces;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // memory_profiler_CompactTraceTable_h
|
||||
168
tools/memory-profiler/GCHeapProfilerImpl.cpp
Normal file
168
tools/memory-profiler/GCHeapProfilerImpl.cpp
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "GCHeapProfilerImpl.h"
|
||||
|
||||
#include "UncensoredAllocator.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
GCHeapProfilerImpl::GCHeapProfilerImpl()
|
||||
{
|
||||
mLock = PR_NewLock();
|
||||
mMarking = false;
|
||||
}
|
||||
|
||||
GCHeapProfilerImpl::~GCHeapProfilerImpl()
|
||||
{
|
||||
if (mLock) {
|
||||
PR_DestroyLock(mLock);
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<nsCString>
|
||||
GCHeapProfilerImpl::GetNames() const
|
||||
{
|
||||
return mTraceTable.GetNames();
|
||||
}
|
||||
|
||||
nsTArray<TrieNode>
|
||||
GCHeapProfilerImpl::GetTraces() const
|
||||
{
|
||||
return mTraceTable.GetTraces();
|
||||
}
|
||||
|
||||
const nsTArray<AllocEvent>&
|
||||
GCHeapProfilerImpl::GetEvents() const
|
||||
{
|
||||
return mAllocEvents;
|
||||
}
|
||||
|
||||
void
|
||||
GCHeapProfilerImpl::reset()
|
||||
{
|
||||
mTraceTable.Reset();
|
||||
mAllocEvents.Clear();
|
||||
mNurseryEntries.Clear();
|
||||
mTenuredEntriesFG.Clear();
|
||||
mTenuredEntriesBG.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
GCHeapProfilerImpl::sampleTenured(void* addr, uint32_t size)
|
||||
{
|
||||
SampleInternal(addr, size, mTenuredEntriesFG);
|
||||
}
|
||||
|
||||
void
|
||||
GCHeapProfilerImpl::sampleNursery(void* addr, uint32_t size)
|
||||
{
|
||||
SampleInternal(addr, size, mNurseryEntries);
|
||||
}
|
||||
|
||||
void
|
||||
GCHeapProfilerImpl::markTenuredStart()
|
||||
{
|
||||
AutoUseUncensoredAllocator ua;
|
||||
AutoMPLock lock(mLock);
|
||||
if (!mMarking) {
|
||||
mMarking = true;
|
||||
mTenuredEntriesFG.SwapElements(mTenuredEntriesBG);
|
||||
MOZ_ASSERT(mTenuredEntriesFG.Count() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCHeapProfilerImpl::markTenured(void* addr)
|
||||
{
|
||||
AutoUseUncensoredAllocator ua;
|
||||
AutoMPLock lock(mLock);
|
||||
if (mMarking) {
|
||||
AllocEntry entry;
|
||||
if (mTenuredEntriesBG.Get(addr, &entry)) {
|
||||
entry.mMarked = true;
|
||||
mTenuredEntriesBG.Put(addr, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCHeapProfilerImpl::sweepTenured()
|
||||
{
|
||||
AutoUseUncensoredAllocator ua;
|
||||
AutoMPLock lock(mLock);
|
||||
if (mMarking) {
|
||||
mMarking = false;
|
||||
for (auto iter = mTenuredEntriesBG.Iter(); !iter.Done(); iter.Next()) {
|
||||
if (iter.Data().mMarked) {
|
||||
iter.Data().mMarked = false;
|
||||
mTenuredEntriesFG.Put(iter.Key(), iter.Data());
|
||||
} else {
|
||||
AllocEvent& oldEvent = mAllocEvents[iter.Data().mEventIdx];
|
||||
AllocEvent newEvent(oldEvent.mTraceIdx, -oldEvent.mSize, TimeStamp::Now());
|
||||
mAllocEvents.AppendElement(newEvent);
|
||||
}
|
||||
}
|
||||
mTenuredEntriesBG.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCHeapProfilerImpl::sweepNursery()
|
||||
{
|
||||
AutoUseUncensoredAllocator ua;
|
||||
AutoMPLock lock(mLock);
|
||||
for (auto iter = mNurseryEntries.Iter(); !iter.Done(); iter.Next()) {
|
||||
AllocEvent& oldEvent = mAllocEvents[iter.Data().mEventIdx];
|
||||
AllocEvent newEvent(oldEvent.mTraceIdx, -oldEvent.mSize, TimeStamp::Now());
|
||||
mAllocEvents.AppendElement(newEvent);
|
||||
}
|
||||
mNurseryEntries.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
GCHeapProfilerImpl::moveNurseryToTenured(void* addrOld, void* addrNew)
|
||||
{
|
||||
AutoUseUncensoredAllocator ua;
|
||||
AutoMPLock lock(mLock);
|
||||
AllocEntry entryOld;
|
||||
if (!mNurseryEntries.Get(addrOld, &entryOld)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Because the tenured heap is sampled, the address might already be there.
|
||||
// If not, the address is inserted with the old event.
|
||||
AllocEntry tenuredEntryOld;
|
||||
if (!mTenuredEntriesFG.Get(addrNew, &tenuredEntryOld)) {
|
||||
mTenuredEntriesFG.Put(addrNew, AllocEntry(entryOld.mEventIdx));
|
||||
} else {
|
||||
// If it is already inserted, the insertion above will fail and the
|
||||
// iterator of the already-inserted element is returned.
|
||||
// We choose to ignore the the new event by setting its size zero and point
|
||||
// the newly allocated address to the old event.
|
||||
// An event of size zero will be skipped when reporting.
|
||||
mAllocEvents[entryOld.mEventIdx].mSize = 0;
|
||||
tenuredEntryOld.mEventIdx = entryOld.mEventIdx;
|
||||
mTenuredEntriesFG.Put(addrNew, tenuredEntryOld);
|
||||
}
|
||||
mNurseryEntries.Remove(addrOld);
|
||||
}
|
||||
|
||||
void
|
||||
GCHeapProfilerImpl::SampleInternal(void* aAddr, uint32_t aSize, AllocMap& aTable)
|
||||
{
|
||||
AutoUseUncensoredAllocator ua;
|
||||
AutoMPLock lock(mLock);
|
||||
size_t nSamples = AddBytesSampled(aSize);
|
||||
if (nSamples > 0) {
|
||||
nsTArray<nsCString> trace = GetStacktrace();
|
||||
AllocEvent ai(mTraceTable.Insert(trace), nSamples * mSampleSize, TimeStamp::Now());
|
||||
aTable.Put(aAddr, AllocEntry(mAllocEvents.Length()));
|
||||
mAllocEvents.AppendElement(ai);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
53
tools/memory-profiler/GCHeapProfilerImpl.h
Normal file
53
tools/memory-profiler/GCHeapProfilerImpl.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/* -*- 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 memory_profiler_GCHeapProfilerImpl_h
|
||||
#define memory_profiler_GCHeapProfilerImpl_h
|
||||
|
||||
#include "CompactTraceTable.h"
|
||||
#include "MemoryProfiler.h"
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class GCHeapProfilerImpl final : public GCHeapProfiler
|
||||
, public ProfilerImpl
|
||||
{
|
||||
public:
|
||||
GCHeapProfilerImpl();
|
||||
~GCHeapProfilerImpl() override;
|
||||
|
||||
nsTArray<nsCString> GetNames() const override;
|
||||
nsTArray<TrieNode> GetTraces() const override;
|
||||
const nsTArray<AllocEvent>& GetEvents() const override;
|
||||
|
||||
void reset() override;
|
||||
void sampleTenured(void* addr, uint32_t size) override;
|
||||
void sampleNursery(void* addr, uint32_t size) override;
|
||||
void markTenuredStart() override;
|
||||
void markTenured(void* addr) override;
|
||||
void sweepTenured() override;
|
||||
void sweepNursery() override;
|
||||
void moveNurseryToTenured(void* addrOld, void* addrNew) override;
|
||||
|
||||
private:
|
||||
void SampleInternal(void* addr, uint32_t size, AllocMap& table);
|
||||
|
||||
PRLock* mLock;
|
||||
bool mMarking;
|
||||
|
||||
AllocMap mNurseryEntries;
|
||||
AllocMap mTenuredEntriesFG;
|
||||
AllocMap mTenuredEntriesBG;
|
||||
|
||||
nsTArray<AllocEvent> mAllocEvents;
|
||||
CompactTraceTable mTraceTable;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // memory_profiler_GCHeapProfilerImpl_h
|
||||
324
tools/memory-profiler/MemoryProfiler.cpp
Normal file
324
tools/memory-profiler/MemoryProfiler.cpp
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "MemoryProfiler.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "GCHeapProfilerImpl.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "NativeProfilerImpl.h"
|
||||
#include "UncensoredAllocator.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "nsIDOMClassInfo.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
#include "prtime.h"
|
||||
#include "xpcprivate.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#define MEMORY_PROFILER_SAMPLE_SIZE 65536
|
||||
#define BACKTRACE_BUFFER_SIZE 16384
|
||||
|
||||
ProfilerImpl::ProfilerImpl()
|
||||
: mSampleSize(MEMORY_PROFILER_SAMPLE_SIZE)
|
||||
{
|
||||
mLog1minusP = std::log(1.0 - 1.0 / mSampleSize);
|
||||
mRemainingBytes = std::floor(std::log(1.0 - DRandom()) / mLog1minusP);
|
||||
}
|
||||
|
||||
nsTArray<nsCString>
|
||||
ProfilerImpl::GetStacktrace()
|
||||
{
|
||||
nsTArray<nsCString> trace;
|
||||
auto output = MakeUnique<char[]>(BACKTRACE_BUFFER_SIZE);
|
||||
|
||||
profiler_get_backtrace_noalloc(output.get(), BACKTRACE_BUFFER_SIZE);
|
||||
for (const char* p = output.get(); *p; p += strlen(p) + 1) {
|
||||
trace.AppendElement()->Assign(p);
|
||||
}
|
||||
|
||||
return trace;
|
||||
}
|
||||
|
||||
// Generate a random number in [0, 1).
|
||||
double
|
||||
ProfilerImpl::DRandom()
|
||||
{
|
||||
return double(((uint64_t(std::rand()) & ((1 << 26) - 1)) << 27) +
|
||||
(uint64_t(std::rand()) & ((1 << 27) - 1)))
|
||||
/ (uint64_t(1) << 53);
|
||||
}
|
||||
|
||||
size_t
|
||||
ProfilerImpl::AddBytesSampled(uint32_t aBytes)
|
||||
{
|
||||
size_t nSamples = 0;
|
||||
while (mRemainingBytes <= aBytes) {
|
||||
mRemainingBytes += std::floor(std::log(1.0 - DRandom()) / mLog1minusP);
|
||||
nSamples++;
|
||||
}
|
||||
mRemainingBytes -= aBytes;
|
||||
return nSamples;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(MemoryProfiler, nsIMemoryProfiler)
|
||||
|
||||
PRLock* MemoryProfiler::sLock;
|
||||
uint32_t MemoryProfiler::sProfileContextCount;
|
||||
StaticAutoPtr<NativeProfilerImpl> MemoryProfiler::sNativeProfiler;
|
||||
StaticAutoPtr<JSContextProfilerMap> MemoryProfiler::sJSContextProfilerMap;
|
||||
TimeStamp MemoryProfiler::sStartTime;
|
||||
|
||||
void
|
||||
MemoryProfiler::InitOnce()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
static bool initialized = false;
|
||||
|
||||
if (!initialized) {
|
||||
MallocHook::Initialize();
|
||||
sLock = PR_NewLock();
|
||||
sProfileContextCount = 0;
|
||||
sJSContextProfilerMap = new JSContextProfilerMap();
|
||||
ClearOnShutdown(&sJSContextProfilerMap);
|
||||
ClearOnShutdown(&sNativeProfiler);
|
||||
std::srand(PR_Now());
|
||||
bool ignored;
|
||||
sStartTime = TimeStamp::ProcessCreation(ignored);
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MemoryProfiler::StartProfiler()
|
||||
{
|
||||
InitOnce();
|
||||
AutoUseUncensoredAllocator ua;
|
||||
AutoMPLock lock(sLock);
|
||||
JSContext* context = XPCJSContext::Get()->Context();
|
||||
ProfilerForJSContext profiler;
|
||||
if (!sJSContextProfilerMap->Get(context, &profiler) ||
|
||||
!profiler.mEnabled) {
|
||||
if (sProfileContextCount == 0) {
|
||||
js::EnableContextProfilingStack(context, true);
|
||||
if (!sNativeProfiler) {
|
||||
sNativeProfiler = new NativeProfilerImpl();
|
||||
}
|
||||
MemProfiler::SetNativeProfiler(sNativeProfiler);
|
||||
}
|
||||
GCHeapProfilerImpl* gp = new GCHeapProfilerImpl();
|
||||
profiler.mEnabled = true;
|
||||
profiler.mProfiler = gp;
|
||||
sJSContextProfilerMap->Put(context, profiler);
|
||||
MemProfiler::GetMemProfiler(context)->start(gp);
|
||||
if (sProfileContextCount == 0) {
|
||||
MallocHook::Enable(sNativeProfiler);
|
||||
}
|
||||
sProfileContextCount++;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MemoryProfiler::StopProfiler()
|
||||
{
|
||||
InitOnce();
|
||||
AutoUseUncensoredAllocator ua;
|
||||
AutoMPLock lock(sLock);
|
||||
JSContext* context = XPCJSContext::Get()->Context();
|
||||
ProfilerForJSContext profiler;
|
||||
if (sJSContextProfilerMap->Get(context, &profiler) &&
|
||||
profiler.mEnabled) {
|
||||
MemProfiler::GetMemProfiler(context)->stop();
|
||||
if (--sProfileContextCount == 0) {
|
||||
MallocHook::Disable();
|
||||
MemProfiler::SetNativeProfiler(nullptr);
|
||||
js::EnableContextProfilingStack(context, false);
|
||||
}
|
||||
profiler.mEnabled = false;
|
||||
sJSContextProfilerMap->Put(context, profiler);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MemoryProfiler::ResetProfiler()
|
||||
{
|
||||
InitOnce();
|
||||
AutoUseUncensoredAllocator ua;
|
||||
AutoMPLock lock(sLock);
|
||||
JSContext* context = XPCJSContext::Get()->Context();
|
||||
ProfilerForJSContext profiler;
|
||||
if (!sJSContextProfilerMap->Get(context, &profiler) ||
|
||||
!profiler.mEnabled) {
|
||||
delete profiler.mProfiler;
|
||||
profiler.mProfiler = nullptr;
|
||||
sJSContextProfilerMap->Put(context, profiler);
|
||||
}
|
||||
if (sProfileContextCount == 0) {
|
||||
sNativeProfiler = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
struct MergedTraces
|
||||
{
|
||||
nsTArray<nsCString> mNames;
|
||||
nsTArray<TrieNode> mTraces;
|
||||
nsTArray<AllocEvent> mEvents;
|
||||
};
|
||||
|
||||
// Merge events and corresponding traces and names.
|
||||
static MergedTraces
|
||||
MergeResults(const nsTArray<nsCString>& names0,
|
||||
const nsTArray<TrieNode>& traces0,
|
||||
const nsTArray<AllocEvent>& events0,
|
||||
const nsTArray<nsCString>& names1,
|
||||
const nsTArray<TrieNode>& traces1,
|
||||
const nsTArray<AllocEvent>& events1)
|
||||
{
|
||||
NodeIndexMap<nsCStringHashKey, nsCString> names;
|
||||
NodeIndexMap<nsGenericHashKey<TrieNode>, TrieNode> traces;
|
||||
nsTArray<AllocEvent> events;
|
||||
|
||||
nsTArray<size_t> names1Tonames0(names1.Length());
|
||||
nsTArray<size_t> traces1Totraces0(traces1.Length());
|
||||
|
||||
// Merge names.
|
||||
for (auto& i: names0) {
|
||||
names.Insert(i);
|
||||
}
|
||||
for (auto& i: names1) {
|
||||
names1Tonames0.AppendElement(names.Insert(i));
|
||||
}
|
||||
|
||||
// Merge traces. Note that traces1[i].parentIdx < i for all i > 0.
|
||||
for (auto& i: traces0) {
|
||||
traces.Insert(i);
|
||||
}
|
||||
traces1Totraces0.AppendElement(0);
|
||||
for (size_t i = 1; i < traces1.Length(); i++) {
|
||||
TrieNode node = traces1[i];
|
||||
node.parentIdx = traces1Totraces0[node.parentIdx];
|
||||
node.nameIdx = names1Tonames0[node.nameIdx];
|
||||
traces1Totraces0.AppendElement(traces.Insert(node));
|
||||
}
|
||||
|
||||
// Merge the events according to timestamps.
|
||||
auto p0 = events0.begin();
|
||||
auto p1 = events1.begin();
|
||||
|
||||
while (p0 != events0.end() && p1 != events1.end()) {
|
||||
if (p0->mTimestamp < p1->mTimestamp) {
|
||||
events.AppendElement(*p0++);
|
||||
} else {
|
||||
events.AppendElement(*p1++);
|
||||
events.LastElement().mTraceIdx =
|
||||
traces1Totraces0[events.LastElement().mTraceIdx];
|
||||
}
|
||||
}
|
||||
|
||||
while (p0 != events0.end()) {
|
||||
events.AppendElement(*p0++);
|
||||
}
|
||||
|
||||
while (p1 != events1.end()) {
|
||||
events.AppendElement(*p1++);
|
||||
events.LastElement().mTraceIdx =
|
||||
traces1Totraces0[events.LastElement().mTraceIdx];
|
||||
}
|
||||
|
||||
return MergedTraces{names.Serialize(), traces.Serialize(), Move(events)};
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MemoryProfiler::GetResults(JSContext* cx, JS::MutableHandle<JS::Value> aResult)
|
||||
{
|
||||
InitOnce();
|
||||
AutoUseUncensoredAllocator ua;
|
||||
AutoMPLock lock(sLock);
|
||||
JSContext* context = XPCJSContext::Get()->Context();
|
||||
// Getting results when the profiler is running is not allowed.
|
||||
if (sProfileContextCount > 0) {
|
||||
return NS_OK;
|
||||
}
|
||||
// Return immediately when native profiler does not exist.
|
||||
if (!sNativeProfiler) {
|
||||
return NS_OK;
|
||||
}
|
||||
// Return immediately when there's no result in current context.
|
||||
ProfilerForJSContext profiler;
|
||||
if (!sJSContextProfilerMap->Get(context, &profiler) ||
|
||||
!profiler.mProfiler) {
|
||||
return NS_OK;
|
||||
}
|
||||
GCHeapProfilerImpl* gp = profiler.mProfiler;
|
||||
|
||||
auto results = MergeResults(gp->GetNames(), gp->GetTraces(), gp->GetEvents(),
|
||||
sNativeProfiler->GetNames(),
|
||||
sNativeProfiler->GetTraces(),
|
||||
sNativeProfiler->GetEvents());
|
||||
const nsTArray<nsCString>& names = results.mNames;
|
||||
const nsTArray<TrieNode>& traces = results.mTraces;
|
||||
const nsTArray<AllocEvent>& events = results.mEvents;
|
||||
|
||||
JS::RootedObject jsnames(cx, JS_NewArrayObject(cx, names.Length()));
|
||||
JS::RootedObject jstraces(cx, JS_NewArrayObject(cx, traces.Length()));
|
||||
JS::RootedObject jsevents(cx, JS_NewArrayObject(cx, events.Length()));
|
||||
|
||||
for (size_t i = 0; i < names.Length(); i++) {
|
||||
JS::RootedString name(cx, JS_NewStringCopyZ(cx, names[i].get()));
|
||||
JS_SetElement(cx, jsnames, i, name);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < traces.Length(); i++) {
|
||||
JS::RootedObject tn(cx, JS_NewPlainObject(cx));
|
||||
JS::RootedValue nameIdx(cx, JS_NumberValue(traces[i].nameIdx));
|
||||
JS::RootedValue parentIdx(cx, JS_NumberValue(traces[i].parentIdx));
|
||||
JS_SetProperty(cx, tn, "nameIdx", nameIdx);
|
||||
JS_SetProperty(cx, tn, "parentIdx", parentIdx);
|
||||
JS_SetElement(cx, jstraces, i, tn);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (auto ent: events) {
|
||||
if (ent.mSize == 0) {
|
||||
continue;
|
||||
}
|
||||
MOZ_ASSERT(!sStartTime.IsNull());
|
||||
double time = (ent.mTimestamp - sStartTime).ToMilliseconds();
|
||||
JS::RootedObject tn(cx, JS_NewPlainObject(cx));
|
||||
JS::RootedValue size(cx, JS_NumberValue(ent.mSize));
|
||||
JS::RootedValue traceIdx(cx, JS_NumberValue(ent.mTraceIdx));
|
||||
JS::RootedValue timestamp(cx, JS_NumberValue(time));
|
||||
JS_SetProperty(cx, tn, "size", size);
|
||||
JS_SetProperty(cx, tn, "traceIdx", traceIdx);
|
||||
JS_SetProperty(cx, tn, "timestamp", timestamp);
|
||||
JS_SetElement(cx, jsevents, i++, tn);
|
||||
}
|
||||
JS_SetArrayLength(cx, jsevents, i);
|
||||
|
||||
JS::RootedObject result(cx, JS_NewPlainObject(cx));
|
||||
JS::RootedValue objnames(cx, ObjectOrNullValue(jsnames));
|
||||
JS_SetProperty(cx, result, "names", objnames);
|
||||
JS::RootedValue objtraces(cx, ObjectOrNullValue(jstraces));
|
||||
JS_SetProperty(cx, result, "traces", objtraces);
|
||||
JS::RootedValue objevents(cx, ObjectOrNullValue(jsevents));
|
||||
JS_SetProperty(cx, result, "events", objevents);
|
||||
aResult.setObject(*result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
159
tools/memory-profiler/MemoryProfiler.h
Normal file
159
tools/memory-profiler/MemoryProfiler.h
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/* -*- 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 tools_profiler_MemoryProfiler_h
|
||||
#define tools_profiler_MemoryProfiler_h
|
||||
|
||||
#include "nsIMemoryProfiler.h"
|
||||
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
#include "CompactTraceTable.h"
|
||||
#include "nsTArray.h"
|
||||
#include "prlock.h"
|
||||
|
||||
#define MEMORY_PROFILER_CID \
|
||||
{ 0xf976eaa2, 0xcc1f, 0x47ee, \
|
||||
{ 0x81, 0x29, 0xb8, 0x26, 0x2a, 0x3d, 0xb6, 0xb2 } }
|
||||
|
||||
#define MEMORY_PROFILER_CONTRACT_ID "@mozilla.org/tools/memory-profiler;1"
|
||||
|
||||
struct PRLock;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class NativeProfilerImpl;
|
||||
class GCHeapProfilerImpl;
|
||||
|
||||
struct ProfilerForJSContext
|
||||
{
|
||||
ProfilerForJSContext()
|
||||
: mProfiler(nullptr)
|
||||
, mEnabled(false)
|
||||
{}
|
||||
GCHeapProfilerImpl* mProfiler;
|
||||
bool mEnabled;
|
||||
};
|
||||
using JSContextProfilerMap =
|
||||
nsDataHashtable<nsClearingPtrHashKey<JSContext>, ProfilerForJSContext>;
|
||||
|
||||
class MemoryProfiler final : public nsIMemoryProfiler
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMEMORYPROFILER
|
||||
|
||||
private:
|
||||
static void InitOnce();
|
||||
~MemoryProfiler() {}
|
||||
|
||||
// The accesses to other static member are guarded by sLock and
|
||||
// sProfileContextCount.
|
||||
static PRLock* sLock;
|
||||
static uint32_t sProfileContextCount;
|
||||
|
||||
static StaticAutoPtr<NativeProfilerImpl> sNativeProfiler;
|
||||
static StaticAutoPtr<JSContextProfilerMap> sJSContextProfilerMap;
|
||||
static TimeStamp sStartTime;
|
||||
};
|
||||
|
||||
// Allocation events to be reported.
|
||||
struct AllocEvent {
|
||||
TimeStamp mTimestamp;
|
||||
// index to a stacktrace singleton.
|
||||
uint32_t mTraceIdx;
|
||||
// Allocation size
|
||||
int32_t mSize;
|
||||
|
||||
AllocEvent(uint32_t aTraceIdx, int32_t aSize, TimeStamp aTimestamp)
|
||||
: mTimestamp(aTimestamp)
|
||||
, mTraceIdx(aTraceIdx)
|
||||
, mSize(aSize)
|
||||
{}
|
||||
};
|
||||
|
||||
// Index to allocation events but also a mark bit to be GC-able.
|
||||
struct AllocEntry {
|
||||
uint32_t mEventIdx : 31;
|
||||
bool mMarked : 1;
|
||||
|
||||
// Default constructor for uninitialized stack value required by
|
||||
// getter methods.
|
||||
AllocEntry()
|
||||
: mEventIdx(0)
|
||||
, mMarked(false)
|
||||
{}
|
||||
explicit AllocEntry(int aEventIdx)
|
||||
: mEventIdx(aEventIdx)
|
||||
, mMarked(false)
|
||||
{}
|
||||
};
|
||||
|
||||
using AllocMap = nsDataHashtable<nsClearingVoidPtrHashKey, AllocEntry>;
|
||||
|
||||
class ProfilerImpl
|
||||
{
|
||||
public:
|
||||
static nsTArray<nsCString> GetStacktrace();
|
||||
static double DRandom();
|
||||
|
||||
ProfilerImpl();
|
||||
virtual nsTArray<nsCString> GetNames() const = 0;
|
||||
virtual nsTArray<TrieNode> GetTraces() const = 0;
|
||||
virtual const nsTArray<AllocEvent>& GetEvents() const = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The sampler generates a random variable which conforms to a geometric
|
||||
* distribution of probability p = 1 / mSampleSize to calculate the
|
||||
* next-to-be-sampled byte directly; It avoids rolling a dice on each byte.
|
||||
*
|
||||
* Let Bn denote a Bernoulli process with first success on n-th trial, the
|
||||
* cumulative distribution function of Bn is Cn = 1 - (1 - p) ^ n.
|
||||
* Let U denote a uniformly distributed random variable in [0, 1).
|
||||
* A geometric random variable can be generated by Cn's reverse function:
|
||||
* G = floor(log(1 - U) / log(1 - p)).
|
||||
*
|
||||
* @param aSize the number of bytes seen
|
||||
* @return the number of events sampled
|
||||
*/
|
||||
size_t AddBytesSampled(uint32_t aBytes);
|
||||
|
||||
uint32_t mSampleSize;
|
||||
|
||||
private:
|
||||
uint32_t mRemainingBytes;
|
||||
double mLog1minusP;
|
||||
};
|
||||
|
||||
/*
|
||||
* This class is used to make sure the profile data is only accessed
|
||||
* on one thread at a time. Don't use mozilla::Mutex because we don't
|
||||
* want to allocate memory.
|
||||
*/
|
||||
class AutoMPLock
|
||||
{
|
||||
public:
|
||||
explicit AutoMPLock(PRLock* aLock)
|
||||
{
|
||||
MOZ_ASSERT(aLock);
|
||||
mLock = aLock;
|
||||
PR_Lock(mLock);
|
||||
}
|
||||
|
||||
~AutoMPLock()
|
||||
{
|
||||
PR_Unlock(mLock);
|
||||
}
|
||||
|
||||
private:
|
||||
PRLock* mLock;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
82
tools/memory-profiler/NativeProfilerImpl.cpp
Normal file
82
tools/memory-profiler/NativeProfilerImpl.cpp
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "NativeProfilerImpl.h"
|
||||
|
||||
#include "UncensoredAllocator.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
NativeProfilerImpl::NativeProfilerImpl()
|
||||
{
|
||||
mLock = PR_NewLock();
|
||||
}
|
||||
|
||||
NativeProfilerImpl::~NativeProfilerImpl()
|
||||
{
|
||||
if (mLock) {
|
||||
PR_DestroyLock(mLock);
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<nsCString>
|
||||
NativeProfilerImpl::GetNames() const
|
||||
{
|
||||
return mTraceTable.GetNames();
|
||||
}
|
||||
|
||||
nsTArray<TrieNode>
|
||||
NativeProfilerImpl::GetTraces() const
|
||||
{
|
||||
return mTraceTable.GetTraces();
|
||||
}
|
||||
|
||||
const nsTArray<AllocEvent>&
|
||||
NativeProfilerImpl::GetEvents() const
|
||||
{
|
||||
return mAllocEvents;
|
||||
}
|
||||
|
||||
void
|
||||
NativeProfilerImpl::reset()
|
||||
{
|
||||
mTraceTable.Reset();
|
||||
mAllocEvents.Clear();
|
||||
mNativeEntries.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
NativeProfilerImpl::sampleNative(void* addr, uint32_t size)
|
||||
{
|
||||
AutoUseUncensoredAllocator ua;
|
||||
AutoMPLock lock(mLock);
|
||||
size_t nSamples = AddBytesSampled(size);
|
||||
if (nSamples > 0) {
|
||||
nsTArray<nsCString> trace = GetStacktrace();
|
||||
AllocEvent ai(mTraceTable.Insert(trace), nSamples * mSampleSize, TimeStamp::Now());
|
||||
mNativeEntries.Put(addr, AllocEntry(mAllocEvents.Length()));
|
||||
mAllocEvents.AppendElement(ai);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NativeProfilerImpl::removeNative(void* addr)
|
||||
{
|
||||
AutoUseUncensoredAllocator ua;
|
||||
AutoMPLock lock(mLock);
|
||||
|
||||
AllocEntry entry;
|
||||
if (!mNativeEntries.Get(addr, &entry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AllocEvent& oldEvent = mAllocEvents[entry.mEventIdx];
|
||||
AllocEvent newEvent(oldEvent.mTraceIdx, -oldEvent.mSize, TimeStamp::Now());
|
||||
mAllocEvents.AppendElement(newEvent);
|
||||
mNativeEntries.Remove(addr);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
43
tools/memory-profiler/NativeProfilerImpl.h
Normal file
43
tools/memory-profiler/NativeProfilerImpl.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* -*- 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 memory_profiler_NativeProfilerImpl_h
|
||||
#define memory_profiler_NativeProfilerImpl_h
|
||||
|
||||
#include "CompactTraceTable.h"
|
||||
#include "MemoryProfiler.h"
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
struct PRLock;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class NativeProfilerImpl final : public NativeProfiler
|
||||
, public ProfilerImpl
|
||||
{
|
||||
public:
|
||||
NativeProfilerImpl();
|
||||
~NativeProfilerImpl() override;
|
||||
|
||||
nsTArray<nsCString> GetNames() const override;
|
||||
nsTArray<TrieNode> GetTraces() const override;
|
||||
const nsTArray<AllocEvent>& GetEvents() const override;
|
||||
|
||||
void reset() override;
|
||||
void sampleNative(void* addr, uint32_t size) override;
|
||||
void removeNative(void* addr) override;
|
||||
|
||||
private:
|
||||
PRLock* mLock;
|
||||
AllocMap mNativeEntries;
|
||||
nsTArray<AllocEvent> mAllocEvents;
|
||||
CompactTraceTable mTraceTable;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // memory_profiler_NativeProfilerImpl_h
|
||||
121
tools/memory-profiler/UncensoredAllocator.cpp
Normal file
121
tools/memory-profiler/UncensoredAllocator.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "UncensoredAllocator.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
#include "MainThreadUtils.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "nsDebug.h"
|
||||
#include "prlock.h"
|
||||
#ifdef MOZ_REPLACE_MALLOC
|
||||
#include "replace_malloc_bridge.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#ifdef MOZ_REPLACE_MALLOC
|
||||
MOZ_THREAD_LOCAL(bool) MallocHook::mEnabledTLS;
|
||||
NativeProfiler* MallocHook::mNativeProfiler;
|
||||
malloc_hook_table_t MallocHook::mMallocHook;
|
||||
#endif
|
||||
|
||||
AutoUseUncensoredAllocator::AutoUseUncensoredAllocator()
|
||||
{
|
||||
#ifdef MOZ_REPLACE_MALLOC
|
||||
MallocHook::mEnabledTLS.set(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
AutoUseUncensoredAllocator::~AutoUseUncensoredAllocator()
|
||||
{
|
||||
#ifdef MOZ_REPLACE_MALLOC
|
||||
MallocHook::mEnabledTLS.set(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
MallocHook::Enabled()
|
||||
{
|
||||
#ifdef MOZ_REPLACE_MALLOC
|
||||
return mEnabledTLS.get() && mNativeProfiler;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void*
|
||||
MallocHook::SampleNative(void* aAddr, size_t aSize)
|
||||
{
|
||||
#ifdef MOZ_REPLACE_MALLOC
|
||||
if (MallocHook::Enabled()) {
|
||||
mNativeProfiler->sampleNative(aAddr, aSize);
|
||||
}
|
||||
#endif
|
||||
return aAddr;
|
||||
}
|
||||
|
||||
void
|
||||
MallocHook::RemoveNative(void* aAddr)
|
||||
{
|
||||
#ifdef MOZ_REPLACE_MALLOC
|
||||
if (MallocHook::Enabled()) {
|
||||
mNativeProfiler->removeNative(aAddr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
MallocHook::Initialize()
|
||||
{
|
||||
#ifdef MOZ_REPLACE_MALLOC
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mMallocHook.free_hook = RemoveNative;
|
||||
mMallocHook.malloc_hook = SampleNative;
|
||||
ReplaceMallocBridge* bridge = ReplaceMallocBridge::Get(3);
|
||||
if (bridge) {
|
||||
mozilla::Unused << bridge->RegisterHook("memory-profiler", nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool success = mEnabledTLS.init();
|
||||
if (NS_WARN_IF(!success)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
MallocHook::Enable(NativeProfiler* aNativeProfiler)
|
||||
{
|
||||
#ifdef MOZ_REPLACE_MALLOC
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ReplaceMallocBridge* bridge = ReplaceMallocBridge::Get(3);
|
||||
if (bridge) {
|
||||
const malloc_table_t* alloc_funcs =
|
||||
bridge->RegisterHook("memory-profiler", nullptr, &mMallocHook);
|
||||
if (alloc_funcs) {
|
||||
mNativeProfiler = aNativeProfiler;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
MallocHook::Disable()
|
||||
{
|
||||
#ifdef MOZ_REPLACE_MALLOC
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ReplaceMallocBridge* bridge = ReplaceMallocBridge::Get(3);
|
||||
if (bridge) {
|
||||
bridge->RegisterHook("memory-profiler", nullptr, nullptr);
|
||||
mNativeProfiler = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
48
tools/memory-profiler/UncensoredAllocator.h
Normal file
48
tools/memory-profiler/UncensoredAllocator.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/* -*- 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 memory_profiler_UncensoredAllocator_h
|
||||
#define memory_profiler_UncensoredAllocator_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
|
||||
#ifdef MOZ_REPLACE_MALLOC
|
||||
#include "replace_malloc_bridge.h"
|
||||
#endif
|
||||
|
||||
class NativeProfiler;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MallocHook final
|
||||
{
|
||||
public:
|
||||
static void Initialize();
|
||||
static void Enable(NativeProfiler* aNativeProfiler);
|
||||
static void Disable();
|
||||
static bool Enabled();
|
||||
private:
|
||||
static void* SampleNative(void* aAddr, size_t aSize);
|
||||
static void RemoveNative(void* aAddr);
|
||||
#ifdef MOZ_REPLACE_MALLOC
|
||||
static MOZ_THREAD_LOCAL(bool) mEnabledTLS;
|
||||
static NativeProfiler* mNativeProfiler;
|
||||
static malloc_hook_table_t mMallocHook;
|
||||
#endif
|
||||
friend class AutoUseUncensoredAllocator;
|
||||
};
|
||||
|
||||
class MOZ_RAII AutoUseUncensoredAllocator final
|
||||
{
|
||||
public:
|
||||
AutoUseUncensoredAllocator();
|
||||
~AutoUseUncensoredAllocator();
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // memory_profiler_UncensoredAllocator_h
|
||||
29
tools/memory-profiler/moz.build
Normal file
29
tools/memory-profiler/moz.build
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
|
||||
XPIDL_MODULE = 'memory_profiler'
|
||||
XPIDL_SOURCES += [
|
||||
'nsIMemoryProfiler.idl',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'GCHeapProfilerImpl.cpp',
|
||||
'MemoryProfiler.cpp',
|
||||
'NativeProfilerImpl.cpp',
|
||||
'nsMemoryProfilerFactory.cpp',
|
||||
'UncensoredAllocator.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/js/xpconnect/src',
|
||||
'/xpcom/base',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
72
tools/memory-profiler/nsIMemoryProfiler.idl
Normal file
72
tools/memory-profiler/nsIMemoryProfiler.idl
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* The memory profiler samples allocation events. An allocation event
|
||||
* includes a type (what and at where is going to be allocated), a
|
||||
* size, a timestamp and the corresponding stack trace. Free events
|
||||
* are also tracked. For managed languages, namely languages relying
|
||||
* on garbage collection, a free event is generated when an object is
|
||||
* reclaimed by the garbage collector. These sampled events can be
|
||||
* used to approximate the full history of allocations afterwards.
|
||||
* That means we can get various memory profiles of a program in
|
||||
* different perspectives by post-processing the history in different
|
||||
* ways. The profiler is designed at the very beginning to support not
|
||||
* only JavaScript but also native codes. Naturally, not only
|
||||
* JavaScript objects but also native allocations are tracked.
|
||||
*
|
||||
* The result returned is the sampled allocation event traces in a
|
||||
* compact format. The events is sorted according to the timestamp
|
||||
* when the event happened. Each event has a trace index pointing to
|
||||
* the traces table. Each trace entry has a name index pointing to the
|
||||
* names table and a parent index pointing to the parent trace in the
|
||||
* traces table. By following the trace index one could rebuild the
|
||||
* complete backtrace of an allocation event.
|
||||
*
|
||||
* [ Events ]
|
||||
* +-------+-------+ +-------+
|
||||
* | Size | Size | | Size |
|
||||
* |-------|-------| |-------|
|
||||
* | Time | Time |......| Time |
|
||||
* |-------|-------| |-------|
|
||||
* +-- Trace | Trace | | Trace |
|
||||
* | +-------+-------+ +-------+
|
||||
* |
|
||||
* | [ Traces ]
|
||||
* +->--------+--------+ +--------+ +--------+
|
||||
* -| Name | Name | | Name | | Name |
|
||||
* / |--------|--------|...|--------|...|--------|
|
||||
* | | Parent | Parent | | Parent | | Parent |
|
||||
* | +---|----+----^--++ +--^--|--+ +---^----+
|
||||
* | | | | | | |
|
||||
* | +---------+ +-------+ +----------+
|
||||
* | [ Names ]
|
||||
* | +-----------------+-----------------+
|
||||
* +-> Function name | Function name |
|
||||
* | & line numbers | & line numbers |......
|
||||
* +-----------------+-----------------+
|
||||
*
|
||||
*/
|
||||
[scriptable, uuid(1e10e7a9-bc05-4878-a687-36c9ea4428b1)]
|
||||
interface nsIMemoryProfiler : nsISupports
|
||||
{
|
||||
void startProfiler();
|
||||
void stopProfiler();
|
||||
void resetProfiler();
|
||||
|
||||
/**
|
||||
* Get results in an object which contains three tables:
|
||||
* {
|
||||
* names, // an array of function names and positions
|
||||
* traces, // an array of {nameIdx, parentIdx}
|
||||
* events, // an array of {size, timestamp, traceIdx}
|
||||
* }
|
||||
* Should only be called after stopProfiler.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
jsval getResults();
|
||||
};
|
||||
32
tools/memory-profiler/nsMemoryProfilerFactory.cpp
Normal file
32
tools/memory-profiler/nsMemoryProfilerFactory.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "MemoryProfiler.h"
|
||||
|
||||
using mozilla::MemoryProfiler;
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(MemoryProfiler)
|
||||
|
||||
NS_DEFINE_NAMED_CID(MEMORY_PROFILER_CID);
|
||||
|
||||
static const mozilla::Module::CIDEntry kMemoryProfilerCIDs[] = {
|
||||
{ &kMEMORY_PROFILER_CID, false, nullptr, MemoryProfilerConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::ContractIDEntry kMemoryProfilerContracts[] = {
|
||||
{ MEMORY_PROFILER_CONTRACT_ID, &kMEMORY_PROFILER_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module kMemoryProfilerModule = {
|
||||
mozilla::Module::kVersion,
|
||||
kMemoryProfilerCIDs,
|
||||
kMemoryProfilerContracts
|
||||
};
|
||||
|
||||
NSMODULE_DEFN(nsMemoryProfilerModule) = &kMemoryProfilerModule;
|
||||
Loading…
Add table
Add a link
Reference in a new issue