mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 12:20:56 +00:00
Bug 1756505 - Remove the ThreadIdNameMapping annotation and all the associated machinery r=Gankra
This has several implications: * A race-condition that could manifest itself both on macOS and Linux is removed. The race could happen if we took the lock to the thread name mapping while another suspended thread was in the memory allocator. Taking the lock required an allocation thus the thread acting within the exception handler would get stuck. * We save a few KiBs of memory per process since we don't have to keep the thread name mapping around. * Thread startup will be slightly faster since the first thing each new thread did was to fill its own mapping. Differential Revision: https://phabricator.services.mozilla.com/D139319
This commit is contained in:
parent
b0e316f75c
commit
8a17aeccc2
@ -87,7 +87,6 @@ def minidump_files(request, tmpdir):
|
||||
"Add-ons":"",
|
||||
"CrashTime":"1494582646",
|
||||
"UptimeTS":"14.9179586",
|
||||
"ThreadIdNameMapping":"",
|
||||
"ContentSandboxEnabled":"1",
|
||||
"ProcessType":"content",
|
||||
"StartupTime":"1000000000",
|
||||
|
@ -1 +1 @@
|
||||
{"ContentSandboxLevel":"2","TelemetryEnvironment":"{\"EscapedField\":\"EscapedData\\n\\nfoo\"}","EMCheckCompatibility":"true","ProductName":"Firefox","ContentSandboxCapabilities":"119","TelemetryClientId":"","Vendor":"Mozilla","InstallTime":"1000000000","Theme":"classic/1.0","ReleaseChannel":"default","ServerURL":"https://crash-reports.mozilla.com","SafeMode":"0","ContentSandboxCapable":"1","useragent_locale":"en-US","Version":"55.0a1","BuildID":"20170512114708","ProductID":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","TelemetryServerURL":"","DOMIPCEnabled":"1","Add-ons":"","CrashTime":"1494582646","UptimeTS":"14.9179586","ThreadIdNameMapping":"","ContentSandboxEnabled":"1","ProcessType":"content","StartupTime":"1000000000","URL":"about:home"}
|
||||
{"ContentSandboxLevel":"2","TelemetryEnvironment":"{\"EscapedField\":\"EscapedData\\n\\nfoo\"}","EMCheckCompatibility":"true","ProductName":"Firefox","ContentSandboxCapabilities":"119","TelemetryClientId":"","Vendor":"Mozilla","InstallTime":"1000000000","Theme":"classic/1.0","ReleaseChannel":"default","ServerURL":"https://crash-reports.mozilla.com","SafeMode":"0","ContentSandboxCapable":"1","useragent_locale":"en-US","Version":"55.0a1","BuildID":"20170512114708","ProductID":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}","TelemetryServerURL":"","DOMIPCEnabled":"1","Add-ons":"","CrashTime":"1494582646","UptimeTS":"14.9179586","ContentSandboxEnabled":"1","ProcessType":"content","StartupTime":"1000000000","URL":"about:home"}
|
||||
|
@ -891,11 +891,6 @@ TextureUsage:
|
||||
type: string
|
||||
ping: true
|
||||
|
||||
ThreadIdNameMapping:
|
||||
description: >
|
||||
List of thread names with their corresponding thread IDs.
|
||||
type: string
|
||||
|
||||
TotalPageFile:
|
||||
description: >
|
||||
Maximum amount of memory that can be committed without extending the swap/page file.
|
||||
|
@ -1,293 +0,0 @@
|
||||
/* 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 "ThreadAnnotation.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "prthread.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsExceptionHandler.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
using mozilla::StaticMutex;
|
||||
using mozilla::StaticMutexAutoLock;
|
||||
using mozilla::UniquePtr;
|
||||
|
||||
namespace CrashReporter {
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
/*
|
||||
* On the Mac, exception handler callbacks are invoked in a context where all
|
||||
* other threads are paused. As a result, attempting to acquire a mutex is
|
||||
* problematic because 1) the mutex may be held by another thread which is
|
||||
* now suspended and 2) acquiring an unheld mutex can trigger memory allocation
|
||||
* which generally requires allocator locks. This class is a wrapper around a
|
||||
* StaticMutex, providing an IsLocked() method which only makes sense to use
|
||||
* in the Mac exception handling context when other threads are paused.
|
||||
*/
|
||||
class MacCrashReporterLock {
|
||||
public:
|
||||
void Lock() {
|
||||
sInnerMutex.Lock();
|
||||
sIsLocked = true;
|
||||
}
|
||||
void Unlock() {
|
||||
sIsLocked = false;
|
||||
sInnerMutex.Unlock();
|
||||
}
|
||||
/*
|
||||
* Returns true if the lock is held at the time the method is called.
|
||||
* The return value is out-of-date by the time this method returns unless
|
||||
* we have a guarantee that other threads are not running such as in Mac
|
||||
* breadkpad exception handler context.
|
||||
*/
|
||||
bool IsLocked() { return sIsLocked; }
|
||||
void AssertCurrentThreadOwns() { sInnerMutex.AssertCurrentThreadOwns(); }
|
||||
|
||||
private:
|
||||
static StaticMutex sInnerMutex;
|
||||
static bool sIsLocked;
|
||||
};
|
||||
StaticMutex MacCrashReporterLock::sInnerMutex;
|
||||
bool MacCrashReporterLock::sIsLocked;
|
||||
|
||||
// Use MacCrashReporterLock for locking
|
||||
typedef mozilla::detail::BaseAutoLock<MacCrashReporterLock&>
|
||||
CrashReporterAutoLock;
|
||||
typedef MacCrashReporterLock CrashReporterLockType;
|
||||
#else /* !XP_MACOSX */
|
||||
// Use StaticMutex for locking
|
||||
typedef StaticMutexAutoLock CrashReporterAutoLock;
|
||||
typedef StaticMutex CrashReporterLockType;
|
||||
#endif /* XP_MACOSX */
|
||||
|
||||
// Protects access to sInitialized and sThreadAnnotations.
|
||||
static CrashReporterLockType sMutex;
|
||||
|
||||
class ThreadAnnotationSpan {
|
||||
public:
|
||||
ThreadAnnotationSpan(uint32_t aBegin, uint32_t aEnd)
|
||||
: mBegin(aBegin), mEnd(aEnd) {
|
||||
MOZ_ASSERT(mBegin < mEnd);
|
||||
}
|
||||
|
||||
~ThreadAnnotationSpan();
|
||||
|
||||
class Comparator {
|
||||
public:
|
||||
bool Equals(const ThreadAnnotationSpan* const& a,
|
||||
const ThreadAnnotationSpan* const& b) const {
|
||||
return a->mBegin == b->mBegin;
|
||||
}
|
||||
|
||||
bool LessThan(const ThreadAnnotationSpan* const& a,
|
||||
const ThreadAnnotationSpan* const& b) const {
|
||||
return a->mBegin < b->mBegin;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
// ~ThreadAnnotationSpan() does nontrivial thing. Make sure we don't
|
||||
// instantiate accidentally.
|
||||
ThreadAnnotationSpan(const ThreadAnnotationSpan& aOther) = delete;
|
||||
ThreadAnnotationSpan& operator=(const ThreadAnnotationSpan& aOther) = delete;
|
||||
|
||||
friend class ThreadAnnotationData;
|
||||
friend class Comparator;
|
||||
|
||||
uint32_t mBegin;
|
||||
uint32_t mEnd;
|
||||
};
|
||||
|
||||
// This class keeps the flat version of thread annotations for each thread.
|
||||
// When a thread calls CrashReporter::SetCurrentThreadName(), it adds
|
||||
// information about the calling thread (thread id and name) to this class.
|
||||
// When crash happens, the crash reporter gets flat representation and add to
|
||||
// the crash annotation file.
|
||||
class ThreadAnnotationData {
|
||||
public:
|
||||
ThreadAnnotationData() = default;
|
||||
|
||||
~ThreadAnnotationData() = default;
|
||||
|
||||
// Adds <pre> tid:"thread name",</pre> annotation to the current annotations.
|
||||
// Returns an instance of ThreadAnnotationSpan for cleanup on thread
|
||||
// termination.
|
||||
ThreadAnnotationSpan* AddThreadAnnotation(ThreadId aTid,
|
||||
const char* aThreadName) {
|
||||
if (!aTid || !aThreadName) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t oldLength = mData.Length();
|
||||
mData.AppendPrintf("%u:\"%s\",", aTid, aThreadName);
|
||||
uint32_t newLength = mData.Length();
|
||||
|
||||
ThreadAnnotationSpan* rv = new ThreadAnnotationSpan(oldLength, newLength);
|
||||
mDataSpans.AppendElement(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Called on thread termination. Removes the thread annotation, represented as
|
||||
// ThreadAnnotationSpan, from the flat representation.
|
||||
void EraseThreadAnnotation(const ThreadAnnotationSpan& aThreadInfo) {
|
||||
uint32_t begin = aThreadInfo.mBegin;
|
||||
uint32_t end = aThreadInfo.mEnd;
|
||||
|
||||
if (!(begin < end && end <= mData.Length())) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t cutLength = end - begin;
|
||||
mData.Cut(begin, cutLength);
|
||||
|
||||
// Adjust the ThreadAnnotationSpan affected by data shifting.
|
||||
size_t index = mDataSpans.BinaryIndexOf(&aThreadInfo,
|
||||
ThreadAnnotationSpan::Comparator());
|
||||
for (size_t i = index + 1; i < mDataSpans.Length(); i++) {
|
||||
ThreadAnnotationSpan* elem = mDataSpans[i];
|
||||
|
||||
MOZ_ASSERT(elem->mBegin >= cutLength);
|
||||
MOZ_ASSERT(elem->mEnd > cutLength);
|
||||
|
||||
elem->mBegin -= cutLength;
|
||||
elem->mEnd -= cutLength;
|
||||
}
|
||||
|
||||
// No loner tracking aThreadInfo.
|
||||
mDataSpans.RemoveElementAt(index);
|
||||
}
|
||||
|
||||
// Gets the flat representation of thread annotations.
|
||||
void GetData(const std::function<void(const char*)>& aCallback) {
|
||||
aCallback(mData.BeginReading());
|
||||
}
|
||||
|
||||
private:
|
||||
// The flat representation of thread annotations.
|
||||
nsCString mData;
|
||||
|
||||
// This array tracks the created ThreadAnnotationSpan instances so that we
|
||||
// can make adjustments accordingly when we cut substrings from mData on
|
||||
// thread exit.
|
||||
nsTArray<ThreadAnnotationSpan*> mDataSpans;
|
||||
};
|
||||
|
||||
static bool sInitialized = false;
|
||||
static UniquePtr<ThreadAnnotationData> sThreadAnnotations;
|
||||
|
||||
static unsigned sTLSThreadInfoKey = (unsigned)-1;
|
||||
void ThreadLocalDestructor(void* aUserData) {
|
||||
MOZ_ASSERT(aUserData);
|
||||
|
||||
CrashReporterAutoLock lock(sMutex);
|
||||
|
||||
ThreadAnnotationSpan* aThreadInfo =
|
||||
static_cast<ThreadAnnotationSpan*>(aUserData);
|
||||
delete aThreadInfo;
|
||||
}
|
||||
|
||||
// This is called on thread termination.
|
||||
ThreadAnnotationSpan::~ThreadAnnotationSpan() {
|
||||
// Note that we can't lock the mutex here because this function may be called
|
||||
// from SetCurrentThreadName().
|
||||
sMutex.AssertCurrentThreadOwns();
|
||||
|
||||
if (sThreadAnnotations) {
|
||||
sThreadAnnotations->EraseThreadAnnotation(*this);
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace.
|
||||
|
||||
void InitThreadAnnotation() {
|
||||
CrashReporterAutoLock lock(sMutex);
|
||||
|
||||
if (sInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
PRStatus status =
|
||||
PR_NewThreadPrivateIndex(&sTLSThreadInfoKey, &ThreadLocalDestructor);
|
||||
if (status == PR_FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
sInitialized = true;
|
||||
|
||||
sThreadAnnotations = mozilla::MakeUnique<ThreadAnnotationData>();
|
||||
}
|
||||
|
||||
void SetCurrentThreadName(const char* aName) {
|
||||
if (PR_GetThreadPrivate(sTLSThreadInfoKey)) {
|
||||
// Explicitly set TLS value to null (and call the dtor function ) before
|
||||
// acquiring sMutex to avoid reentrant deadlock.
|
||||
PR_SetThreadPrivate(sTLSThreadInfoKey, nullptr);
|
||||
}
|
||||
|
||||
CrashReporterAutoLock lock(sMutex);
|
||||
|
||||
if (!sInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadAnnotationSpan* threadInfo =
|
||||
sThreadAnnotations->AddThreadAnnotation(CurrentThreadId(), aName);
|
||||
// This may destroy the old insatnce.
|
||||
PR_SetThreadPrivate(sTLSThreadInfoKey, threadInfo);
|
||||
}
|
||||
|
||||
void GetFlatThreadAnnotation(const std::function<void(const char*)>& aCallback,
|
||||
bool aIsHandlingException) {
|
||||
bool lockNeeded = !aIsHandlingException;
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
if (aIsHandlingException) {
|
||||
// Don't acquire the lock on Mac because we are
|
||||
// executing in exception context where all other
|
||||
// threads are paused. If the lock is held, skip
|
||||
// thread annotations to avoid deadlock caused by
|
||||
// waiting for a suspended thread. If the lock
|
||||
// isn't held, acquiring it serves no purpose and
|
||||
// can trigger memory allocations.
|
||||
if (sMutex.IsLocked()) {
|
||||
aCallback("");
|
||||
return;
|
||||
}
|
||||
lockNeeded = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lockNeeded) {
|
||||
sMutex.Lock();
|
||||
}
|
||||
|
||||
if (sThreadAnnotations) {
|
||||
sThreadAnnotations->GetData(aCallback);
|
||||
} else {
|
||||
// Maybe already shutdown: call aCallback with empty annotation data.
|
||||
aCallback("");
|
||||
}
|
||||
|
||||
if (lockNeeded) {
|
||||
sMutex.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void ShutdownThreadAnnotation() {
|
||||
CrashReporterAutoLock lock(sMutex);
|
||||
|
||||
sInitialized = false;
|
||||
sThreadAnnotations.reset();
|
||||
}
|
||||
|
||||
} // namespace CrashReporter
|
@ -1,24 +0,0 @@
|
||||
/* 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 ThreadAnnotation_h
|
||||
#define ThreadAnnotation_h
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "nsExceptionHandler.h"
|
||||
|
||||
// Thread annotation interfaces for the crash reporter.
|
||||
namespace CrashReporter {
|
||||
|
||||
void InitThreadAnnotation();
|
||||
|
||||
void ShutdownThreadAnnotation();
|
||||
|
||||
void GetFlatThreadAnnotation(const std::function<void(const char*)>& aCallback,
|
||||
bool aIsHandlingException = false);
|
||||
|
||||
} // namespace CrashReporter
|
||||
|
||||
#endif
|
@ -73,14 +73,10 @@ if CONFIG["MOZ_CRASHREPORTER"]:
|
||||
"LoadLibraryRemote.cpp",
|
||||
]
|
||||
|
||||
if CONFIG["ENABLE_TESTS"]:
|
||||
DIRS += ["test/gtest"]
|
||||
|
||||
TEST_DIRS += ["test"]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
"nsExceptionHandler.cpp",
|
||||
"ThreadAnnotation.cpp",
|
||||
]
|
||||
|
||||
if CONFIG["OS_ARCH"] == "Darwin":
|
||||
|
@ -260,15 +260,4 @@ void AddLibraryMapping(const char* library_name, uintptr_t start_address,
|
||||
size_t mapping_length, size_t file_offset) {}
|
||||
#endif
|
||||
|
||||
// From ThreadAnnotation.cpp
|
||||
|
||||
void InitThreadAnnotation() {}
|
||||
|
||||
void SetCurrentThreadName(const char* aName) {}
|
||||
|
||||
void GetFlatThreadAnnotation(
|
||||
const std::function<void(const char*)>& aCallback) {}
|
||||
|
||||
void ShutdownThreadAnnotation() {}
|
||||
|
||||
} // namespace CrashReporter
|
||||
|
@ -30,7 +30,6 @@
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsThread.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "ThreadAnnotation.h"
|
||||
#include "private/pprio.h"
|
||||
#include "base/process_util.h"
|
||||
#include "common/basictypes.h"
|
||||
@ -1379,15 +1378,6 @@ static void WriteAnnotationsForMainProcessCrash(PlatformWriter& pw,
|
||||
#ifdef MOZ_PHC
|
||||
WritePHCAddrInfo(writer, addrInfo);
|
||||
#endif
|
||||
|
||||
std::function<void(const char*)> getThreadAnnotationCB =
|
||||
[&](const char* aValue) -> void {
|
||||
if (aValue) {
|
||||
writer.Write(Annotation::ThreadIdNameMapping, aValue);
|
||||
}
|
||||
};
|
||||
GetFlatThreadAnnotation(getThreadAnnotationCB,
|
||||
/* aIsHandlingException */ true);
|
||||
}
|
||||
|
||||
static void WriteCrashEventFile(time_t crashTime, const char* crashTimeString,
|
||||
@ -1656,15 +1646,6 @@ static void PrepareChildExceptionTimeAnnotations(
|
||||
WritePHCAddrInfo(writer, addrInfo);
|
||||
#endif
|
||||
|
||||
std::function<void(const char*)> getThreadAnnotationCB =
|
||||
[&](const char* aValue) -> void {
|
||||
if (aValue) {
|
||||
writer.Write(Annotation::ThreadIdNameMapping, aValue);
|
||||
}
|
||||
};
|
||||
GetFlatThreadAnnotation(getThreadAnnotationCB,
|
||||
/* aIsHandlingException */ true);
|
||||
|
||||
WriteAnnotations(writer, crashReporterAPIData_Table);
|
||||
}
|
||||
|
||||
@ -1907,7 +1888,6 @@ static void InitializeAnnotationFacilities() {
|
||||
crashReporterAPILock = new Mutex("crashReporterAPILock");
|
||||
notesFieldLock = new Mutex("notesFieldLock");
|
||||
notesField = new nsCString();
|
||||
InitThreadAnnotation();
|
||||
if (!XRE_IsParentProcess()) {
|
||||
InitChildAnnotationsFlusher();
|
||||
}
|
||||
@ -1925,8 +1905,6 @@ static void TeardownAnnotationFacilities() {
|
||||
|
||||
delete notesField;
|
||||
notesField = nullptr;
|
||||
|
||||
ShutdownThreadAnnotation();
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
@ -344,9 +344,6 @@ void SetNotificationPipeForChild(FileHandle childCrashFd);
|
||||
void SetCrashAnnotationPipeForChild(FileHandle childCrashAnnotationFd);
|
||||
#endif
|
||||
|
||||
// Annotates the crash report with the name of the calling thread.
|
||||
void SetCurrentThreadName(const char* aName);
|
||||
|
||||
} // namespace CrashReporter
|
||||
|
||||
#endif /* nsExceptionHandler_h__ */
|
||||
|
@ -1,362 +0,0 @@
|
||||
/* 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 "ThreadAnnotation.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "base/thread.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
using mozilla::Monitor;
|
||||
using mozilla::MonitorAutoLock;
|
||||
using mozilla::UniquePtr;
|
||||
|
||||
namespace CrashReporter {
|
||||
namespace {
|
||||
|
||||
TEST(TestCrashThreadAnnotation, TestInitShutdown)
|
||||
{
|
||||
InitThreadAnnotation();
|
||||
ShutdownThreadAnnotation();
|
||||
}
|
||||
|
||||
TEST(TestCrashThreadAnnotation, TestNestedInitShutdown)
|
||||
{
|
||||
// No bad things should happen in case we have extra init/shutdown calls.
|
||||
InitThreadAnnotation();
|
||||
InitThreadAnnotation();
|
||||
ShutdownThreadAnnotation();
|
||||
ShutdownThreadAnnotation();
|
||||
}
|
||||
|
||||
TEST(TestCrashThreadAnnotation, TestUnbalancedInit)
|
||||
{
|
||||
// No bad things should happen in case we have unbalanced init/shutdown calls.
|
||||
InitThreadAnnotation();
|
||||
InitThreadAnnotation();
|
||||
ShutdownThreadAnnotation();
|
||||
}
|
||||
|
||||
TEST(TestCrashThreadAnnotation, TestUnbalancedShutdown)
|
||||
{
|
||||
// No bad things should happen in case we have unbalanced init/shutdown calls.
|
||||
InitThreadAnnotation();
|
||||
ShutdownThreadAnnotation();
|
||||
ShutdownThreadAnnotation();
|
||||
}
|
||||
|
||||
TEST(TestCrashThreadAnnotation, TestGetFlatThreadAnnotation_BeforeInit)
|
||||
{
|
||||
// GetFlatThreadAnnotation() should not return anything before init.
|
||||
std::function<void(const char*)> getThreadAnnotationCB =
|
||||
[&](const char* aAnnotation) -> void { ASSERT_STREQ(aAnnotation, ""); };
|
||||
GetFlatThreadAnnotation(getThreadAnnotationCB);
|
||||
}
|
||||
|
||||
TEST(TestCrashThreadAnnotation, TestGetFlatThreadAnnotation_AfterShutdown)
|
||||
{
|
||||
// GetFlatThreadAnnotation() should not return anything after shutdown.
|
||||
InitThreadAnnotation();
|
||||
ShutdownThreadAnnotation();
|
||||
|
||||
std::function<void(const char*)> getThreadAnnotationCB =
|
||||
[&](const char* aAnnotation) -> void { ASSERT_STREQ(aAnnotation, ""); };
|
||||
GetFlatThreadAnnotation(getThreadAnnotationCB);
|
||||
}
|
||||
|
||||
already_AddRefed<nsIThread> CreateTestThread(const char* aName,
|
||||
Monitor& aMonitor, bool& aDone) {
|
||||
nsCOMPtr<nsIRunnable> setNameRunnable = NS_NewRunnableFunction(
|
||||
"CrashReporter::CreateTestThread", [aName, &aMonitor, &aDone]() -> void {
|
||||
NS_SetCurrentThreadName(aName);
|
||||
|
||||
MonitorAutoLock lock(aMonitor);
|
||||
aDone = true;
|
||||
aMonitor.NotifyAll();
|
||||
});
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
mozilla::Unused << NS_NewNamedThread("Test Thread", getter_AddRefs(thread),
|
||||
setNameRunnable);
|
||||
|
||||
return thread.forget();
|
||||
}
|
||||
|
||||
TEST(TestCrashThreadAnnotation, TestGetFlatThreadAnnotation_OneThread)
|
||||
{
|
||||
InitThreadAnnotation();
|
||||
|
||||
Monitor monitor("TestCrashThreadAnnotation");
|
||||
bool threadNameSet = false;
|
||||
nsCOMPtr<nsIThread> thread =
|
||||
CreateTestThread("Thread1", monitor, threadNameSet);
|
||||
ASSERT_TRUE(!!thread);
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(monitor);
|
||||
while (!threadNameSet) {
|
||||
monitor.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
std::function<void(const char*)> getThreadAnnotationCB =
|
||||
[&](const char* aAnnotation) -> void {
|
||||
ASSERT_TRUE(!!strstr(aAnnotation, "Thread1"));
|
||||
};
|
||||
GetFlatThreadAnnotation(getThreadAnnotationCB);
|
||||
|
||||
ShutdownThreadAnnotation();
|
||||
|
||||
thread->Shutdown();
|
||||
}
|
||||
|
||||
TEST(TestCrashThreadAnnotation, TestGetFlatThreadAnnotation_SetNameTwice)
|
||||
{
|
||||
InitThreadAnnotation();
|
||||
|
||||
Monitor monitor("TestCrashThreadAnnotation");
|
||||
bool threadNameSet = false;
|
||||
|
||||
nsCOMPtr<nsIRunnable> setNameRunnable = NS_NewRunnableFunction(
|
||||
"CrashReporter::TestCrashThreadAnnotation_TestGetFlatThreadAnnotation_"
|
||||
"SetNameTwice_Test::TestBody",
|
||||
[&]() -> void {
|
||||
NS_SetCurrentThreadName("Thread1");
|
||||
// Set the name again. We should get the latest name.
|
||||
NS_SetCurrentThreadName("Thread1Again");
|
||||
|
||||
MonitorAutoLock lock(monitor);
|
||||
threadNameSet = true;
|
||||
monitor.NotifyAll();
|
||||
});
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
nsresult rv =
|
||||
NS_NewNamedThread("Test Thread", getter_AddRefs(thread), setNameRunnable);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(monitor);
|
||||
while (!threadNameSet) {
|
||||
monitor.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
std::function<void(const char*)> getThreadAnnotationCB =
|
||||
[&](const char* aAnnotation) -> void {
|
||||
ASSERT_TRUE(!!strstr(aAnnotation, "Thread1Again"));
|
||||
};
|
||||
GetFlatThreadAnnotation(getThreadAnnotationCB);
|
||||
|
||||
ShutdownThreadAnnotation();
|
||||
|
||||
thread->Shutdown();
|
||||
}
|
||||
|
||||
TEST(TestCrashThreadAnnotation, TestGetFlatThreadAnnotation_TwoThreads)
|
||||
{
|
||||
InitThreadAnnotation();
|
||||
|
||||
Monitor monitor("TestCrashThreadAnnotation");
|
||||
bool thread1NameSet = false;
|
||||
bool thread2NameSet = false;
|
||||
|
||||
nsCOMPtr<nsIThread> thread1 =
|
||||
CreateTestThread("Thread1", monitor, thread1NameSet);
|
||||
ASSERT_TRUE(!!thread1);
|
||||
|
||||
nsCOMPtr<nsIThread> thread2 =
|
||||
CreateTestThread("Thread2", monitor, thread2NameSet);
|
||||
ASSERT_TRUE(!!thread2);
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(monitor);
|
||||
while (!(thread1NameSet && thread2NameSet)) {
|
||||
monitor.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
std::function<void(const char*)> getThreadAnnotationCB =
|
||||
[&](const char* aAnnotation) -> void {
|
||||
// Assert that Thread1 and Thread2 are both in the annotation data.
|
||||
ASSERT_TRUE(!!strstr(aAnnotation, "Thread1"));
|
||||
ASSERT_TRUE(!!strstr(aAnnotation, "Thread2"));
|
||||
};
|
||||
GetFlatThreadAnnotation(getThreadAnnotationCB);
|
||||
|
||||
ShutdownThreadAnnotation();
|
||||
|
||||
thread1->Shutdown();
|
||||
thread2->Shutdown();
|
||||
}
|
||||
|
||||
TEST(TestCrashThreadAnnotation, TestGetFlatThreadAnnotation_ShutdownOneThread)
|
||||
{
|
||||
InitThreadAnnotation();
|
||||
|
||||
Monitor monitor("TestCrashThreadAnnotation");
|
||||
bool thread1NameSet = false;
|
||||
bool thread2NameSet = false;
|
||||
|
||||
nsCOMPtr<nsIThread> thread1 =
|
||||
CreateTestThread("Thread1", monitor, thread1NameSet);
|
||||
ASSERT_TRUE(!!thread1);
|
||||
|
||||
nsCOMPtr<nsIThread> thread2 =
|
||||
CreateTestThread("Thread2", monitor, thread2NameSet);
|
||||
ASSERT_TRUE(!!thread2);
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(monitor);
|
||||
while (!(thread1NameSet && thread2NameSet)) {
|
||||
monitor.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = thread1->Shutdown();
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
std::function<void(const char*)> getThreadAnnotationCB =
|
||||
[&](const char* aAnnotation) -> void {
|
||||
// Assert that only Thread2 is present in the annotation data.
|
||||
ASSERT_TRUE(!strstr(aAnnotation, "Thread1"));
|
||||
ASSERT_TRUE(!!strstr(aAnnotation, "Thread2"));
|
||||
};
|
||||
GetFlatThreadAnnotation(getThreadAnnotationCB);
|
||||
|
||||
ShutdownThreadAnnotation();
|
||||
|
||||
thread2->Shutdown();
|
||||
}
|
||||
|
||||
TEST(TestCrashThreadAnnotation, TestGetFlatThreadAnnotation_ShutdownBothThreads)
|
||||
{
|
||||
InitThreadAnnotation();
|
||||
|
||||
Monitor monitor("TestCrashThreadAnnotation");
|
||||
bool thread1NameSet = false;
|
||||
bool thread2NameSet = false;
|
||||
|
||||
nsCOMPtr<nsIThread> thread1 =
|
||||
CreateTestThread("Thread1", monitor, thread1NameSet);
|
||||
ASSERT_TRUE(!!thread1);
|
||||
|
||||
nsCOMPtr<nsIThread> thread2 =
|
||||
CreateTestThread("Thread2", monitor, thread2NameSet);
|
||||
ASSERT_TRUE(!!thread2);
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(monitor);
|
||||
while (!(thread1NameSet && thread2NameSet)) {
|
||||
monitor.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = thread1->Shutdown();
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
rv = thread2->Shutdown();
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
std::function<void(const char*)> getThreadAnnotationCB =
|
||||
[&](const char* aAnnotation) -> void {
|
||||
// No leftover in annnotation data.
|
||||
ASSERT_STREQ(aAnnotation, "");
|
||||
};
|
||||
GetFlatThreadAnnotation(getThreadAnnotationCB);
|
||||
|
||||
ShutdownThreadAnnotation();
|
||||
}
|
||||
|
||||
TEST(TestCrashThreadAnnotation,
|
||||
TestGetFlatThreadAnnotation_TestNameOfBaseThread)
|
||||
{
|
||||
InitThreadAnnotation();
|
||||
|
||||
Monitor monitor("TestCrashThreadAnnotation");
|
||||
|
||||
UniquePtr<base::Thread> thread1 =
|
||||
mozilla::MakeUnique<base::Thread>("base thread 1");
|
||||
ASSERT_TRUE(!!thread1 && thread1->Start());
|
||||
|
||||
UniquePtr<base::Thread> thread2 =
|
||||
mozilla::MakeUnique<base::Thread>("base thread 2");
|
||||
ASSERT_TRUE(!!thread2 && thread2->Start());
|
||||
|
||||
std::function<void(const char*)> getThreadAnnotationCB =
|
||||
[&](const char* aAnnotation) -> void {
|
||||
ASSERT_TRUE(!!strstr(aAnnotation, "base thread 1"));
|
||||
ASSERT_TRUE(!!strstr(aAnnotation, "base thread 2"));
|
||||
};
|
||||
GetFlatThreadAnnotation(getThreadAnnotationCB);
|
||||
|
||||
thread1->Stop();
|
||||
thread2->Stop();
|
||||
|
||||
ShutdownThreadAnnotation();
|
||||
}
|
||||
|
||||
TEST(TestCrashThreadAnnotation,
|
||||
TestGetFlatThreadAnnotation_TestShutdownBaseThread)
|
||||
{
|
||||
InitThreadAnnotation();
|
||||
|
||||
Monitor monitor("TestCrashThreadAnnotation");
|
||||
|
||||
UniquePtr<base::Thread> thread1 =
|
||||
mozilla::MakeUnique<base::Thread>("base thread 1");
|
||||
ASSERT_TRUE(!!thread1 && thread1->Start());
|
||||
|
||||
UniquePtr<base::Thread> thread2 =
|
||||
mozilla::MakeUnique<base::Thread>("base thread 2");
|
||||
ASSERT_TRUE(!!thread2 && thread2->Start());
|
||||
|
||||
thread1->Stop();
|
||||
|
||||
std::function<void(const char*)> getThreadAnnotationCB =
|
||||
[&](const char* aAnnotation) -> void {
|
||||
ASSERT_TRUE(!strstr(aAnnotation, "base thread 1"));
|
||||
ASSERT_TRUE(!!strstr(aAnnotation, "base thread 2"));
|
||||
};
|
||||
GetFlatThreadAnnotation(getThreadAnnotationCB);
|
||||
|
||||
thread2->Stop();
|
||||
|
||||
ShutdownThreadAnnotation();
|
||||
}
|
||||
|
||||
TEST(TestCrashThreadAnnotation,
|
||||
TestGetFlatThreadAnnotation_TestShutdownBothBaseThreads)
|
||||
{
|
||||
InitThreadAnnotation();
|
||||
|
||||
Monitor monitor("TestCrashThreadAnnotation");
|
||||
|
||||
UniquePtr<base::Thread> thread1 =
|
||||
mozilla::MakeUnique<base::Thread>("base thread 1");
|
||||
ASSERT_TRUE(!!thread1 && thread1->Start());
|
||||
|
||||
UniquePtr<base::Thread> thread2 =
|
||||
mozilla::MakeUnique<base::Thread>("base thread 2");
|
||||
ASSERT_TRUE(!!thread2 && thread2->Start());
|
||||
|
||||
thread1->Stop();
|
||||
thread2->Stop();
|
||||
|
||||
std::function<void(const char*)> getThreadAnnotationCB =
|
||||
[&](const char* aAnnotation) -> void {
|
||||
ASSERT_TRUE(!strlen(aAnnotation));
|
||||
};
|
||||
GetFlatThreadAnnotation(getThreadAnnotationCB);
|
||||
|
||||
ShutdownThreadAnnotation();
|
||||
}
|
||||
|
||||
} // Anonymous namespace.
|
||||
} // namespace CrashReporter
|
@ -1,15 +0,0 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
"TestCrashThreadAnnotation.cpp",
|
||||
]
|
||||
|
||||
include("/ipc/chromium/chromium-config.mozbuild")
|
||||
|
||||
LOCAL_INCLUDES += ["../../"]
|
||||
|
||||
FINAL_LIBRARY = "xul-gtest"
|
@ -1,18 +0,0 @@
|
||||
add_task(async function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_crash_thread_annotation.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_INVALID_POINTER_DEREF;
|
||||
},
|
||||
function(mdump, extra) {
|
||||
Assert.ok("ThreadIdNameMapping" in extra);
|
||||
},
|
||||
true
|
||||
);
|
||||
});
|
@ -23,8 +23,6 @@ skip-if = os == 'win'
|
||||
|
||||
[test_crash_uncaught_exception.js]
|
||||
|
||||
[test_crash_thread_annotation.js]
|
||||
|
||||
[test_crash_with_memory_report.js]
|
||||
[test_crashreporter.js]
|
||||
[test_crashreporter_crash.js]
|
||||
|
@ -479,7 +479,6 @@ void NS_SetCurrentThreadName(const char* aName) {
|
||||
nsThread* thread = nsThreadManager::get().GetCurrentThread();
|
||||
thread->SetThreadNameInternal(nsDependentCString(aName));
|
||||
}
|
||||
CrashReporter::SetCurrentThreadName(aName);
|
||||
}
|
||||
|
||||
nsIThread* NS_GetCurrentThread() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user