Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / tools / profiler / core / ProfilerThreadRegistration.cpp
blobc81d00573daaa562740bd757e237ebc8dbec4b16
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ProfilerThreadRegistration.h"
9 #include "mozilla/ProfilerMarkers.h"
10 #include "mozilla/ProfilerThreadRegistry.h"
11 #include "nsString.h"
12 #ifdef MOZ_GECKO_PROFILER
13 # include "platform.h"
14 #else
15 # define profiler_mark_thread_awake()
16 # define profiler_mark_thread_asleep()
17 #endif
19 namespace mozilla::profiler {
21 /* static */
22 MOZ_THREAD_LOCAL(ThreadRegistration*) ThreadRegistration::tlsThreadRegistration;
24 ThreadRegistration::ThreadRegistration(const char* aName, const void* aStackTop)
25 : mData(aName, aStackTop) {
26 auto* tls = GetTLS();
27 if (MOZ_UNLIKELY(!tls)) {
28 // No TLS, nothing can be done without it.
29 return;
32 if (ThreadRegistration* rootRegistration = tls->get(); rootRegistration) {
33 // This is a nested ThreadRegistration object, so the thread is already
34 // registered in the TLS and ThreadRegistry and we don't need to register
35 // again.
36 MOZ_ASSERT(
37 mData.Info().ThreadId() == rootRegistration->mData.Info().ThreadId(),
38 "Thread being re-registered has changed its TID");
39 // TODO: Use new name. This is currently not possible because the
40 // TLS-stored RegisteredThread's ThreadInfo cannot be changed.
41 // In the meantime, we record a marker that could be used in the frontend.
42 PROFILER_MARKER_TEXT("Nested ThreadRegistration()", OTHER_Profiling,
43 MarkerOptions{},
44 ProfilerString8View::WrapNullTerminatedString(aName));
45 return;
48 tls->set(this);
49 ThreadRegistry::Register(OnThreadRef{*this});
50 profiler_mark_thread_awake();
53 ThreadRegistration::~ThreadRegistration() {
54 MOZ_ASSERT(profiler_current_thread_id() == mData.mInfo.ThreadId(),
55 "ThreadRegistration must be destroyed on its thread");
56 MOZ_ASSERT(!mDataMutex.IsLockedOnCurrentThread(),
57 "Mutex shouldn't be locked here, as it's about to be destroyed "
58 "in ~ThreadRegistration()");
59 auto* tls = GetTLS();
60 if (MOZ_UNLIKELY(!tls)) {
61 // No TLS, nothing can be done without it.
62 return;
65 if (ThreadRegistration* rootRegistration = tls->get(); rootRegistration) {
66 if (rootRegistration != this) {
67 // `this` is not in the TLS, so it was a nested registration, there is
68 // nothing to unregister yet.
69 PROFILER_MARKER_TEXT(
70 "Nested ~ThreadRegistration()", OTHER_Profiling, MarkerOptions{},
71 ProfilerString8View::WrapNullTerminatedString(mData.Info().Name()));
72 return;
75 profiler_mark_thread_asleep();
76 #ifdef NIGHTLY_BUILD
77 mData.RecordWakeCount();
78 #endif
79 ThreadRegistry::Unregister(OnThreadRef{*this});
80 #ifdef DEBUG
81 // After ThreadRegistry::Unregister, other threads should not be able to
82 // find this ThreadRegistration, and shouldn't have kept any reference to
83 // it across the ThreadRegistry mutex.
84 MOZ_ASSERT(mDataMutex.TryLock(),
85 "Mutex shouldn't be locked in any thread, as it's about to be "
86 "destroyed in ~ThreadRegistration()");
87 // Undo the above successful TryLock.
88 mDataMutex.Unlock();
89 #endif // DEBUG
91 tls->set(nullptr);
92 return;
95 // Already removed from the TLS!? This could happen with improperly-nested
96 // register/unregister calls, and the first ThreadRegistration has already
97 // been unregistered.
98 // We cannot record a marker on this thread because it was already
99 // unregistered. Send it to the main thread (unless this *is* already the
100 // main thread, which has been unregistered); this may be useful to catch
101 // mismatched register/unregister pairs in Firefox.
102 if (!profiler_is_main_thread()) {
103 nsAutoCString threadId("thread id: ");
104 threadId.AppendInt(profiler_current_thread_id().ToNumber());
105 threadId.AppendLiteral(", name: \"");
106 threadId.AppendASCII(mData.Info().Name());
107 threadId.AppendLiteral("\"");
108 PROFILER_MARKER_TEXT(
109 "~ThreadRegistration() but TLS is empty", OTHER_Profiling,
110 MarkerOptions(MarkerThreadId::MainThread(), MarkerStack::Capture()),
111 threadId);
115 /* static */
116 ProfilingStack* ThreadRegistration::RegisterThread(const char* aName,
117 const void* aStackTop) {
118 auto* tls = GetTLS();
119 if (MOZ_UNLIKELY(!tls)) {
120 // No TLS, nothing can be done without it.
121 return nullptr;
124 if (ThreadRegistration* rootRegistration = tls->get(); rootRegistration) {
125 // Already registered, record the extra depth to ignore the matching
126 // UnregisterThread.
127 ++rootRegistration->mOtherRegistrations;
128 // TODO: Use new name. This is currently not possible because the
129 // TLS-stored RegisteredThread's ThreadInfo cannot be changed.
130 // In the meantime, we record a marker that could be used in the frontend.
131 PROFILER_MARKER_TEXT("Nested ThreadRegistration::RegisterThread()",
132 OTHER_Profiling, MarkerOptions{},
133 ProfilerString8View::WrapNullTerminatedString(aName));
134 return &rootRegistration->mData.mProfilingStack;
137 // Create on heap, it self-registers with the TLS (its effective owner, so
138 // we can forget the pointer after this), and with the Profiler.
139 ThreadRegistration* tr = new ThreadRegistration(aName, aStackTop);
140 tr->mIsOnHeap = true;
141 return &tr->mData.mProfilingStack;
144 /* static */
145 void ThreadRegistration::UnregisterThread() {
146 auto* tls = GetTLS();
147 if (MOZ_UNLIKELY(!tls)) {
148 // No TLS, nothing can be done without it.
149 return;
152 if (ThreadRegistration* rootRegistration = tls->get(); rootRegistration) {
153 if (rootRegistration->mOtherRegistrations != 0) {
154 // This is assumed to be a matching UnregisterThread() for a nested
155 // RegisterThread(). Decrease depth and we're done.
156 --rootRegistration->mOtherRegistrations;
157 // We don't know what name was used in the related RegisterThread().
158 PROFILER_MARKER_UNTYPED("Nested ThreadRegistration::UnregisterThread()",
159 OTHER_Profiling);
160 return;
163 if (!rootRegistration->mIsOnHeap) {
164 // The root registration was not added by `RegisterThread()`, so it
165 // shouldn't be deleted!
166 // This could happen if there are un-paired `UnregisterThread` calls when
167 // the initial registration (still alive) was done on the stack. We don't
168 // know what name was used in the related RegisterThread().
169 PROFILER_MARKER_UNTYPED("Excess ThreadRegistration::UnregisterThread()",
170 OTHER_Profiling, MarkerStack::Capture());
171 return;
174 // This is the last `UnregisterThread()` that should match the first
175 // `RegisterThread()` that created this ThreadRegistration on the heap.
176 // Just delete this root registration, it will de-register itself from the
177 // TLS (and from the Profiler).
178 delete rootRegistration;
179 return;
182 // There is no known ThreadRegistration for this thread, ignore this
183 // request. We cannot record a marker on this thread because it was already
184 // unregistered. Send it to the main thread (unless this *is* already the
185 // main thread, which has been unregistered); this may be useful to catch
186 // mismatched register/unregister pairs in Firefox.
187 if (!profiler_is_main_thread()) {
188 nsAutoCString threadId("thread id: ");
189 threadId.AppendInt(profiler_current_thread_id().ToNumber());
190 PROFILER_MARKER_TEXT(
191 "ThreadRegistration::UnregisterThread() but TLS is empty",
192 OTHER_Profiling,
193 MarkerOptions(MarkerThreadId::MainThread(), MarkerStack::Capture()),
194 threadId);
198 } // namespace mozilla::profiler