Bug 1464509: Add per-process profiler counters r=mstange

This commit is contained in:
Randell Jesup 2018-10-09 22:28:21 -04:00
Родитель eed72a31cc
Коммит 708b2160bb
4 изменённых файлов: 342 добавлений и 4 удалений

Просмотреть файл

@ -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