diff options
Diffstat (limited to 'tools/profiler/public/GeckoProfilerImpl.h')
-rw-r--r-- | tools/profiler/public/GeckoProfilerImpl.h | 522 |
1 files changed, 522 insertions, 0 deletions
diff --git a/tools/profiler/public/GeckoProfilerImpl.h b/tools/profiler/public/GeckoProfilerImpl.h new file mode 100644 index 0000000000..a32096b943 --- /dev/null +++ b/tools/profiler/public/GeckoProfilerImpl.h @@ -0,0 +1,522 @@ +/* -*- Mode: C++; 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/. */ +// IWYU pragma: private, include "GeckoProfiler.h" + +#ifndef TOOLS_SPS_SAMPLER_H_ +#define TOOLS_SPS_SAMPLER_H_ + +#include <stdlib.h> +#include <signal.h> +#include <stdarg.h> +#include "mozilla/Assertions.h" +#include "mozilla/GuardObjects.h" +#include "mozilla/Sprintf.h" +#include "mozilla/ThreadLocal.h" +#include "mozilla/UniquePtr.h" +#ifndef SPS_STANDALONE +#include "nscore.h" +#include "nsISupports.h" +#endif +#include "GeckoProfilerFunc.h" +#include "PseudoStack.h" +#include "ProfilerBacktrace.h" + +// Make sure that we can use std::min here without the Windows headers messing with us. +#ifdef min +#undef min +#endif + +class GeckoSampler; + +namespace mozilla { +class TimeStamp; +} // namespace mozilla + +extern MOZ_THREAD_LOCAL(PseudoStack *) tlsPseudoStack; +extern MOZ_THREAD_LOCAL(GeckoSampler *) tlsTicker; +extern MOZ_THREAD_LOCAL(void *) tlsStackTop; +extern bool stack_key_initialized; + +#ifndef SAMPLE_FUNCTION_NAME +# ifdef __GNUC__ +# define SAMPLE_FUNCTION_NAME __FUNCTION__ +# elif defined(_MSC_VER) +# define SAMPLE_FUNCTION_NAME __FUNCTION__ +# else +# define SAMPLE_FUNCTION_NAME __func__ // defined in C99, supported in various C++ compilers. Just raw function name. +# endif +#endif + +static inline +void profiler_init(void* stackTop) +{ + mozilla_sampler_init(stackTop); +} + +static inline +void profiler_shutdown() +{ + mozilla_sampler_shutdown(); +} + +static inline +void profiler_start(int aProfileEntries, double aInterval, + const char** aFeatures, uint32_t aFeatureCount, + const char** aThreadNameFilters, uint32_t aFilterCount) +{ + mozilla_sampler_start(aProfileEntries, aInterval, aFeatures, aFeatureCount, aThreadNameFilters, aFilterCount); +} + +static inline +void profiler_stop() +{ + mozilla_sampler_stop(); +} + +static inline +bool profiler_is_paused() +{ + return mozilla_sampler_is_paused(); +} + +static inline +void profiler_pause() +{ + mozilla_sampler_pause(); +} + +static inline +void profiler_resume() +{ + mozilla_sampler_resume(); +} + +static inline +ProfilerBacktrace* profiler_get_backtrace() +{ + return mozilla_sampler_get_backtrace(); +} + +static inline +void profiler_free_backtrace(ProfilerBacktrace* aBacktrace) +{ + mozilla_sampler_free_backtrace(aBacktrace); +} + +static inline +void profiler_get_backtrace_noalloc(char *output, size_t outputSize) +{ + return mozilla_sampler_get_backtrace_noalloc(output, outputSize); +} + +static inline +bool profiler_is_active() +{ + return mozilla_sampler_is_active(); +} + +static inline +bool profiler_feature_active(const char* aName) +{ + return mozilla_sampler_feature_active(aName); +} + +static inline +void profiler_responsiveness(const mozilla::TimeStamp& aTime) +{ + mozilla_sampler_responsiveness(aTime); +} + +static inline +void profiler_set_frame_number(int frameNumber) +{ + return mozilla_sampler_frame_number(frameNumber); +} + +static inline +mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0) +{ + return mozilla_sampler_get_profile(aSinceTime); +} + +#ifndef SPS_STANDALONE +static inline +JSObject* profiler_get_profile_jsobject(JSContext* aCx, double aSinceTime = 0) +{ + return mozilla_sampler_get_profile_data(aCx, aSinceTime); +} + +static inline +void profiler_get_profile_jsobject_async(double aSinceTime = 0, + mozilla::dom::Promise* aPromise = 0) +{ + mozilla_sampler_get_profile_data_async(aSinceTime, aPromise); +} + +static inline +void profiler_get_start_params(int* aEntrySize, + double* aInterval, + mozilla::Vector<const char*>* aFilters, + mozilla::Vector<const char*>* aFeatures) +{ + mozilla_sampler_get_profiler_start_params(aEntrySize, aInterval, aFilters, aFeatures); +} + +static inline +void profiler_get_gatherer(nsISupports** aRetVal) +{ + mozilla_sampler_get_gatherer(aRetVal); +} + +#endif + +static inline +void profiler_save_profile_to_file(const char* aFilename) +{ + return mozilla_sampler_save_profile_to_file(aFilename); +} + +static inline +const char** profiler_get_features() +{ + return mozilla_sampler_get_features(); +} + +static inline +void profiler_get_buffer_info(uint32_t *aCurrentPosition, uint32_t *aTotalSize, + uint32_t *aGeneration) +{ + return mozilla_sampler_get_buffer_info(aCurrentPosition, aTotalSize, aGeneration); +} + +static inline +void profiler_lock() +{ + return mozilla_sampler_lock(); +} + +static inline +void profiler_unlock() +{ + return mozilla_sampler_unlock(); +} + +static inline +void profiler_register_thread(const char* name, void* guessStackTop) +{ + mozilla_sampler_register_thread(name, guessStackTop); +} + +static inline +void profiler_unregister_thread() +{ + mozilla_sampler_unregister_thread(); +} + +static inline +void profiler_sleep_start() +{ + mozilla_sampler_sleep_start(); +} + +static inline +void profiler_sleep_end() +{ + mozilla_sampler_sleep_end(); +} + +static inline +bool profiler_is_sleeping() +{ + return mozilla_sampler_is_sleeping(); +} + +#ifndef SPS_STANDALONE +static inline +void profiler_js_operation_callback() +{ + PseudoStack *stack = tlsPseudoStack.get(); + if (!stack) { + return; + } + + stack->jsOperationCallback(); +} +#endif + +static inline +double profiler_time() +{ + return mozilla_sampler_time(); +} + +static inline +double profiler_time(const mozilla::TimeStamp& aTime) +{ + return mozilla_sampler_time(aTime); +} + +static inline +bool profiler_in_privacy_mode() +{ + PseudoStack *stack = tlsPseudoStack.get(); + if (!stack) { + return false; + } + return stack->mPrivacyMode; +} + +static inline void profiler_tracing(const char* aCategory, const char* aInfo, + ProfilerBacktrace* aCause, + TracingMetadata aMetaData = TRACING_DEFAULT) +{ + // Don't insert a marker if we're not profiling to avoid + // the heap copy (malloc). + if (!stack_key_initialized || !profiler_is_active()) { + delete aCause; + return; + } + + mozilla_sampler_tracing(aCategory, aInfo, aCause, aMetaData); +} + +static inline void profiler_tracing(const char* aCategory, const char* aInfo, + TracingMetadata aMetaData = TRACING_DEFAULT) +{ + if (!stack_key_initialized) + return; + + // Don't insert a marker if we're not profiling to avoid + // the heap copy (malloc). + if (!profiler_is_active()) { + return; + } + + mozilla_sampler_tracing(aCategory, aInfo, aMetaData); +} + +#define SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) id ## line +#define SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, line) SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) +#define SAMPLER_APPEND_LINE_NUMBER(id) SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, __LINE__) + +// Uncomment this to turn on systrace or build with +// ac_add_options --enable-systace +//#define MOZ_USE_SYSTRACE +#ifdef MOZ_USE_SYSTRACE +#ifndef ATRACE_TAG +# define ATRACE_TAG ATRACE_TAG_ALWAYS +#endif +// We need HAVE_ANDROID_OS to be defined for Trace.h. +// If its not set we will set it temporary and remove it. +# ifndef HAVE_ANDROID_OS +# define HAVE_ANDROID_OS +# define REMOVE_HAVE_ANDROID_OS +# endif +// Android source code will include <cutils/trace.h> before this. There is no +// HAVE_ANDROID_OS defined in Firefox OS build at that time. Enabled it globally +// will cause other build break. So atrace_begin and atrace_end are not defined. +// It will cause a build-break when we include <utils/Trace.h>. Use undef +// _LIBS_CUTILS_TRACE_H will force <cutils/trace.h> to define atrace_begin and +// atrace_end with defined HAVE_ANDROID_OS again. Then there is no build-break. +# undef _LIBS_CUTILS_TRACE_H +# include <utils/Trace.h> +# define MOZ_PLATFORM_TRACING(name) android::ScopedTrace SAMPLER_APPEND_LINE_NUMBER(scopedTrace)(ATRACE_TAG, name); +# ifdef REMOVE_HAVE_ANDROID_OS +# undef HAVE_ANDROID_OS +# undef REMOVE_HAVE_ANDROID_OS +# endif +#else +# define MOZ_PLATFORM_TRACING(name) +#endif + +// we want the class and function name but can't easily get that using preprocessor macros +// __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters + +#define PROFILER_LABEL(name_space, info, category) MOZ_PLATFORM_TRACING(name_space "::" info) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__) +#define PROFILER_LABEL_FUNC(category) MOZ_PLATFORM_TRACING(SAMPLE_FUNCTION_NAME) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(SAMPLE_FUNCTION_NAME, category, __LINE__) +#define PROFILER_LABEL_PRINTF(name_space, info, category, ...) MOZ_PLATFORM_TRACING(name_space "::" info) mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__, __VA_ARGS__) + +#define PROFILER_MARKER(info) mozilla_sampler_add_marker(info) +#define PROFILER_MARKER_PAYLOAD(info, payload) mozilla_sampler_add_marker(info, payload) +#define PROFILER_MAIN_THREAD_MARKER(info) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla_sampler_add_marker(info) + +#define PROFILER_MAIN_THREAD_LABEL(name_space, info, category) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__) +#define PROFILER_MAIN_THREAD_LABEL_PRINTF(name_space, info, category, ...) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__, __VA_ARGS__) + + +/* FIXME/bug 789667: memory constraints wouldn't much of a problem for + * this small a sample buffer size, except that serializing the + * profile data is extremely, unnecessarily memory intensive. */ +#ifdef MOZ_WIDGET_GONK +# define PLATFORM_LIKELY_MEMORY_CONSTRAINED +#endif + +#if !defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED) && !defined(ARCH_ARMV6) +# define PROFILE_DEFAULT_ENTRY 1000000 +#else +# define PROFILE_DEFAULT_ENTRY 100000 +#endif + +// In the case of profiler_get_backtrace we know that we only need enough space +// for a single backtrace. +#define GET_BACKTRACE_DEFAULT_ENTRY 1000 + +#if defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED) +/* A 1ms sampling interval has been shown to be a large perf hit + * (10fps) on memory-contrained (low-end) platforms, and additionally + * to yield different results from the profiler. Where this is the + * important case, b2g, there are also many gecko processes which + * magnify these effects. */ +# define PROFILE_DEFAULT_INTERVAL 10 +#elif defined(ANDROID) +// We use a lower frequency on Android, in order to make things work +// more smoothly on phones. This value can be adjusted later with +// some libunwind optimizations. +// In one sample measurement on Galaxy Nexus, out of about 700 backtraces, +// 60 of them took more than 25ms, and the average and standard deviation +// were 6.17ms and 9.71ms respectively. + +// For now since we don't support stackwalking let's use 1ms since it's fast +// enough. +#define PROFILE_DEFAULT_INTERVAL 1 +#else +#define PROFILE_DEFAULT_INTERVAL 1 +#endif +#define PROFILE_DEFAULT_FEATURES NULL +#define PROFILE_DEFAULT_FEATURE_COUNT 0 + +namespace mozilla { + +class MOZ_RAII GeckoProfilerTracingRAII { +public: + GeckoProfilerTracingRAII(const char* aCategory, const char* aInfo, + mozilla::UniquePtr<ProfilerBacktrace> aBacktrace + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mCategory(aCategory) + , mInfo(aInfo) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + profiler_tracing(mCategory, mInfo, aBacktrace.release(), TRACING_INTERVAL_START); + } + + ~GeckoProfilerTracingRAII() { + profiler_tracing(mCategory, mInfo, TRACING_INTERVAL_END); + } + +protected: + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER + const char* mCategory; + const char* mInfo; +}; + +class MOZ_RAII SamplerStackFrameRAII { +public: + // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then. + SamplerStackFrameRAII(const char *aInfo, + js::ProfileEntry::Category aCategory, uint32_t line + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + mHandle = mozilla_sampler_call_enter(aInfo, aCategory, this, false, line); + } + ~SamplerStackFrameRAII() { + mozilla_sampler_call_exit(mHandle); + } +private: + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER + void* mHandle; +}; + +static const int SAMPLER_MAX_STRING = 128; +class MOZ_RAII SamplerStackFramePrintfRAII { +public: + // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then. + SamplerStackFramePrintfRAII(const char *aInfo, + js::ProfileEntry::Category aCategory, uint32_t line, const char *aFormat, ...) + : mHandle(nullptr) + { + if (profiler_is_active() && !profiler_in_privacy_mode()) { + va_list args; + va_start(args, aFormat); + char buff[SAMPLER_MAX_STRING]; + + // We have to use seperate printf's because we're using + // the vargs. + VsprintfLiteral(buff, aFormat, args); + SprintfLiteral(mDest, "%s %s", aInfo, buff); + + mHandle = mozilla_sampler_call_enter(mDest, aCategory, this, true, line); + va_end(args); + } else { + mHandle = mozilla_sampler_call_enter(aInfo, aCategory, this, false, line); + } + } + ~SamplerStackFramePrintfRAII() { + mozilla_sampler_call_exit(mHandle); + } +private: + char mDest[SAMPLER_MAX_STRING]; + void* mHandle; +}; + +} // namespace mozilla + +inline PseudoStack* mozilla_get_pseudo_stack(void) +{ + if (!stack_key_initialized) + return nullptr; + return tlsPseudoStack.get(); +} + +inline void* mozilla_sampler_call_enter(const char *aInfo, + js::ProfileEntry::Category aCategory, void *aFrameAddress, bool aCopy, uint32_t line) +{ + // check if we've been initialized to avoid calling pthread_getspecific + // with a null tlsStack which will return undefined results. + if (!stack_key_initialized) + return nullptr; + + PseudoStack *stack = tlsPseudoStack.get(); + // we can't infer whether 'stack' has been initialized + // based on the value of stack_key_intiailized because + // 'stack' is only intialized when a thread is being + // profiled. + if (!stack) { + return stack; + } + stack->push(aInfo, aCategory, aFrameAddress, aCopy, line); + + // The handle is meant to support future changes + // but for now it is simply use to save a call to + // pthread_getspecific on exit. It also supports the + // case where the sampler is initialized between + // enter and exit. + return stack; +} + +inline void mozilla_sampler_call_exit(void *aHandle) +{ + if (!aHandle) + return; + + PseudoStack *stack = (PseudoStack*)aHandle; + stack->popAndMaybeDelete(); +} + +void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload); + +static inline +void profiler_log(const char *str) +{ + profiler_tracing("log", str, TRACING_EVENT); +} + +static inline +void profiler_log(const char *fmt, va_list args) +{ + mozilla_sampler_log(fmt, args); +} + +#endif /* ndef TOOLS_SPS_SAMPLER_H_ */ |