Bug 1721569 - Handle different sizes of Profiler{Process,Thread}Id - r=florian
authorGerald Squelart <gsquelart@mozilla.com>
Wed, 11 Aug 2021 03:27:50 +0000
changeset 588520 e3d5648f410e154df54a19f6731da2e16659e52c
parent 588519 c1effaec69ba51040c57f35243d0fe673cf4f360
child 588521 92c07c54be8adca59a0a4e22076b244ceab0e2e9
push id38694
push userarchaeopteryx@coole-files.de
push dateWed, 11 Aug 2021 09:37:02 +0000
treeherdermozilla-central@857bc25bbf76 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersflorian
bugs1721569
milestone93.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1721569 - Handle different sizes of Profiler{Process,Thread}Id - r=florian Since ProfilerProcessId and ProfilerThreadId (and their NumberTypes) will potentially grow to 64 bits on some platforms (in a later patch), all code that uses them must be able to handle bigger types. Differential Revision: https://phabricator.services.mozilla.com/D121049
mozglue/baseprofiler/core/ProfileBuffer.cpp
mozglue/baseprofiler/core/ProfileBufferEntry.cpp
mozglue/baseprofiler/core/ProfileBufferEntry.h
mozglue/baseprofiler/core/ProfiledThreadData.cpp
mozglue/baseprofiler/core/platform.cpp
mozglue/baseprofiler/lul/LulMain.cpp
mozglue/baseprofiler/public/ProfileBufferEntryKinds.h
mozglue/tests/TestBaseProfiler.cpp
tools/profiler/core/ProfileBuffer.cpp
tools/profiler/core/ProfileBufferEntry.cpp
tools/profiler/core/ProfileBufferEntry.h
tools/profiler/core/ProfiledThreadData.cpp
tools/profiler/core/memory_hooks.cpp
tools/profiler/core/platform.cpp
tools/profiler/core/platform.h
tools/profiler/gecko/ProfilerIOInterposeObserver.cpp
tools/profiler/lul/LulMain.cpp
tools/profiler/lul/platform-linux-lul.cpp
tools/profiler/tests/gtest/GeckoProfiler.cpp
--- a/mozglue/baseprofiler/core/ProfileBuffer.cpp
+++ b/mozglue/baseprofiler/core/ProfileBuffer.cpp
@@ -44,17 +44,17 @@ uint64_t ProfileBuffer::AddEntry(const P
   return AddEntry(mEntries, aEntry).ConvertToProfileBufferIndex();
 }
 
 /* static */
 ProfileBufferBlockIndex ProfileBuffer::AddThreadIdEntry(
     ProfileChunkedBuffer& aProfileChunkedBuffer,
     BaseProfilerThreadId aThreadId) {
   return AddEntry(aProfileChunkedBuffer,
-                  ProfileBufferEntry::ThreadId(aThreadId.ToNumber()));
+                  ProfileBufferEntry::ThreadId(aThreadId));
 }
 
 uint64_t ProfileBuffer::AddThreadIdEntry(BaseProfilerThreadId aThreadId) {
   return AddThreadIdEntry(mEntries, aThreadId).ConvertToProfileBufferIndex();
 }
 
 void ProfileBuffer::CollectCodeLocation(
     const char* aLabel, const char* aStr, uint32_t aFrameFlags,
--- a/mozglue/baseprofiler/core/ProfileBufferEntry.cpp
+++ b/mozglue/baseprofiler/core/ProfileBufferEntry.cpp
@@ -58,16 +58,24 @@ ProfileBufferEntry::ProfileBufferEntry(K
   memcpy(mStorage, &aInt64, sizeof(aInt64));
 }
 
 ProfileBufferEntry::ProfileBufferEntry(Kind aKind, uint64_t aUint64)
     : mKind(aKind) {
   memcpy(mStorage, &aUint64, sizeof(aUint64));
 }
 
+ProfileBufferEntry::ProfileBufferEntry(Kind aKind,
+                                       BaseProfilerThreadId aThreadId)
+    : mKind(aKind) {
+  static_assert(std::is_trivially_copyable_v<BaseProfilerThreadId>);
+  static_assert(sizeof(aThreadId) <= sizeof(mStorage));
+  memcpy(mStorage, &aThreadId, sizeof(aThreadId));
+}
+
 const char* ProfileBufferEntry::GetString() const {
   const char* result;
   memcpy(&result, mStorage, sizeof(result));
   return result;
 }
 
 void* ProfileBufferEntry::GetPtr() const {
   void* result;
@@ -94,16 +102,23 @@ int64_t ProfileBufferEntry::GetInt64() c
 }
 
 uint64_t ProfileBufferEntry::GetUint64() const {
   uint64_t result;
   memcpy(&result, mStorage, sizeof(result));
   return result;
 }
 
+BaseProfilerThreadId ProfileBufferEntry::GetThreadId() const {
+  BaseProfilerThreadId result;
+  static_assert(std::is_trivially_copyable_v<BaseProfilerThreadId>);
+  memcpy(&result, mStorage, sizeof(result));
+  return result;
+}
+
 void ProfileBufferEntry::CopyCharsInto(char (&aOutArray)[kNumChars]) const {
   memcpy(aOutArray, mStorage, kNumChars);
 }
 
 // END ProfileBufferEntry
 ////////////////////////////////////////////////////////////////////////
 
 // As mentioned in ProfileBufferEntry.h, the JSON format contains many
@@ -577,18 +592,17 @@ BaseProfilerThreadId ProfileBuffer::Stre
       if (!e.Has()) {
         break;
       }
 
       // Due to the skip_to_next_sample block above, if we have an entry here it
       // must be a ThreadId entry.
       MOZ_ASSERT(e.Get().IsThreadId());
 
-      BaseProfilerThreadId threadId =
-          BaseProfilerThreadId::FromNumber(e.Get().GetInt());
+      BaseProfilerThreadId threadId = e.Get().GetThreadId();
       e.Next();
 
       // Ignore samples that are for the wrong thread.
       if (threadId != aThreadId && aThreadId.IsSpecified()) {
         continue;
       }
 
       MOZ_ASSERT(
@@ -1204,18 +1218,17 @@ bool ProfileBuffer::DuplicateLastSample(
     if (e.CurPos() != *aLastSample) {
       // The last sample is no longer within the buffer range, so we cannot
       // use it. Reset the stored buffer position to Nothing().
       aLastSample.reset();
       return false;
     }
 
     MOZ_RELEASE_ASSERT(e.Has() && e.Get().IsThreadId() &&
-                       BaseProfilerThreadId::FromNumber(e.Get().GetInt()) ==
-                           aThreadId);
+                       e.Get().GetThreadId() == aThreadId);
 
     e.Next();
 
     // Go through the whole entry and duplicate it, until we find the next
     // one.
     while (e.Has()) {
       switch (e.Get().GetKind()) {
         case ProfileBufferEntry::Kind::Pause:
--- a/mozglue/baseprofiler/core/ProfileBufferEntry.h
+++ b/mozglue/baseprofiler/core/ProfileBufferEntry.h
@@ -36,16 +36,17 @@ class ProfileBufferEntry {
   // aString must be a static string.
   ProfileBufferEntry(Kind aKind, const char* aString);
   ProfileBufferEntry(Kind aKind, char aChars[kNumChars]);
   ProfileBufferEntry(Kind aKind, void* aPtr);
   ProfileBufferEntry(Kind aKind, double aDouble);
   ProfileBufferEntry(Kind aKind, int64_t aInt64);
   ProfileBufferEntry(Kind aKind, uint64_t aUint64);
   ProfileBufferEntry(Kind aKind, int aInt);
+  ProfileBufferEntry(Kind aKind, BaseProfilerThreadId aThreadId);
 
  public:
 #define CTOR(KIND, TYPE, SIZE)                   \
   static ProfileBufferEntry KIND(TYPE aVal) {    \
     return ProfileBufferEntry(Kind::KIND, aVal); \
   }
   FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(CTOR)
 #undef CTOR
@@ -69,16 +70,17 @@ class ProfileBufferEntry {
   uint8_t mStorage[kNumChars];
 
   const char* GetString() const;
   void* GetPtr() const;
   double GetDouble() const;
   int GetInt() const;
   int64_t GetInt64() const;
   uint64_t GetUint64() const;
+  BaseProfilerThreadId GetThreadId() const;
   void CopyCharsInto(char (&aOutArray)[kNumChars]) const;
 };
 
 // Packed layout: 1 byte for the tag + 8 bytes for the value.
 static_assert(sizeof(ProfileBufferEntry) == 9, "bad ProfileBufferEntry size");
 
 class UniqueStacks {
  public:
--- a/mozglue/baseprofiler/core/ProfiledThreadData.cpp
+++ b/mozglue/baseprofiler/core/ProfiledThreadData.cpp
@@ -170,16 +170,20 @@ BaseProfilerThreadId StreamSamplesAndMar
     {
       aBuffer.StreamMarkersToJSON(aWriter, aThreadId, aProcessStartTime,
                                   aSinceTime, aUniqueStacks);
     }
     aWriter.EndArray();
   }
   aWriter.EndObject();
 
+  // Tech note: If `ToNumber()` returns a uint64_t, the conversion to int64_t is
+  // "implementation-defined" before C++20. This is acceptable here, because
+  // this is a one-way conversion to a unique identifier that's used to visually
+  // separate data by thread on the front-end.
   aWriter.IntProperty(
       "pid", static_cast<int64_t>(profiler_current_process_id().ToNumber()));
   aWriter.IntProperty("tid",
                       static_cast<int64_t>(aThreadId.IsSpecified()
                                                ? aThreadId.ToNumber()
                                                : processedThreadId.ToNumber()));
 
   return processedThreadId;
--- a/mozglue/baseprofiler/core/platform.cpp
+++ b/mozglue/baseprofiler/core/platform.cpp
@@ -3360,18 +3360,19 @@ ProfilingStack* profiler_register_thread
   DEBUG_LOG("profiler_register_thread(%s)", aName);
 
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
   PSAutoLock lock;
 
   if (RegisteredThread* thread = FindCurrentThreadRegisteredThread(lock);
       thread) {
-    LOG("profiler_register_thread(%s) - thread %d already registered as %s",
-        aName, int(profiler_current_thread_id().ToNumber()),
+    LOG("profiler_register_thread(%s) - thread %" PRIu64
+        " already registered as %s",
+        aName, uint64_t(profiler_current_thread_id().ToNumber()),
         thread->Info()->Name());
     // TODO: Use new name. This is currently not possible because the
     // RegisteredThread's ThreadInfo cannot be changed.
     // In the meantime, we record a marker that could be used in the frontend.
     std::string text("Thread ");
     text += std::to_string(profiler_current_thread_id().ToNumber());
     text += " \"";
     text += thread->Info()->Name();
@@ -3411,18 +3412,19 @@ void profiler_unregister_thread() {
     // Clear the pointer to the RegisteredThread object that we're about to
     // destroy.
     TLSRegisteredThread::SetRegisteredThread(lock, nullptr);
 
     // Remove the thread from the list of registered threads. This deletes the
     // registeredThread object.
     CorePS::RemoveRegisteredThread(lock, registeredThread);
   } else {
-    LOG("profiler_unregister_thread() - thread %d already unregistered",
-        (profiler_current_thread_id().ToNumber()));
+    LOG("profiler_unregister_thread() - thread %" PRIu64
+        " already unregistered",
+        uint64_t(profiler_current_thread_id().ToNumber()));
     // We cannot record a marker on this thread because it was already
     // unregistered. Send it to the main thread (unless this *is* already the
     // main thread, which has been unregistered); this may be useful to catch
     // mismatched register/unregister pairs in Firefox.
     if (BaseProfilerThreadId tid = profiler_current_thread_id();
         tid != profiler_main_thread_id()) {
       BASE_PROFILER_MARKER_TEXT(
           "profiler_unregister_thread again", OTHER_Profiling,
--- a/mozglue/baseprofiler/lul/LulMain.cpp
+++ b/mozglue/baseprofiler/lul/LulMain.cpp
@@ -680,25 +680,25 @@ class PriMap {
   // a logging sink, for debugging.
   void (*mLog)(const char*);
 };
 
 ////////////////////////////////////////////////////////////////
 // LUL                                                        //
 ////////////////////////////////////////////////////////////////
 
-#define LUL_LOG(_str)                                                  \
-  do {                                                                 \
-    char buf[200];                                                     \
-    SprintfLiteral(buf, "LUL: pid %d tid %d lul-obj %p: %s",           \
-                   int(profiler_current_process_id().ToNumber()),      \
-                   int(profiler_current_thread_id().ToNumber()), this, \
-                   (_str));                                            \
-    buf[sizeof(buf) - 1] = 0;                                          \
-    mLog(buf);                                                         \
+#define LUL_LOG(_str)                                                          \
+  do {                                                                         \
+    char buf[200];                                                             \
+    SprintfLiteral(buf, "LUL: pid %" PRIu64 " tid %" PRIu64 " lul-obj %p: %s", \
+                   uint64_t(profiler_current_process_id().ToNumber()),         \
+                   uint64_t(profiler_current_thread_id().ToNumber()), this,    \
+                   (_str));                                                    \
+    buf[sizeof(buf) - 1] = 0;                                                  \
+    mLog(buf);                                                                 \
   } while (0)
 
 LUL::LUL(void (*aLog)(const char*))
     : mLog(aLog),
       mAdminMode(true),
       mAdminThreadId(profiler_current_thread_id()),
       mPriMap(new PriMap(aLog)),
       mSegArray(new SegArray()),
--- a/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h
+++ b/mozglue/baseprofiler/public/ProfileBufferEntryKinds.h
@@ -2,16 +2,18 @@
 /* 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 ProfileBufferEntryKinds_h
 #define ProfileBufferEntryKinds_h
 
+#include "mozilla/BaseProfilerUtils.h"
+
 #include <cstdint>
 
 namespace mozilla {
 
 // This is equal to sizeof(double), which is the largest non-char variant in
 // |u|.
 static constexpr size_t ProfileBufferEntryNumChars = 8;
 
@@ -30,17 +32,18 @@ static constexpr size_t ProfileBufferEnt
   MACRO(LineNumber, int, sizeof(int))                             \
   MACRO(ColumnNumber, int, sizeof(int))                           \
   MACRO(NativeLeafAddr, void*, sizeof(void*))                     \
   MACRO(Pause, double, sizeof(double))                            \
   MACRO(Resume, double, sizeof(double))                           \
   MACRO(PauseSampling, double, sizeof(double))                    \
   MACRO(ResumeSampling, double, sizeof(double))                   \
   MACRO(Responsiveness, double, sizeof(double))                   \
-  MACRO(ThreadId, int, sizeof(int))                               \
+  MACRO(ThreadId, ::mozilla::baseprofiler::BaseProfilerThreadId,  \
+        sizeof(::mozilla::baseprofiler::BaseProfilerThreadId))    \
   MACRO(Time, double, sizeof(double))                             \
   MACRO(TimeBeforeCompactStack, double, sizeof(double))           \
   MACRO(CounterId, void*, sizeof(void*))                          \
   MACRO(CounterKey, uint64_t, sizeof(uint64_t))                   \
   MACRO(Number, uint64_t, sizeof(uint64_t))                       \
   MACRO(Count, int64_t, sizeof(int64_t))                          \
   MACRO(ProfilerOverheadTime, double, sizeof(double))             \
   MACRO(ProfilerOverheadDuration, double, sizeof(double))
--- a/mozglue/tests/TestBaseProfiler.cpp
+++ b/mozglue/tests/TestBaseProfiler.cpp
@@ -3792,19 +3792,19 @@ MOZ_NEVER_INLINE unsigned long long Fibo
     BASE_PROFILER_MARKER_TEXT("fib", OTHER,
                               MarkerTiming::IntervalUntilNowFrom(start),
                               std::to_string(DEPTH));
   }
   return f2 + f1;
 }
 
 void TestProfiler() {
-  printf("TestProfiler starting -- pid: %d, tid: %d\n",
-         int(baseprofiler::profiler_current_process_id().ToNumber()),
-         int(baseprofiler::profiler_current_thread_id().ToNumber()));
+  printf("TestProfiler starting -- pid: %" PRIu64 ", tid: %" PRIu64 "\n",
+         uint64_t(baseprofiler::profiler_current_process_id().ToNumber()),
+         uint64_t(baseprofiler::profiler_current_thread_id().ToNumber()));
   // ::SleepMilli(10000);
 
   TestProfilerDependencies();
 
   {
     printf("profiler_init()...\n");
     AUTO_BASE_PROFILER_INIT;
 
@@ -4556,19 +4556,20 @@ void TestPredefinedMarkers() {
 #  endif
 
   PrintMarkers(buffer);
 
   printf("TestPredefinedMarkers done\n");
 }
 
 void TestProfilerMarkers() {
-  printf("TestProfilerMarkers -- pid: %d, tid: %d\n",
-         int(mozilla::baseprofiler::profiler_current_process_id().ToNumber()),
-         int(mozilla::baseprofiler::profiler_current_thread_id().ToNumber()));
+  printf(
+      "TestProfilerMarkers -- pid: %" PRIu64 ", tid: %" PRIu64 "\n",
+      uint64_t(mozilla::baseprofiler::profiler_current_process_id().ToNumber()),
+      uint64_t(mozilla::baseprofiler::profiler_current_thread_id().ToNumber()));
   // ::SleepMilli(10000);
 
   TestUniqueJSONStrings();
   TestMarkerCategory();
   TestMarkerThreadId();
   TestMarkerNoPayload();
   TestUserMarker();
   TestPredefinedMarkers();
@@ -4636,19 +4637,19 @@ void TestProfilerMarkers() {
 
 #if defined(XP_WIN)
 int wmain()
 #else
 int main()
 #endif  // defined(XP_WIN)
 {
 #ifdef MOZ_GECKO_PROFILER
-  printf("BaseTestProfiler -- pid: %d, tid: %d\n",
-         int(baseprofiler::profiler_current_process_id().ToNumber()),
-         int(baseprofiler::profiler_current_thread_id().ToNumber()));
+  printf("BaseTestProfiler -- pid: %" PRIu64 ", tid: %" PRIu64 "\n",
+         uint64_t(baseprofiler::profiler_current_process_id().ToNumber()),
+         uint64_t(baseprofiler::profiler_current_thread_id().ToNumber()));
   // ::SleepMilli(10000);
 #endif  // MOZ_GECKO_PROFILER
 
   TestProfilerUtils();
   // Note that there are two `TestProfiler{,Markers}` functions above, depending
   // on whether MOZ_GECKO_PROFILER is #defined.
   TestProfiler();
   TestProfilerMarkers();
--- a/tools/profiler/core/ProfileBuffer.cpp
+++ b/tools/profiler/core/ProfileBuffer.cpp
@@ -44,17 +44,17 @@ ProfileBufferBlockIndex ProfileBuffer::A
 uint64_t ProfileBuffer::AddEntry(const ProfileBufferEntry& aEntry) {
   return AddEntry(mEntries, aEntry).ConvertToProfileBufferIndex();
 }
 
 /* static */
 ProfileBufferBlockIndex ProfileBuffer::AddThreadIdEntry(
     ProfileChunkedBuffer& aProfileChunkedBuffer, ProfilerThreadId aThreadId) {
   return AddEntry(aProfileChunkedBuffer,
-                  ProfileBufferEntry::ThreadId(aThreadId.ToNumber()));
+                  ProfileBufferEntry::ThreadId(aThreadId));
 }
 
 uint64_t ProfileBuffer::AddThreadIdEntry(ProfilerThreadId aThreadId) {
   return AddThreadIdEntry(mEntries, aThreadId).ConvertToProfileBufferIndex();
 }
 
 void ProfileBuffer::CollectCodeLocation(
     const char* aLabel, const char* aStr, uint32_t aFrameFlags,
--- a/tools/profiler/core/ProfileBufferEntry.cpp
+++ b/tools/profiler/core/ProfileBufferEntry.cpp
@@ -64,16 +64,23 @@ ProfileBufferEntry::ProfileBufferEntry(K
   memcpy(mStorage, &aInt64, sizeof(aInt64));
 }
 
 ProfileBufferEntry::ProfileBufferEntry(Kind aKind, uint64_t aUint64)
     : mKind(aKind) {
   memcpy(mStorage, &aUint64, sizeof(aUint64));
 }
 
+ProfileBufferEntry::ProfileBufferEntry(Kind aKind, ProfilerThreadId aThreadId)
+    : mKind(aKind) {
+  static_assert(std::is_trivially_copyable_v<ProfilerThreadId>);
+  static_assert(sizeof(aThreadId) <= sizeof(mStorage));
+  memcpy(mStorage, &aThreadId, sizeof(aThreadId));
+}
+
 const char* ProfileBufferEntry::GetString() const {
   const char* result;
   memcpy(&result, mStorage, sizeof(result));
   return result;
 }
 
 void* ProfileBufferEntry::GetPtr() const {
   void* result;
@@ -100,16 +107,23 @@ int64_t ProfileBufferEntry::GetInt64() c
 }
 
 uint64_t ProfileBufferEntry::GetUint64() const {
   uint64_t result;
   memcpy(&result, mStorage, sizeof(result));
   return result;
 }
 
+ProfilerThreadId ProfileBufferEntry::GetThreadId() const {
+  ProfilerThreadId result;
+  static_assert(std::is_trivially_copyable_v<ProfilerThreadId>);
+  memcpy(&result, mStorage, sizeof(result));
+  return result;
+}
+
 void ProfileBufferEntry::CopyCharsInto(char (&aOutArray)[kNumChars]) const {
   memcpy(aOutArray, mStorage, kNumChars);
 }
 
 // END ProfileBufferEntry
 ////////////////////////////////////////////////////////////////////////
 
 struct TypeInfo {
@@ -811,18 +825,17 @@ ProfilerThreadId ProfileBuffer::StreamSa
       if (!e.Has()) {
         break;
       }
 
       // Due to the skip_to_next_sample block above, if we have an entry here it
       // must be a ThreadId entry.
       MOZ_ASSERT(e.Get().IsThreadId());
 
-      ProfilerThreadId threadId =
-          ProfilerThreadId::FromNumber(e.Get().GetInt());
+      ProfilerThreadId threadId = e.Get().GetThreadId();
       e.Next();
 
       // Ignore samples that are for the wrong thread.
       if (threadId != aThreadId && aThreadId.IsSpecified()) {
         continue;
       }
 
       MOZ_ASSERT(
@@ -1105,18 +1118,17 @@ void ProfileBuffer::AddJITInfoForRange(u
             while (e.Has() && !e.Get().IsThreadId()) {
               e.Next();
             }
             if (!e.Has()) {
               break;
             }
 
             MOZ_ASSERT(e.Get().IsThreadId());
-            ProfilerThreadId threadId =
-                ProfilerThreadId::FromNumber(e.Get().GetInt());
+            ProfilerThreadId threadId = e.Get().GetThreadId();
             e.Next();
 
             // Ignore samples that are for a different thread.
             if (threadId != aThreadId) {
               continue;
             }
 
             if (e.Has() && e.Get().IsTime()) {
@@ -1609,18 +1621,17 @@ bool ProfileBuffer::DuplicateLastSample(
     if (e.CurPos() != *aLastSample) {
       // The last sample is no longer within the buffer range, so we cannot
       // use it. Reset the stored buffer position to Nothing().
       aLastSample.reset();
       return false;
     }
 
     MOZ_RELEASE_ASSERT(e.Has() && e.Get().IsThreadId() &&
-                       ProfilerThreadId::FromNumber(e.Get().GetInt()) ==
-                           aThreadId);
+                       e.Get().GetThreadId() == aThreadId);
 
     e.Next();
 
     // Go through the whole entry and duplicate it, until we find the next
     // one.
     while (e.Has()) {
       switch (e.Get().GetKind()) {
         case ProfileBufferEntry::Kind::Pause:
--- a/tools/profiler/core/ProfileBufferEntry.h
+++ b/tools/profiler/core/ProfileBufferEntry.h
@@ -14,16 +14,17 @@
 #include "gtest/MozGtestFriend.h"
 #include "js/ProfilingCategory.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/HashTable.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/ProfileBufferEntryKinds.h"
 #include "mozilla/ProfileJSONWriter.h"
+#include "mozilla/ProfilerUtils.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Variant.h"
 #include "mozilla/Vector.h"
 #include "nsString.h"
 
 class ProfilerCodeAddressService;
 struct JSContext;
 
@@ -40,16 +41,17 @@ class ProfileBufferEntry {
   // aString must be a static string.
   ProfileBufferEntry(Kind aKind, const char* aString);
   ProfileBufferEntry(Kind aKind, char aChars[kNumChars]);
   ProfileBufferEntry(Kind aKind, void* aPtr);
   ProfileBufferEntry(Kind aKind, double aDouble);
   ProfileBufferEntry(Kind aKind, int64_t aInt64);
   ProfileBufferEntry(Kind aKind, uint64_t aUint64);
   ProfileBufferEntry(Kind aKind, int aInt);
+  ProfileBufferEntry(Kind aKind, ProfilerThreadId aThreadId);
 
  public:
 #define CTOR(KIND, TYPE, SIZE)                   \
   static ProfileBufferEntry KIND(TYPE aVal) {    \
     return ProfileBufferEntry(Kind::KIND, aVal); \
   }
   FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(CTOR)
 #undef CTOR
@@ -73,16 +75,17 @@ class ProfileBufferEntry {
   uint8_t mStorage[kNumChars];
 
   const char* GetString() const;
   void* GetPtr() const;
   double GetDouble() const;
   int GetInt() const;
   int64_t GetInt64() const;
   uint64_t GetUint64() const;
+  ProfilerThreadId GetThreadId() const;
   void CopyCharsInto(char (&aOutArray)[kNumChars]) const;
 };
 
 // Packed layout: 1 byte for the tag + 8 bytes for the value.
 static_assert(sizeof(ProfileBufferEntry) == 9, "bad ProfileBufferEntry size");
 
 // Contains all the information about JIT frames that is needed to stream stack
 // frames for JitReturnAddr entries in the profiler buffer.
--- a/tools/profiler/core/ProfiledThreadData.cpp
+++ b/tools/profiler/core/ProfiledThreadData.cpp
@@ -283,16 +283,20 @@ ProfilerThreadId StreamSamplesAndMarkers
     {
       aBuffer.StreamMarkersToJSON(aWriter, aThreadId, aProcessStartTime,
                                   aSinceTime, aUniqueStacks);
     }
     aWriter.EndArray();
   }
   aWriter.EndObject();
 
+  // Tech note: If `ToNumber()` returns a uint64_t, the conversion to int64_t is
+  // "implementation-defined" before C++20. This is acceptable here, because
+  // this is a one-way conversion to a unique identifier that's used to visually
+  // separate data by thread on the front-end.
   aWriter.IntProperty(
       "pid", static_cast<int64_t>(profiler_current_process_id().ToNumber()));
   aWriter.IntProperty("tid",
                       static_cast<int64_t>(aThreadId.IsSpecified()
                                                ? aThreadId.ToNumber()
                                                : processedThreadId.ToNumber()));
 
   return processedThreadId;
--- a/tools/profiler/core/memory_hooks.cpp
+++ b/tools/profiler/core/memory_hooks.cpp
@@ -91,17 +91,22 @@ static bool profiler_add_native_allocati
       return mozilla::MakeStringSpan("Native allocation");
     }
     static void StreamJSONMarkerData(
         mozilla::baseprofiler::SpliceableJSONWriter& aWriter, int64_t aSize,
         uintptr_t aMemoryAddress, ProfilerThreadId aThreadId) {
       aWriter.IntProperty("size", aSize);
       aWriter.IntProperty("memoryAddress",
                           static_cast<int64_t>(aMemoryAddress));
-      aWriter.IntProperty("threadId", aThreadId.ToNumber());
+      // Tech note: If `ToNumber()` returns a uint64_t, the conversion to
+      // int64_t is "implementation-defined" before C++20. This is acceptable
+      // here, because this is a one-way conversion to a unique identifier
+      // that's used to visually separate data by thread on the front-end.
+      aWriter.IntProperty("threadId",
+                          static_cast<int64_t>(aThreadId.ToNumber()));
     }
     static mozilla::MarkerSchema MarkerTypeDisplay() {
       return mozilla::MarkerSchema::SpecialFrontendLocation{};
     }
   };
 
   profiler_add_marker("Native allocation", geckoprofiler::category::OTHER,
                       {MarkerThreadId::MainThread(), MarkerStack::Capture()},
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -1602,18 +1602,19 @@ AutoProfilerLabel::ProfilingStackOwnerTL
 void AutoProfilerLabel::ProfilingStackOwnerTLS::Init() {
   MOZ_ASSERT(sState == State::Uninitialized, "Already initialized");
   sState =
       sProfilingStackOwnerTLS.init() ? State::Initialized : State::Unavailable;
 }
 
 void ProfilingStackOwner::DumpStackAndCrash() const {
   fprintf(stderr,
-          "ProfilingStackOwner::DumpStackAndCrash() thread id: %d, size: %u\n",
-          int(profiler_current_thread_id().ToNumber()),
+          "ProfilingStackOwner::DumpStackAndCrash() thread id: %" PRIu64
+          ", size: %u\n",
+          uint64_t(profiler_current_thread_id().ToNumber()),
           unsigned(mProfilingStack.stackSize()));
   js::ProfilingStackFrame* allFrames = mProfilingStack.frames;
   for (uint32_t i = 0; i < mProfilingStack.stackSize(); i++) {
     js::ProfilingStackFrame& frame = allFrames[i];
     if (frame.isLabelFrame()) {
       fprintf(stderr, "%u: label frame, sp=%p, label='%s' (%s)\n", unsigned(i),
               frame.stackAddress(), frame.label(),
               frame.dynamicString() ? frame.dynamicString() : "-");
@@ -5496,18 +5497,19 @@ ProfilingStack* profiler_register_thread
 
   if (RegisteredThread* thread = TLSRegisteredThread::RegisteredThread(lock)) {
     MOZ_RELEASE_ASSERT(IsRegisteredThreadInRegisteredThreadsList(lock, thread),
                        "Thread being re-registered is not in registered thread "
                        "list even though its TLS is non-null");
     MOZ_RELEASE_ASSERT(
         thread->Info()->ThreadId() == profiler_current_thread_id(),
         "Thread being re-registered has changed its TID");
-    LOG("profiler_register_thread(%s) - thread %d already registered as %s",
-        aName, int(profiler_current_thread_id().ToNumber()),
+    LOG("profiler_register_thread(%s) - thread %" PRIu64
+        " already registered as %s",
+        aName, uint64_t(profiler_current_thread_id().ToNumber()),
         thread->Info()->Name());
     // TODO: Use new name. This is currently not possible because the
     // RegisteredThread's ThreadInfo cannot be changed.
     // In the meantime, we record a marker that could be used in the frontend.
     nsCString text("Thread ");
     text.AppendInt(profiler_current_thread_id().ToNumber());
     text.AppendLiteral(" \"");
     text.AppendASCII(thread->Info()->Name());
@@ -5580,18 +5582,19 @@ void profiler_unregister_thread() {
   } else {
     // There are two ways TLSRegisteredThread::RegisteredThread() might be
     // empty.
     //
     // - TLSRegisteredThread::Init() failed in locked_register_thread().
     //
     // - We've already called profiler_unregister_thread() for this thread.
     //   (Whether or not it should, this does happen in practice.)
-    LOG("profiler_unregister_thread() - thread %d already unregistered",
-        int(profiler_current_thread_id().ToNumber()));
+    LOG("profiler_unregister_thread() - thread %" PRIu64
+        " already unregistered",
+        uint64_t(profiler_current_thread_id().ToNumber()));
     // We cannot record a marker on this thread because it was already
     // unregistered. Send it to the main thread (unless this *is* already the
     // main thread, which has been unregistered); this may be useful to catch
     // mismatched register/unregister pairs in Firefox.
     if (ProfilerThreadId tid = profiler_current_thread_id();
         tid != profiler_main_thread_id()) {
       nsCString threadIdString;
       threadIdString.AppendInt(tid.ToNumber());
--- a/tools/profiler/core/platform.h
+++ b/tools/profiler/core/platform.h
@@ -50,28 +50,28 @@ namespace mozilla {
 struct SymbolTable;
 }
 
 extern mozilla::LazyLogModule gProfilerLog;
 
 // These are for MOZ_LOG="prof:3" or higher. It's the default logging level for
 // the profiler, and should be used sparingly.
 #define LOG_TEST MOZ_LOG_TEST(gProfilerLog, mozilla::LogLevel::Info)
-#define LOG(arg, ...)                                                  \
-  MOZ_LOG(gProfilerLog, mozilla::LogLevel::Info,                       \
-          ("[%d] " arg, int(profiler_current_process_id().ToNumber()), \
-           ##__VA_ARGS__))
+#define LOG(arg, ...)                            \
+  MOZ_LOG(gProfilerLog, mozilla::LogLevel::Info, \
+          ("[%" PRIu64 "d] " arg,                \
+           uint64_t(profiler_current_process_id().ToNumber()), ##__VA_ARGS__))
 
 // These are for MOZ_LOG="prof:4" or higher. It should be used for logging that
 // is somewhat more verbose than LOG.
 #define DEBUG_LOG_TEST MOZ_LOG_TEST(gProfilerLog, mozilla::LogLevel::Debug)
-#define DEBUG_LOG(arg, ...)                                            \
-  MOZ_LOG(gProfilerLog, mozilla::LogLevel::Debug,                      \
-          ("[%d] " arg, int(profiler_current_process_id().ToNumber()), \
-           ##__VA_ARGS__))
+#define DEBUG_LOG(arg, ...)                       \
+  MOZ_LOG(gProfilerLog, mozilla::LogLevel::Debug, \
+          ("[%" PRIu64 "] " arg,                  \
+           uint64_t(profiler_current_process_id().ToNumber()), ##__VA_ARGS__))
 
 typedef uint8_t* Address;
 
 // ----------------------------------------------------------------------------
 // Miscellaneous
 
 class PlatformData;
 
--- a/tools/profiler/gecko/ProfilerIOInterposeObserver.cpp
+++ b/tools/profiler/gecko/ProfilerIOInterposeObserver.cpp
@@ -18,17 +18,23 @@ struct FileIOMarker {
                                    const ProfilerString8View& aFilename,
                                    MarkerThreadId aOperationThreadId) {
     aWriter.StringProperty("operation", aOperation);
     aWriter.StringProperty("source", aSource);
     if (aFilename.Length() != 0) {
       aWriter.StringProperty("filename", aFilename);
     }
     if (!aOperationThreadId.IsUnspecified()) {
-      aWriter.IntProperty("threadId", aOperationThreadId.ThreadId().ToNumber());
+      // Tech note: If `ToNumber()` returns a uint64_t, the conversion to
+      // int64_t is "implementation-defined" before C++20. This is acceptable
+      // here, because this is a one-way conversion to a unique identifier
+      // that's used to visually separate data by thread on the front-end.
+      aWriter.IntProperty(
+          "threadId",
+          static_cast<int64_t>(aOperationThreadId.ThreadId().ToNumber()));
     }
   }
   static MarkerSchema MarkerTypeDisplay() {
     using MS = MarkerSchema;
     MS schema{MS::Location::markerChart, MS::Location::markerTable,
               MS::Location::timelineFileIO};
     schema.AddKeyLabelFormatSearchable("operation", "Operation",
                                        MS::Format::string,
--- a/tools/profiler/lul/LulMain.cpp
+++ b/tools/profiler/lul/LulMain.cpp
@@ -794,25 +794,25 @@ class PriMap {
   // a logging sink, for debugging.
   void (*mLog)(const char*);
 };
 
 ////////////////////////////////////////////////////////////////
 // LUL                                                        //
 ////////////////////////////////////////////////////////////////
 
-#define LUL_LOG(_str)                                                  \
-  do {                                                                 \
-    char buf[200];                                                     \
-    SprintfLiteral(buf, "LUL: pid %d tid %d lul-obj %p: %s",           \
-                   int(profiler_current_process_id().ToNumber()),      \
-                   int(profiler_current_thread_id().ToNumber()), this, \
-                   (_str));                                            \
-    buf[sizeof(buf) - 1] = 0;                                          \
-    mLog(buf);                                                         \
+#define LUL_LOG(_str)                                                          \
+  do {                                                                         \
+    char buf[200];                                                             \
+    SprintfLiteral(buf, "LUL: pid %" PRIu64 " tid %" PRIu64 " lul-obj %p: %s", \
+                   uint64_t(profiler_current_process_id().ToNumber()),         \
+                   uint64_t(profiler_current_thread_id().ToNumber()), this,    \
+                   (_str));                                                    \
+    buf[sizeof(buf) - 1] = 0;                                                  \
+    mLog(buf);                                                                 \
   } while (0)
 
 LUL::LUL(void (*aLog)(const char*))
     : mLog(aLog),
       mAdminMode(true),
       mAdminThreadId(profiler_current_thread_id()),
       mPriMap(new PriMap(aLog)),
       mSegArray(new SegArray()),
--- a/tools/profiler/lul/platform-linux-lul.cpp
+++ b/tools/profiler/lul/platform-linux-lul.cpp
@@ -65,10 +65,11 @@ void read_procmaps(lul::LUL* aLUL) {
 
 // LUL needs a callback for its logging sink.
 void logging_sink_for_LUL(const char* str) {
   // These are only printed when Verbose logging is enabled (e.g. with
   // MOZ_LOG="prof:5"). This is because LUL's logging is much more verbose than
   // the rest of the profiler's logging, which occurs at the Info (3) and Debug
   // (4) levels.
   MOZ_LOG(gProfilerLog, mozilla::LogLevel::Verbose,
-          ("[%d] %s", int(profiler_current_process_id().ToNumber()), str));
+          ("[%" PRIu64 "] %s",
+           uint64_t(profiler_current_process_id().ToNumber()), str));
 }
--- a/tools/profiler/tests/gtest/GeckoProfiler.cpp
+++ b/tools/profiler/tests/gtest/GeckoProfiler.cpp
@@ -269,18 +269,18 @@ static void JSONRootCheck(const Json::Va
   const Json::ArrayIndex threadCount = threads.size();
   for (Json::ArrayIndex i = 0; i < threadCount; ++i) {
     GET_JSON(thread, threads[i], Object);
     EXPECT_HAS_JSON(thread["processType"], String);
     EXPECT_HAS_JSON(thread["name"], String);
     EXPECT_HAS_JSON(thread["registerTime"], Double);
     EXPECT_HAS_JSON(thread["samples"], Object);
     EXPECT_HAS_JSON(thread["markers"], Object);
-    EXPECT_HAS_JSON(thread["pid"], UInt);
-    EXPECT_HAS_JSON(thread["tid"], UInt);
+    EXPECT_HAS_JSON(thread["pid"], Int64);
+    EXPECT_HAS_JSON(thread["tid"], Int64);
     EXPECT_HAS_JSON(thread["stackTable"], Object);
     EXPECT_HAS_JSON(thread["frameTable"], Object);
     EXPECT_HAS_JSON(thread["stringTable"], Array);
   }
 
   if (aWithMainThread) {
     ASSERT_GT(threadCount, 0u);
     GET_JSON(thread0, threads[0], Object);