summaryrefslogtreecommitdiffstats
path: root/tools/profiler/tasktracer
diff options
context:
space:
mode:
Diffstat (limited to 'tools/profiler/tasktracer')
-rw-r--r--tools/profiler/tasktracer/GeckoTaskTracer.cpp472
-rw-r--r--tools/profiler/tasktracer/GeckoTaskTracer.h92
-rw-r--r--tools/profiler/tasktracer/GeckoTaskTracerImpl.h102
-rw-r--r--tools/profiler/tasktracer/SourceEventTypeMap.h11
-rw-r--r--tools/profiler/tasktracer/TracedTaskCommon.cpp169
-rw-r--r--tools/profiler/tasktracer/TracedTaskCommon.h73
6 files changed, 919 insertions, 0 deletions
diff --git a/tools/profiler/tasktracer/GeckoTaskTracer.cpp b/tools/profiler/tasktracer/GeckoTaskTracer.cpp
new file mode 100644
index 000000000..ada695614
--- /dev/null
+++ b/tools/profiler/tasktracer/GeckoTaskTracer.cpp
@@ -0,0 +1,472 @@
+/* -*- 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 "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>
+
+// We need a definition of gettid(), but glibc doesn't provide a
+// wrapper for it.
+#if defined(__GLIBC__)
+#include <unistd.h>
+#include <sys/syscall.h>
+static inline pid_t gettid()
+{
+ return (pid_t) syscall(SYS_gettid);
+}
+#elif defined(XP_MACOSX)
+#include <unistd.h>
+#include <sys/syscall.h>
+static inline pid_t gettid()
+{
+ return (pid_t) syscall(SYS_thread_selfid);
+}
+#elif defined(LINUX)
+#include <sys/types.h>
+pid_t gettid();
+#endif
+
+// 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 {
+
+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.
+static mozilla::Atomic<bool> sStarted;
+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)
+{
+ StaticMutexAutoLock lock(sMutex);
+
+ auto* info = sTraceInfos->AppendElement(MakeUnique<TraceInfo>(aTid));
+
+ return info->get();
+}
+
+static void
+SaveCurTraceInfo()
+{
+ TraceInfo* info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ info->mSavedCurTraceSourceId = info->mCurTraceSourceId;
+ info->mSavedCurTraceSourceType = info->mCurTraceSourceType;
+ info->mSavedCurTaskId = info->mCurTaskId;
+}
+
+static void
+RestoreCurTraceInfo()
+{
+ TraceInfo* info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ info->mCurTraceSourceId = info->mSavedCurTraceSourceId;
+ info->mCurTraceSourceType = info->mSavedCurTraceSourceType;
+ info->mCurTaskId = info->mSavedCurTaskId;
+}
+
+static void
+CreateSourceEvent(SourceEventType aType)
+{
+ // Save the currently traced source event info.
+ SaveCurTraceInfo();
+
+ // Create a new unique task id.
+ uint64_t newId = GenNewUniqueTaskId();
+ TraceInfo* 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: \
+ { \
+ static int CreateSourceEvent##type; \
+ namePtr = (uintptr_t*)&CreateSourceEvent##type; \
+ break; \
+ }
+
+ switch (aType) {
+#include "SourceEventTypeMap.h"
+ default:
+ MOZ_CRASH("Unknown SourceEvent.");
+ }
+#undef CREATE_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.
+ TraceInfo* info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ LogEnd(info->mCurTraceSourceId, info->mCurTraceSourceId);
+
+ // Restore the previously saved source event info.
+ RestoreCurTraceInfo();
+}
+
+inline static bool
+IsStartLogging()
+{
+ return sStarted;
+}
+
+static void
+SetLogStarted(bool aIsStartLogging)
+{
+ MOZ_ASSERT(aIsStartLogging != IsStartLogging());
+ sStarted = aIsStartLogging;
+
+ StaticMutexAutoLock lock(sMutex);
+ if (!aIsStartLogging) {
+ for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) {
+ (*sTraceInfos)[i]->mObsolete = true;
+ }
+ }
+}
+
+static void
+CleanUp()
+{
+ SetLogStarted(false);
+ StaticMutexAutoLock lock(sMutex);
+
+ if (sTraceInfos) {
+ delete sTraceInfos;
+ sTraceInfos = nullptr;
+ }
+}
+
+inline static void
+ObsoleteCurrentTraceInfos()
+{
+ // Note that we can't and don't need to acquire sMutex here because this
+ // function is called before the other threads are recreated.
+ for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) {
+ (*sTraceInfos)[i]->mObsolete = true;
+ }
+}
+
+} // namespace anonymous
+
+nsCString*
+TraceInfo::AppendLog()
+{
+ MutexAutoLock lock(mLogsMutex);
+ return mLogs.AppendElement();
+}
+
+void
+TraceInfo::MoveLogsInto(TraceInfoLogsType& aResult)
+{
+ MutexAutoLock lock(mLogsMutex);
+ aResult.AppendElements(Move(mLogs));
+}
+
+void
+InitTaskTracer(uint32_t aFlags)
+{
+ if (aFlags & FORKED_AFTER_NUWA) {
+ ObsoleteCurrentTraceInfos();
+ return;
+ }
+
+ MOZ_ASSERT(!sTraceInfos);
+ sTraceInfos = new nsTArray<UniquePtr<TraceInfo>>();
+
+ if (!sTraceInfoTLS.initialized()) {
+ Unused << sTraceInfoTLS.init();
+ }
+}
+
+void
+ShutdownTaskTracer()
+{
+ CleanUp();
+}
+
+static void
+FreeTraceInfo(TraceInfo* aTraceInfo)
+{
+ StaticMutexAutoLock lock(sMutex);
+ if (aTraceInfo) {
+ sTraceInfos->RemoveElement(aTraceInfo);
+ }
+}
+
+void FreeTraceInfo()
+{
+ FreeTraceInfo(sTraceInfoTLS.get());
+}
+
+TraceInfo*
+GetOrCreateTraceInfo()
+{
+ ENSURE_TRUE(sTraceInfoTLS.initialized(), nullptr);
+ ENSURE_TRUE(IsStartLogging(), nullptr);
+
+ TraceInfo* info = sTraceInfoTLS.get();
+ if (info && info->mObsolete) {
+ // TraceInfo is obsolete: remove it.
+ FreeTraceInfo(info);
+ info = nullptr;
+ }
+
+ if (!info) {
+ info = AllocTraceInfo(gettid());
+ sTraceInfoTLS.set(info);
+ }
+
+ return info;
+}
+
+uint64_t
+GenNewUniqueTaskId()
+{
+ TraceInfo* info = GetOrCreateTraceInfo();
+ ENSURE_TRUE(info, 0);
+
+ pid_t tid = gettid();
+ uint64_t taskid = ((uint64_t)tid << 32) | ++info->mLastUniqueTaskId;
+ return taskid;
+}
+
+AutoSaveCurTraceInfo::AutoSaveCurTraceInfo()
+{
+ SaveCurTraceInfo();
+}
+
+AutoSaveCurTraceInfo::~AutoSaveCurTraceInfo()
+{
+ RestoreCurTraceInfo();
+}
+
+void
+SetCurTraceInfo(uint64_t aSourceEventId, uint64_t aParentTaskId,
+ SourceEventType aSourceEventType)
+{
+ TraceInfo* 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)
+{
+ TraceInfo* 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)
+{
+ TraceInfo* 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]
+ nsCString* log = info->AppendLog();
+ if (log) {
+ log->AppendPrintf("%d %lld %lld %lld %d %lld",
+ ACTION_DISPATCH, aTaskId, time, aSourceEventId,
+ aSourceEventType, aParentTaskId);
+ }
+}
+
+void
+LogBegin(uint64_t aTaskId, uint64_t aSourceEventId)
+{
+ TraceInfo* info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ // Log format:
+ // [1 taskId beginTime processId threadId]
+ nsCString* log = info->AppendLog();
+ if (log) {
+ log->AppendPrintf("%d %lld %lld %d %d",
+ ACTION_BEGIN, aTaskId, GetTimestamp(), getpid(), gettid());
+ }
+}
+
+void
+LogEnd(uint64_t aTaskId, uint64_t aSourceEventId)
+{
+ TraceInfo* info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ // Log format:
+ // [2 taskId endTime]
+ nsCString* log = info->AppendLog();
+ if (log) {
+ log->AppendPrintf("%d %lld %lld", ACTION_END, aTaskId, GetTimestamp());
+ }
+}
+
+void
+LogVirtualTablePtr(uint64_t aTaskId, uint64_t aSourceEventId, uintptr_t* aVptr)
+{
+ TraceInfo* info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ // Log format:
+ // [4 taskId address]
+ nsCString* log = info->AppendLog();
+ if (log) {
+ log->AppendPrintf("%d %lld %p", ACTION_GET_VTABLE, aTaskId, aVptr);
+ }
+}
+
+AutoSourceEvent::AutoSourceEvent(SourceEventType aType)
+{
+ CreateSourceEvent(aType);
+}
+
+AutoSourceEvent::~AutoSourceEvent()
+{
+ DestroySourceEvent();
+}
+
+void AddLabel(const char* aFormat, ...)
+{
+ TraceInfo* info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ va_list args;
+ va_start(args, aFormat);
+ nsAutoCString buffer;
+ buffer.AppendPrintf(aFormat, args);
+ va_end(args);
+
+ // Log format:
+ // [3 taskId "label"]
+ nsCString* log = info->AppendLog();
+ if (log) {
+ log->AppendPrintf("%d %lld %lld \"%s\"", ACTION_ADD_LABEL, info->mCurTaskId,
+ GetTimestamp(), buffer.get());
+ }
+}
+
+// Functions used by GeckoProfiler.
+
+void
+StartLogging()
+{
+ sStartTime = GetTimestamp();
+ SetLogStarted(true);
+}
+
+void
+StopLogging()
+{
+ SetLogStarted(false);
+}
+
+UniquePtr<TraceInfoLogsType>
+GetLoggedData(TimeStamp aTimeStamp)
+{
+ auto result = MakeUnique<TraceInfoLogsType>();
+
+ // TODO: This is called from a signal handler. Use semaphore instead.
+ StaticMutexAutoLock lock(sMutex);
+
+ for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) {
+ (*sTraceInfos)[i]->MoveLogsInto(*result);
+ }
+
+ return result;
+}
+
+const 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 000000000..9e36b3f0b
--- /dev/null
+++ b/tools/profiler/tasktracer/GeckoTaskTracer.h
@@ -0,0 +1,92 @@
+/* -*- 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/UniquePtr.h"
+#include "nsCOMPtr.h"
+#include "nsTArrayForwardDeclare.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 Task;
+class nsIRunnable;
+class nsCString;
+
+namespace mozilla {
+
+class TimeStamp;
+
+namespace tasktracer {
+
+enum {
+ FORKED_AFTER_NUWA = 1 << 0
+};
+
+enum SourceEventType {
+ Unknown = 0,
+ Touch,
+ Mouse,
+ Key,
+ Bluetooth,
+ Unixsocket,
+ Wifi
+};
+
+class AutoSourceEvent
+{
+public:
+ AutoSourceEvent(SourceEventType aType);
+ ~AutoSourceEvent();
+};
+
+void InitTaskTracer(uint32_t aFlags = 0);
+void ShutdownTaskTracer();
+
+// Add a label to the currently running task, aFormat is the message to log,
+// followed by corresponding parameters.
+void AddLabel(const char* aFormat, ...);
+
+void StartLogging();
+void StopLogging();
+UniquePtr<nsTArray<nsCString>> GetLoggedData(TimeStamp aStartTime);
+
+// Returns the timestamp when Task Tracer is enabled in this process.
+const PRTime GetStartTime();
+
+/**
+ * Internal functions.
+ */
+
+Task* CreateTracedTask(Task* aTask);
+
+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();
+
+} // namespace tasktracer
+} // namespace mozilla.
+
+#endif
diff --git a/tools/profiler/tasktracer/GeckoTaskTracerImpl.h b/tools/profiler/tasktracer/GeckoTaskTracerImpl.h
new file mode 100644
index 000000000..5b748fb96
--- /dev/null
+++ b/tools/profiler/tasktracer/GeckoTaskTracerImpl.h
@@ -0,0 +1,102 @@
+/* -*- 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 {
+
+typedef nsTArray<nsCString> TraceInfoLogsType;
+
+struct TraceInfo
+{
+ TraceInfo(uint32_t aThreadId)
+ : mCurTraceSourceId(0)
+ , mCurTaskId(0)
+ , mSavedCurTraceSourceId(0)
+ , mSavedCurTaskId(0)
+ , mCurTraceSourceType(Unknown)
+ , mSavedCurTraceSourceType(Unknown)
+ , mThreadId(aThreadId)
+ , mLastUniqueTaskId(0)
+ , mObsolete(false)
+ , mLogsMutex("TraceInfoMutex")
+ {
+ MOZ_COUNT_CTOR(TraceInfo);
+ }
+
+ ~TraceInfo() { MOZ_COUNT_DTOR(TraceInfo); }
+
+ nsCString* AppendLog();
+ void MoveLogsInto(TraceInfoLogsType& aResult);
+
+ uint64_t mCurTraceSourceId;
+ uint64_t mCurTaskId;
+ uint64_t mSavedCurTraceSourceId;
+ uint64_t mSavedCurTaskId;
+ SourceEventType mCurTraceSourceType;
+ SourceEventType mSavedCurTraceSourceType;
+ uint32_t mThreadId;
+ uint32_t mLastUniqueTaskId;
+ mozilla::Atomic<bool> mObsolete;
+
+ // This mutex protects the following log array because MoveLogsInto() might
+ // be called on another thread.
+ mozilla::Mutex mLogsMutex;
+ TraceInfoLogsType mLogs;
+};
+
+// Return the TraceInfo of current thread, allocate a new one if not exit.
+TraceInfo* GetOrCreateTraceInfo();
+
+uint64_t GenNewUniqueTaskId();
+
+class AutoSaveCurTraceInfo
+{
+public:
+ AutoSaveCurTraceInfo();
+ ~AutoSaveCurTraceInfo();
+};
+
+void SetCurTraceInfo(uint64_t aSourceEventId, uint64_t aParentTaskId,
+ SourceEventType aSourceEventType);
+
+void GetCurTraceInfo(uint64_t* aOutSourceEventId, uint64_t* aOutParentTaskId,
+ SourceEventType* aOutSourceEventType);
+
+/**
+ * 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 mozilla
+} // namespace tasktracer
+
+#endif
diff --git a/tools/profiler/tasktracer/SourceEventTypeMap.h b/tools/profiler/tasktracer/SourceEventTypeMap.h
new file mode 100644
index 000000000..77dbc8330
--- /dev/null
+++ b/tools/profiler/tasktracer/SourceEventTypeMap.h
@@ -0,0 +1,11 @@
+/* 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/. */
+
+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)
diff --git a/tools/profiler/tasktracer/TracedTaskCommon.cpp b/tools/profiler/tasktracer/TracedTaskCommon.cpp
new file mode 100644
index 000000000..770eb202c
--- /dev/null
+++ b/tools/profiler/tasktracer/TracedTaskCommon.cpp
@@ -0,0 +1,169 @@
+/* -*- 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 "GeckoTaskTracerImpl.h"
+#include "TracedTaskCommon.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()
+{
+ TraceInfo* info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ mTaskId = GenNewUniqueTaskId();
+ mSourceEventId = info->mCurTraceSourceId;
+ mSourceEventType = info->mCurTraceSourceType;
+ mParentTaskId = info->mCurTaskId;
+ mIsTraceInfoInit = true;
+}
+
+void
+TracedTaskCommon::DispatchTask(int aDelayTimeMs)
+{
+ LogDispatch(mTaskId, mParentTaskId, mSourceEventId, mSourceEventType,
+ aDelayTimeMs);
+}
+
+void
+TracedTaskCommon::GetTLSTraceInfo()
+{
+ TraceInfo* info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ mSourceEventType = info->mCurTraceSourceType;
+ mSourceEventId = info->mCurTraceSourceId;
+ mTaskId = info->mCurTaskId;
+ mIsTraceInfoInit = true;
+}
+
+void
+TracedTaskCommon::SetTLSTraceInfo()
+{
+ TraceInfo* info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ if (mIsTraceInfoInit) {
+ info->mCurTraceSourceId = mSourceEventId;
+ info->mCurTraceSourceType = mSourceEventType;
+ info->mCurTaskId = mTaskId;
+ }
+}
+
+void
+TracedTaskCommon::ClearTLSTraceInfo()
+{
+ TraceInfo* info = GetOrCreateTraceInfo();
+ ENSURE_TRUE_VOID(info);
+
+ info->mCurTraceSourceId = 0;
+ info->mCurTraceSourceType = SourceEventType::Unknown;
+ info->mCurTaskId = 0;
+}
+
+/**
+ * Implementation of class TracedRunnable.
+ */
+TracedRunnable::TracedRunnable(already_AddRefed<nsIRunnable>&& aOriginalObj)
+ : TracedTaskCommon()
+ , mOriginalObj(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;
+}
+
+/**
+ * Implementation of class TracedTask.
+ */
+TracedTask::TracedTask(Task* aOriginalObj)
+ : TracedTaskCommon()
+ , mOriginalObj(aOriginalObj)
+{
+ Init();
+ LogVirtualTablePtr(mTaskId, mSourceEventId, reinterpret_cast<uintptr_t*>(aOriginalObj));
+}
+
+TracedTask::~TracedTask()
+{
+ if (mOriginalObj) {
+ delete mOriginalObj;
+ mOriginalObj = nullptr;
+ }
+}
+
+void
+TracedTask::Run()
+{
+ SetTLSTraceInfo();
+ LogBegin(mTaskId, mSourceEventId);
+ mOriginalObj->Run();
+ LogEnd(mTaskId, mSourceEventId);
+ ClearTLSTraceInfo();
+}
+
+/**
+ * CreateTracedRunnable() returns a TracedRunnable wrapping the original
+ * nsIRunnable object, aRunnable.
+ */
+already_AddRefed<nsIRunnable>
+CreateTracedRunnable(already_AddRefed<nsIRunnable>&& aRunnable)
+{
+ nsCOMPtr<nsIRunnable> runnable = new TracedRunnable(Move(aRunnable));
+ return runnable.forget();
+}
+
+/**
+ * CreateTracedTask() returns a TracedTask wrapping the original Task object,
+ * aTask.
+ */
+Task*
+CreateTracedTask(Task* aTask)
+{
+ Task* task = new TracedTask(aTask);
+ return task;
+}
+
+} // namespace tasktracer
+} // namespace mozilla
diff --git a/tools/profiler/tasktracer/TracedTaskCommon.h b/tools/profiler/tasktracer/TracedTaskCommon.h
new file mode 100644
index 000000000..3594b8e9e
--- /dev/null
+++ b/tools/profiler/tasktracer/TracedTaskCommon.h
@@ -0,0 +1,73 @@
+/* -*- 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 "base/task.h"
+#include "GeckoTaskTracer.h"
+#include "nsCOMPtr.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace tasktracer {
+
+class TracedTaskCommon
+{
+public:
+ TracedTaskCommon();
+ virtual ~TracedTaskCommon();
+
+ void DispatchTask(int aDelayTimeMs = 0);
+
+ void SetTLSTraceInfo();
+ void GetTLSTraceInfo();
+ void ClearTLSTraceInfo();
+
+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 nsRunnable
+{
+public:
+ NS_DECL_NSIRUNNABLE
+
+ TracedRunnable(already_AddRefed<nsIRunnable>&& aOriginalObj);
+
+private:
+ virtual ~TracedRunnable();
+
+ nsCOMPtr<nsIRunnable> mOriginalObj;
+};
+
+class TracedTask : public TracedTaskCommon
+ , public Task
+{
+public:
+ TracedTask(Task* aOriginalObj);
+ ~TracedTask();
+
+ virtual void Run();
+
+private:
+ Task* mOriginalObj;
+};
+
+} // namespace tasktracer
+} // namespace mozilla
+
+#endif