summaryrefslogtreecommitdiffstats
path: root/tools/profiler/public
diff options
context:
space:
mode:
Diffstat (limited to 'tools/profiler/public')
-rw-r--r--tools/profiler/public/GeckoProfiler.h300
-rw-r--r--tools/profiler/public/GeckoProfilerFunc.h125
-rw-r--r--tools/profiler/public/GeckoProfilerImpl.h522
-rw-r--r--tools/profiler/public/ProfileGatherer.h42
-rw-r--r--tools/profiler/public/ProfilerBacktrace.h36
-rw-r--r--tools/profiler/public/ProfilerMarkers.h193
-rw-r--r--tools/profiler/public/PseudoStack.h469
-rw-r--r--tools/profiler/public/shared-libraries.h137
8 files changed, 1824 insertions, 0 deletions
diff --git a/tools/profiler/public/GeckoProfiler.h b/tools/profiler/public/GeckoProfiler.h
new file mode 100644
index 000000000..bef017d11
--- /dev/null
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -0,0 +1,300 @@
+/* -*- 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/. */
+
+/* *************** SPS Sampler Information ****************
+ *
+ * SPS is an always on profiler that takes fast and low overheads samples
+ * of the program execution using only userspace functionity for portability.
+ * The goal of this module is to provide performance data in a generic
+ * cross platform way without requiring custom tools or kernel support.
+ *
+ * Non goals: Support features that are platform specific or replace
+ * platform specific profilers.
+ *
+ * Samples are collected to form a timeline with optional timeline event (markers)
+ * used for filtering.
+ *
+ * SPS collects samples in a platform independant way by using a speudo stack abstraction
+ * of the real program stack by using 'sample stack frames'. When a sample is collected
+ * all active sample stack frames and the program counter are recorded.
+ */
+
+/* *************** SPS Sampler File Format ****************
+ *
+ * Simple new line seperated tag format:
+ * S -> BOF tags EOF
+ * tags -> tag tags
+ * tag -> CHAR - STRING
+ *
+ * Tags:
+ * 's' - Sample tag followed by the first stack frame followed by 0 or more 'c' tags.
+ * 'c' - Continue Sample tag gives remaining tag element. If a 'c' tag is seen without
+ * a preceding 's' tag it should be ignored. This is to support the behavior
+ * of circular buffers.
+ * If the 'stackwalk' feature is enabled this tag will have the format
+ * 'l-<library name>@<hex address>' and will expect an external tool to translate
+ * the tag into something readable through a symbolication processing step.
+ * 'm' - Timeline marker. Zero or more may appear before a 's' tag.
+ * 'l' - Information about the program counter library and address. Post processing
+ * can include function and source line. If built with leaf data enabled
+ * this tag will describe the last 'c' tag.
+ * 'r' - Responsiveness tag following an 's' tag. Gives an indication on how well the
+ * application is responding to the event loop. Lower is better.
+ * 't' - Elapse time since recording started.
+ *
+ */
+
+#ifndef SAMPLER_H
+#define SAMPLER_H
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#ifndef SPS_STANDALONE
+#include "js/TypeDecls.h"
+#endif
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+
+namespace mozilla {
+class TimeStamp;
+
+namespace dom {
+class Promise;
+} // namespace dom
+
+} // namespace mozilla
+
+#ifndef SPS_STANDALONE
+class nsIProfilerStartParams;
+#endif
+
+enum TracingMetadata {
+ TRACING_DEFAULT,
+ TRACING_INTERVAL_START,
+ TRACING_INTERVAL_END,
+ TRACING_EVENT,
+ TRACING_EVENT_BACKTRACE,
+ TRACING_TIMESTAMP
+};
+
+#if !defined(MOZ_ENABLE_PROFILER_SPS)
+
+#include <stdint.h>
+#include <stdarg.h>
+
+// Insert a RAII in this scope to active a pseudo label. Any samples collected
+// in this scope will contain this annotation. For dynamic strings use
+// PROFILER_LABEL_PRINTF. Arguments must be string literals.
+#define PROFILER_LABEL(name_space, info, category) do {} while (0)
+
+// Similar to PROFILER_LABEL, PROFILER_LABEL_FUNC will push/pop the enclosing
+// functon name as the pseudostack label.
+#define PROFILER_LABEL_FUNC(category) do {} while (0)
+
+// Format a dynamic string as a pseudo label. These labels will a considerable
+// storage size in the circular buffer compared to regular labels. This function
+// can be used to annotate custom information such as URL for the resource being
+// decoded or the size of the paint.
+#define PROFILER_LABEL_PRINTF(name_space, info, category, format, ...) do {} while (0)
+
+// Insert a marker in the profile timeline. This is useful to delimit something
+// important happening such as the first paint. Unlike profiler_label that are
+// only recorded if a sample is collected while it is active, marker will always
+// be collected.
+#define PROFILER_MARKER(info) do {} while (0)
+#define PROFILER_MARKER_PAYLOAD(info, payload) do { mozilla::UniquePtr<ProfilerMarkerPayload> payloadDeletor(payload); } while (0)
+
+// Main thread specilization to avoid TLS lookup for performance critical use.
+#define PROFILER_MAIN_THREAD_LABEL(name_space, info, category) do {} while (0)
+#define PROFILER_MAIN_THREAD_LABEL_PRINTF(name_space, info, category, format, ...) do {} while (0)
+
+static inline void profiler_tracing(const char* aCategory, const char* aInfo,
+ TracingMetadata metaData = TRACING_DEFAULT) {}
+class ProfilerBacktrace;
+
+static inline void profiler_tracing(const char* aCategory, const char* aInfo,
+ ProfilerBacktrace* aCause,
+ TracingMetadata metaData = TRACING_DEFAULT) {}
+
+// Initilize the profiler TLS, signal handlers on linux. If MOZ_PROFILER_STARTUP
+// is set the profiler will be started. This call must happen before any other
+// sampler calls. Particularly sampler_label/sampler_marker.
+static inline void profiler_init(void* stackTop) {};
+
+// Clean up the profiler module, stopping it if required. This function may
+// also save a shutdown profile if requested. No profiler calls should happen
+// after this point and all pseudo labels should have been popped.
+static inline void profiler_shutdown() {};
+
+// Start the profiler with the selected options. The samples will be
+// recorded in a circular buffer.
+// "aProfileEntries" is an abstract size indication of how big
+// the profile's circular buffer should be. Multiply by 4
+// words to get the cost.
+// "aInterval" the sampling interval. The profiler will do its
+// best to sample at this interval. The profiler visualization
+// should represent the actual sampling accuracy.
+static inline void profiler_start(int aProfileEntries, double aInterval,
+ const char** aFeatures, uint32_t aFeatureCount,
+ const char** aThreadNameFilters, uint32_t aFilterCount) {}
+
+// Stop the profiler and discard the profile. Call 'profiler_save' before this
+// to retrieve the profile.
+static inline void profiler_stop() {}
+
+// These functions pause and resume the profiler. While paused the profile will not
+// take any samples and will not record any data into its buffers. The profiler
+// remains fully initialized in this state. Timeline markers will still be stored.
+// This feature will keep javascript profiling enabled, thus allowing toggling the
+// profiler without invalidating the JIT.
+static inline bool profiler_is_paused() { return false; }
+static inline void profiler_pause() {}
+static inline void profiler_resume() {}
+
+
+// Immediately capture the current thread's call stack and return it
+static inline ProfilerBacktrace* profiler_get_backtrace() { return nullptr; }
+static inline void profiler_get_backtrace_noalloc(char *output, size_t outputSize) { return; }
+
+// Free a ProfilerBacktrace returned by profiler_get_backtrace()
+static inline void profiler_free_backtrace(ProfilerBacktrace* aBacktrace) {}
+
+static inline bool profiler_is_active() { return false; }
+
+// Check if an external profiler feature is active.
+// Supported:
+// * gpu
+static inline bool profiler_feature_active(const char*) { return false; }
+
+// Internal-only. Used by the event tracer.
+static inline void profiler_responsiveness(const mozilla::TimeStamp& aTime) {}
+
+// Internal-only.
+static inline void profiler_set_frame_number(int frameNumber) {}
+
+// Get the profile encoded as a JSON string.
+static inline mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0) {
+ return nullptr;
+}
+
+// Get the profile encoded as a JSON object.
+static inline JSObject* profiler_get_profile_jsobject(JSContext* aCx,
+ double aSinceTime = 0) {
+ return nullptr;
+}
+
+#ifndef SPS_STANDALONE
+// Get the profile encoded as a JSON object.
+static inline void profiler_get_profile_jsobject_async(double aSinceTime = 0,
+ mozilla::dom::Promise* = 0) {}
+static inline void profiler_get_start_params(int* aEntrySize,
+ double* aInterval,
+ mozilla::Vector<const char*>* aFilters,
+ mozilla::Vector<const char*>* aFeatures) {}
+#endif
+
+// Get the profile and write it into a file
+static inline void profiler_save_profile_to_file(char* aFilename) { }
+
+// Get the features supported by the profiler that are accepted by profiler_init.
+// Returns a null terminated char* array.
+static inline char** profiler_get_features() { return nullptr; }
+
+// Get information about the current buffer status.
+// Retursn (using outparams) the current write position in the buffer,
+// the total size of the buffer, and the generation of the buffer.
+// This information may be useful to a user-interface displaying the
+// current status of the profiler, allowing the user to get a sense
+// for how fast the buffer is being written to, and how much
+// data is visible.
+static inline void profiler_get_buffer_info(uint32_t *aCurrentPosition,
+ uint32_t *aTotalSize,
+ uint32_t *aGeneration)
+{
+ *aCurrentPosition = 0;
+ *aTotalSize = 0;
+ *aGeneration = 0;
+}
+
+// Discard the profile, throw away the profile and notify 'profiler-locked'.
+// This function is to be used when entering private browsing to prevent
+// the profiler from collecting sensitive data.
+static inline void profiler_lock() {}
+
+// Re-enable the profiler and notify 'profiler-unlocked'.
+static inline void profiler_unlock() {}
+
+static inline void profiler_register_thread(const char* name, void* guessStackTop) {}
+static inline void profiler_unregister_thread() {}
+
+// These functions tell the profiler that a thread went to sleep so that we can avoid
+// sampling it while it's sleeping. Calling profiler_sleep_start() twice without
+// profiler_sleep_end() is an error.
+static inline void profiler_sleep_start() {}
+static inline void profiler_sleep_end() {}
+static inline bool profiler_is_sleeping() { return false; }
+
+// Call by the JSRuntime's operation callback. This is used to enable
+// profiling on auxilerary threads.
+static inline void profiler_js_operation_callback() {}
+
+static inline double profiler_time() { return 0; }
+static inline double profiler_time(const mozilla::TimeStamp& aTime) { return 0; }
+
+static inline bool profiler_in_privacy_mode() { return false; }
+
+static inline void profiler_log(const char *str) {}
+static inline void profiler_log(const char *fmt, va_list args) {}
+
+#else
+
+#include "GeckoProfilerImpl.h"
+
+#endif
+
+class MOZ_RAII GeckoProfilerInitRAII {
+public:
+ explicit GeckoProfilerInitRAII(void* stackTop) {
+ profiler_init(stackTop);
+ }
+ ~GeckoProfilerInitRAII() {
+ profiler_shutdown();
+ }
+};
+
+class MOZ_RAII GeckoProfilerSleepRAII {
+public:
+ GeckoProfilerSleepRAII() {
+ profiler_sleep_start();
+ }
+ ~GeckoProfilerSleepRAII() {
+ profiler_sleep_end();
+ }
+};
+
+/**
+ * Temporarily wake up the profiler while servicing events such as
+ * Asynchronous Procedure Calls (APCs).
+ */
+class MOZ_RAII GeckoProfilerWakeRAII {
+public:
+ GeckoProfilerWakeRAII()
+ : mIssuedWake(profiler_is_sleeping())
+ {
+ if (mIssuedWake) {
+ profiler_sleep_end();
+ }
+ }
+ ~GeckoProfilerWakeRAII() {
+ if (mIssuedWake) {
+ MOZ_ASSERT(!profiler_is_sleeping());
+ profiler_sleep_start();
+ }
+ }
+private:
+ bool mIssuedWake;
+};
+
+#endif // ifndef SAMPLER_H
diff --git a/tools/profiler/public/GeckoProfilerFunc.h b/tools/profiler/public/GeckoProfilerFunc.h
new file mode 100644
index 000000000..e0d27f593
--- /dev/null
+++ b/tools/profiler/public/GeckoProfilerFunc.h
@@ -0,0 +1,125 @@
+/* -*- 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/. */
+
+#ifndef PROFILER_FUNCS_H
+#define PROFILER_FUNCS_H
+
+#ifndef SPS_STANDALONE
+#include "js/TypeDecls.h"
+#endif
+#include "js/ProfilingStack.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+#include <stdint.h>
+
+class nsISupports;
+
+namespace mozilla {
+class TimeStamp;
+
+namespace dom {
+class Promise;
+} // namespace dom
+
+} // namespace mozilla
+
+class ProfilerBacktrace;
+class ProfilerMarkerPayload;
+
+// Returns a handle to pass on exit. This can check that we are popping the
+// correct callstack.
+inline void* mozilla_sampler_call_enter(const char *aInfo, js::ProfileEntry::Category aCategory,
+ void *aFrameAddress = nullptr, bool aCopy = false,
+ uint32_t line = 0);
+
+inline void mozilla_sampler_call_exit(void* handle);
+
+void mozilla_sampler_add_marker(const char *aInfo,
+ ProfilerMarkerPayload *aPayload = nullptr);
+
+void mozilla_sampler_start(int aEntries, double aInterval,
+ const char** aFeatures, uint32_t aFeatureCount,
+ const char** aThreadNameFilters, uint32_t aFilterCount);
+
+void mozilla_sampler_stop();
+
+bool mozilla_sampler_is_paused();
+void mozilla_sampler_pause();
+void mozilla_sampler_resume();
+
+ProfilerBacktrace* mozilla_sampler_get_backtrace();
+void mozilla_sampler_free_backtrace(ProfilerBacktrace* aBacktrace);
+void mozilla_sampler_get_backtrace_noalloc(char *output, size_t outputSize);
+
+bool mozilla_sampler_is_active();
+
+bool mozilla_sampler_feature_active(const char* aName);
+
+void mozilla_sampler_responsiveness(const mozilla::TimeStamp& time);
+
+void mozilla_sampler_frame_number(int frameNumber);
+
+const double* mozilla_sampler_get_responsiveness();
+
+void mozilla_sampler_save();
+
+mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(double aSinceTime);
+
+#ifndef SPS_STANDALONE
+JSObject *mozilla_sampler_get_profile_data(JSContext* aCx, double aSinceTime);
+void mozilla_sampler_get_profile_data_async(double aSinceTime,
+ mozilla::dom::Promise* aPromise);
+void mozilla_sampler_get_profiler_start_params(int* aEntrySize,
+ double* aInterval,
+ mozilla::Vector<const char*>* aFilters,
+ mozilla::Vector<const char*>* aFeatures);
+void mozilla_sampler_get_gatherer(nsISupports** aRetVal);
+#endif
+
+// Make this function easily callable from a debugger in a build without
+// debugging information (work around http://llvm.org/bugs/show_bug.cgi?id=22211)
+extern "C" {
+ void mozilla_sampler_save_profile_to_file(const char* aFilename);
+}
+
+const char** mozilla_sampler_get_features();
+
+void mozilla_sampler_get_buffer_info(uint32_t *aCurrentPosition, uint32_t *aTotalSize,
+ uint32_t *aGeneration);
+
+void mozilla_sampler_init(void* stackTop);
+
+void mozilla_sampler_shutdown();
+
+// Lock the profiler. When locked the profiler is (1) stopped,
+// (2) profile data is cleared, (3) profiler-locked is fired.
+// This is used to lock down the profiler during private browsing
+void mozilla_sampler_lock();
+
+// Unlock the profiler, leaving it stopped and fires profiler-unlocked.
+void mozilla_sampler_unlock();
+
+// Register/unregister threads with the profiler
+bool mozilla_sampler_register_thread(const char* name, void* stackTop);
+void mozilla_sampler_unregister_thread();
+
+void mozilla_sampler_sleep_start();
+void mozilla_sampler_sleep_end();
+bool mozilla_sampler_is_sleeping();
+
+double mozilla_sampler_time();
+double mozilla_sampler_time(const mozilla::TimeStamp& aTime);
+
+void mozilla_sampler_tracing(const char* aCategory, const char* aInfo,
+ TracingMetadata aMetaData);
+
+void mozilla_sampler_tracing(const char* aCategory, const char* aInfo,
+ ProfilerBacktrace* aCause,
+ TracingMetadata aMetaData);
+
+void mozilla_sampler_log(const char *fmt, va_list args);
+
+#endif
+
diff --git a/tools/profiler/public/GeckoProfilerImpl.h b/tools/profiler/public/GeckoProfilerImpl.h
new file mode 100644
index 000000000..a32096b94
--- /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_ */
diff --git a/tools/profiler/public/ProfileGatherer.h b/tools/profiler/public/ProfileGatherer.h
new file mode 100644
index 000000000..4e39a4f5c
--- /dev/null
+++ b/tools/profiler/public/ProfileGatherer.h
@@ -0,0 +1,42 @@
+/* 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 MOZ_PROFILE_GATHERER_H
+#define MOZ_PROFILE_GATHERER_H
+
+#include "mozilla/dom/Promise.h"
+
+class GeckoSampler;
+
+namespace mozilla {
+
+class ProfileGatherer final : public nsIObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ explicit ProfileGatherer(GeckoSampler* aTicker);
+ void WillGatherOOPProfile();
+ void GatheredOOPProfile();
+ void Start(double aSinceTime, mozilla::dom::Promise* aPromise);
+ void Cancel();
+ void OOPExitProfile(const nsCString& aProfile);
+
+private:
+ ~ProfileGatherer() {};
+ void Finish();
+ void Reset();
+
+ nsTArray<nsCString> mExitProfiles;
+ RefPtr<mozilla::dom::Promise> mPromise;
+ GeckoSampler* mTicker;
+ double mSinceTime;
+ uint32_t mPendingProfiles;
+ bool mGathering;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/tools/profiler/public/ProfilerBacktrace.h b/tools/profiler/public/ProfilerBacktrace.h
new file mode 100644
index 000000000..bcaab3563
--- /dev/null
+++ b/tools/profiler/public/ProfilerBacktrace.h
@@ -0,0 +1,36 @@
+/* -*- 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 __PROFILER_BACKTRACE_H
+#define __PROFILER_BACKTRACE_H
+
+class SyncProfile;
+class SpliceableJSONWriter;
+class UniqueStacks;
+
+class ProfilerBacktrace
+{
+public:
+ explicit ProfilerBacktrace(SyncProfile* aProfile);
+ ~ProfilerBacktrace();
+
+ // ProfilerBacktraces' stacks are deduplicated in the context of the
+ // profile that contains the backtrace as a marker payload.
+ //
+ // That is, markers that contain backtraces should not need their own stack,
+ // frame, and string tables. They should instead reuse their parent
+ // profile's tables.
+ void StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks);
+
+private:
+ ProfilerBacktrace(const ProfilerBacktrace&);
+ ProfilerBacktrace& operator=(const ProfilerBacktrace&);
+
+ SyncProfile* mProfile;
+};
+
+#endif // __PROFILER_BACKTRACE_H
+
diff --git a/tools/profiler/public/ProfilerMarkers.h b/tools/profiler/public/ProfilerMarkers.h
new file mode 100644
index 000000000..29711f210
--- /dev/null
+++ b/tools/profiler/public/ProfilerMarkers.h
@@ -0,0 +1,193 @@
+/* -*- 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/. */
+
+#ifndef PROFILER_MARKERS_H
+#define PROFILER_MARKERS_H
+
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Attributes.h"
+
+namespace mozilla {
+namespace layers {
+class Layer;
+} // namespace layers
+} // namespace mozilla
+
+class SpliceableJSONWriter;
+class UniqueStacks;
+
+/**
+ * This is an abstract object that can be implied to supply
+ * data to be attached with a profiler marker. Most data inserted
+ * into a profile is stored in a circular buffer. This buffer
+ * typically wraps around and overwrites most entries. Because
+ * of this, this structure is designed to defer the work of
+ * prepare the payload only when 'preparePayload' is called.
+ *
+ * Note when implementing that this object is typically constructed
+ * on a particular thread but 'preparePayload' and the destructor
+ * is called from the main thread.
+ */
+class ProfilerMarkerPayload
+{
+public:
+ /**
+ * ProfilerMarkerPayload takes ownership of aStack
+ */
+ explicit ProfilerMarkerPayload(ProfilerBacktrace* aStack = nullptr);
+ ProfilerMarkerPayload(const mozilla::TimeStamp& aStartTime,
+ const mozilla::TimeStamp& aEndTime,
+ ProfilerBacktrace* aStack = nullptr);
+
+ /**
+ * Called from the main thread
+ */
+ virtual ~ProfilerMarkerPayload();
+
+ /**
+ * Called from the main thread
+ */
+ virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+ UniqueStacks& aUniqueStacks) = 0;
+
+ mozilla::TimeStamp GetStartTime() const { return mStartTime; }
+
+protected:
+ /**
+ * Called from the main thread
+ */
+ void streamCommonProps(const char* aMarkerType, SpliceableJSONWriter& aWriter,
+ UniqueStacks& aUniqueStacks);
+
+ void SetStack(ProfilerBacktrace* aStack) { mStack = aStack; }
+
+private:
+ mozilla::TimeStamp mStartTime;
+ mozilla::TimeStamp mEndTime;
+ ProfilerBacktrace* mStack;
+};
+
+class ProfilerMarkerTracing : public ProfilerMarkerPayload
+{
+public:
+ ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData);
+ ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData, ProfilerBacktrace* aCause);
+
+ const char *GetCategory() const { return mCategory; }
+ TracingMetadata GetMetaData() const { return mMetaData; }
+
+ virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+ UniqueStacks& aUniqueStacks) override;
+
+private:
+ const char *mCategory;
+ TracingMetadata mMetaData;
+};
+
+
+#ifndef SPS_STANDALONE
+#include "gfxASurface.h"
+class ProfilerMarkerImagePayload : public ProfilerMarkerPayload
+{
+public:
+ explicit ProfilerMarkerImagePayload(gfxASurface *aImg);
+
+ virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+ UniqueStacks& aUniqueStacks) override;
+
+private:
+ RefPtr<gfxASurface> mImg;
+};
+
+class IOMarkerPayload : public ProfilerMarkerPayload
+{
+public:
+ IOMarkerPayload(const char* aSource, const char* aFilename, const mozilla::TimeStamp& aStartTime,
+ const mozilla::TimeStamp& aEndTime,
+ ProfilerBacktrace* aStack);
+ ~IOMarkerPayload();
+
+ virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+ UniqueStacks& aUniqueStacks) override;
+
+private:
+ const char* mSource;
+ char* mFilename;
+};
+
+/**
+ * Contains the translation applied to a 2d layer so we can
+ * track the layer position at each frame.
+ */
+class LayerTranslationPayload : public ProfilerMarkerPayload
+{
+public:
+ LayerTranslationPayload(mozilla::layers::Layer* aLayer,
+ mozilla::gfx::Point aPoint);
+
+ virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+ UniqueStacks& aUniqueStacks) override;
+
+private:
+ mozilla::layers::Layer* mLayer;
+ mozilla::gfx::Point mPoint;
+};
+
+#include "Units.h" // For ScreenIntPoint
+
+/**
+ * Tracks when touch events are processed by gecko, not when
+ * the touch actually occured in gonk/android.
+ */
+class TouchDataPayload : public ProfilerMarkerPayload
+{
+public:
+ explicit TouchDataPayload(const mozilla::ScreenIntPoint& aPoint);
+ virtual ~TouchDataPayload() {}
+
+ virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+ UniqueStacks& aUniqueStacks) override;
+
+private:
+ mozilla::ScreenIntPoint mPoint;
+};
+
+/**
+ * Tracks when a vsync occurs according to the HardwareComposer.
+ */
+class VsyncPayload : public ProfilerMarkerPayload
+{
+public:
+ explicit VsyncPayload(mozilla::TimeStamp aVsyncTimestamp);
+ virtual ~VsyncPayload() {}
+
+ virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+ UniqueStacks& aUniqueStacks) override;
+
+private:
+ mozilla::TimeStamp mVsyncTimestamp;
+};
+
+class GPUMarkerPayload : public ProfilerMarkerPayload
+{
+public:
+ GPUMarkerPayload(const mozilla::TimeStamp& aCpuTimeStart,
+ const mozilla::TimeStamp& aCpuTimeEnd,
+ uint64_t aGpuTimeStart,
+ uint64_t aGpuTimeEnd);
+ ~GPUMarkerPayload() {}
+
+ virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+ UniqueStacks& aUniqueStacks) override;
+
+private:
+ mozilla::TimeStamp mCpuTimeStart;
+ mozilla::TimeStamp mCpuTimeEnd;
+ uint64_t mGpuTimeStart;
+ uint64_t mGpuTimeEnd;
+};
+#endif
+
+#endif // PROFILER_MARKERS_H
diff --git a/tools/profiler/public/PseudoStack.h b/tools/profiler/public/PseudoStack.h
new file mode 100644
index 000000000..f9e3836ea
--- /dev/null
+++ b/tools/profiler/public/PseudoStack.h
@@ -0,0 +1,469 @@
+/* -*- 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/. */
+
+#ifndef PROFILER_PSEUDO_STACK_H_
+#define PROFILER_PSEUDO_STACK_H_
+
+#include "mozilla/ArrayUtils.h"
+#include <stdint.h>
+#include "js/ProfilingStack.h"
+#include <stdlib.h>
+#include "mozilla/Atomics.h"
+#ifndef SPS_STANDALONE
+#include "nsISupportsImpl.h"
+#endif
+
+/* we duplicate this code here to avoid header dependencies
+ * which make it more difficult to include in other places */
+#if defined(_M_X64) || defined(__x86_64__)
+#define V8_HOST_ARCH_X64 1
+#elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
+#define V8_HOST_ARCH_IA32 1
+#elif defined(__ARMEL__)
+#define V8_HOST_ARCH_ARM 1
+#else
+#warning Please add support for your architecture in chromium_types.h
+#endif
+
+// STORE_SEQUENCER: Because signals can interrupt our profile modification
+// we need to make stores are not re-ordered by the compiler
+// or hardware to make sure the profile is consistent at
+// every point the signal can fire.
+#ifdef V8_HOST_ARCH_ARM
+// TODO Is there something cheaper that will prevent
+// memory stores from being reordered
+
+typedef void (*LinuxKernelMemoryBarrierFunc)(void);
+LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
+ (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
+
+# define STORE_SEQUENCER() pLinuxKernelMemoryBarrier()
+#elif defined(V8_HOST_ARCH_IA32) || defined(V8_HOST_ARCH_X64)
+# if defined(_MSC_VER)
+# include <intrin.h>
+# define STORE_SEQUENCER() _ReadWriteBarrier();
+# elif defined(__INTEL_COMPILER)
+# define STORE_SEQUENCER() __memory_barrier();
+# elif __GNUC__
+# define STORE_SEQUENCER() asm volatile("" ::: "memory");
+# else
+# error "Memory clobber not supported for your compiler."
+# endif
+#else
+# error "Memory clobber not supported for your platform."
+#endif
+
+// We can't include <algorithm> because it causes issues on OS X, so we use
+// our own min function.
+static inline uint32_t sMin(uint32_t l, uint32_t r) {
+ return l < r ? l : r;
+}
+
+// A stack entry exists to allow the JS engine to inform SPS of the current
+// backtrace, but also to instrument particular points in C++ in case stack
+// walking is not available on the platform we are running on.
+//
+// Each entry has a descriptive string, a relevant stack address, and some extra
+// information the JS engine might want to inform SPS of. This class inherits
+// from the JS engine's version of the entry to ensure that the size and layout
+// of the two representations are consistent.
+class StackEntry : public js::ProfileEntry
+{
+};
+
+class ProfilerMarkerPayload;
+template<typename T>
+class ProfilerLinkedList;
+class SpliceableJSONWriter;
+class UniqueStacks;
+
+class ProfilerMarker {
+ friend class ProfilerLinkedList<ProfilerMarker>;
+public:
+ explicit ProfilerMarker(const char* aMarkerName,
+ ProfilerMarkerPayload* aPayload = nullptr,
+ double aTime = 0);
+
+ ~ProfilerMarker();
+
+ const char* GetMarkerName() const {
+ return mMarkerName;
+ }
+
+ void StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks) const;
+
+ void SetGeneration(uint32_t aGenID);
+
+ bool HasExpired(uint32_t aGenID) const {
+ return mGenID + 2 <= aGenID;
+ }
+
+ double GetTime() const;
+
+private:
+ char* mMarkerName;
+ ProfilerMarkerPayload* mPayload;
+ ProfilerMarker* mNext;
+ double mTime;
+ uint32_t mGenID;
+};
+
+template<typename T>
+class ProfilerLinkedList {
+public:
+ ProfilerLinkedList()
+ : mHead(nullptr)
+ , mTail(nullptr)
+ {}
+
+ void insert(T* elem)
+ {
+ if (!mTail) {
+ mHead = elem;
+ mTail = elem;
+ } else {
+ mTail->mNext = elem;
+ mTail = elem;
+ }
+ elem->mNext = nullptr;
+ }
+
+ T* popHead()
+ {
+ if (!mHead) {
+ MOZ_ASSERT(false);
+ return nullptr;
+ }
+
+ T* head = mHead;
+
+ mHead = head->mNext;
+ if (!mHead) {
+ mTail = nullptr;
+ }
+
+ return head;
+ }
+
+ const T* peek() {
+ return mHead;
+ }
+
+private:
+ T* mHead;
+ T* mTail;
+};
+
+typedef ProfilerLinkedList<ProfilerMarker> ProfilerMarkerLinkedList;
+
+template<typename T>
+class ProfilerSignalSafeLinkedList {
+public:
+ ProfilerSignalSafeLinkedList()
+ : mSignalLock(false)
+ {}
+
+ ~ProfilerSignalSafeLinkedList()
+ {
+ if (mSignalLock) {
+ // Some thread is modifying the list. We should only be released on that
+ // thread.
+ abort();
+ }
+
+ while (mList.peek()) {
+ delete mList.popHead();
+ }
+ }
+
+ // Insert an item into the list.
+ // Must only be called from the owning thread.
+ // Must not be called while the list from accessList() is being accessed.
+ // In the profiler, we ensure that by interrupting the profiled thread
+ // (which is the one that owns this list and calls insert() on it) until
+ // we're done reading the list from the signal handler.
+ void insert(T* aElement) {
+ MOZ_ASSERT(aElement);
+
+ mSignalLock = true;
+ STORE_SEQUENCER();
+
+ mList.insert(aElement);
+
+ STORE_SEQUENCER();
+ mSignalLock = false;
+ }
+
+ // Called within signal, from any thread, possibly while insert() is in the
+ // middle of modifying the list (on the owning thread). Will return null if
+ // that is the case.
+ // Function must be reentrant.
+ ProfilerLinkedList<T>* accessList()
+ {
+ if (mSignalLock) {
+ return nullptr;
+ }
+ return &mList;
+ }
+
+private:
+ ProfilerLinkedList<T> mList;
+
+ // If this is set, then it's not safe to read the list because its contents
+ // are being changed.
+ volatile bool mSignalLock;
+};
+
+// Stub eventMarker function for js-engine event generation.
+void ProfilerJSEventMarker(const char *event);
+
+// the PseudoStack members are read by signal
+// handlers, so the mutation of them needs to be signal-safe.
+struct PseudoStack
+{
+public:
+ // Create a new PseudoStack and acquire a reference to it.
+ static PseudoStack *create()
+ {
+ return new PseudoStack();
+ }
+
+ // This is called on every profiler restart. Put things that should happen at that time here.
+ void reinitializeOnResume() {
+ // This is needed to cause an initial sample to be taken from sleeping threads. Otherwise sleeping
+ // threads would not have any samples to copy forward while sleeping.
+ mSleepId++;
+ }
+
+ void addMarker(const char* aMarkerStr, ProfilerMarkerPayload* aPayload, double aTime)
+ {
+ ProfilerMarker* marker = new ProfilerMarker(aMarkerStr, aPayload, aTime);
+ mPendingMarkers.insert(marker);
+ }
+
+ // called within signal. Function must be reentrant
+ ProfilerMarkerLinkedList* getPendingMarkers()
+ {
+ // The profiled thread is interrupted, so we can access the list safely.
+ // Unless the profiled thread was in the middle of changing the list when
+ // we interrupted it - in that case, accessList() will return null.
+ return mPendingMarkers.accessList();
+ }
+
+ void push(const char *aName, js::ProfileEntry::Category aCategory, uint32_t line)
+ {
+ push(aName, aCategory, nullptr, false, line);
+ }
+
+ void push(const char *aName, js::ProfileEntry::Category aCategory,
+ void *aStackAddress, bool aCopy, uint32_t line)
+ {
+ if (size_t(mStackPointer) >= mozilla::ArrayLength(mStack)) {
+ mStackPointer++;
+ return;
+ }
+
+ // In order to ensure this object is kept alive while it is
+ // active, we acquire a reference at the outermost push. This is
+ // released by the corresponding pop.
+ if (mStackPointer == 0) {
+ ref();
+ }
+
+ volatile StackEntry &entry = mStack[mStackPointer];
+
+ // Make sure we increment the pointer after the name has
+ // been written such that mStack is always consistent.
+ entry.initCppFrame(aStackAddress, line);
+ entry.setLabel(aName);
+ MOZ_ASSERT(entry.flags() == js::ProfileEntry::IS_CPP_ENTRY);
+ entry.setCategory(aCategory);
+
+ // Track if mLabel needs a copy.
+ if (aCopy)
+ entry.setFlag(js::ProfileEntry::FRAME_LABEL_COPY);
+ else
+ entry.unsetFlag(js::ProfileEntry::FRAME_LABEL_COPY);
+
+ // Prevent the optimizer from re-ordering these instructions
+ STORE_SEQUENCER();
+ mStackPointer++;
+ }
+
+ // Pop the stack. If the stack is empty and all other references to
+ // this PseudoStack have been dropped, then the PseudoStack is
+ // deleted and "false" is returned. Otherwise "true" is returned.
+ bool popAndMaybeDelete()
+ {
+ mStackPointer--;
+ if (mStackPointer == 0) {
+ // Release our self-owned reference count. See 'push'.
+ deref();
+ return false;
+ } else {
+ return true;
+ }
+ }
+ bool isEmpty()
+ {
+ return mStackPointer == 0;
+ }
+ uint32_t stackSize() const
+ {
+ return sMin(mStackPointer, mozilla::sig_safe_t(mozilla::ArrayLength(mStack)));
+ }
+
+ void sampleContext(JSContext* context) {
+#ifndef SPS_STANDALONE
+ if (mContext && !context) {
+ // On JS shut down, flush the current buffer as stringifying JIT samples
+ // requires a live JSContext.
+ flushSamplerOnJSShutdown();
+ }
+
+ mContext = context;
+
+ if (!context) {
+ return;
+ }
+
+ static_assert(sizeof(mStack[0]) == sizeof(js::ProfileEntry),
+ "mStack must be binary compatible with js::ProfileEntry.");
+ js::SetContextProfilingStack(context,
+ (js::ProfileEntry*) mStack,
+ (uint32_t*) &mStackPointer,
+ (uint32_t) mozilla::ArrayLength(mStack));
+ if (mStartJSSampling)
+ enableJSSampling();
+#endif
+ }
+#ifndef SPS_STANDALONE
+ void enableJSSampling() {
+ if (mContext) {
+ js::EnableContextProfilingStack(mContext, true);
+ js::RegisterContextProfilingEventMarker(mContext, &ProfilerJSEventMarker);
+ mStartJSSampling = false;
+ } else {
+ mStartJSSampling = true;
+ }
+ }
+ void jsOperationCallback() {
+ if (mStartJSSampling)
+ enableJSSampling();
+ }
+ void disableJSSampling() {
+ mStartJSSampling = false;
+ if (mContext)
+ js::EnableContextProfilingStack(mContext, false);
+ }
+#endif
+
+ // Keep a list of active checkpoints
+ StackEntry volatile mStack[1024];
+ private:
+
+ // A PseudoStack can only be created via the "create" method.
+ PseudoStack()
+ : mStackPointer(0)
+ , mSleepId(0)
+ , mSleepIdObserved(0)
+ , mSleeping(false)
+ , mRefCnt(1)
+#ifndef SPS_STANDALONE
+ , mContext(nullptr)
+#endif
+ , mStartJSSampling(false)
+ , mPrivacyMode(false)
+ {
+ MOZ_COUNT_CTOR(PseudoStack);
+ }
+
+ // A PseudoStack can only be deleted via deref.
+ ~PseudoStack() {
+ MOZ_COUNT_DTOR(PseudoStack);
+ if (mStackPointer != 0) {
+ // We're releasing the pseudostack while it's still in use.
+ // The label macros keep a non ref counted reference to the
+ // stack to avoid a TLS. If these are not all cleared we will
+ // get a use-after-free so better to crash now.
+ abort();
+ }
+ }
+
+ // No copying.
+ PseudoStack(const PseudoStack&) = delete;
+ void operator=(const PseudoStack&) = delete;
+
+ void flushSamplerOnJSShutdown();
+
+ // Keep a list of pending markers that must be moved
+ // to the circular buffer
+ ProfilerSignalSafeLinkedList<ProfilerMarker> mPendingMarkers;
+ // This may exceed the length of mStack, so instead use the stackSize() method
+ // to determine the number of valid samples in mStack
+ mozilla::sig_safe_t mStackPointer;
+ // Incremented at every sleep/wake up of the thread
+ int mSleepId;
+ // Previous id observed. If this is not the same as mSleepId, this thread is not sleeping in the same place any more
+ mozilla::Atomic<int> mSleepIdObserved;
+ // Keeps tack of whether the thread is sleeping or not (1 when sleeping 0 when awake)
+ mozilla::Atomic<int> mSleeping;
+ // This class is reference counted because it must be kept alive by
+ // the ThreadInfo, by the reference from tlsPseudoStack, and by the
+ // current thread when callbacks are in progress.
+ mozilla::Atomic<int> mRefCnt;
+
+ public:
+#ifndef SPS_STANDALONE
+ // The context which is being sampled
+ JSContext *mContext;
+#endif
+ // Start JS Profiling when possible
+ bool mStartJSSampling;
+ bool mPrivacyMode;
+
+ enum SleepState {NOT_SLEEPING, SLEEPING_FIRST, SLEEPING_AGAIN};
+
+ // The first time this is called per sleep cycle we return SLEEPING_FIRST
+ // and any other subsequent call within the same sleep cycle we return SLEEPING_AGAIN
+ SleepState observeSleeping() {
+ if (mSleeping != 0) {
+ if (mSleepIdObserved == mSleepId) {
+ return SLEEPING_AGAIN;
+ } else {
+ mSleepIdObserved = mSleepId;
+ return SLEEPING_FIRST;
+ }
+ } else {
+ return NOT_SLEEPING;
+ }
+ }
+
+
+ // Call this whenever the current thread sleeps or wakes up
+ // Calling setSleeping with the same value twice in a row is an error
+ void setSleeping(int sleeping) {
+ MOZ_ASSERT(mSleeping != sleeping);
+ mSleepId++;
+ mSleeping = sleeping;
+ }
+
+ bool isSleeping() {
+ return !!mSleeping;
+ }
+
+ void ref() {
+ ++mRefCnt;
+ }
+
+ void deref() {
+ int newValue = --mRefCnt;
+ if (newValue == 0) {
+ delete this;
+ }
+ }
+};
+
+#endif
diff --git a/tools/profiler/public/shared-libraries.h b/tools/profiler/public/shared-libraries.h
new file mode 100644
index 000000000..b02a1fb08
--- /dev/null
+++ b/tools/profiler/public/shared-libraries.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 SHARED_LIBRARIES_H_
+#define SHARED_LIBRARIES_H_
+
+#ifndef MOZ_ENABLE_PROFILER_SPS
+#error This header does not have a useful implementation on your platform!
+#endif
+
+#include <algorithm>
+#include <vector>
+#include <string>
+#include <stdlib.h>
+#include <stdint.h>
+#ifndef SPS_STANDALONE
+#include <nsID.h>
+#endif
+
+class SharedLibrary {
+public:
+
+ SharedLibrary(uintptr_t aStart,
+ uintptr_t aEnd,
+ uintptr_t aOffset,
+ const std::string& aBreakpadId,
+ const std::string& aName)
+ : mStart(aStart)
+ , mEnd(aEnd)
+ , mOffset(aOffset)
+ , mBreakpadId(aBreakpadId)
+ , mName(aName)
+ {}
+
+ SharedLibrary(const SharedLibrary& aEntry)
+ : mStart(aEntry.mStart)
+ , mEnd(aEntry.mEnd)
+ , mOffset(aEntry.mOffset)
+ , mBreakpadId(aEntry.mBreakpadId)
+ , mName(aEntry.mName)
+ {}
+
+ SharedLibrary& operator=(const SharedLibrary& aEntry)
+ {
+ // Gracefully handle self assignment
+ if (this == &aEntry) return *this;
+
+ mStart = aEntry.mStart;
+ mEnd = aEntry.mEnd;
+ mOffset = aEntry.mOffset;
+ mBreakpadId = aEntry.mBreakpadId;
+ mName = aEntry.mName;
+ return *this;
+ }
+
+ bool operator==(const SharedLibrary& other) const
+ {
+ return (mStart == other.mStart) &&
+ (mEnd == other.mEnd) &&
+ (mOffset == other.mOffset) &&
+ (mName == other.mName) &&
+ (mBreakpadId == other.mBreakpadId);
+ }
+
+ uintptr_t GetStart() const { return mStart; }
+ uintptr_t GetEnd() const { return mEnd; }
+ uintptr_t GetOffset() const { return mOffset; }
+ const std::string &GetBreakpadId() const { return mBreakpadId; }
+ const std::string &GetName() const { return mName; }
+
+private:
+ SharedLibrary() {}
+
+ uintptr_t mStart;
+ uintptr_t mEnd;
+ uintptr_t mOffset;
+ std::string mBreakpadId;
+ std::string mName;
+};
+
+static bool
+CompareAddresses(const SharedLibrary& first, const SharedLibrary& second)
+{
+ return first.GetStart() < second.GetStart();
+}
+
+class SharedLibraryInfo {
+public:
+ static SharedLibraryInfo GetInfoForSelf();
+ SharedLibraryInfo() {}
+
+ void AddSharedLibrary(SharedLibrary entry)
+ {
+ mEntries.push_back(entry);
+ }
+
+ const SharedLibrary& GetEntry(size_t i) const
+ {
+ return mEntries[i];
+ }
+
+ // Removes items in the range [first, last)
+ // i.e. element at the "last" index is not removed
+ void RemoveEntries(size_t first, size_t last)
+ {
+ mEntries.erase(mEntries.begin() + first, mEntries.begin() + last);
+ }
+
+ bool Contains(const SharedLibrary& searchItem) const
+ {
+ return (mEntries.end() !=
+ std::find(mEntries.begin(), mEntries.end(), searchItem));
+ }
+
+ size_t GetSize() const
+ {
+ return mEntries.size();
+ }
+
+ void SortByAddress()
+ {
+ std::sort(mEntries.begin(), mEntries.end(), CompareAddresses);
+ }
+
+ void Clear()
+ {
+ mEntries.clear();
+ }
+
+private:
+ std::vector<SharedLibrary> mEntries;
+};
+
+#endif