Backed out 2 changesets (bug 1756505) for causing hazard failures. CLOSED TREE

Backed out changeset 3622bb57fb67 (bug 1756505)
Backed out changeset d0e31f4a0ee1 (bug 1756505)
This commit is contained in:
Butkovits Atila 2022-03-04 00:10:46 +02:00
Родитель cc1a64f238
Коммит f71a8aad71
20 изменённых файлов: 763 добавлений и 99 удалений

Просмотреть файл

@ -87,6 +87,7 @@ 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","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","ThreadIdNameMapping":"","ContentSandboxEnabled":"1","ProcessType":"content","StartupTime":"1000000000","URL":"about:home"}

Просмотреть файл

@ -891,6 +891,11 @@ 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.

Просмотреть файл

@ -0,0 +1,293 @@
/* 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

Просмотреть файл

@ -0,0 +1,24 @@
/* 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

Просмотреть файл

@ -119,12 +119,6 @@ bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
return true;
}
bool LinuxCoreDumper::GetThreadNameByIndex(size_t index, char* name,
size_t size) {
// Not implemented
return false;
}
bool LinuxCoreDumper::IsPostMortem() const {
return true;
}

Просмотреть файл

@ -80,11 +80,6 @@ class LinuxCoreDumper : public LinuxDumper {
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
// Implements LinuxDumper::GetThreadNameByIndex().
// Reads the name of the |index|-th thread of |threads_|.
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadNameByIndex(size_t index, char* info, size_t size);
// Implements LinuxDumper::IsPostMortem().
// Always returns true to indicate that this dumper performs a
// post-mortem dump of a crashed process via a core dump file.

Просмотреть файл

@ -102,10 +102,6 @@ class LinuxDumper {
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0;
// Read the name ofthe |index|-th thread of |threads_|.
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadNameByIndex(size_t index, char* name, size_t size) = 0;
size_t GetMainThreadIndex() const {
for (size_t i = 0; i < threads_.size(); ++i) {
if (threads_[i] == pid_) return i;

Просмотреть файл

@ -306,33 +306,6 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
return true;
}
bool LinuxPtraceDumper::GetThreadNameByIndex(size_t index, char* name,
size_t size) {
if (index >= threads_.size())
return false;
pid_t tid = threads_[index];
assert(name != NULL);
char path[NAME_MAX];
// Read the thread name (aka comm entry in /proc)
if (!BuildProcPath(path, tid, "comm"))
return false;
const int fd = sys_open(path, O_RDONLY, 0);
if (fd < 0)
return false;
const int len = sys_read(fd, name, size);
if (len > 0)
name[len - 1] = '\0'; // Get rid of the newline
sys_close(fd);
return len > 0;
}
bool LinuxPtraceDumper::IsPostMortem() const {
return false;
}

Просмотреть файл

@ -64,11 +64,6 @@ class LinuxPtraceDumper : public LinuxDumper {
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
// Implements LinuxDumper::GetThreadNameByIndex().
// Reads the name of the |index|-th thread of |threads_|.
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadNameByIndex(size_t index, char* name, size_t size);
// Implements LinuxDumper::IsPostMortem().
// Always returns false to indicate this dumper performs a dump of
// a crashed process via ptrace.

Просмотреть файл

@ -219,7 +219,7 @@ class MinidumpWriter {
bool Dump() {
// A minidump file contains a number of tagged streams. This is the number
// of stream which we write.
unsigned kNumWriters = 14;
unsigned kNumWriters = 13;
TypedMDRVA<MDRawDirectory> dir(&minidump_writer_);
{
@ -248,10 +248,6 @@ class MinidumpWriter {
return false;
dir.CopyIndex(dir_index++, &dirent);
if (!WriteThreadNamesStream(&dirent))
return false;
dir.CopyIndex(dir_index++, &dirent);
if (!WriteMappings(&dirent))
return false;
dir.CopyIndex(dir_index++, &dirent);
@ -517,52 +513,6 @@ class MinidumpWriter {
return true;
}
bool WriteThreadName(pid_t tid, char* name, MDRawThreadName *thread_name) {
MDLocationDescriptor string_location;
if (!minidump_writer_.WriteString(name, 0, &string_location))
return false;
thread_name->thread_id = tid;
thread_name->rva_of_thread_name = string_location.rva;
return true;
}
// Write the threads' names.
bool WriteThreadNamesStream(MDRawDirectory* thread_names_stream) {
TypedMDRVA<MDRawThreadNamesList> list(&minidump_writer_);
const unsigned num_threads = dumper_->threads().size();
if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThreadName))) {
return false;
}
thread_names_stream->stream_type = MD_THREAD_NAMES_STREAM;
thread_names_stream->location = list.location();
list.get()->number_of_thread_names = num_threads;
MDRawThreadName thread_name;
int thread_idx = 0;
for (unsigned int i = 0; i < num_threads; ++i) {
const pid_t tid = dumper_->threads()[i];
// This is a constant from the Linux kernel, documented in man 5 proc.
// The comm entries in /proc are no longer than this.
static const size_t TASK_COMM_LEN = 16;
char name[TASK_COMM_LEN];
memset(&thread_name, 0, sizeof(MDRawThreadName));
if (dumper_->GetThreadNameByIndex(i, name, sizeof(name))) {
if (WriteThreadName(tid, name, &thread_name)) {
list.CopyIndexAfterObject(thread_idx++, &thread_name,
sizeof(MDRawThreadName));
}
}
}
return true;
}
// Write application-provided memory regions.
bool WriteAppMemory() {
for (AppMemoryList::const_iterator iter = app_memory_list_.begin();

Просмотреть файл

@ -73,10 +73,14 @@ 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,4 +260,15 @@ 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,6 +30,7 @@
#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"
@ -1378,6 +1379,15 @@ 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,
@ -1646,6 +1656,15 @@ 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);
}
@ -1888,6 +1907,7 @@ static void InitializeAnnotationFacilities() {
crashReporterAPILock = new Mutex("crashReporterAPILock");
notesFieldLock = new Mutex("notesFieldLock");
notesField = new nsCString();
InitThreadAnnotation();
if (!XRE_IsParentProcess()) {
InitChildAnnotationsFlusher();
}
@ -1905,6 +1925,8 @@ static void TeardownAnnotationFacilities() {
delete notesField;
notesField = nullptr;
ShutdownThreadAnnotation();
}
#ifdef XP_WIN

Просмотреть файл

@ -344,6 +344,9 @@ 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__ */

Просмотреть файл

@ -0,0 +1,362 @@
/* 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

Просмотреть файл

@ -0,0 +1,15 @@
# -*- 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"

Просмотреть файл

@ -0,0 +1,18 @@
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,6 +23,8 @@ 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,6 +479,7 @@ void NS_SetCurrentThreadName(const char* aName) {
nsThread* thread = nsThreadManager::get().GetCurrentThread();
thread->SetThreadNameInternal(nsDependentCString(aName));
}
CrashReporter::SetCurrentThreadName(aName);
}
nsIThread* NS_GetCurrentThread() {