mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 12:20:56 +00:00

The overhead is showing up in Speedometer 3, especially in the innerHTML setter when it calls into the frame constructor. Full breakdown of callers across sp3 is here: https://share.firefox.dev/3rfckTG Time spent in AutoProfilerLabel during TodoMVC-jQuery innerHTML: Before: https://share.firefox.dev/3Znlydp 378 sampes After: https://share.firefox.dev/45VdVgr 71 samples Differential Revision: https://phabricator.services.mozilla.com/D188487
327 lines
15 KiB
C++
327 lines
15 KiB
C++
/* -*- 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/. */
|
|
|
|
// This header contains all definitions related to profiler labels.
|
|
// It is safe to include unconditionally, and only defines empty macros if
|
|
// MOZ_GECKO_PROFILER is not set.
|
|
|
|
#ifndef ProfilerLabels_h
|
|
#define ProfilerLabels_h
|
|
|
|
#include "mozilla/ProfilerState.h"
|
|
#include "mozilla/ProfilerThreadState.h"
|
|
|
|
#include "js/ProfilingCategory.h"
|
|
#include "js/ProfilingStack.h"
|
|
#include "js/RootingAPI.h"
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/Atomics.h"
|
|
#include "mozilla/Attributes.h"
|
|
#include "mozilla/BaseProfilerRAIIMacro.h"
|
|
#include "mozilla/Maybe.h"
|
|
#include "mozilla/ProfilerThreadRegistration.h"
|
|
#include "mozilla/ThreadLocal.h"
|
|
#include "nsString.h"
|
|
|
|
#include <stdint.h>
|
|
|
|
struct JSContext;
|
|
|
|
// Insert an RAII object in this scope to enter a label stack frame. Any
|
|
// samples collected in this scope will contain this label in their stack.
|
|
// The label argument must be a static C string. It is usually of the
|
|
// form "ClassName::FunctionName". (Ideally we'd use the compiler to provide
|
|
// that for us, but __func__ gives us the function name without the class
|
|
// name.) If the label applies to only part of a function, you can qualify it
|
|
// like this: "ClassName::FunctionName:PartName".
|
|
//
|
|
// Use AUTO_PROFILER_LABEL_DYNAMIC_* if you want to add additional / dynamic
|
|
// information to the label stack frame, and AUTO_PROFILER_LABEL_HOT if you're
|
|
// instrumenting functions for which overhead on the order of nanoseconds is
|
|
// noticeable.
|
|
#define AUTO_PROFILER_LABEL(label, categoryPair) \
|
|
mozilla::AutoProfilerLabel PROFILER_RAII( \
|
|
label, nullptr, JS::ProfilingCategoryPair::categoryPair)
|
|
|
|
// Like AUTO_PROFILER_LABEL, but for super-hot code where overhead must be
|
|
// kept to the absolute minimum. This variant doesn't push the label if the
|
|
// profiler isn't running.
|
|
// Don't use this for long-running functions: If the profiler is started in
|
|
// the middle of the function, this label won't be on the stack until the
|
|
// function is entered the next time. As a result, category information for
|
|
// samples at the start of the profile can be misleading.
|
|
// For short-running functions, that's often an acceptable trade-off.
|
|
#define AUTO_PROFILER_LABEL_HOT(label, categoryPair) \
|
|
mozilla::AutoProfilerLabelHot PROFILER_RAII( \
|
|
label, nullptr, JS::ProfilingCategoryPair::categoryPair)
|
|
|
|
// Similar to AUTO_PROFILER_LABEL, but that adds the RELEVANT_FOR_JS flag.
|
|
#define AUTO_PROFILER_LABEL_RELEVANT_FOR_JS(label, categoryPair) \
|
|
mozilla::AutoProfilerLabel PROFILER_RAII( \
|
|
label, nullptr, JS::ProfilingCategoryPair::categoryPair, \
|
|
uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS))
|
|
|
|
// Similar to AUTO_PROFILER_LABEL, but with only one argument: the category
|
|
// pair. The label string is taken from the category pair. This is convenient
|
|
// for labels like AUTO_PROFILER_LABEL_CATEGORY_PAIR(GRAPHICS_LayerBuilding)
|
|
// which would otherwise just repeat the string.
|
|
#define AUTO_PROFILER_LABEL_CATEGORY_PAIR(categoryPair) \
|
|
mozilla::AutoProfilerLabel PROFILER_RAII( \
|
|
"", nullptr, JS::ProfilingCategoryPair::categoryPair, \
|
|
uint32_t( \
|
|
js::ProfilingStackFrame::Flags::LABEL_DETERMINED_BY_CATEGORY_PAIR))
|
|
|
|
// Similar to AUTO_PROFILER_LABEL_CATEGORY_PAIR but adding the RELEVANT_FOR_JS
|
|
// flag.
|
|
#define AUTO_PROFILER_LABEL_CATEGORY_PAIR_RELEVANT_FOR_JS(categoryPair) \
|
|
mozilla::AutoProfilerLabel PROFILER_RAII( \
|
|
"", nullptr, JS::ProfilingCategoryPair::categoryPair, \
|
|
uint32_t( \
|
|
js::ProfilingStackFrame::Flags::LABEL_DETERMINED_BY_CATEGORY_PAIR) | \
|
|
uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS))
|
|
|
|
// Similar to AUTO_PROFILER_LABEL, but with an additional string. The inserted
|
|
// RAII object stores the cStr pointer in a field; it does not copy the string.
|
|
//
|
|
// WARNING: This means that the string you pass to this macro needs to live at
|
|
// least until the end of the current scope. Be careful using this macro with
|
|
// ns[C]String; the other AUTO_PROFILER_LABEL_DYNAMIC_* macros below are
|
|
// preferred because they avoid this problem.
|
|
//
|
|
// If the profiler samples the current thread and walks the label stack while
|
|
// this RAII object is on the stack, it will copy the supplied string into the
|
|
// profile buffer. So there's one string copy operation, and it happens at
|
|
// sample time.
|
|
//
|
|
// Compare this to the plain AUTO_PROFILER_LABEL macro, which only accepts
|
|
// literal strings: When the label stack frames generated by
|
|
// AUTO_PROFILER_LABEL are sampled, no string copy needs to be made because the
|
|
// profile buffer can just store the raw pointers to the literal strings.
|
|
// Consequently, AUTO_PROFILER_LABEL frames take up considerably less space in
|
|
// the profile buffer than AUTO_PROFILER_LABEL_DYNAMIC_* frames.
|
|
#define AUTO_PROFILER_LABEL_DYNAMIC_CSTR(label, categoryPair, cStr) \
|
|
mozilla::AutoProfilerLabel PROFILER_RAII( \
|
|
label, cStr, JS::ProfilingCategoryPair::categoryPair)
|
|
|
|
// Like AUTO_PROFILER_LABEL_DYNAMIC_CSTR, but with the NONSENSITIVE flag to
|
|
// note that it does not contain sensitive information (so we can include it
|
|
// in, for example, the BackgroundHangMonitor)
|
|
#define AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(label, categoryPair, \
|
|
cStr) \
|
|
mozilla::AutoProfilerLabel PROFILER_RAII( \
|
|
label, cStr, JS::ProfilingCategoryPair::categoryPair, \
|
|
uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE))
|
|
|
|
// Similar to AUTO_PROFILER_LABEL_DYNAMIC_CSTR, but takes an nsACString.
|
|
//
|
|
// Note: The use of the Maybe<>s ensures the scopes for the dynamic string and
|
|
// the AutoProfilerLabel are appropriate, while also not incurring the runtime
|
|
// cost of the string assignment unless the profiler is active. Therefore,
|
|
// unlike AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC_CSTR, this macro
|
|
// doesn't push/pop a label when the profiler is inactive.
|
|
#define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(label, categoryPair, nsCStr) \
|
|
mozilla::Maybe<nsAutoCString> autoCStr; \
|
|
mozilla::Maybe<mozilla::AutoProfilerLabel> raiiObjectNsCString; \
|
|
if (profiler_is_active()) { \
|
|
autoCStr.emplace(nsCStr); \
|
|
raiiObjectNsCString.emplace(label, autoCStr->get(), \
|
|
JS::ProfilingCategoryPair::categoryPair); \
|
|
}
|
|
|
|
#define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_RELEVANT_FOR_JS( \
|
|
label, categoryPair, nsCStr) \
|
|
mozilla::Maybe<nsAutoCString> autoCStr; \
|
|
mozilla::Maybe<mozilla::AutoProfilerLabel> raiiObjectNsCString; \
|
|
if (profiler_is_active()) { \
|
|
autoCStr.emplace(nsCStr); \
|
|
raiiObjectNsCString.emplace( \
|
|
label, autoCStr->get(), JS::ProfilingCategoryPair::categoryPair, \
|
|
uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)); \
|
|
}
|
|
|
|
// Match the conditions for MOZ_ENABLE_BACKGROUND_HANG_MONITOR
|
|
#if defined(NIGHTLY_BUILD) && !defined(MOZ_DEBUG) && !defined(MOZ_TSAN) && \
|
|
!defined(MOZ_ASAN)
|
|
# define SHOULD_CREATE_ALL_NONSENSITIVE_LABEL_FRAMES true
|
|
#else
|
|
# define SHOULD_CREATE_ALL_NONSENSITIVE_LABEL_FRAMES profiler_is_active()
|
|
#endif
|
|
|
|
// See note above AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE
|
|
#define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE( \
|
|
label, categoryPair, nsCStr) \
|
|
mozilla::Maybe<nsAutoCString> autoCStr; \
|
|
mozilla::Maybe<mozilla::AutoProfilerLabel> raiiObjectNsCString; \
|
|
if (SHOULD_CREATE_ALL_NONSENSITIVE_LABEL_FRAMES) { \
|
|
autoCStr.emplace(nsCStr); \
|
|
raiiObjectNsCString.emplace( \
|
|
label, autoCStr->get(), JS::ProfilingCategoryPair::categoryPair, \
|
|
uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE)); \
|
|
}
|
|
|
|
// Similar to AUTO_PROFILER_LABEL_DYNAMIC_CSTR, but takes an nsString that is
|
|
// is lossily converted to an ASCII string.
|
|
//
|
|
// Note: The use of the Maybe<>s ensures the scopes for the converted dynamic
|
|
// string and the AutoProfilerLabel are appropriate, while also not incurring
|
|
// the runtime cost of the string conversion unless the profiler is active.
|
|
// Therefore, unlike AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC_CSTR,
|
|
// this macro doesn't push/pop a label when the profiler is inactive.
|
|
#define AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(label, categoryPair, nsStr) \
|
|
mozilla::Maybe<NS_LossyConvertUTF16toASCII> asciiStr; \
|
|
mozilla::Maybe<mozilla::AutoProfilerLabel> raiiObjectLossyNsString; \
|
|
if (profiler_is_active()) { \
|
|
asciiStr.emplace(nsStr); \
|
|
raiiObjectLossyNsString.emplace(label, asciiStr->get(), \
|
|
JS::ProfilingCategoryPair::categoryPair); \
|
|
}
|
|
|
|
// Similar to AUTO_PROFILER_LABEL, but accepting a JSContext* parameter, and a
|
|
// no-op if the profiler is disabled.
|
|
// Used to annotate functions for which overhead in the range of nanoseconds is
|
|
// noticeable. It avoids overhead from the TLS lookup because it can get the
|
|
// ProfilingStack from the JS context, and avoids almost all overhead in the
|
|
// case where the profiler is disabled.
|
|
#define AUTO_PROFILER_LABEL_FAST(label, categoryPair, ctx) \
|
|
mozilla::AutoProfilerLabelHot PROFILER_RAII( \
|
|
ctx, label, nullptr, JS::ProfilingCategoryPair::categoryPair)
|
|
|
|
// Similar to AUTO_PROFILER_LABEL_FAST, but also takes an extra string and an
|
|
// additional set of flags. The flags parameter should carry values from the
|
|
// js::ProfilingStackFrame::Flags enum.
|
|
#define AUTO_PROFILER_LABEL_DYNAMIC_FAST(label, dynamicString, categoryPair, \
|
|
ctx, flags) \
|
|
mozilla::AutoProfilerLabelHot PROFILER_RAII( \
|
|
ctx, label, dynamicString, JS::ProfilingCategoryPair::categoryPair, \
|
|
flags)
|
|
|
|
namespace mozilla {
|
|
|
|
#ifndef MOZ_GECKO_PROFILER
|
|
|
|
class MOZ_RAII AutoProfilerLabel {
|
|
public:
|
|
// This is the AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC variant.
|
|
AutoProfilerLabel(const char* aLabel, const char* aDynamicString,
|
|
JS::ProfilingCategoryPair aCategoryPair,
|
|
uint32_t aFlags = 0) {}
|
|
|
|
~AutoProfilerLabel() {}
|
|
};
|
|
|
|
class MOZ_RAII AutoProfilerLabelHot {
|
|
public:
|
|
// This is the AUTO_PROFILER_LABEL_HOT variant.
|
|
AutoProfilerLabelHot(const char* aLabel, const char* aDynamicString,
|
|
JS::ProfilingCategoryPair aCategoryPair,
|
|
uint32_t aFlags = 0) {}
|
|
|
|
// This is the AUTO_PROFILER_LABEL_FAST variant.
|
|
AutoProfilerLabelHot(JSContext* aJSContext, const char* aLabel,
|
|
const char* aDynamicString,
|
|
JS::ProfilingCategoryPair aCategoryPair,
|
|
uint32_t aFlags) {}
|
|
|
|
~AutoProfilerLabelHot() {}
|
|
};
|
|
|
|
#else // !MOZ_GECKO_PROFILER
|
|
|
|
// This class creates a non-owning ProfilingStack reference. Objects of this
|
|
// class are stack-allocated, and so exist within a thread, and are thus bounded
|
|
// by the lifetime of the thread, which ensures that the references held can't
|
|
// be used after the ProfilingStack is destroyed.
|
|
class MOZ_RAII AutoProfilerLabel {
|
|
public:
|
|
// This is the AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC variant.
|
|
AutoProfilerLabel(const char* aLabel, const char* aDynamicString,
|
|
JS::ProfilingCategoryPair aCategoryPair,
|
|
uint32_t aFlags = 0) {
|
|
// Get the ProfilingStack from TLS.
|
|
mProfilingStack = profiler::ThreadRegistration::WithOnThreadRefOr(
|
|
[](profiler::ThreadRegistration::OnThreadRef aThread) {
|
|
return &aThread.UnlockedConstReaderAndAtomicRWRef()
|
|
.ProfilingStackRef();
|
|
},
|
|
nullptr);
|
|
if (mProfilingStack) {
|
|
mProfilingStack->pushLabelFrame(aLabel, aDynamicString, this,
|
|
aCategoryPair, aFlags);
|
|
}
|
|
}
|
|
|
|
~AutoProfilerLabel() {
|
|
// This function runs both on and off the main thread.
|
|
|
|
if (mProfilingStack) {
|
|
mProfilingStack->pop();
|
|
}
|
|
}
|
|
|
|
private:
|
|
// We save a ProfilingStack pointer in the ctor so we don't have to redo the
|
|
// TLS lookup in the dtor.
|
|
ProfilingStack* mProfilingStack;
|
|
};
|
|
|
|
class MOZ_RAII AutoProfilerLabelHot {
|
|
public:
|
|
// This is the AUTO_PROFILER_LABEL_HOT variant. It does nothing if
|
|
// the profiler is inactive.
|
|
AutoProfilerLabelHot(const char* aLabel, const char* aDynamicString,
|
|
JS::ProfilingCategoryPair aCategoryPair,
|
|
uint32_t aFlags = 0) {
|
|
if (MOZ_LIKELY(!profiler_is_active())) {
|
|
mProfilingStack = nullptr;
|
|
return;
|
|
}
|
|
|
|
// Get the ProfilingStack from TLS.
|
|
mProfilingStack = profiler::ThreadRegistration::WithOnThreadRefOr(
|
|
[](profiler::ThreadRegistration::OnThreadRef aThread) {
|
|
return &aThread.UnlockedConstReaderAndAtomicRWRef()
|
|
.ProfilingStackRef();
|
|
},
|
|
nullptr);
|
|
if (mProfilingStack) {
|
|
mProfilingStack->pushLabelFrame(aLabel, aDynamicString, this,
|
|
aCategoryPair, aFlags);
|
|
}
|
|
}
|
|
|
|
// This is the AUTO_PROFILER_LABEL_FAST variant. It retrieves the
|
|
// ProfilingStack from the JSContext and does nothing if the profiler is
|
|
// inactive.
|
|
AutoProfilerLabelHot(JSContext* aJSContext, const char* aLabel,
|
|
const char* aDynamicString,
|
|
JS::ProfilingCategoryPair aCategoryPair,
|
|
uint32_t aFlags) {
|
|
mProfilingStack = js::GetContextProfilingStackIfEnabled(aJSContext);
|
|
if (MOZ_UNLIKELY(mProfilingStack)) {
|
|
mProfilingStack->pushLabelFrame(aLabel, aDynamicString, this,
|
|
aCategoryPair, aFlags);
|
|
}
|
|
}
|
|
|
|
~AutoProfilerLabelHot() {
|
|
// This function runs both on and off the main thread.
|
|
if (MOZ_UNLIKELY(mProfilingStack)) {
|
|
mProfilingStack->pop();
|
|
}
|
|
}
|
|
|
|
private:
|
|
// We save a ProfilingStack pointer in the ctor so we don't have to redo the
|
|
// TLS lookup in the dtor.
|
|
ProfilingStack* mProfilingStack;
|
|
};
|
|
|
|
#endif // !MOZ_GECKO_PROFILER
|
|
|
|
} // namespace mozilla
|
|
|
|
#endif // ProfilerLabels_h
|