зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
cc1a64f238
Коммит
f71a8aad71
|
@ -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() {
|
||||
|
|
Загрузка…
Ссылка в новой задаче