mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
34c0c5a32a
Differential Revision: https://phabricator.services.mozilla.com/D141147
199 lines
7.8 KiB
C++
199 lines
7.8 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* 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/. */
|
|
|
|
#include "mozilla/ProfilerThreadRegistration.h"
|
|
|
|
#include "mozilla/ProfilerMarkers.h"
|
|
#include "mozilla/ProfilerThreadRegistry.h"
|
|
#include "nsString.h"
|
|
#ifdef MOZ_GECKO_PROFILER
|
|
# include "platform.h"
|
|
#else
|
|
# define profiler_mark_thread_awake()
|
|
# define profiler_mark_thread_asleep()
|
|
#endif
|
|
|
|
namespace mozilla::profiler {
|
|
|
|
/* static */
|
|
MOZ_THREAD_LOCAL(ThreadRegistration*) ThreadRegistration::tlsThreadRegistration;
|
|
|
|
ThreadRegistration::ThreadRegistration(const char* aName, const void* aStackTop)
|
|
: mData(aName, aStackTop) {
|
|
auto* tls = GetTLS();
|
|
if (MOZ_UNLIKELY(!tls)) {
|
|
// No TLS, nothing can be done without it.
|
|
return;
|
|
}
|
|
|
|
if (ThreadRegistration* rootRegistration = tls->get(); rootRegistration) {
|
|
// This is a nested ThreadRegistration object, so the thread is already
|
|
// registered in the TLS and ThreadRegistry and we don't need to register
|
|
// again.
|
|
MOZ_ASSERT(
|
|
mData.Info().ThreadId() == rootRegistration->mData.Info().ThreadId(),
|
|
"Thread being re-registered has changed its TID");
|
|
// TODO: Use new name. This is currently not possible because the
|
|
// TLS-stored RegisteredThread's ThreadInfo cannot be changed.
|
|
// In the meantime, we record a marker that could be used in the frontend.
|
|
PROFILER_MARKER_TEXT("Nested ThreadRegistration()", OTHER_Profiling,
|
|
MarkerOptions{},
|
|
ProfilerString8View::WrapNullTerminatedString(aName));
|
|
return;
|
|
}
|
|
|
|
tls->set(this);
|
|
ThreadRegistry::Register(OnThreadRef{*this});
|
|
profiler_mark_thread_awake();
|
|
}
|
|
|
|
ThreadRegistration::~ThreadRegistration() {
|
|
MOZ_ASSERT(profiler_current_thread_id() == mData.mInfo.ThreadId(),
|
|
"ThreadRegistration must be destroyed on its thread");
|
|
MOZ_ASSERT(!mDataMutex.IsLockedOnCurrentThread(),
|
|
"Mutex shouldn't be locked here, as it's about to be destroyed "
|
|
"in ~ThreadRegistration()");
|
|
auto* tls = GetTLS();
|
|
if (MOZ_UNLIKELY(!tls)) {
|
|
// No TLS, nothing can be done without it.
|
|
return;
|
|
}
|
|
|
|
if (ThreadRegistration* rootRegistration = tls->get(); rootRegistration) {
|
|
if (rootRegistration != this) {
|
|
// `this` is not in the TLS, so it was a nested registration, there is
|
|
// nothing to unregister yet.
|
|
PROFILER_MARKER_TEXT(
|
|
"Nested ~ThreadRegistration()", OTHER_Profiling, MarkerOptions{},
|
|
ProfilerString8View::WrapNullTerminatedString(mData.Info().Name()));
|
|
return;
|
|
}
|
|
|
|
profiler_mark_thread_asleep();
|
|
#ifdef NIGHTLY_BUILD
|
|
mData.RecordWakeCount();
|
|
#endif
|
|
ThreadRegistry::Unregister(OnThreadRef{*this});
|
|
#ifdef DEBUG
|
|
// After ThreadRegistry::Unregister, other threads should not be able to
|
|
// find this ThreadRegistration, and shouldn't have kept any reference to
|
|
// it across the ThreadRegistry mutex.
|
|
MOZ_ASSERT(mDataMutex.TryLock(),
|
|
"Mutex shouldn't be locked in any thread, as it's about to be "
|
|
"destroyed in ~ThreadRegistration()");
|
|
// Undo the above successful TryLock.
|
|
mDataMutex.Unlock();
|
|
#endif // DEBUG
|
|
|
|
tls->set(nullptr);
|
|
return;
|
|
}
|
|
|
|
// Already removed from the TLS!? This could happen with improperly-nested
|
|
// register/unregister calls, and the first ThreadRegistration has already
|
|
// been unregistered.
|
|
// 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 (!profiler_is_main_thread()) {
|
|
nsAutoCString threadId("thread id: ");
|
|
threadId.AppendInt(profiler_current_thread_id().ToNumber());
|
|
threadId.AppendLiteral(", name: \"");
|
|
threadId.AppendASCII(mData.Info().Name());
|
|
threadId.AppendLiteral("\"");
|
|
PROFILER_MARKER_TEXT(
|
|
"~ThreadRegistration() but TLS is empty", OTHER_Profiling,
|
|
MarkerOptions(MarkerThreadId::MainThread(), MarkerStack::Capture()),
|
|
threadId);
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
ProfilingStack* ThreadRegistration::RegisterThread(const char* aName,
|
|
const void* aStackTop) {
|
|
auto* tls = GetTLS();
|
|
if (MOZ_UNLIKELY(!tls)) {
|
|
// No TLS, nothing can be done without it.
|
|
return nullptr;
|
|
}
|
|
|
|
if (ThreadRegistration* rootRegistration = tls->get(); rootRegistration) {
|
|
// Already registered, record the extra depth to ignore the matching
|
|
// UnregisterThread.
|
|
++rootRegistration->mOtherRegistrations;
|
|
// TODO: Use new name. This is currently not possible because the
|
|
// TLS-stored RegisteredThread's ThreadInfo cannot be changed.
|
|
// In the meantime, we record a marker that could be used in the frontend.
|
|
PROFILER_MARKER_TEXT("Nested ThreadRegistration::RegisterThread()",
|
|
OTHER_Profiling, MarkerOptions{},
|
|
ProfilerString8View::WrapNullTerminatedString(aName));
|
|
return &rootRegistration->mData.mProfilingStack;
|
|
}
|
|
|
|
// Create on heap, it self-registers with the TLS (its effective owner, so
|
|
// we can forget the pointer after this), and with the Profiler.
|
|
ThreadRegistration* tr = new ThreadRegistration(aName, aStackTop);
|
|
tr->mIsOnHeap = true;
|
|
return &tr->mData.mProfilingStack;
|
|
}
|
|
|
|
/* static */
|
|
void ThreadRegistration::UnregisterThread() {
|
|
auto* tls = GetTLS();
|
|
if (MOZ_UNLIKELY(!tls)) {
|
|
// No TLS, nothing can be done without it.
|
|
return;
|
|
}
|
|
|
|
if (ThreadRegistration* rootRegistration = tls->get(); rootRegistration) {
|
|
if (rootRegistration->mOtherRegistrations != 0) {
|
|
// This is assumed to be a matching UnregisterThread() for a nested
|
|
// RegisterThread(). Decrease depth and we're done.
|
|
--rootRegistration->mOtherRegistrations;
|
|
// We don't know what name was used in the related RegisterThread().
|
|
PROFILER_MARKER_UNTYPED("Nested ThreadRegistration::UnregisterThread()",
|
|
OTHER_Profiling);
|
|
return;
|
|
}
|
|
|
|
if (!rootRegistration->mIsOnHeap) {
|
|
// The root registration was not added by `RegisterThread()`, so it
|
|
// shouldn't be deleted!
|
|
// This could happen if there are un-paired `UnregisterThread` calls when
|
|
// the initial registration (still alive) was done on the stack. We don't
|
|
// know what name was used in the related RegisterThread().
|
|
PROFILER_MARKER_UNTYPED("Excess ThreadRegistration::UnregisterThread()",
|
|
OTHER_Profiling, MarkerStack::Capture());
|
|
return;
|
|
}
|
|
|
|
// This is the last `UnregisterThread()` that should match the first
|
|
// `RegisterThread()` that created this ThreadRegistration on the heap.
|
|
// Just delete this root registration, it will de-register itself from the
|
|
// TLS (and from the Profiler).
|
|
delete rootRegistration;
|
|
return;
|
|
}
|
|
|
|
// There is no known ThreadRegistration for this thread, ignore this
|
|
// request. 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 (!profiler_is_main_thread()) {
|
|
nsAutoCString threadId("thread id: ");
|
|
threadId.AppendInt(profiler_current_thread_id().ToNumber());
|
|
PROFILER_MARKER_TEXT(
|
|
"ThreadRegistration::UnregisterThread() but TLS is empty",
|
|
OTHER_Profiling,
|
|
MarkerOptions(MarkerThreadId::MainThread(), MarkerStack::Capture()),
|
|
threadId);
|
|
}
|
|
}
|
|
|
|
} // namespace mozilla::profiler
|