mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-03 23:30:46 +00:00
Backed out changeset c7b4e89f5ee6 (bug 1594577) for xpcshell failures on test_watchdog_hibernate.js . CLOSED TREE
This commit is contained in:
parent
0aadc17b15
commit
46f83924c1
@ -5792,7 +5792,7 @@ mozilla::ipc::IPCResult ContentParent::RecvBHRThreadHang(
|
||||
// XXX: We should be able to avoid this potentially expensive copy here by
|
||||
// moving our deserialized argument.
|
||||
nsCOMPtr<nsIHangDetails> hangDetails =
|
||||
new nsHangDetails(HangDetails(aDetails), PersistedToDisk::No);
|
||||
new nsHangDetails(HangDetails(aDetails));
|
||||
obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr);
|
||||
}
|
||||
return IPC_OK();
|
||||
|
@ -269,7 +269,7 @@ mozilla::ipc::IPCResult GPUChild::RecvBHRThreadHang(
|
||||
// XXX: We should be able to avoid this potentially expensive copy here by
|
||||
// moving our deserialized argument.
|
||||
nsCOMPtr<nsIHangDetails> hangDetails =
|
||||
new nsHangDetails(HangDetails(aDetails), PersistedToDisk::No);
|
||||
new nsHangDetails(HangDetails(aDetails));
|
||||
obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr);
|
||||
}
|
||||
return IPC_OK();
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm", this);
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"TelemetryController",
|
||||
@ -36,7 +35,6 @@ BHRTelemetryService.prototype = Object.freeze({
|
||||
modules: [],
|
||||
hangs: [],
|
||||
};
|
||||
this.clearPermahangFile = false;
|
||||
},
|
||||
|
||||
recordHang({
|
||||
@ -48,7 +46,6 @@ BHRTelemetryService.prototype = Object.freeze({
|
||||
remoteType,
|
||||
modules,
|
||||
annotations,
|
||||
wasPersisted,
|
||||
}) {
|
||||
if (!Services.telemetry.canRecordExtended) {
|
||||
return;
|
||||
@ -102,10 +99,6 @@ BHRTelemetryService.prototype = Object.freeze({
|
||||
stack,
|
||||
});
|
||||
|
||||
if (wasPersisted) {
|
||||
this.clearPermahangFile = true;
|
||||
}
|
||||
|
||||
// If we have collected enough hangs, we can submit the hangs we have
|
||||
// collected to telemetry.
|
||||
if (this.payload.hangs.length > this.TRANSMIT_HANG_COUNT) {
|
||||
@ -114,13 +107,6 @@ BHRTelemetryService.prototype = Object.freeze({
|
||||
},
|
||||
|
||||
submit() {
|
||||
if (this.clearPermahangFile) {
|
||||
OS.File.remove(
|
||||
OS.Path.join(OS.Constants.Path.profileDir, "last_permahang.bin"),
|
||||
{ ignoreAbsent: true }
|
||||
);
|
||||
}
|
||||
|
||||
if (!Services.telemetry.canRecordExtended) {
|
||||
return;
|
||||
}
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "prinrval.h"
|
||||
#include "prthread.h"
|
||||
#include "ThreadStackHelper.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "mozilla/Services.h"
|
||||
@ -105,10 +104,6 @@ class BackgroundHangManager : public nsIObserver {
|
||||
// Unwinding and reporting of hangs is despatched to this thread.
|
||||
nsCOMPtr<nsIThread> mHangProcessingThread;
|
||||
|
||||
// Used for recording a permahang in case we don't ever make it back to
|
||||
// the main thread to record/send it.
|
||||
nsCOMPtr<nsIFile> mPermahangFile;
|
||||
|
||||
// Allows us to watch CPU usage and annotate hangs when the system is
|
||||
// under high external load.
|
||||
CPUUsageWatcher mCPUUsageWatcher;
|
||||
@ -136,36 +131,13 @@ NS_IMPL_ISUPPORTS(BackgroundHangManager, nsIObserver)
|
||||
NS_IMETHODIMP
|
||||
BackgroundHangManager::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData) {
|
||||
if (!strcmp(aTopic, "browser-delayed-startup-finished")) {
|
||||
MonitorAutoLock autoLock(mLock);
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
|
||||
getter_AddRefs(mPermahangFile));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mPermahangFile->AppendNative(NS_LITERAL_CSTRING("last_permahang.bin"));
|
||||
} else {
|
||||
mPermahangFile = nullptr;
|
||||
}
|
||||
NS_ENSURE_TRUE(!strcmp(aTopic, "profile-after-change"), NS_ERROR_UNEXPECTED);
|
||||
BackgroundHangMonitor::DisableOnBeta();
|
||||
|
||||
if (mHangProcessingThread && mPermahangFile) {
|
||||
nsCOMPtr<nsIRunnable> submitRunnable =
|
||||
new SubmitPersistedPermahangRunnable(mPermahangFile);
|
||||
mHangProcessingThread->Dispatch(submitRunnable.forget());
|
||||
}
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
MOZ_ASSERT(observerService);
|
||||
observerService->RemoveObserver(BackgroundHangManager::sInstance,
|
||||
"browser-delayed-startup-finished");
|
||||
} else if (!strcmp(aTopic, "profile-after-change")) {
|
||||
BackgroundHangMonitor::DisableOnBeta();
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
MOZ_ASSERT(observerService);
|
||||
observerService->RemoveObserver(BackgroundHangManager::sInstance,
|
||||
"profile-after-change");
|
||||
} else {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
MOZ_ASSERT(observerService);
|
||||
observerService->RemoveObserver(this, "profile-after-change");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -244,8 +216,7 @@ class BackgroundHangThread : public LinkedListElement<BackgroundHangThread> {
|
||||
|
||||
// Report a hang; aManager->mLock IS locked. The hang will be processed
|
||||
// off-main-thread, and will then be submitted back.
|
||||
void ReportHang(TimeDuration aHangTime,
|
||||
PersistedToDisk aPersistedToDisk = PersistedToDisk::No);
|
||||
void ReportHang(TimeDuration aHangTime);
|
||||
// Report a permanent hang; aManager->mLock IS locked
|
||||
void ReportPermaHang();
|
||||
// Called by BackgroundHangMonitor::NotifyActivity
|
||||
@ -497,8 +468,7 @@ BackgroundHangThread::~BackgroundHangThread() {
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundHangThread::ReportHang(TimeDuration aHangTime,
|
||||
PersistedToDisk aPersistedToDisk) {
|
||||
void BackgroundHangThread::ReportHang(TimeDuration aHangTime) {
|
||||
// Recovered from a hang; called on the monitor thread
|
||||
// mManager->mLock IS locked
|
||||
|
||||
@ -508,24 +478,17 @@ void BackgroundHangThread::ReportHang(TimeDuration aHangTime,
|
||||
VoidString(), mThreadName, mRunnableName, std::move(mHangStack),
|
||||
std::move(mAnnotations));
|
||||
|
||||
PersistedToDisk persistedToDisk = aPersistedToDisk;
|
||||
if (aPersistedToDisk == PersistedToDisk::Yes && XRE_IsParentProcess()) {
|
||||
auto res = WriteHangDetailsToFile(hangDetails, mManager->mPermahangFile);
|
||||
persistedToDisk = res.isOk() ? PersistedToDisk::Yes : PersistedToDisk::No;
|
||||
}
|
||||
|
||||
// If the hang processing thread exists, we can process the native stack
|
||||
// on it. Otherwise, we are unable to report a native stack, so we just
|
||||
// report without one.
|
||||
if (mManager->mHangProcessingThread) {
|
||||
nsCOMPtr<nsIRunnable> processHangStackRunnable =
|
||||
new ProcessHangStackRunnable(std::move(hangDetails), persistedToDisk);
|
||||
new ProcessHangStackRunnable(std::move(hangDetails));
|
||||
mManager->mHangProcessingThread->Dispatch(
|
||||
processHangStackRunnable.forget());
|
||||
} else {
|
||||
NS_WARNING("Unable to report native stack without a BHR processing thread");
|
||||
RefPtr<nsHangDetails> hd =
|
||||
new nsHangDetails(std::move(hangDetails), persistedToDisk);
|
||||
RefPtr<nsHangDetails> hd = new nsHangDetails(std::move(hangDetails));
|
||||
hd->Submit();
|
||||
}
|
||||
|
||||
@ -546,11 +509,13 @@ void BackgroundHangThread::ReportPermaHang() {
|
||||
// Permanently hanged; called on the monitor thread
|
||||
// mManager->mLock IS locked
|
||||
|
||||
// The significance of a permahang is that it's likely that we won't ever
|
||||
// recover and be allowed to submit this hang. On the parent thread, we
|
||||
// compensate for this by writing the hang details to disk on this thread,
|
||||
// and in our next session we'll try to read those details
|
||||
ReportHang(mMaxTimeout, PersistedToDisk::Yes);
|
||||
// NOTE: We used to capture a native stack in this situation if one had not
|
||||
// already been captured, but with the new ReportHang design that is less
|
||||
// practical.
|
||||
//
|
||||
// We currently don't look at hang reports outside of nightly, and already
|
||||
// collect native stacks eagerly on nightly, so this should be OK.
|
||||
ReportHang(mMaxTimeout);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void BackgroundHangThread::Update() {
|
||||
@ -648,16 +613,17 @@ void BackgroundHangMonitor::Startup() {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
MOZ_ASSERT(observerService);
|
||||
|
||||
if (!strcmp(MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL), "beta")) {
|
||||
if (XRE_IsParentProcess()) { // cached ClientID hasn't been read yet
|
||||
BackgroundHangThread::Startup();
|
||||
BackgroundHangManager::sInstance = new BackgroundHangManager();
|
||||
Unused << NS_WARN_IF(
|
||||
BackgroundHangManager::sInstance->mCPUUsageWatcher.Init().isErr());
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
MOZ_ASSERT(observerService);
|
||||
|
||||
observerService->AddObserver(BackgroundHangManager::sInstance,
|
||||
"profile-after-change", false);
|
||||
return;
|
||||
@ -670,10 +636,6 @@ void BackgroundHangMonitor::Startup() {
|
||||
BackgroundHangManager::sInstance = new BackgroundHangManager();
|
||||
Unused << NS_WARN_IF(
|
||||
BackgroundHangManager::sInstance->mCPUUsageWatcher.Init().isErr());
|
||||
if (XRE_IsParentProcess()) {
|
||||
observerService->AddObserver(BackgroundHangManager::sInstance,
|
||||
"browser-delayed-startup-finished", false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -12,22 +12,13 @@
|
||||
#include "mozilla/dom/ContentParent.h" // For RemoteTypePrefix
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/GfxMessageUtils.h" // For ParamTraits<GeckoProcessType>
|
||||
#include "mozilla/ResultExtensions.h"
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
# include "shared-libraries.h"
|
||||
#endif
|
||||
|
||||
static const char MAGIC[] = "permahangsavev1";
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHangDetails::GetWasPersisted(bool* aWasPersisted) {
|
||||
*aWasPersisted = mPersistedToDisk == PersistedToDisk::Yes;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHangDetails::GetDuration(double* aDuration) {
|
||||
*aDuration = mDetails.duration().ToMilliseconds();
|
||||
@ -385,322 +376,14 @@ void ReadModuleInformation(HangStack& stack) {
|
||||
#endif
|
||||
}
|
||||
|
||||
Result<Ok, nsresult> ReadData(PRFileDesc* aFile, void* aPtr, size_t aLength) {
|
||||
int32_t readResult = PR_Read(aFile, aPtr, aLength);
|
||||
if (readResult < 0 || size_t(readResult) != aLength) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<Ok, nsresult> WriteData(PRFileDesc* aFile, void* aPtr, size_t aLength) {
|
||||
int32_t writeResult = PR_Write(aFile, aPtr, aLength);
|
||||
if (writeResult < 0 || size_t(writeResult) != aLength) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<Ok, nsresult> WriteUint(PRFileDesc* aFile, const CheckedUint32& aInt) {
|
||||
if (!aInt.isValid()) {
|
||||
MOZ_ASSERT_UNREACHABLE("Integer value out of bounds.");
|
||||
return Err(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
int32_t value = aInt.value();
|
||||
MOZ_TRY(WriteData(aFile, (void*)&value, sizeof(value)));
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<uint32_t, nsresult> ReadUint(PRFileDesc* aFile) {
|
||||
int32_t value;
|
||||
MOZ_TRY(ReadData(aFile, (void*)&value, sizeof(value)));
|
||||
return value;
|
||||
}
|
||||
|
||||
Result<Ok, nsresult> WriteCString(PRFileDesc* aFile, const char* aString) {
|
||||
size_t length = strlen(aString);
|
||||
MOZ_TRY(WriteUint(aFile, CheckedUint32(length)));
|
||||
MOZ_TRY(WriteData(aFile, (void*)aString, length));
|
||||
return Ok();
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
Result<Ok, nsresult> WriteTString(PRFileDesc* aFile,
|
||||
const nsTString<CharT>& aString) {
|
||||
MOZ_TRY(WriteUint(aFile, CheckedUint32(aString.Length())));
|
||||
size_t size = aString.Length() * sizeof(CharT);
|
||||
MOZ_TRY(WriteData(aFile, (void*)aString.get(), size));
|
||||
return Ok();
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
Result<nsTString<CharT>, nsresult> ReadTString(PRFileDesc* aFile) {
|
||||
uint32_t length;
|
||||
MOZ_TRY_VAR(length, ReadUint(aFile));
|
||||
nsTString<CharT> result;
|
||||
CharT buffer[512];
|
||||
size_t bufferLength = sizeof(buffer) / sizeof(CharT);
|
||||
while (length != 0) {
|
||||
size_t toRead = std::min(bufferLength, size_t(length));
|
||||
size_t toReadSize = toRead * sizeof(CharT);
|
||||
MOZ_TRY(ReadData(aFile, (void*)buffer, toReadSize));
|
||||
|
||||
if (!result.Append(buffer, toRead, mozilla::fallible)) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
if (length > bufferLength) {
|
||||
length -= bufferLength;
|
||||
} else {
|
||||
length = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Result<Ok, nsresult> WriteEntry(PRFileDesc* aFile, const HangStack& aStack,
|
||||
const HangEntry& aEntry) {
|
||||
MOZ_TRY(WriteUint(aFile, uint32_t(aEntry.type())));
|
||||
switch (aEntry.type()) {
|
||||
case HangEntry::TnsCString: {
|
||||
MOZ_TRY(WriteTString(aFile, aEntry.get_nsCString()));
|
||||
break;
|
||||
}
|
||||
case HangEntry::THangEntryBufOffset: {
|
||||
uint32_t offset = aEntry.get_HangEntryBufOffset().index();
|
||||
|
||||
if (NS_WARN_IF(aStack.strbuffer().IsEmpty() ||
|
||||
offset >= aStack.strbuffer().Length())) {
|
||||
MOZ_ASSERT_UNREACHABLE("Corrupted offset data");
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
if (aStack.strbuffer().LastElement() != '\0') {
|
||||
MOZ_ASSERT_UNREACHABLE("Corrupted strbuffer data");
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
const char* start = (const char*)aStack.strbuffer().Elements() + offset;
|
||||
MOZ_TRY(WriteCString(aFile, start));
|
||||
break;
|
||||
}
|
||||
case HangEntry::THangEntryModOffset: {
|
||||
const HangEntryModOffset& mo = aEntry.get_HangEntryModOffset();
|
||||
|
||||
MOZ_TRY(WriteUint(aFile, CheckedUint32(mo.module())));
|
||||
MOZ_TRY(WriteUint(aFile, CheckedUint32(mo.offset())));
|
||||
break;
|
||||
}
|
||||
case HangEntry::THangEntryProgCounter:
|
||||
case HangEntry::THangEntryContent:
|
||||
case HangEntry::THangEntryJit:
|
||||
case HangEntry::THangEntryWasm:
|
||||
case HangEntry::THangEntryChromeScript:
|
||||
case HangEntry::THangEntrySuppressed: {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_CRASH("Unsupported HangEntry type?");
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<Ok, nsresult> ReadEntry(PRFileDesc* aFile, HangStack& aStack) {
|
||||
uint32_t type;
|
||||
MOZ_TRY_VAR(type, ReadUint(aFile));
|
||||
HangEntry::Type entryType = HangEntry::Type(type);
|
||||
switch (entryType) {
|
||||
case HangEntry::TnsCString:
|
||||
case HangEntry::THangEntryBufOffset: {
|
||||
nsCString str;
|
||||
MOZ_TRY_VAR(str, ReadTString<char>(aFile));
|
||||
aStack.stack().AppendElement(std::move(str));
|
||||
break;
|
||||
}
|
||||
case HangEntry::THangEntryModOffset: {
|
||||
uint32_t module;
|
||||
MOZ_TRY_VAR(module, ReadUint(aFile));
|
||||
uint32_t offset;
|
||||
MOZ_TRY_VAR(offset, ReadUint(aFile));
|
||||
aStack.stack().AppendElement(HangEntryModOffset(module, offset));
|
||||
break;
|
||||
}
|
||||
case HangEntry::THangEntryProgCounter: {
|
||||
aStack.stack().AppendElement(HangEntryProgCounter());
|
||||
break;
|
||||
}
|
||||
case HangEntry::THangEntryContent: {
|
||||
aStack.stack().AppendElement(HangEntryContent());
|
||||
break;
|
||||
}
|
||||
case HangEntry::THangEntryJit: {
|
||||
aStack.stack().AppendElement(HangEntryJit());
|
||||
break;
|
||||
}
|
||||
case HangEntry::THangEntryWasm: {
|
||||
aStack.stack().AppendElement(HangEntryWasm());
|
||||
break;
|
||||
}
|
||||
case HangEntry::THangEntryChromeScript: {
|
||||
aStack.stack().AppendElement(HangEntryChromeScript());
|
||||
break;
|
||||
}
|
||||
case HangEntry::THangEntrySuppressed: {
|
||||
aStack.stack().AppendElement(HangEntrySuppressed());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_CRASH("Unsupported HangEntry type?");
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<HangDetails, nsresult> ReadHangDetailsFromFile(nsIFile* aFile) {
|
||||
AutoFDClose fd;
|
||||
nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0644, &fd.rwget());
|
||||
if (NS_FAILED(rv)) {
|
||||
return Err(rv);
|
||||
}
|
||||
|
||||
uint8_t magicBuffer[sizeof(MAGIC)];
|
||||
MOZ_TRY(ReadData(fd, (void*)magicBuffer, sizeof(MAGIC)));
|
||||
|
||||
if (memcmp(magicBuffer, MAGIC, sizeof(MAGIC)) != 0) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
HangDetails result;
|
||||
uint32_t duration;
|
||||
MOZ_TRY_VAR(duration, ReadUint(fd));
|
||||
result.duration() = TimeDuration::FromMilliseconds(double(duration));
|
||||
MOZ_TRY_VAR(result.threadName(), ReadTString<char>(fd));
|
||||
MOZ_TRY_VAR(result.runnableName(), ReadTString<char>(fd));
|
||||
MOZ_TRY_VAR(result.process(), ReadTString<char>(fd));
|
||||
MOZ_TRY_VAR(result.remoteType(), ReadTString<char16_t>(fd));
|
||||
|
||||
uint32_t numAnnotations;
|
||||
MOZ_TRY_VAR(numAnnotations, ReadUint(fd));
|
||||
auto& annotations = result.annotations();
|
||||
|
||||
// Add a "Unrecovered" annotation so we can know when processing this that
|
||||
// the hang persisted until the process was closed.
|
||||
if (!annotations.SetCapacity(numAnnotations + 1, mozilla::fallible)) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
annotations.AppendElement(HangAnnotation(NS_LITERAL_STRING("Unrecovered"),
|
||||
NS_LITERAL_STRING("true")));
|
||||
|
||||
for (size_t i = 0; i < numAnnotations; ++i) {
|
||||
HangAnnotation annot;
|
||||
MOZ_TRY_VAR(annot.name(), ReadTString<char16_t>(fd));
|
||||
MOZ_TRY_VAR(annot.value(), ReadTString<char16_t>(fd));
|
||||
annotations.AppendElement(std::move(annot));
|
||||
}
|
||||
|
||||
auto& stack = result.stack();
|
||||
uint32_t numFrames;
|
||||
MOZ_TRY_VAR(numFrames, ReadUint(fd));
|
||||
if (!stack.stack().SetCapacity(numFrames, mozilla::fallible)) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < numFrames; ++i) {
|
||||
MOZ_TRY(ReadEntry(fd, stack));
|
||||
}
|
||||
|
||||
uint32_t numModules;
|
||||
MOZ_TRY_VAR(numModules, ReadUint(fd));
|
||||
auto& modules = stack.modules();
|
||||
if (!annotations.SetCapacity(numModules, mozilla::fallible)) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < numModules; ++i) {
|
||||
HangModule module;
|
||||
MOZ_TRY_VAR(module.name(), ReadTString<char16_t>(fd));
|
||||
MOZ_TRY_VAR(module.breakpadId(), ReadTString<char>(fd));
|
||||
modules.AppendElement(std::move(module));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Result<Ok, nsresult> WriteHangDetailsToFile(HangDetails& aDetails,
|
||||
nsIFile* aFile) {
|
||||
AutoFDClose fd;
|
||||
nsresult rv = aFile->OpenNSPRFileDesc(
|
||||
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0644, &fd.rwget());
|
||||
if (NS_FAILED(rv)) {
|
||||
return Err(rv);
|
||||
}
|
||||
|
||||
MOZ_TRY(WriteData(fd, (void*)MAGIC, sizeof(MAGIC)));
|
||||
|
||||
double duration = aDetails.duration().ToMilliseconds();
|
||||
if (duration > double(MaxValue<uint32_t>::value)) {
|
||||
// Something has gone terribly wrong if we've hung for more than 2^32 ms.
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
MOZ_TRY(WriteUint(fd, uint32_t(duration)));
|
||||
MOZ_TRY(WriteTString(fd, aDetails.threadName()));
|
||||
MOZ_TRY(WriteTString(fd, aDetails.runnableName()));
|
||||
MOZ_TRY(WriteTString(fd, aDetails.process()));
|
||||
MOZ_TRY(WriteTString(fd, aDetails.remoteType()));
|
||||
MOZ_TRY(WriteUint(fd, CheckedUint32(aDetails.annotations().Length())));
|
||||
|
||||
for (auto& annot : aDetails.annotations()) {
|
||||
MOZ_TRY(WriteTString(fd, annot.name()));
|
||||
MOZ_TRY(WriteTString(fd, annot.value()));
|
||||
}
|
||||
|
||||
auto& stack = aDetails.stack();
|
||||
ReadModuleInformation(stack);
|
||||
|
||||
MOZ_TRY(WriteUint(fd, CheckedUint32(stack.stack().Length())));
|
||||
for (auto& entry : stack.stack()) {
|
||||
MOZ_TRY(WriteEntry(fd, stack, entry));
|
||||
}
|
||||
|
||||
auto& modules = stack.modules();
|
||||
MOZ_TRY(WriteUint(fd, CheckedUint32(modules.Length())));
|
||||
|
||||
for (auto& module : modules) {
|
||||
MOZ_TRY(WriteTString(fd, module.name()));
|
||||
MOZ_TRY(WriteTString(fd, module.breakpadId()));
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ProcessHangStackRunnable::Run() {
|
||||
// NOTE: Reading module information can take a long time, which is why we do
|
||||
// it off-main-thread.
|
||||
if (mHangDetails.stack().modules().IsEmpty()) {
|
||||
ReadModuleInformation(mHangDetails.stack());
|
||||
}
|
||||
ReadModuleInformation(mHangDetails.stack());
|
||||
|
||||
RefPtr<nsHangDetails> hangDetails =
|
||||
new nsHangDetails(std::move(mHangDetails), mPersistedToDisk);
|
||||
hangDetails->Submit();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SubmitPersistedPermahangRunnable::Run() {
|
||||
auto hangDetailsResult = ReadHangDetailsFromFile(mPermahangFile);
|
||||
if (hangDetailsResult.isErr()) {
|
||||
// If we somehow failed in trying to deserialize the hang file, go ahead
|
||||
// and delete it to prevent future runs from having to go through the
|
||||
// same thing. If we succeeded, however, the file should be cleaned up
|
||||
// once the hang is submitted.
|
||||
Unused << mPermahangFile->Remove(false);
|
||||
return hangDetailsResult.unwrapErr();
|
||||
}
|
||||
RefPtr<nsHangDetails> hangDetails =
|
||||
new nsHangDetails(hangDetailsResult.unwrap(), PersistedToDisk::Yes);
|
||||
new nsHangDetails(std::move(mHangDetails));
|
||||
hangDetails->Submit();
|
||||
|
||||
return NS_OK;
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "mozilla/ProcessedStack.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/Result.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/HangTypes.h"
|
||||
#include "mozilla/HangAnnotations.h"
|
||||
@ -20,11 +19,6 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
enum class PersistedToDisk {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
/**
|
||||
* HangDetails is the concrete implementaion of nsIHangDetails, and contains the
|
||||
* infromation which we want to expose to observers of the bhr-thread-hang
|
||||
@ -35,9 +29,8 @@ class nsHangDetails : public nsIHangDetails {
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIHANGDETAILS
|
||||
|
||||
explicit nsHangDetails(HangDetails&& aDetails,
|
||||
PersistedToDisk aPersistedToDisk)
|
||||
: mDetails(std::move(aDetails)), mPersistedToDisk(aPersistedToDisk) {}
|
||||
explicit nsHangDetails(HangDetails&& aDetails)
|
||||
: mDetails(std::move(aDetails)) {}
|
||||
|
||||
// Submit these HangDetails to the main thread. This will dispatch a runnable
|
||||
// to the main thread which will fire off the bhr-thread-hang observer
|
||||
@ -48,12 +41,8 @@ class nsHangDetails : public nsIHangDetails {
|
||||
virtual ~nsHangDetails() {}
|
||||
|
||||
HangDetails mDetails;
|
||||
PersistedToDisk mPersistedToDisk;
|
||||
};
|
||||
|
||||
Result<Ok, nsresult> WriteHangDetailsToFile(HangDetails& aDetails,
|
||||
nsIFile* aFile);
|
||||
|
||||
/**
|
||||
* This runnable is run on the StreamTransportService threadpool in order to
|
||||
* process the stack off main thread before submitting it to the main thread as
|
||||
@ -64,34 +53,14 @@ Result<Ok, nsresult> WriteHangDetailsToFile(HangDetails& aDetails,
|
||||
*/
|
||||
class ProcessHangStackRunnable final : public Runnable {
|
||||
public:
|
||||
explicit ProcessHangStackRunnable(HangDetails&& aHangDetails,
|
||||
PersistedToDisk aPersistedToDisk)
|
||||
explicit ProcessHangStackRunnable(HangDetails&& aHangDetails)
|
||||
: Runnable("ProcessHangStackRunnable"),
|
||||
mHangDetails(std::move(aHangDetails)),
|
||||
mPersistedToDisk(aPersistedToDisk) {}
|
||||
mHangDetails(std::move(aHangDetails)) {}
|
||||
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
private:
|
||||
HangDetails mHangDetails;
|
||||
PersistedToDisk mPersistedToDisk;
|
||||
};
|
||||
|
||||
/**
|
||||
* This runnable handles checking whether our last session wrote a permahang to
|
||||
* disk which we were unable to submit through telemetry. If so, we read the
|
||||
* permahang out and try again to submit it.
|
||||
*/
|
||||
class SubmitPersistedPermahangRunnable final : public Runnable {
|
||||
public:
|
||||
explicit SubmitPersistedPermahangRunnable(nsIFile* aPermahangFile)
|
||||
: Runnable("SubmitPersistedPermahangRunnable"),
|
||||
mPermahangFile(aPermahangFile) {}
|
||||
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIFile> mPermahangFile;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -20,12 +20,6 @@ class HangDetails;
|
||||
[scriptable, uuid(23d63fff-38d6-4003-9c57-2c90aca1180a)]
|
||||
interface nsIHangDetails : nsISupports
|
||||
{
|
||||
/**
|
||||
* The hang was persisted to disk as a permahang, so we can clear the
|
||||
* permahang file once we submit this.
|
||||
*/
|
||||
readonly attribute bool wasPersisted;
|
||||
|
||||
/**
|
||||
* The detected duration of the hang in milliseconds.
|
||||
*/
|
||||
|
@ -153,5 +153,3 @@ The following annotations are currently present in tree:
|
||||
+-----------------+-------------------------------------------------+
|
||||
| HangUIDontShow | "true" if the hang UI was not shown |
|
||||
+-----------------+-------------------------------------------------+
|
||||
| Unrecovered | "true" if the hang persisted until process exit |
|
||||
+-----------------+-------------------------------------------------+
|
||||
|
Loading…
x
Reference in New Issue
Block a user