michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: // IWYU pragma: private, include "GeckoProfiler.h" michael@0: michael@0: #ifndef TOOLS_SPS_SAMPLER_H_ michael@0: #define TOOLS_SPS_SAMPLER_H_ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include "mozilla/ThreadLocal.h" michael@0: #include "mozilla/Assertions.h" michael@0: #include "nscore.h" michael@0: #include "GeckoProfilerFunc.h" michael@0: #include "PseudoStack.h" michael@0: #include "nsISupports.h" michael@0: michael@0: #ifdef MOZ_TASK_TRACER michael@0: #include "GeckoTaskTracerImpl.h" michael@0: #endif michael@0: michael@0: /* QT has a #define for the word "slots" and jsfriendapi.h has a struct with michael@0: * this variable name, causing compilation problems. Alleviate this for now by michael@0: * removing this #define */ michael@0: #ifdef MOZ_WIDGET_QT michael@0: #undef slots michael@0: #endif michael@0: michael@0: // Make sure that we can use std::min here without the Windows headers messing with us. michael@0: #ifdef min michael@0: #undef min michael@0: #endif michael@0: michael@0: class TableTicker; michael@0: class JSCustomObject; michael@0: michael@0: namespace mozilla { michael@0: class TimeStamp; michael@0: } michael@0: michael@0: extern mozilla::ThreadLocal tlsPseudoStack; michael@0: extern mozilla::ThreadLocal tlsTicker; michael@0: extern mozilla::ThreadLocal tlsStackTop; michael@0: extern bool stack_key_initialized; michael@0: michael@0: #ifndef SAMPLE_FUNCTION_NAME michael@0: # ifdef __GNUC__ michael@0: # define SAMPLE_FUNCTION_NAME __FUNCTION__ michael@0: # elif defined(_MSC_VER) michael@0: # define SAMPLE_FUNCTION_NAME __FUNCTION__ michael@0: # else michael@0: # define SAMPLE_FUNCTION_NAME __func__ // defined in C99, supported in various C++ compilers. Just raw function name. michael@0: # endif michael@0: #endif michael@0: michael@0: static inline michael@0: void profiler_init(void* stackTop) michael@0: { michael@0: #ifdef MOZ_TASK_TRACER michael@0: mozilla::tasktracer::InitTaskTracer(); michael@0: #endif michael@0: mozilla_sampler_init(stackTop); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_shutdown() michael@0: { michael@0: #ifdef MOZ_TASK_TRACER michael@0: mozilla::tasktracer::ShutdownTaskTracer(); michael@0: #endif michael@0: mozilla_sampler_shutdown(); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_start(int aProfileEntries, int aInterval, michael@0: const char** aFeatures, uint32_t aFeatureCount, michael@0: const char** aThreadNameFilters, uint32_t aFilterCount) michael@0: { michael@0: mozilla_sampler_start(aProfileEntries, aInterval, aFeatures, aFeatureCount, aThreadNameFilters, aFilterCount); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_stop() michael@0: { michael@0: mozilla_sampler_stop(); michael@0: } michael@0: michael@0: static inline michael@0: bool profiler_is_paused() michael@0: { michael@0: return mozilla_sampler_is_paused(); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_pause() michael@0: { michael@0: mozilla_sampler_pause(); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_resume() michael@0: { michael@0: mozilla_sampler_resume(); michael@0: } michael@0: michael@0: static inline michael@0: ProfilerBacktrace* profiler_get_backtrace() michael@0: { michael@0: return mozilla_sampler_get_backtrace(); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_free_backtrace(ProfilerBacktrace* aBacktrace) michael@0: { michael@0: mozilla_sampler_free_backtrace(aBacktrace); michael@0: } michael@0: michael@0: static inline michael@0: bool profiler_is_active() michael@0: { michael@0: return mozilla_sampler_is_active(); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_responsiveness(const mozilla::TimeStamp& aTime) michael@0: { michael@0: mozilla_sampler_responsiveness(aTime); michael@0: } michael@0: michael@0: static inline michael@0: const double* profiler_get_responsiveness() michael@0: { michael@0: return mozilla_sampler_get_responsiveness(); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_set_frame_number(int frameNumber) michael@0: { michael@0: return mozilla_sampler_frame_number(frameNumber); michael@0: } michael@0: michael@0: static inline michael@0: char* profiler_get_profile() michael@0: { michael@0: return mozilla_sampler_get_profile(); michael@0: } michael@0: michael@0: static inline michael@0: JSObject* profiler_get_profile_jsobject(JSContext* aCx) michael@0: { michael@0: return mozilla_sampler_get_profile_data(aCx); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_save_profile_to_file(const char* aFilename) michael@0: { michael@0: return mozilla_sampler_save_profile_to_file(aFilename); michael@0: } michael@0: michael@0: static inline michael@0: const char** profiler_get_features() michael@0: { michael@0: return mozilla_sampler_get_features(); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_print_location() michael@0: { michael@0: if (!sps_version2()) { michael@0: return mozilla_sampler_print_location1(); michael@0: } else { michael@0: return mozilla_sampler_print_location2(); michael@0: } michael@0: } michael@0: michael@0: static inline michael@0: void profiler_lock() michael@0: { michael@0: return mozilla_sampler_lock(); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_unlock() michael@0: { michael@0: return mozilla_sampler_unlock(); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_register_thread(const char* name, void* stackTop) michael@0: { michael@0: mozilla_sampler_register_thread(name, stackTop); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_unregister_thread() michael@0: { michael@0: mozilla_sampler_unregister_thread(); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_sleep_start() michael@0: { michael@0: mozilla_sampler_sleep_start(); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_sleep_end() michael@0: { michael@0: mozilla_sampler_sleep_end(); michael@0: } michael@0: michael@0: static inline michael@0: void profiler_js_operation_callback() michael@0: { michael@0: PseudoStack *stack = tlsPseudoStack.get(); michael@0: if (!stack) { michael@0: return; michael@0: } michael@0: michael@0: stack->jsOperationCallback(); michael@0: } michael@0: michael@0: static inline michael@0: double profiler_time() michael@0: { michael@0: return mozilla_sampler_time(); michael@0: } michael@0: michael@0: static inline michael@0: double profiler_time(const mozilla::TimeStamp& aTime) michael@0: { michael@0: return mozilla_sampler_time(aTime); michael@0: } michael@0: michael@0: static inline michael@0: bool profiler_in_privacy_mode() michael@0: { michael@0: PseudoStack *stack = tlsPseudoStack.get(); michael@0: if (!stack) { michael@0: return false; michael@0: } michael@0: return stack->mPrivacyMode; michael@0: } michael@0: michael@0: static inline void profiler_tracing(const char* aCategory, const char* aInfo, michael@0: TracingMetadata aMetaData = TRACING_DEFAULT) michael@0: { michael@0: if (!stack_key_initialized) michael@0: return; michael@0: michael@0: // Don't insert a marker if we're not profiling to avoid michael@0: // the heap copy (malloc). michael@0: if (!profiler_is_active()) { michael@0: return; michael@0: } michael@0: michael@0: mozilla_sampler_tracing(aCategory, aInfo, aMetaData); michael@0: } michael@0: michael@0: // Uncomment this to turn on systrace or build with michael@0: // ac_add_options --enable-systace michael@0: //#define MOZ_USE_SYSTRACE michael@0: #ifdef MOZ_USE_SYSTRACE michael@0: # define ATRACE_TAG ATRACE_TAG_GRAPHICS michael@0: // We need HAVE_ANDROID_OS to be defined for Trace.h. michael@0: // If its not set we will set it temporary and remove it. michael@0: # ifndef HAVE_ANDROID_OS michael@0: # define HAVE_ANDROID_OS michael@0: # define REMOVE_HAVE_ANDROID_OS michael@0: # endif michael@0: # include michael@0: # define MOZ_PLATFORM_TRACING ATRACE_CALL(); michael@0: # ifdef REMOVE_HAVE_ANDROID_OS michael@0: # undef HAVE_ANDROID_OS michael@0: # undef REMOVE_HAVE_ANDROID_OS michael@0: # endif michael@0: #else michael@0: # define MOZ_PLATFORM_TRACING michael@0: #endif michael@0: michael@0: // we want the class and function name but can't easily get that using preprocessor macros michael@0: // __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters michael@0: michael@0: #define SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) id ## line michael@0: #define SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, line) SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) michael@0: #define SAMPLER_APPEND_LINE_NUMBER(id) SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, __LINE__) michael@0: michael@0: #define PROFILER_LABEL(name_space, info) MOZ_PLATFORM_TRACING mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__) michael@0: #define PROFILER_LABEL_PRINTF(name_space, info, ...) MOZ_PLATFORM_TRACING mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__, __VA_ARGS__) michael@0: michael@0: #define PROFILER_MARKER(info) mozilla_sampler_add_marker(info) michael@0: #define PROFILER_MARKER_PAYLOAD(info, payload) mozilla_sampler_add_marker(info, payload) michael@0: #define PROFILER_MAIN_THREAD_MARKER(info) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla_sampler_add_marker(info) michael@0: michael@0: #define PROFILER_MAIN_THREAD_LABEL(name_space, info) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__) michael@0: #define PROFILER_MAIN_THREAD_LABEL_PRINTF(name_space, info, ...) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__, __VA_ARGS__) michael@0: michael@0: michael@0: /* FIXME/bug 789667: memory constraints wouldn't much of a problem for michael@0: * this small a sample buffer size, except that serializing the michael@0: * profile data is extremely, unnecessarily memory intensive. */ michael@0: #ifdef MOZ_WIDGET_GONK michael@0: # define PLATFORM_LIKELY_MEMORY_CONSTRAINED michael@0: #endif michael@0: michael@0: #if !defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED) && !defined(ARCH_ARMV6) michael@0: # define PROFILE_DEFAULT_ENTRY 1000000 michael@0: #else michael@0: # define PROFILE_DEFAULT_ENTRY 100000 michael@0: #endif michael@0: michael@0: // In the case of profiler_get_backtrace we know that we only need enough space michael@0: // for a single backtrace. michael@0: #define GET_BACKTRACE_DEFAULT_ENTRY 1000 michael@0: michael@0: #if defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED) michael@0: /* A 1ms sampling interval has been shown to be a large perf hit michael@0: * (10fps) on memory-contrained (low-end) platforms, and additionally michael@0: * to yield different results from the profiler. Where this is the michael@0: * important case, b2g, there are also many gecko processes which michael@0: * magnify these effects. */ michael@0: # define PROFILE_DEFAULT_INTERVAL 10 michael@0: #elif defined(ANDROID) michael@0: // We use a lower frequency on Android, in order to make things work michael@0: // more smoothly on phones. This value can be adjusted later with michael@0: // some libunwind optimizations. michael@0: // In one sample measurement on Galaxy Nexus, out of about 700 backtraces, michael@0: // 60 of them took more than 25ms, and the average and standard deviation michael@0: // were 6.17ms and 9.71ms respectively. michael@0: michael@0: // For now since we don't support stackwalking let's use 1ms since it's fast michael@0: // enough. michael@0: #define PROFILE_DEFAULT_INTERVAL 1 michael@0: #else michael@0: #define PROFILE_DEFAULT_INTERVAL 1 michael@0: #endif michael@0: #define PROFILE_DEFAULT_FEATURES NULL michael@0: #define PROFILE_DEFAULT_FEATURE_COUNT 0 michael@0: michael@0: namespace mozilla { michael@0: michael@0: class MOZ_STACK_CLASS SamplerStackFrameRAII { michael@0: public: michael@0: // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then. michael@0: SamplerStackFrameRAII(const char *aInfo, uint32_t line) { michael@0: mHandle = mozilla_sampler_call_enter(aInfo, this, false, line); michael@0: } michael@0: ~SamplerStackFrameRAII() { michael@0: mozilla_sampler_call_exit(mHandle); michael@0: } michael@0: private: michael@0: void* mHandle; michael@0: }; michael@0: michael@0: static const int SAMPLER_MAX_STRING = 128; michael@0: class MOZ_STACK_CLASS SamplerStackFramePrintfRAII { michael@0: public: michael@0: // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then. michael@0: SamplerStackFramePrintfRAII(const char *aDefault, uint32_t line, const char *aFormat, ...) { michael@0: if (profiler_is_active() && !profiler_in_privacy_mode()) { michael@0: va_list args; michael@0: va_start(args, aFormat); michael@0: char buff[SAMPLER_MAX_STRING]; michael@0: michael@0: // We have to use seperate printf's because we're using michael@0: // the vargs. michael@0: #if _MSC_VER michael@0: _vsnprintf(buff, SAMPLER_MAX_STRING, aFormat, args); michael@0: _snprintf(mDest, SAMPLER_MAX_STRING, "%s %s", aDefault, buff); michael@0: #else michael@0: ::vsnprintf(buff, SAMPLER_MAX_STRING, aFormat, args); michael@0: ::snprintf(mDest, SAMPLER_MAX_STRING, "%s %s", aDefault, buff); michael@0: #endif michael@0: mHandle = mozilla_sampler_call_enter(mDest, this, true, line); michael@0: va_end(args); michael@0: } else { michael@0: mHandle = mozilla_sampler_call_enter(aDefault, this, false, line); michael@0: } michael@0: } michael@0: ~SamplerStackFramePrintfRAII() { michael@0: mozilla_sampler_call_exit(mHandle); michael@0: } michael@0: private: michael@0: char mDest[SAMPLER_MAX_STRING]; michael@0: void* mHandle; michael@0: }; michael@0: michael@0: } //mozilla michael@0: michael@0: inline PseudoStack* mozilla_get_pseudo_stack(void) michael@0: { michael@0: if (!stack_key_initialized) michael@0: return nullptr; michael@0: return tlsPseudoStack.get(); michael@0: } michael@0: michael@0: inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress, michael@0: bool aCopy, uint32_t line) michael@0: { michael@0: // check if we've been initialized to avoid calling pthread_getspecific michael@0: // with a null tlsStack which will return undefined results. michael@0: if (!stack_key_initialized) michael@0: return nullptr; michael@0: michael@0: PseudoStack *stack = tlsPseudoStack.get(); michael@0: // we can't infer whether 'stack' has been initialized michael@0: // based on the value of stack_key_intiailized because michael@0: // 'stack' is only intialized when a thread is being michael@0: // profiled. michael@0: if (!stack) { michael@0: return stack; michael@0: } michael@0: stack->push(aInfo, aFrameAddress, aCopy, line); michael@0: michael@0: // The handle is meant to support future changes michael@0: // but for now it is simply use to save a call to michael@0: // pthread_getspecific on exit. It also supports the michael@0: // case where the sampler is initialized between michael@0: // enter and exit. michael@0: return stack; michael@0: } michael@0: michael@0: inline void mozilla_sampler_call_exit(void *aHandle) michael@0: { michael@0: if (!aHandle) michael@0: return; michael@0: michael@0: PseudoStack *stack = (PseudoStack*)aHandle; michael@0: stack->pop(); michael@0: } michael@0: michael@0: void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload); michael@0: michael@0: #endif /* ndef TOOLS_SPS_SAMPLER_H_ */