зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1464509: Add per-process profiler counters r=mstange
This commit is contained in:
Родитель
eed72a31cc
Коммит
708b2160bb
|
@ -295,6 +295,23 @@ public:
|
|||
[&](UniquePtr<RegisteredThread>& rt) { return rt.get() == aRegisteredThread; });
|
||||
}
|
||||
|
||||
PS_GET(const nsTArray<BaseProfilerCount*>&, Counters)
|
||||
|
||||
static void AppendCounter(PSLockRef, BaseProfilerCount* aCounter)
|
||||
{
|
||||
// we don't own the counter; they may be stored in static objects
|
||||
sInstance->mCounters.AppendElement(aCounter);
|
||||
}
|
||||
|
||||
static void RemoveCounter(PSLockRef, BaseProfilerCount* aCounter)
|
||||
{
|
||||
// we may be called to remove a counter after the profiler is stopped or
|
||||
// late in shutdown.
|
||||
if (sInstance) {
|
||||
sInstance->mCounters.RemoveElement(aCounter);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_LUL_STACKWALK
|
||||
static lul::LUL* Lul(PSLockRef) { return sInstance->mLul.get(); }
|
||||
static void SetLul(PSLockRef, UniquePtr<lul::LUL> aLul)
|
||||
|
@ -314,6 +331,9 @@ private:
|
|||
// ThreadIds in mRegisteredThreads are unique.
|
||||
nsTArray<UniquePtr<RegisteredThread>> mRegisteredThreads;
|
||||
|
||||
// Non-owning pointers to all active counters
|
||||
nsTArray<BaseProfilerCount*> mCounters;
|
||||
|
||||
#ifdef USE_LUL_STACKWALK
|
||||
// LUL's state. Null prior to the first activation, non-null thereafter.
|
||||
UniquePtr<lul::LUL> mLul;
|
||||
|
@ -3302,6 +3322,24 @@ profiler_feature_active(uint32_t aFeature)
|
|||
return RacyFeatures::IsActiveWithFeature(aFeature);
|
||||
}
|
||||
|
||||
void
|
||||
profiler_add_sampled_counter(BaseProfilerCount* aCounter)
|
||||
{
|
||||
DEBUG_LOG("profiler_add_sampled_counter(%s)", aCounter->mLabel);
|
||||
PSAutoLock lock(gPSMutex);
|
||||
CorePS::AppendCounter(lock, aCounter);
|
||||
}
|
||||
|
||||
void
|
||||
profiler_remove_sampled_counter(BaseProfilerCount* aCounter)
|
||||
{
|
||||
DEBUG_LOG("profiler_remove_sampled_counter(%s)", aCounter->mLabel);
|
||||
PSAutoLock lock(gPSMutex);
|
||||
// Note: we don't enforce a final sample, though we could do so if the
|
||||
// profiler was active
|
||||
CorePS::RemoveCounter(lock, aCounter);
|
||||
}
|
||||
|
||||
ProfilingStack*
|
||||
profiler_register_thread(const char* aName, void* aGuessStackTop)
|
||||
{
|
||||
|
|
|
@ -117,12 +117,18 @@ IPDL_SOURCES += [
|
|||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
# GeckoProfiler.h is the only code that's visible in non-MOZ_GECKO_PROFILER
|
||||
# builds, and it only contains no-op macros in that case.
|
||||
# GeckoProfiler.h and ProfilerCounts.h are the only code that's visible in
|
||||
# non-MOZ_GECKO_PROFILER builds, and they only contains no-op macros in that
|
||||
# case.
|
||||
EXPORTS += [
|
||||
'public/GeckoProfiler.h',
|
||||
]
|
||||
|
||||
# vm/GeckoProfiler.h needs to include this and doesn't like #include "ProfilerCounts.h"
|
||||
EXPORTS.mozilla += [
|
||||
'public/ProfilerCounts.h',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_VTUNE']:
|
||||
DEFINES['MOZ_VTUNE_INSTRUMENTATION'] = True
|
||||
UNIFIED_SOURCES += [
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
#ifndef GeckoProfiler_h
|
||||
#define GeckoProfiler_h
|
||||
|
||||
// everything in here is also safe to include unconditionally, and only defines
|
||||
// empty macros if MOZ_GECKO_PROFILER is unset
|
||||
#include "mozilla/ProfilerCounts.h"
|
||||
|
||||
#ifndef MOZ_GECKO_PROFILER
|
||||
|
||||
// This file can be #included unconditionally. However, everything within this
|
||||
|
@ -59,18 +63,17 @@
|
|||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/GuardObjects.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/net/TimingStruct.h"
|
||||
#include "js/ProfilingStack.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "nscore.h"
|
||||
#include "nsIURI.h"
|
||||
|
||||
// Make sure that we can use std::min here without the Windows headers messing
|
||||
// with us.
|
||||
|
@ -81,6 +84,12 @@
|
|||
class ProfilerBacktrace;
|
||||
class ProfilerMarkerPayload;
|
||||
class SpliceableJSONWriter;
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
struct TimingStruct;
|
||||
}
|
||||
}
|
||||
class nsIURI;
|
||||
|
||||
namespace mozilla {
|
||||
class MallocAllocPolicy;
|
||||
|
@ -284,6 +293,10 @@ void profiler_ensure_started(uint32_t aEntries, double aInterval,
|
|||
ProfilingStack* profiler_register_thread(const char* name, void* guessStackTop);
|
||||
void profiler_unregister_thread();
|
||||
|
||||
class BaseProfilerCount;
|
||||
void profiler_add_sampled_counter(BaseProfilerCount* aCounter);
|
||||
void profiler_remove_sampled_counter(BaseProfilerCount* aCounter);
|
||||
|
||||
// Register and unregister a thread within a scope.
|
||||
#define AUTO_PROFILER_REGISTER_THREAD(name) \
|
||||
mozilla::AutoProfilerRegisterThread PROFILER_RAII(name)
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 ProfilerCounts_h
|
||||
#define ProfilerCounts_h
|
||||
|
||||
#ifndef MOZ_GECKO_PROFILER
|
||||
|
||||
#define PROFILER_DEFINE_COUNT_TOTAL(label, category, description)
|
||||
#define PROFILER_DEFINE_COUNT(label, category, description)
|
||||
#define PROFILER_DEFINE_STATIC_COUNT_TOTAL(label, category, description)
|
||||
#define AUTO_PROFILER_TOTAL(label, count)
|
||||
#define AUTO_PROFILER_COUNT(label)
|
||||
#define AUTO_PROFILER_STATIC_COUNT(label, count)
|
||||
|
||||
#else
|
||||
#include "mozilla/Atomics.h"
|
||||
|
||||
class BaseProfilerCount;
|
||||
void profiler_add_sampled_counter(BaseProfilerCount* aCounter);
|
||||
void profiler_remove_sampled_counter(BaseProfilerCount* aCounter);
|
||||
|
||||
typedef mozilla::Atomic<int64_t, mozilla::MemoryOrdering::Relaxed> ProfilerAtomicSigned;
|
||||
typedef mozilla::Atomic<uint64_t, mozilla::MemoryOrdering::Relaxed> ProfilerAtomicUnsigned;
|
||||
|
||||
// Counter support
|
||||
// There are two types of counters:
|
||||
// 1) a simple counter which can be added to or subtracted from. This could track the number
|
||||
// of objects of a type, the number of calls to something (reflow, JIT, etc).
|
||||
// 2) a combined counter which has the above, plus a number-of-calls counter that is
|
||||
// incremented by 1 for each call to modify the count. This provides an optional source for
|
||||
// a 'heatmap' of access. This can be used (for example) to track the amount of memory
|
||||
// allocated, and provide a heatmap of memory operations (allocs/frees).
|
||||
//
|
||||
// Counters are sampled by the profiler once per sample-period. At this time, all counters
|
||||
// are global to the process. In the future, there might be more versions with per-thread
|
||||
// or other discriminators.
|
||||
//
|
||||
// Typical usage:
|
||||
// There are two ways to use counters: With heap-created counter objects,
|
||||
// or using macros. Note: the macros use statics, and will be slightly
|
||||
// faster/smaller, and you need to care about creating them before using
|
||||
// them. They're similar to the use-pattern for the other AUTO_PROFILER*
|
||||
// macros, but they do need the PROFILER_DEFINE* to be use to instantiate
|
||||
// the statics.
|
||||
//
|
||||
// PROFILER_DEFINE_COUNT(mything, "JIT", "Some JIT byte count")
|
||||
// ...
|
||||
// void foo() { ... AUTO_PROFILER_COUNT(mything, number_of_bytes_used); ... }
|
||||
//
|
||||
// or (to also get a heatmap)
|
||||
//
|
||||
// PROFILER_DEFINE_COUNT_TOTAL(mything, "JIT", "Some JIT byte count")
|
||||
// ...
|
||||
// void foo() { ... AUTO_PROFILER_COUNT_TOTAL(mything, number_of_bytes_generated); ... }
|
||||
//
|
||||
// To use without statics/macros:
|
||||
//
|
||||
// UniquePtr<ProfilerCounter> myCounter;
|
||||
// ...
|
||||
// myCounter = MakeUnique<ProfilerCounter>("mything", "JIT", "Some JIT byte count"));
|
||||
// ...
|
||||
// void foo() { ... myCounter->Add(number_of_bytes_generated0; ... }
|
||||
|
||||
class BaseProfilerCount
|
||||
{
|
||||
public:
|
||||
BaseProfilerCount(const char* aLabel,
|
||||
ProfilerAtomicSigned* aCounter,
|
||||
ProfilerAtomicUnsigned* aNumber,
|
||||
const char* aCategory,
|
||||
const char* aDescription)
|
||||
: mLabel(aLabel)
|
||||
, mCategory(aCategory)
|
||||
, mDescription(aDescription)
|
||||
, mCounter(aCounter)
|
||||
, mNumber(aNumber)
|
||||
{
|
||||
#define COUNTER_CANARY 0xDEADBEEF
|
||||
#ifdef DEBUG
|
||||
mCanary = COUNTER_CANARY;
|
||||
#endif
|
||||
// Can't call profiler_* here since this may be non-xul-library
|
||||
}
|
||||
#ifdef DEBUG
|
||||
~BaseProfilerCount() {
|
||||
mCanary = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Sample(int64_t &aCounter, uint64_t &aNumber)
|
||||
{
|
||||
MOZ_ASSERT(mCanary == COUNTER_CANARY);
|
||||
|
||||
aCounter = *mCounter;
|
||||
aNumber = mNumber ? *mNumber : 0;
|
||||
}
|
||||
|
||||
// We don't define ++ and Add() here, since the static defines directly
|
||||
// increment the atomic counters, and the subclasses implement ++ and
|
||||
// Add() directly.
|
||||
|
||||
// These typically are static strings (for example if you use the macros
|
||||
// below)
|
||||
const char* mLabel;
|
||||
const char* mCategory;
|
||||
const char* mDescription;
|
||||
// We're ok with these being un-ordered in race conditions. These are
|
||||
// pointers because we want to be able to use statics and increment them
|
||||
// directly. Otherwise we could just have them inline, and not need the
|
||||
// constructor args.
|
||||
// These can be static globals (using the macros below), though they
|
||||
// don't have to be - their lifetime must be longer than the use of them
|
||||
// by the profiler (see profiler_add/remove_sampled_counter()). If you're
|
||||
// using a lot of these, they probably should be allocated at runtime (see
|
||||
// class ProfilerCountOnly below).
|
||||
ProfilerAtomicSigned* mCounter;
|
||||
ProfilerAtomicUnsigned* mNumber; // may be null
|
||||
|
||||
#ifdef DEBUG
|
||||
uint32_t mCanary;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Designed to be allocated dynamically, and simply incremented with obj++
|
||||
// or obj->Add(n)
|
||||
class ProfilerCounter final : public BaseProfilerCount
|
||||
{
|
||||
public:
|
||||
ProfilerCounter(const char* aLabel,
|
||||
const char* aCategory,
|
||||
const char* aDescription)
|
||||
: BaseProfilerCount(aLabel, &mCounter, nullptr, aCategory, aDescription)
|
||||
{
|
||||
// Assume we're in libxul
|
||||
profiler_add_sampled_counter(this);
|
||||
}
|
||||
|
||||
virtual ~ProfilerCounter()
|
||||
{
|
||||
profiler_remove_sampled_counter(this);
|
||||
}
|
||||
|
||||
BaseProfilerCount& operator++()
|
||||
{
|
||||
Add(1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Add(int64_t aNumber)
|
||||
{
|
||||
mCounter += aNumber;
|
||||
}
|
||||
|
||||
ProfilerAtomicSigned mCounter;
|
||||
};
|
||||
|
||||
// Also keeps a heatmap (number of calls to ++/Add())
|
||||
class ProfilerCounterTotal final : public BaseProfilerCount
|
||||
{
|
||||
public:
|
||||
ProfilerCounterTotal(const char* aLabel,
|
||||
const char* aCategory,
|
||||
const char* aDescription)
|
||||
: BaseProfilerCount(aLabel, &mCounter, &mNumber, aCategory, aDescription)
|
||||
{
|
||||
// Assume we're in libxul
|
||||
profiler_add_sampled_counter(this);
|
||||
}
|
||||
|
||||
virtual ~ProfilerCounterTotal()
|
||||
{
|
||||
profiler_remove_sampled_counter(this);
|
||||
}
|
||||
|
||||
BaseProfilerCount& operator++()
|
||||
{
|
||||
Add(1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Add(int64_t aNumber)
|
||||
{
|
||||
mCounter += aNumber;
|
||||
mNumber++;
|
||||
}
|
||||
|
||||
ProfilerAtomicSigned mCounter;
|
||||
ProfilerAtomicUnsigned mNumber;
|
||||
};
|
||||
|
||||
// Defines a counter that is sampled on each profiler tick, with a running
|
||||
// count (signed), and number-of-instances. Note that because these are two
|
||||
// independent Atomics, there is a possiblity that count will not include
|
||||
// the last call, but number of uses will. I think this is not worth
|
||||
// worrying about
|
||||
#define PROFILER_DEFINE_COUNT_TOTAL(label, category, description) \
|
||||
ProfilerAtomicSigned profiler_count_ ## label(0); \
|
||||
ProfilerAtomicUnsigned profiler_number_ ## label(0); \
|
||||
const char profiler_category_ ## label[] = category; \
|
||||
const char profiler_description_ ## label[] = description; \
|
||||
mozilla::UniquePtr<BaseProfilerCount> AutoCount_ ## label;
|
||||
|
||||
// This counts, but doesn't keep track of the number of calls to
|
||||
// AUTO_PROFILER_COUNT()
|
||||
#define PROFILER_DEFINE_COUNT(label, category, description) \
|
||||
ProfilerAtomicSigned profiler_count_ ## label(0); \
|
||||
const char profiler_category_ ## label[] = category; \
|
||||
const char profiler_description_ ## label[] = description; \
|
||||
mozilla::UniquePtr<BaseProfilerCount> AutoCount_ ## label;
|
||||
|
||||
// This will create a static initializer if used, but avoids a possible
|
||||
// allocation.
|
||||
#define PROFILER_DEFINE_STATIC_COUNT_TOTAL(label, category, description) \
|
||||
ProfilerAtomicSigned profiler_count_ ## label(0); \
|
||||
ProfilerAtomicUnsigned profiler_number_ ## label(0); \
|
||||
BaseProfilerCount AutoCount_ ## label(#label, \
|
||||
&profiler_count_ ## label, \
|
||||
&profiler_number_ ## label, \
|
||||
category, \
|
||||
description);
|
||||
|
||||
// If we didn't care about static initializers, we could avoid the need for
|
||||
// a ptr to the BaseProfilerCount object.
|
||||
|
||||
// XXX It would be better to do this without the if() and without the
|
||||
// theoretical race to set the UniquePtr (i.e. possible leak).
|
||||
#define AUTO_PROFILER_COUNT_TOTAL(label, count) \
|
||||
do { \
|
||||
profiler_number_ ## label++; /* do this first*/ \
|
||||
profiler_count_ ## label += count; \
|
||||
if (!AutoCount_ ## label) { \
|
||||
/* Ignore that we could call this twice in theory, and that we leak them */ \
|
||||
AutoCount_ ## label .reset(new BaseProfilerCount(#label, \
|
||||
&profiler_count_ ## label, \
|
||||
&profiler_number_ ## label, \
|
||||
profiler_category_ ## label, \
|
||||
profiler_description_ ## label)); \
|
||||
profiler_add_sampled_counter(AutoCount_ ## label .get()); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define AUTO_PROFILER_COUNT(label, count) \
|
||||
do { \
|
||||
profiler_count_ ## label += count; /* do this first*/ \
|
||||
if (!AutoCount_ ## label) { \
|
||||
/* Ignore that we could call this twice in theory, and that we leak them */ \
|
||||
AutoCount_ ## label .reset(new BaseProfilerCount(#label, \
|
||||
nullptr, \
|
||||
&profiler_number_ ## label, \
|
||||
profiler_category_ ## label, \
|
||||
profiler_description_ ## label)); \
|
||||
profiler_add_sampled_counter(AutoCount_ ## label .get()); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define AUTO_PROFILER_STATIC_COUNT(label, count) \
|
||||
do { \
|
||||
profiler_number_ ## label++; /* do this first*/ \
|
||||
profiler_count_ ## label += count; \
|
||||
} while (0)
|
||||
|
||||
// if we need to force the allocation
|
||||
#define AUTO_PROFILER_FORCE_ALLOCATION(label) \
|
||||
do { \
|
||||
if (!AutoCount_ ## label) { \
|
||||
/* Ignore that we could call this twice in theory, and that we leak them */ \
|
||||
AutoCount_ ## label .reset(new BaseProfilerCount(#label, \
|
||||
&profiler_count_ ## label, \
|
||||
&profiler_number_ ## label, \
|
||||
profiler_category_ ## label, \
|
||||
profiler_description_ ## label)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif // !MOZ_GECKO_PROFILER
|
||||
|
||||
#endif // ProfilerCounts_h
|
Загрузка…
Ссылка в новой задаче