summaryrefslogtreecommitdiffstats
path: root/tools/profiler/tasktracer/GeckoTaskTracer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/profiler/tasktracer/GeckoTaskTracer.cpp')
-rw-r--r--tools/profiler/tasktracer/GeckoTaskTracer.cpp472
1 files changed, 472 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