summaryrefslogtreecommitdiffstats
path: root/tools/profiler/tasktracer
diff options
context:
space:
mode:
Diffstat (limited to 'tools/profiler/tasktracer')
-rw-r--r--tools/profiler/tasktracer/GeckoTaskTracer.cpp464
-rw-r--r--tools/profiler/tasktracer/GeckoTaskTracer.h182
-rw-r--r--tools/profiler/tasktracer/GeckoTaskTracerImpl.h180
-rw-r--r--tools/profiler/tasktracer/SourceEventTypeMap.h16
-rw-r--r--tools/profiler/tasktracer/TracedTaskCommon.cpp128
-rw-r--r--tools/profiler/tasktracer/TracedTaskCommon.h130
6 files changed, 1100 insertions, 0 deletions
diff --git a/tools/profiler/tasktracer/GeckoTaskTracer.cpp b/tools/profiler/tasktracer/GeckoTaskTracer.cpp
new file mode 100644
index 0000000000..5a8ca8625c
--- /dev/null
+++ b/tools/profiler/tasktracer/GeckoTaskTracer.cpp
@@ -0,0 +1,464 @@
+/* -*- 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/. */
+
+#include "GeckoTaskTracer.h"
+#include "GeckoTaskTracerImpl.h"
+
+#include "platform.h"
+
+#include "mozilla/DebugOnly.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/ThreadLocal.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+#include "prtime.h"
+
+#include <stdarg.h>
+
+#define MAX_SIZE_LOG (1024 * 128)
+
+// NS_ENSURE_TRUE_VOID() without the warning on the debug build.
+#define ENSURE_TRUE_VOID(x) \
+ do { \
+ if (MOZ_UNLIKELY(!(x))) { \
+ return; \
+ } \
+ } while (0)
+
+// NS_ENSURE_TRUE() without the warning on the debug build.
+#define ENSURE_TRUE(x, ret) \
+ do { \
+ if (MOZ_UNLIKELY(!(x))) { \
+ return ret; \
+ } \
+ } while (0)
+
+namespace mozilla {
+namespace tasktracer {
+
+#define SOURCE_EVENT_NAME(type) \
+ const char* CreateSourceEvent##type() { return "SourceEvent" #type; }
+#include "SourceEventTypeMap.h"
+#undef SOURCE_EVENT_NAME
+
+static MOZ_THREAD_LOCAL(TraceInfo*) sTraceInfoTLS;
+static mozilla::StaticMutex sMutex;
+
+// The generation of TraceInfo. It will be > 0 if the Task Tracer is started and
+// <= 0 if stopped.
+bool gStarted(false);
+static nsTArray<UniquePtr<TraceInfo>>* sTraceInfos = nullptr;
+static PRTime sStartTime;
+
+static const char sJSLabelPrefix[] = "#tt#";
+
+namespace {
+
+static PRTime GetTimestamp() { return PR_Now() / 1000; }
+
+static TraceInfo* AllocTraceInfo(int aTid) {
+ sMutex.AssertCurrentThreadOwns();
+ MOZ_ASSERT(sTraceInfos);
+ auto* info = sTraceInfos->AppendElement(MakeUnique<TraceInfo>(aTid));
+
+ return info->get();
+}
+
+static void CreateSourceEvent(SourceEventType aType) {
+ // Create a new unique task id.
+ uint64_t newId = GenNewUniqueTaskId();
+ {
+ TraceInfoHolder info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ info->mCurTraceSourceId = newId;
+ info->mCurTraceSourceType = aType;
+ info->mCurTaskId = newId;
+ }
+
+ uintptr_t* namePtr;
+#define SOURCE_EVENT_NAME(type) \
+ case SourceEventType::type: { \
+ namePtr = (uintptr_t*)&CreateSourceEvent##type; \
+ break; \
+ }
+
+ switch (aType) {
+#include "SourceEventTypeMap.h"
+ default:
+ MOZ_CRASH("Unknown SourceEvent.");
+ }
+#undef SOURCE_EVENT_NAME
+
+ // Log a fake dispatch and start for this source event.
+ LogDispatch(newId, newId, newId, aType);
+ LogVirtualTablePtr(newId, newId, namePtr);
+ LogBegin(newId, newId);
+}
+
+static void DestroySourceEvent() {
+ // Log a fake end for this source event.
+ TraceInfoHolder info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ uint64_t curTraceSourceId;
+ curTraceSourceId = info->mCurTraceSourceId;
+ info.Reset();
+
+ LogEnd(curTraceSourceId, curTraceSourceId);
+}
+
+inline static void ObsoleteCurrentTraceInfos() {
+ MOZ_ASSERT(sTraceInfos);
+ for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) {
+ (*sTraceInfos)[i]->mObsolete = true;
+ }
+}
+
+static void SetLogStarted(bool aIsStartLogging) {
+ MOZ_ASSERT(aIsStartLogging != gStarted);
+ StaticMutexAutoLock lock(sMutex);
+
+ gStarted = aIsStartLogging;
+
+ if (aIsStartLogging && sTraceInfos == nullptr) {
+ sTraceInfos = new nsTArray<UniquePtr<TraceInfo>>();
+ }
+
+ if (!aIsStartLogging && sTraceInfos) {
+ ObsoleteCurrentTraceInfos();
+ }
+}
+
+} // namespace
+
+TraceInfoLogType* TraceInfo::AppendLog() {
+ if (mLogsSize >= MAX_SIZE_LOG) {
+ return nullptr;
+ }
+ TraceInfoLogNode* node = new TraceInfoLogNode;
+ node->mNext = nullptr;
+ if (mLogsTail) {
+ mLogsTail->mNext = node;
+ mLogsTail = node;
+ } else {
+ mLogsTail = mLogsHead = node;
+ }
+ mLogsSize++;
+ return &node->mLog;
+}
+
+void InitTaskTracer(uint32_t aFlags) {
+ StaticMutexAutoLock lock(sMutex);
+
+ if (aFlags & FORKED_AFTER_NUWA) {
+ ObsoleteCurrentTraceInfos();
+ return;
+ }
+
+ MOZ_ASSERT(!sTraceInfos);
+
+ bool success = sTraceInfoTLS.init();
+ if (!success) {
+ MOZ_CRASH();
+ }
+}
+
+void ShutdownTaskTracer() {
+ if (IsStartLogging()) {
+ SetLogStarted(false);
+
+ StaticMutexAutoLock lock(sMutex);
+ // Make sure all threads are out of holding mutics.
+ // See |GetOrCreateTraceInfo()|
+ for (auto& traceinfo : *sTraceInfos) {
+ MutexAutoLock lock(traceinfo->mLogsMutex);
+ }
+ delete sTraceInfos;
+ sTraceInfos = nullptr;
+ }
+}
+
+static void FreeTraceInfo(TraceInfo* aTraceInfo) {
+ sMutex.AssertCurrentThreadOwns();
+ if (aTraceInfo) {
+ UniquePtr<TraceInfo> traceinfo(aTraceInfo);
+ mozilla::DebugOnly<bool> removed = sTraceInfos->RemoveElement(traceinfo);
+ MOZ_ASSERT(removed);
+ Unused << traceinfo.release(); // A dirty hack to prevent double free.
+ }
+}
+
+void FreeTraceInfo() {
+ StaticMutexAutoLock lock(sMutex);
+ if (sTraceInfos) {
+ FreeTraceInfo(sTraceInfoTLS.get());
+ }
+}
+
+TraceInfoHolder GetOrCreateTraceInfo() {
+ TraceInfo* info = sTraceInfoTLS.get();
+ StaticMutexAutoLock lock(sMutex);
+ ENSURE_TRUE(IsStartLogging(), TraceInfoHolder{});
+
+ if (info && info->mObsolete) {
+ // TraceInfo is obsolete: remove it.
+ FreeTraceInfo(info);
+ info = nullptr;
+ }
+
+ if (!info) {
+ info = AllocTraceInfo(profiler_current_thread_id());
+ sTraceInfoTLS.set(info);
+ }
+
+ return TraceInfoHolder{info}; // |mLogsMutex| will be held, then
+ // ||sMutex| will be released for
+ // efficiency reason.
+}
+
+uint64_t GenNewUniqueTaskId() {
+ TraceInfoHolder info = GetOrCreateTraceInfo();
+ ENSURE_TRUE(info, 0);
+
+ int tid = profiler_current_thread_id();
+ uint64_t taskid = ((uint64_t)tid << 32) | ++info->mLastUniqueTaskId;
+ return taskid;
+}
+
+AutoSaveCurTraceInfoImpl::AutoSaveCurTraceInfoImpl() {
+ GetCurTraceInfo(&mSavedSourceEventId, &mSavedTaskId, &mSavedSourceEventType);
+}
+
+AutoSaveCurTraceInfoImpl::~AutoSaveCurTraceInfoImpl() {
+ SetCurTraceInfo(mSavedSourceEventId, mSavedTaskId, mSavedSourceEventType);
+}
+
+void SetCurTraceInfo(uint64_t aSourceEventId, uint64_t aParentTaskId,
+ SourceEventType aSourceEventType) {
+ TraceInfoHolder info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ info->mCurTraceSourceId = aSourceEventId;
+ info->mCurTaskId = aParentTaskId;
+ info->mCurTraceSourceType = aSourceEventType;
+}
+
+void GetCurTraceInfo(uint64_t* aOutSourceEventId, uint64_t* aOutParentTaskId,
+ SourceEventType* aOutSourceEventType) {
+ TraceInfoHolder info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ *aOutSourceEventId = info->mCurTraceSourceId;
+ *aOutParentTaskId = info->mCurTaskId;
+ *aOutSourceEventType = info->mCurTraceSourceType;
+}
+
+void LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId,
+ uint64_t aSourceEventId, SourceEventType aSourceEventType) {
+ LogDispatch(aTaskId, aParentTaskId, aSourceEventId, aSourceEventType, 0);
+}
+
+void LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId,
+ uint64_t aSourceEventId, SourceEventType aSourceEventType,
+ int aDelayTimeMs) {
+ TraceInfoHolder info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ // aDelayTimeMs is the expected delay time in milliseconds, thus the dispatch
+ // time calculated of it might be slightly off in the real world.
+ uint64_t time =
+ (aDelayTimeMs <= 0) ? GetTimestamp() : GetTimestamp() + aDelayTimeMs;
+
+ // Log format:
+ // [0 taskId dispatchTime sourceEventId sourceEventType parentTaskId]
+ TraceInfoLogType* log = info->AppendLog();
+ if (log) {
+ log->mDispatch.mType = ACTION_DISPATCH;
+ log->mDispatch.mTaskId = aTaskId;
+ log->mDispatch.mTime = time;
+ log->mDispatch.mSourceEventId = aSourceEventId;
+ log->mDispatch.mSourceEventType = aSourceEventType;
+ log->mDispatch.mParentTaskId = aParentTaskId;
+ }
+}
+
+void LogBegin(uint64_t aTaskId, uint64_t aSourceEventId) {
+ TraceInfoHolder info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ // Log format:
+ // [1 taskId beginTime processId threadId]
+ TraceInfoLogType* log = info->AppendLog();
+ if (log) {
+ log->mBegin.mType = ACTION_BEGIN;
+ log->mBegin.mTaskId = aTaskId;
+ log->mBegin.mTime = GetTimestamp();
+ log->mBegin.mPid = profiler_current_process_id();
+ log->mBegin.mTid = profiler_current_thread_id();
+
+ MOZ_ASSERT(log->mBegin.mPid >= 0,
+ "native process ID is < 0 (signed integer overflow)");
+ MOZ_ASSERT(log->mBegin.mTid >= 0,
+ "native thread ID is < 0 (signed integer overflow)");
+ }
+}
+
+void LogEnd(uint64_t aTaskId, uint64_t aSourceEventId) {
+ TraceInfoHolder info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ // Log format:
+ // [2 taskId endTime]
+ TraceInfoLogType* log = info->AppendLog();
+ if (log) {
+ log->mEnd.mType = ACTION_END;
+ log->mEnd.mTaskId = aTaskId;
+ log->mEnd.mTime = GetTimestamp();
+ }
+}
+
+void LogVirtualTablePtr(uint64_t aTaskId, uint64_t aSourceEventId,
+ uintptr_t* aVptr) {
+ TraceInfoHolder info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ // Log format:
+ // [4 taskId address]
+ TraceInfoLogType* log = info->AppendLog();
+ if (log) {
+ // Since addr2line used by the Gecko Profiler addon can not solve
+ // non-function addresses, we use the first entry of vtable as the symbol
+ // to solve. We should find a better solution later.
+ log->mVPtr.mType = ACTION_GET_VTABLE;
+ log->mVPtr.mTaskId = aTaskId;
+ log->mVPtr.mVPtr = reinterpret_cast<uintptr_t>(aVptr);
+ }
+}
+
+void AutoSourceEvent::StartScope(SourceEventType aType) {
+ CreateSourceEvent(aType);
+}
+
+void AutoSourceEvent::StopScope() { DestroySourceEvent(); }
+
+void AutoScopedLabel::Init(const char* aFormat, va_list& aArgs) {
+ nsCString label;
+ va_list& args = aArgs;
+ label.AppendPrintf(aFormat, args);
+ mLabel = strdup(label.get());
+ AddLabel("Begin %s", mLabel);
+}
+
+void DoAddLabel(const char* aFormat, va_list& aArgs) {
+ TraceInfoHolder info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ // Log format:
+ // [3 taskId "label"]
+ TraceInfoLogType* log = info->AppendLog();
+ if (log) {
+ va_list& args = aArgs;
+ nsCString& buffer = *info->mStrs.AppendElement();
+ buffer.AppendPrintf(aFormat, args);
+
+ log->mLabel.mType = ACTION_ADD_LABEL;
+ log->mLabel.mTaskId = info->mCurTaskId;
+ log->mLabel.mTime = GetTimestamp();
+ log->mLabel.mStrIdx = info->mStrs.Length() - 1;
+ }
+}
+
+// Functions used by GeckoProfiler.
+
+void StartLogging() {
+ sStartTime = GetTimestamp();
+ SetLogStarted(true);
+}
+
+void StopLogging() { SetLogStarted(false); }
+
+UniquePtr<Vector<nsCString>> GetLoggedData(TimeStamp aTimeStamp) {
+ auto result = MakeUnique<Vector<nsCString>>();
+
+ // TODO: This is called from a signal handler. Use semaphore instead.
+ StaticMutexAutoLock lock(sMutex);
+
+ if (sTraceInfos == nullptr) {
+ return result;
+ }
+
+ for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) {
+ TraceInfo* info = (*sTraceInfos)[i].get();
+ MutexAutoLock lockLogs(info->mLogsMutex);
+ if (info->mObsolete) {
+ continue;
+ }
+
+ nsTArray<nsCString>& strs = info->mStrs;
+ for (TraceInfoLogNode* node = info->mLogsHead; node; node = node->mNext) {
+ TraceInfoLogType& log = node->mLog;
+ MOZ_RELEASE_ASSERT(result->append(nsCString()));
+ nsCString& buffer = result->back();
+
+ switch (log.mType) {
+ case ACTION_DISPATCH:
+ buffer.AppendPrintf("%d %llu %llu %llu %d %llu", ACTION_DISPATCH,
+ (unsigned long long)log.mDispatch.mTaskId,
+ (unsigned long long)log.mDispatch.mTime,
+ (unsigned long long)log.mDispatch.mSourceEventId,
+ log.mDispatch.mSourceEventType,
+ (unsigned long long)log.mDispatch.mParentTaskId);
+ break;
+
+ case ACTION_BEGIN:
+ buffer.AppendPrintf("%d %llu %llu %d %d", ACTION_BEGIN,
+ (unsigned long long)log.mBegin.mTaskId,
+ (unsigned long long)log.mBegin.mTime,
+ log.mBegin.mPid, log.mBegin.mTid);
+ break;
+
+ case ACTION_END:
+ buffer.AppendPrintf("%d %llu %llu", ACTION_END,
+ (unsigned long long)log.mEnd.mTaskId,
+ (unsigned long long)log.mEnd.mTime);
+ break;
+
+ case ACTION_GET_VTABLE:
+ buffer.AppendPrintf("%d %llu %p", ACTION_GET_VTABLE,
+ (unsigned long long)log.mVPtr.mTaskId,
+ (void*)log.mVPtr.mVPtr);
+ break;
+
+ case ACTION_ADD_LABEL:
+ buffer.AppendPrintf("%d %llu %llu2 \"%s\"", ACTION_ADD_LABEL,
+ (unsigned long long)log.mLabel.mTaskId,
+ (unsigned long long)log.mLabel.mTime,
+ strs[log.mLabel.mStrIdx].get());
+ break;
+
+ default:
+ MOZ_CRASH("Unknow TaskTracer log type!");
+ }
+ }
+ }
+
+ return result;
+}
+
+PRTime GetStartTime() { return sStartTime; }
+
+const char* GetJSLabelPrefix() { return sJSLabelPrefix; }
+
+#undef ENSURE_TRUE_VOID
+#undef ENSURE_TRUE
+
+} // namespace tasktracer
+} // namespace mozilla
diff --git a/tools/profiler/tasktracer/GeckoTaskTracer.h b/tools/profiler/tasktracer/GeckoTaskTracer.h
new file mode 100644
index 0000000000..dc299ac8fd
--- /dev/null
+++ b/tools/profiler/tasktracer/GeckoTaskTracer.h
@@ -0,0 +1,182 @@
+/* -*- 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 GECKO_TASK_TRACER_H
+#define GECKO_TASK_TRACER_H
+
+#include "mozilla/Atomics.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+#include "nsCOMPtr.h"
+#include "nsStringFwd.h"
+
+#include <stdarg.h>
+
+/**
+ * TaskTracer provides a way to trace the correlation between different tasks
+ * across threads and processes. Unlike sampling based profilers, TaskTracer can
+ * tell you where a task is dispatched from, what its original source was, how
+ * long it waited in the event queue, and how long it took to execute.
+ *
+ * Source Events are usually some kinds of I/O events we're interested in, such
+ * as touch events, timer events, network events, etc. When a source event is
+ * created, TaskTracer records the entire chain of Tasks and nsRunnables as they
+ * are dispatched to different threads and processes. It records latency,
+ * execution time, etc. for each Task and nsRunnable that chains back to the
+ * original source event.
+ */
+
+class nsIRunnable;
+
+namespace mozilla {
+
+class TimeStamp;
+class Runnable;
+
+namespace tasktracer {
+
+extern bool gStarted;
+
+/**
+ * Check if the TaskTracer has been started.
+ */
+inline bool IsStartLogging() {
+ // |gStarted| is not an atomic variable, but it is fine for it is a
+ // boolean value and will be changed under the protection of
+ // |sMutex|.
+ //
+ // There is a latency between the change of the value and the
+ // observation of threads. |gStarted| would be checked again with
+ // the protection of mutex in logging functions; for example,
+ // |AddLabel()|, so all false positive would be blocked with the
+ // double checks. For false negative, it is fine to lose some
+ // records at begin of logging.
+ return gStarted;
+}
+
+enum { FORKED_AFTER_NUWA = 1 << 0 };
+
+enum SourceEventType {
+#define SOURCE_EVENT_NAME(x) x,
+#include "SourceEventTypeMap.h"
+#undef SOURCE_EVENT_NAME
+};
+
+class AutoSaveCurTraceInfoImpl {
+ uint64_t mSavedTaskId;
+ uint64_t mSavedSourceEventId;
+ SourceEventType mSavedSourceEventType;
+
+ public:
+ AutoSaveCurTraceInfoImpl();
+ ~AutoSaveCurTraceInfoImpl();
+};
+
+class AutoSaveCurTraceInfo {
+ Maybe<AutoSaveCurTraceInfoImpl> mSaved;
+
+ public:
+ AutoSaveCurTraceInfo() {
+ if (IsStartLogging()) {
+ mSaved.emplace();
+ }
+ }
+
+ /**
+ * The instance had saved TraceInfo.
+ *
+ * It means that TaskTrace had been enabled when the instance was
+ * created.
+ */
+ bool HasSavedTraceInfo() { return !!mSaved; }
+};
+
+class AutoSourceEvent : public AutoSaveCurTraceInfo {
+ void StartScope(SourceEventType aType);
+ void StopScope();
+
+ public:
+ explicit AutoSourceEvent(SourceEventType aType) : AutoSaveCurTraceInfo() {
+ if (HasSavedTraceInfo()) {
+ StartScope(aType);
+ }
+ }
+
+ ~AutoSourceEvent() {
+ if (HasSavedTraceInfo()) {
+ StopScope();
+ }
+ }
+};
+
+void InitTaskTracer(uint32_t aFlags = 0);
+void ShutdownTaskTracer();
+
+void DoAddLabel(const char* aFormat, va_list& aArgs);
+
+// Add a label to the currently running task, aFormat is the message to log,
+// followed by corresponding parameters.
+inline void AddLabel(const char* aFormat, ...) MOZ_FORMAT_PRINTF(1, 2);
+inline void AddLabel(const char* aFormat, ...) {
+ if (IsStartLogging()) {
+ va_list args;
+ va_start(args, aFormat);
+ DoAddLabel(aFormat, args);
+ va_end(args);
+ }
+}
+
+void StartLogging();
+void StopLogging();
+UniquePtr<Vector<nsCString>> GetLoggedData(TimeStamp aStartTime);
+
+// Returns the timestamp when Task Tracer is enabled in this process.
+PRTime GetStartTime();
+
+/**
+ * Internal functions.
+ */
+
+already_AddRefed<nsIRunnable> CreateTracedRunnable(
+ already_AddRefed<nsIRunnable>&& aRunnable);
+
+// Free the TraceInfo allocated on a thread's TLS. Currently we are wrapping
+// tasks running on nsThreads and base::thread, so FreeTraceInfo is called at
+// where nsThread and base::thread release themselves.
+void FreeTraceInfo();
+
+const char* GetJSLabelPrefix();
+
+void GetCurTraceInfo(uint64_t* aOutSourceEventId, uint64_t* aOutParentTaskId,
+ SourceEventType* aOutSourceEventType);
+
+class AutoScopedLabel {
+ char* mLabel;
+ void Init(const char* aFormat, va_list& aArgs);
+
+ public:
+ explicit AutoScopedLabel(const char* aFormat, ...) : mLabel(nullptr) {
+ if (IsStartLogging()) {
+ va_list args;
+ va_start(args, aFormat);
+ Init(aFormat, args);
+ va_end(args);
+ }
+ }
+
+ ~AutoScopedLabel() {
+ if (mLabel) {
+ AddLabel("End %s", mLabel);
+ free(mLabel);
+ }
+ }
+};
+
+} // namespace tasktracer
+} // namespace mozilla.
+
+#endif
diff --git a/tools/profiler/tasktracer/GeckoTaskTracerImpl.h b/tools/profiler/tasktracer/GeckoTaskTracerImpl.h
new file mode 100644
index 0000000000..2b44a0f904
--- /dev/null
+++ b/tools/profiler/tasktracer/GeckoTaskTracerImpl.h
@@ -0,0 +1,180 @@
+/* -*- 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 GECKO_TASK_TRACER_IMPL_H
+#define GECKO_TASK_TRACER_IMPL_H
+
+#include "GeckoTaskTracer.h"
+
+#include "mozilla/Mutex.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace tasktracer {
+
+struct LogRecDispatch {
+ uint32_t mType;
+ uint32_t mSourceEventType;
+ uint64_t mTaskId;
+ uint64_t mTime;
+ uint64_t mSourceEventId;
+ uint64_t mParentTaskId;
+};
+
+struct LogRecBegin {
+ uint32_t mType;
+ uint64_t mTaskId;
+ uint64_t mTime;
+ uint32_t mPid;
+ uint32_t mTid;
+};
+
+struct LogRecEnd {
+ uint32_t mType;
+ uint64_t mTaskId;
+ uint64_t mTime;
+};
+
+struct LogRecVPtr {
+ uint32_t mType;
+ uint64_t mTaskId;
+ uintptr_t mVPtr;
+};
+
+struct LogRecLabel {
+ uint32_t mType;
+ uint32_t mStrIdx;
+ uint64_t mTaskId;
+ uint64_t mTime;
+};
+
+union TraceInfoLogType {
+ uint32_t mType;
+ LogRecDispatch mDispatch;
+ LogRecBegin mBegin;
+ LogRecEnd mEnd;
+ LogRecVPtr mVPtr;
+ LogRecLabel mLabel;
+};
+
+struct TraceInfoLogNode {
+ TraceInfoLogType mLog;
+ TraceInfoLogNode* mNext;
+};
+
+struct TraceInfo {
+ explicit TraceInfo(uint32_t aThreadId)
+ : mCurTraceSourceId(0),
+ mCurTaskId(0),
+ mCurTraceSourceType(Unknown),
+ mThreadId(aThreadId),
+ mLastUniqueTaskId(0),
+ mObsolete(false),
+ mLogsMutex("TraceInfoMutex"),
+ mLogsHead(nullptr),
+ mLogsTail(nullptr),
+ mLogsSize(0) {
+ MOZ_COUNT_CTOR(TraceInfo);
+ }
+
+ ~TraceInfo() {
+ MOZ_COUNT_DTOR(TraceInfo);
+ while (mLogsHead) {
+ auto node = mLogsHead;
+ mLogsHead = node->mNext;
+ delete node;
+ }
+ }
+
+ TraceInfoLogType* AppendLog();
+
+ uint64_t mCurTraceSourceId;
+ uint64_t mCurTaskId;
+ SourceEventType mCurTraceSourceType;
+ uint32_t mThreadId;
+ uint32_t mLastUniqueTaskId;
+ mozilla::Atomic<bool> mObsolete;
+
+ // This mutex protects the following log
+ mozilla::Mutex mLogsMutex;
+ TraceInfoLogNode* mLogsHead;
+ TraceInfoLogNode* mLogsTail;
+ int mLogsSize;
+ nsTArray<nsCString> mStrs;
+};
+
+class TraceInfoHolder {
+ public:
+ TraceInfoHolder() : mInfo(nullptr) {}
+ explicit TraceInfoHolder(TraceInfo* aInfo) : mInfo(aInfo) {
+ aInfo->mLogsMutex.AssertNotCurrentThreadOwns(); // in case of recursive
+ aInfo->mLogsMutex.Lock();
+ MOZ_ASSERT(aInfo);
+ }
+ TraceInfoHolder(const TraceInfoHolder& aOther) = delete;
+ TraceInfoHolder(TraceInfoHolder&& aOther) : mInfo(aOther.mInfo) {
+ if (!!aOther) {
+ aOther->mLogsMutex.AssertCurrentThreadOwns();
+ }
+ aOther.mInfo = nullptr;
+ }
+ ~TraceInfoHolder() {
+ if (mInfo) mInfo->mLogsMutex.Unlock();
+ }
+ explicit operator bool() const { return !!mInfo; }
+ TraceInfo* operator->() { return mInfo; }
+ bool operator==(TraceInfo* aOther) const { return mInfo == aOther; }
+ bool operator==(const TraceInfoHolder& aOther) const {
+ return mInfo == aOther.mInfo;
+ }
+ void Reset() {
+ if (mInfo) {
+ mInfo->mLogsMutex.Unlock();
+ mInfo = nullptr;
+ }
+ }
+
+ private:
+ TraceInfo* mInfo;
+};
+
+// Return the TraceInfo of current thread, allocate a new one if not exit.
+TraceInfoHolder GetOrCreateTraceInfo();
+
+uint64_t GenNewUniqueTaskId();
+
+void SetCurTraceInfo(uint64_t aSourceEventId, uint64_t aParentTaskId,
+ SourceEventType aSourceEventType);
+
+/**
+ * Logging functions of different trace actions.
+ */
+enum ActionType {
+ ACTION_DISPATCH = 0,
+ ACTION_BEGIN,
+ ACTION_END,
+ ACTION_ADD_LABEL,
+ ACTION_GET_VTABLE
+};
+
+void LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId,
+ uint64_t aSourceEventId, SourceEventType aSourceEventType);
+
+void LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId,
+ uint64_t aSourceEventId, SourceEventType aSourceEventType,
+ int aDelayTimeMs);
+
+void LogBegin(uint64_t aTaskId, uint64_t aSourceEventId);
+
+void LogEnd(uint64_t aTaskId, uint64_t aSourceEventId);
+
+void LogVirtualTablePtr(uint64_t aTaskId, uint64_t aSourceEventId,
+ uintptr_t* aVptr);
+
+} // namespace tasktracer
+} // namespace mozilla
+
+#endif
diff --git a/tools/profiler/tasktracer/SourceEventTypeMap.h b/tools/profiler/tasktracer/SourceEventTypeMap.h
new file mode 100644
index 0000000000..6e687ecb1e
--- /dev/null
+++ b/tools/profiler/tasktracer/SourceEventTypeMap.h
@@ -0,0 +1,16 @@
+/* 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 GECKO_TASK_TRACER_H
+# error "Don't include this file directly"
+#endif
+
+SOURCE_EVENT_NAME(Unknown)
+SOURCE_EVENT_NAME(Touch)
+SOURCE_EVENT_NAME(Mouse)
+SOURCE_EVENT_NAME(Key)
+SOURCE_EVENT_NAME(Bluetooth)
+SOURCE_EVENT_NAME(Unixsocket)
+SOURCE_EVENT_NAME(Wifi)
+SOURCE_EVENT_NAME(SocketIO)
diff --git a/tools/profiler/tasktracer/TracedTaskCommon.cpp b/tools/profiler/tasktracer/TracedTaskCommon.cpp
new file mode 100644
index 0000000000..4b5d4d4cc6
--- /dev/null
+++ b/tools/profiler/tasktracer/TracedTaskCommon.cpp
@@ -0,0 +1,128 @@
+/* -*- 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/. */
+
+#include "TracedTaskCommon.h"
+
+#include "GeckoTaskTracerImpl.h"
+
+// NS_ENSURE_TRUE_VOID() without the warning on the debug build.
+#define ENSURE_TRUE_VOID(x) \
+ do { \
+ if (MOZ_UNLIKELY(!(x))) { \
+ return; \
+ } \
+ } while (0)
+
+namespace mozilla {
+namespace tasktracer {
+
+TracedTaskCommon::TracedTaskCommon()
+ : mSourceEventType(SourceEventType::Unknown),
+ mSourceEventId(0),
+ mParentTaskId(0),
+ mTaskId(0),
+ mIsTraceInfoInit(false) {}
+
+TracedTaskCommon::~TracedTaskCommon() {}
+
+void TracedTaskCommon::Init() {
+ // Keep the following line before GetOrCreateTraceInfo() to avoid a
+ // deadlock.
+ uint64_t taskid = GenNewUniqueTaskId();
+
+ TraceInfoHolder info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ mTaskId = taskid;
+ mSourceEventId = info->mCurTraceSourceId;
+ mSourceEventType = info->mCurTraceSourceType;
+ mParentTaskId = info->mCurTaskId;
+ mIsTraceInfoInit = true;
+}
+
+void TracedTaskCommon::DispatchTask(int aDelayTimeMs) {
+ LogDispatch(mTaskId, mParentTaskId, mSourceEventId, mSourceEventType,
+ aDelayTimeMs);
+}
+
+void TracedTaskCommon::DoGetTLSTraceInfo() {
+ TraceInfoHolder info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+ MOZ_ASSERT(!mIsTraceInfoInit);
+
+ mSourceEventType = info->mCurTraceSourceType;
+ mSourceEventId = info->mCurTraceSourceId;
+ mTaskId = info->mCurTaskId;
+ mIsTraceInfoInit = true;
+}
+
+void TracedTaskCommon::DoSetTLSTraceInfo() {
+ TraceInfoHolder info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ if (mIsTraceInfoInit) {
+ info->mCurTraceSourceId = mSourceEventId;
+ info->mCurTraceSourceType = mSourceEventType;
+ info->mCurTaskId = mTaskId;
+ }
+}
+
+void TracedTaskCommon::ClearTLSTraceInfo() {
+ TraceInfoHolder info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ info->mCurTraceSourceId = 0;
+ info->mCurTraceSourceType = SourceEventType::Unknown;
+ info->mCurTaskId = 0;
+}
+
+/**
+ * Implementation of class TracedRunnable.
+ */
+
+NS_IMPL_ISUPPORTS(TracedRunnable, nsIRunnable);
+
+TracedRunnable::TracedRunnable(already_AddRefed<nsIRunnable>&& aOriginalObj)
+ : TracedTaskCommon(), mOriginalObj(std::move(aOriginalObj)) {
+ Init();
+ LogVirtualTablePtr(mTaskId, mSourceEventId,
+ *reinterpret_cast<uintptr_t**>(mOriginalObj.get()));
+}
+
+TracedRunnable::~TracedRunnable() {}
+
+NS_IMETHODIMP
+TracedRunnable::Run() {
+ SetTLSTraceInfo();
+ LogBegin(mTaskId, mSourceEventId);
+ nsresult rv = mOriginalObj->Run();
+ LogEnd(mTaskId, mSourceEventId);
+ ClearTLSTraceInfo();
+
+ return rv;
+}
+
+/**
+ * CreateTracedRunnable() returns a TracedRunnable wrapping the original
+ * nsIRunnable object, aRunnable.
+ */
+already_AddRefed<nsIRunnable> CreateTracedRunnable(
+ already_AddRefed<nsIRunnable>&& aRunnable) {
+ RefPtr<nsIRunnable> runnable = new TracedRunnable(std::move(aRunnable));
+ return runnable.forget();
+}
+
+void VirtualTask::AutoRunTask::StartScope(VirtualTask* aTask) {
+ mTask->SetTLSTraceInfo();
+ LogBegin(mTask->mTaskId, mTask->mSourceEventId);
+}
+
+void VirtualTask::AutoRunTask::StopScope() {
+ LogEnd(mTask->mTaskId, mTask->mSourceEventId);
+}
+
+} // namespace tasktracer
+} // namespace mozilla
diff --git a/tools/profiler/tasktracer/TracedTaskCommon.h b/tools/profiler/tasktracer/TracedTaskCommon.h
new file mode 100644
index 0000000000..d93e1b38e7
--- /dev/null
+++ b/tools/profiler/tasktracer/TracedTaskCommon.h
@@ -0,0 +1,130 @@
+/* -*- 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 TRACED_TASK_COMMON_H
+#define TRACED_TASK_COMMON_H
+
+#include "GeckoTaskTracer.h"
+
+#include "nsCOMPtr.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace tasktracer {
+
+class TracedTaskCommon {
+ public:
+ TracedTaskCommon();
+ TracedTaskCommon(const TracedTaskCommon& aSrc)
+ : mSourceEventType(aSrc.mSourceEventType),
+ mSourceEventId(aSrc.mSourceEventId),
+ mParentTaskId(aSrc.mParentTaskId),
+ mTaskId(aSrc.mTaskId),
+ mIsTraceInfoInit(aSrc.mIsTraceInfoInit) {}
+ virtual ~TracedTaskCommon();
+
+ void DispatchTask(int aDelayTimeMs = 0);
+
+ void SetTLSTraceInfo() {
+ if (mIsTraceInfoInit) {
+ DoSetTLSTraceInfo();
+ }
+ }
+ void GetTLSTraceInfo() {
+ if (IsStartLogging()) {
+ DoGetTLSTraceInfo();
+ }
+ }
+ void ClearTLSTraceInfo();
+
+ private:
+ void DoSetTLSTraceInfo();
+ void DoGetTLSTraceInfo();
+
+ protected:
+ void Init();
+
+ // TraceInfo of TLS will be set by the following parameters, including source
+ // event type, source event ID, parent task ID, and task ID of this traced
+ // task/runnable.
+ SourceEventType mSourceEventType;
+ uint64_t mSourceEventId;
+ uint64_t mParentTaskId;
+ uint64_t mTaskId;
+ bool mIsTraceInfoInit;
+};
+
+class TracedRunnable : public TracedTaskCommon, public nsIRunnable {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+
+ explicit TracedRunnable(already_AddRefed<nsIRunnable>&& aOriginalObj);
+
+ private:
+ virtual ~TracedRunnable();
+
+ nsCOMPtr<nsIRunnable> mOriginalObj;
+};
+
+/**
+ * This class is used to create a logical task, without a real
+ * runnable.
+ */
+class VirtualTask : public TracedTaskCommon {
+ public:
+ VirtualTask() : TracedTaskCommon() {}
+
+ VirtualTask(const VirtualTask& aSrc) : TracedTaskCommon(aSrc) {}
+
+ /**
+ * Initialize the task to create an unique ID, and store other
+ * information.
+ *
+ * This method may be called for one or more times.
+ */
+ void Init(uintptr_t* aVPtr = nullptr) {
+ TracedTaskCommon::Init();
+ if (aVPtr) {
+ extern void LogVirtualTablePtr(uint64_t aTaskId, uint64_t aSourceEventId,
+ uintptr_t * aVptr);
+ LogVirtualTablePtr(mTaskId, mSourceEventId, aVPtr);
+ }
+ DispatchTask();
+ }
+
+ /**
+ * Define the life-span of a VirtualTask.
+ *
+ * VirtualTask is not a real task, goes without a runnable, it's
+ * instances are never dispatched and ran by event loops. This
+ * class used to define running time as the life-span of it's
+ * instance.
+ */
+ class AutoRunTask : public AutoSaveCurTraceInfo {
+ VirtualTask* mTask;
+ void StartScope(VirtualTask* aTask);
+ void StopScope();
+
+ public:
+ explicit AutoRunTask(VirtualTask* aTask)
+ : AutoSaveCurTraceInfo(), mTask(aTask) {
+ if (HasSavedTraceInfo()) {
+ StartScope(aTask);
+ }
+ }
+ ~AutoRunTask() {
+ if (HasSavedTraceInfo()) {
+ StopScope();
+ }
+ }
+ };
+};
+
+} // namespace tasktracer
+} // namespace mozilla
+
+#endif