Backed out 3 changesets (bug 1587722) for causing xpcshell failures on test_crashreporter_crash.js CLOSED TREE

Backed out changeset 6aad53d9e447 (bug 1587722)
Backed out changeset 7d664635c1dc (bug 1587722)
Backed out changeset c9dbcfa6a938 (bug 1587722)

--HG--
rename : toolkit/crashreporter/test/unit/test_oom_annotation.js => toolkit/crashreporter/test/unit/test_oom_annotation_windows.js
This commit is contained in:
Arthur Iakab 2019-12-30 17:28:31 +02:00
Родитель 7d9dd50759
Коммит f26446fbad
5 изменённых файлов: 72 добавлений и 414 удалений

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

@ -104,13 +104,9 @@ AsyncShutdownTimeout:
AvailablePageFile:
description: >
Available commit-space in bytes.
- Under Windows, computed from the PERFORMANCE_INFORMATION structure by substracting
the CommitTotal field from the CommitLimit field.
- Under Linux, computed from /proc/meminfo's CommitLimit - Committed_AS. Note that
the kernel is not guaranteed to enforce that CommittedLimit >= Committed_AS. If
Committed_AS > CommittedLimit, this value is set to 0.
- Not available on other platforms.
Windows-only, available commit-space in bytes. This annotation is computed
from the PERFORMANCE_INFORMATION structure by substracting the CommitTotal
field from the CommitLimit field.
type: string
ping: true
@ -120,28 +116,22 @@ AvailablePhysicalMemory:
- Under Windows, populated with the contents of the MEMORYSTATUSEX's structure
ullAvailPhys field.
- Under macOS, populated with vm_statistics64_data_t::free_count.
- Under Linux, populated with /proc/meminfo's MemFree.
- Not available on other platforms.
- Not available under other platforms.
type: string
ping: true
AvailableSwapMemory:
description: >
Amount of free swap space in bytes.
- Under macOS, populated with the contents of
sysctl "vm.swapusage" :: xsu_avail.
- Under Linux, populated with /proc/meminfo's SwapFree.
- Not available on other platforms.
macOS-only, amount of free swap space. Populated with the coontents of
sysctl "vm.swapusage" :: xsu_avail.
type: string
ping: true
AvailableVirtualMemory:
description: >
Amount of free virtual memory in bytes
- Under Windows, populated with the contents of the MEMORYSTATUSEX's structure ullAvailVirtual field.
- Under Linux, populated with /proc/meminfo's MemAvailable.
- Not available on other platforms.
- For macOS, see AvailableSwapMemory, AvailablePhysicalMemory and PurgeablePhysicalMemory.
Windows-only, amount of free virtual memory in bytes. Populated with the contents
of the MEMORYSTATUSEX's structure ullAvailVirtual field.
For macOS, see AvailableSwapMemory, AvailablePhysicalMemory and PurgeablePhysicalMemory.
type: string
ping: true
@ -853,11 +843,9 @@ ThreadIdNameMapping:
TotalPageFile:
description: >
Maximum amount of memory that can be committed without extending the swap/page file.
Maximum amount of memory that can be committed.
- Under Windows, populated with the contents of the PERFORMANCE_INFORMATION's
structure CommitLimit field.
- Under Linux, populated with /proc/meminfo MemTotal + SwapTotal. The swap file
typically cannot be extended, so that's a hard limit.
- Not available on other systems.
type: string
ping: true
@ -868,17 +856,14 @@ TotalPhysicalMemory:
- Under Windows, populated with the contents of the MEMORYSTATUSEX's structure
ullTotalPhys field.
- Under macOS, populated with sysctl "hw.memsize".
- Under Linux, populated with /proc/meminfo's "MemTotal".
- Not available on other systems.
type: string
ping: true
TotalVirtualMemory:
description: >
Size of the virtual address space.
- Under Windows, populated with the contents of the MEMORYSTATUSEX's structure
ullTotalVirtual field.
- Not available on other platforms.
Windows-only. Size of the virtual address space. This annotation is
populated with the contents of the MEMORYSTATUSEX's structure
ullTotalVirtual field.
type: string
ping: true

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

@ -76,7 +76,6 @@
# include "common/linux/eintr_wrapper.h"
# include <fcntl.h>
# include <sys/types.h>
# include "sys/sysinfo.h"
# include <sys/wait.h>
# include <unistd.h>
#else
@ -771,63 +770,50 @@ static void OpenAPIData(PlatformWriter& aWriter, const XP_CHAR* dump_path,
aWriter.Open(extraDataPath);
}
#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
#if defined(XP_WIN) || defined(XP_MACOSX)
static void WriteMemoryAnnotation(AnnotationTable& aTable,
static void WriteMemoryAnnotation(AnnotationWriter& aWriter,
Annotation aAnnotation, uint64_t aValue) {
// This function is used to write values of 64 bits, which can safely be
// assumed to fit within 20 decimal digits, so we need neither allocation nor
// overflow checking.
char buffer[128];
# ifdef XP_LINUX
// Under Linux, we cannot call into libc from the exception handler,
// so we need to call `my_inttostring`.
intmax_t value = intmax_t(aValue);
if (uint64_t(value) != aValue) {
// Our uint64_t doesn't cast properly to intmax_t. In this (unlikely) case,
// report the maximal value that can be represented with intmax_t.
value = intmax_t(-1);
}
my_inttostring(value, buffer, sizeof(buffer));
aTable[aAnnotation] = nsDependentCString(buffer);
# else
if (SprintfLiteral(buffer, "%llu", aValue) > 0) {
aTable[aAnnotation] = nsDependentCString(buffer);
aWriter.Write(aAnnotation, buffer);
}
# endif // XP_LINUX || else
}
#endif // XP_WIN || XP_MACOSX || XP_LINUX
#endif // XP_WIN || XP_MACOSX
#ifdef XP_WIN
static void AnnotateMemoryStatus(AnnotationTable& aTable) {
static void WriteMemoryStatus(AnnotationWriter& aWriter) {
MEMORYSTATUSEX statex;
statex.dwLength = sizeof(statex);
if (GlobalMemoryStatusEx(&statex)) {
WriteMemoryAnnotation(aTable, Annotation::SystemMemoryUsePercentage,
WriteMemoryAnnotation(aWriter, Annotation::SystemMemoryUsePercentage,
statex.dwMemoryLoad);
WriteMemoryAnnotation(aTable, Annotation::TotalVirtualMemory,
WriteMemoryAnnotation(aWriter, Annotation::TotalVirtualMemory,
statex.ullTotalVirtual);
WriteMemoryAnnotation(aTable, Annotation::AvailableVirtualMemory,
WriteMemoryAnnotation(aWriter, Annotation::AvailableVirtualMemory,
statex.ullAvailVirtual);
WriteMemoryAnnotation(aTable, Annotation::TotalPhysicalMemory,
WriteMemoryAnnotation(aWriter, Annotation::TotalPhysicalMemory,
statex.ullTotalPhys);
WriteMemoryAnnotation(aTable, Annotation::AvailablePhysicalMemory,
WriteMemoryAnnotation(aWriter, Annotation::AvailablePhysicalMemory,
statex.ullAvailPhys);
}
PERFORMANCE_INFORMATION info;
if (K32GetPerformanceInfo(&info, sizeof(info))) {
WriteMemoryAnnotation(aTable, Annotation::TotalPageFile,
WriteMemoryAnnotation(aWriter, Annotation::TotalPageFile,
info.CommitLimit * info.PageSize);
WriteMemoryAnnotation(
aTable, Annotation::AvailablePageFile,
aWriter, Annotation::AvailablePageFile,
(info.CommitLimit - info.CommitTotal) * info.PageSize);
}
}
#elif XP_MACOSX
// Extract the total physical memory of the system.
static void WritePhysicalMemoryStatus(AnnotationTable& aTable) {
static void WritePhysicalMemoryStatus(AnnotationWriter& aWriter) {
uint64_t physicalMemoryByteSize = 0;
const size_t NAME_LEN = 2;
int name[NAME_LEN] = {/* Hardware */ CTL_HW,
@ -836,27 +822,27 @@ static void WritePhysicalMemoryStatus(AnnotationTable& aTable) {
if (sysctl(name, NAME_LEN, &physicalMemoryByteSize, &infoByteSize,
/* We do not replace data */ nullptr,
/* We do not replace data */ 0) != -1) {
WriteMemoryAnnotation(aTable, Annotation::TotalPhysicalMemory,
WriteMemoryAnnotation(aWriter, Annotation::TotalPhysicalMemory,
physicalMemoryByteSize);
}
}
// Extract available and purgeable physical memory.
static void WriteAvailableMemoryStatus(AnnotationTable& aTable) {
static void WriteAvailableMemoryStatus(AnnotationWriter& aWriter) {
auto host = mach_host_self();
vm_statistics64_data_t stats;
unsigned int count = HOST_VM_INFO64_COUNT;
if (host_statistics64(host, HOST_VM_INFO64, (host_info64_t)&stats, &count) ==
KERN_SUCCESS) {
WriteMemoryAnnotation(aTable, Annotation::AvailablePhysicalMemory,
WriteMemoryAnnotation(aWriter, Annotation::AvailablePhysicalMemory,
stats.free_count * vm_page_size);
WriteMemoryAnnotation(aTable, Annotation::PurgeablePhysicalMemory,
WriteMemoryAnnotation(aWriter, Annotation::PurgeablePhysicalMemory,
stats.purgeable_count * vm_page_size);
}
}
// Extract the status of the swap.
static void WriteSwapFileStatus(AnnotationTable& aTable) {
static void WriteSwapFileStatus(AnnotationWriter& aWriter) {
const size_t NAME_LEN = 2;
int name[] = {/* Hardware */ CTL_VM,
/* 64-bit physical memory size */ VM_SWAPUSAGE};
@ -865,298 +851,22 @@ static void WriteSwapFileStatus(AnnotationTable& aTable) {
if (sysctl(name, NAME_LEN, &swapUsage, &infoByteSize,
/* We do not replace data */ nullptr,
/* We do not replace data */ 0) != -1) {
WriteMemoryAnnotation(aTable, Annotation::AvailableSwapMemory,
WriteMemoryAnnotation(aWriter, Annotation::AvailableSwapMemory,
swapUsage.xsu_avail);
}
}
static void AnnotateMemoryStatus(AnnotationTable& aTable) {
WritePhysicalMemoryStatus(aTable);
WriteAvailableMemoryStatus(aTable);
WriteSwapFileStatus(aTable);
static void WriteMemoryStatus(AnnotationWriter& aWriter) {
WritePhysicalMemoryStatus(aWriter);
WriteAvailableMemoryStatus(aWriter);
WriteSwapFileStatus(aWriter);
}
#elif XP_LINUX
static void AnnotateMemoryStatus(AnnotationTable& aTable) {
// We can't simply call `sysinfo` as this requires libc.
// So we need to parse /proc/meminfo.
// We read the entire file to memory prior to parsing
// as it makes the parser code a little bit simpler.
// As /proc/meminfo is synchronized via `proc_create_single`,
// there's no risk of race condition regardless of how we
// read it.
// The buffer in which we're going to load the entire file.
// A typical size for /proc/meminfo is 1kb, so 10kB should
// be large enough until further notice.
const size_t BUFFER_SIZE_BYTES = 10000;
char buffer[BUFFER_SIZE_BYTES];
ssize_t bufferLen = 0;
{
// Read and load into memory.
int fd = sys_open("/proc/meminfo", O_RDONLY, /* chmod */ 0);
if (fd == -1) {
// No /proc/meminfo? Well, fail silently.
return;
}
auto Guard = MakeScopeExit([fd]() { mozilla::Unused << sys_close(fd); });
if ((bufferLen = sys_read(fd, buffer, BUFFER_SIZE_BYTES)) <= 0) {
// Cannot read for some reason. Let's give up.
return;
}
}
// Each line of /proc/meminfo looks like
// SomeLabel: number unit
// The last line is empty.
// Let's write a parser.
// Note that we don't care about writing a normative parser, so
// we happily skip whitespaces without checking that it's necessary.
// A stack-allocated structure containing a 0-terminated string.
// We could avoid the memory copies and make it a slice at the cost
// of a slightly more complicated parser. Since we're not in a
// performance-critical section, we didn't.
struct DataBuffer {
DataBuffer() : data{0}, pos(0) {}
// Clear the buffer.
void reset() {
pos = 0;
data[0] = 0;
}
// Append a character.
//
// In case of error (if c is '\0' or the buffer is full), does nothing.
void append(char c) {
if (c == 0 || pos >= sizeof(data) - 1) {
return;
}
data[pos++] = c;
data[pos] = 0;
}
// Compare the buffer against a nul-terminated string.
bool operator==(const char* s) const {
for (size_t i = 0; i < pos; ++i) {
if (s[i] != data[i]) {
// Note: Since `data` never contains a '0' in positions [0,pos)
// this will bailout once we have reached the end of `s`.
return false;
}
}
return true;
}
// A NUL-terminated string of `pos + 1` chars (the +1 is for the 0).
char data[256];
// Invariant: < 256.
size_t pos;
};
// A DataBuffer holding the string representation of a non-negative number.
struct NumberBuffer : DataBuffer {
// If possible, convert the string into a number.
// Returns `true` in case of success, `false` in case of failure.
bool asNumber(size_t* number) {
int result;
if (!my_strtoui(&result, data)) {
return false;
}
*number = result;
return true;
}
};
// A DataBuffer holding the string representation of a unit. As of this
// writing, we only support unit `kB`, which seems to be the only unit used in
// `/proc/meminfo`.
struct UnitBuffer : DataBuffer {
// If possible, convert the string into a multiplier, e.g. `kB => 1024`.
// Return `true` in case of success, `false` in case of failure.
bool asMultiplier(size_t* multiplier) {
if (*this == "kB") {
*multiplier = 1024;
return true;
}
// Other units don't seem to be specified/used.
return false;
}
};
// The state of the mini-parser.
enum class State {
// Reading the label, including the trailing ':'.
Label,
// Reading the number, ignoring any whitespace.
Number,
// Reading the unit, ignoring any whitespace.
Unit,
};
// A single measure being read from /proc/meminfo, e.g.
// the total physical memory available on the system.
struct Measure {
Measure() : state(State::Label) {}
// Reset the measure for a new read.
void reset() {
state = State::Label;
label.reset();
number.reset();
unit.reset();
}
// Attempt to convert the measure into a number.
// Return `true` if both the number and the multiplier could be
// converted, `false` otherwise.
// In case of overflow, produces the maximal possible `size_t`.
bool asValue(size_t* result) {
size_t numberAsSize = 0;
if (!number.asNumber(&numberAsSize)) {
return false;
}
size_t unitAsMultiplier = 0;
if (!unit.asMultiplier(&unitAsMultiplier)) {
return false;
}
if (numberAsSize * unitAsMultiplier >= numberAsSize) {
*result = numberAsSize * unitAsMultiplier;
} else {
// Overflow. Unlikely, but just in case, let's return
// the maximal possible value.
*result = size_t(-1);
}
return true;
}
// The label being read, e.g. `MemFree`. Does not include the trailing ':'.
DataBuffer label;
// The number being read, e.g. "1024".
NumberBuffer number;
// The unit being read, e.g. "kB".
UnitBuffer unit;
// What we're reading at the moment.
State state;
};
// A value we wish to store for later processing.
// e.g. to compute `AvailablePageFile`, we need to
// store `CommitLimit` and `Committed_AS`.
struct ValueStore {
ValueStore() : value(0), found(false) {}
size_t value;
bool found;
};
ValueStore commitLimit;
ValueStore committedAS;
ValueStore memTotal;
ValueStore swapTotal;
// The current measure.
Measure measure;
for (size_t pos = 0; pos < size_t(bufferLen); ++pos) {
const char c = buffer[pos];
switch (measure.state) {
case State::Label:
if (c == ':') {
// We have finished reading the label.
measure.state = State::Number;
} else {
measure.label.append(c);
}
break;
case State::Number:
if (c == ' ') {
// Ignore whitespace
} else if ('0' <= c && c <= '9') {
// Accumulate numbers.
measure.number.append(c);
} else {
// We have jumped to the unit.
measure.unit.append(c);
measure.state = State::Unit;
}
break;
case State::Unit:
if (c == ' ') {
// Ignore whitespace
} else if (c == '\n') {
// Flush line.
// - If this one of the measures we're interested in, write it.
// - Once we're done, reset the parser.
auto Guard = MakeScopeExit([&measure]() { measure.reset(); });
struct PointOfInterest {
// The label we're looking for, e.g. "MemTotal".
const char* label;
// If non-nullptr, store the value at this address.
ValueStore* dest;
// If other than Annotation::Count, write the value for this
// annotation.
Annotation annotation;
};
const PointOfInterest POINTS_OF_INTEREST[] = {
{"MemTotal", &memTotal, Annotation::TotalPhysicalMemory},
{"MemFree", nullptr, Annotation::AvailablePhysicalMemory},
{"MemAvailable", nullptr, Annotation::AvailableVirtualMemory},
{"SwapFree", nullptr, Annotation::AvailableSwapMemory},
{"SwapTotal", &swapTotal, Annotation::Count},
{"CommitLimit", &commitLimit, Annotation::Count},
{"Committed_AS", &committedAS, Annotation::Count},
};
for (const auto& pointOfInterest : POINTS_OF_INTEREST) {
if (measure.label == pointOfInterest.label) {
size_t value;
if (measure.asValue(&value)) {
if (pointOfInterest.dest != nullptr) {
pointOfInterest.dest->found = true;
pointOfInterest.dest->value = value;
}
if (pointOfInterest.annotation != Annotation::Count) {
WriteMemoryAnnotation(aTable, pointOfInterest.annotation,
value);
}
}
break;
}
}
// Otherwise, ignore.
} else {
measure.unit.append(c);
}
break;
}
}
if (commitLimit.found && committedAS.found) {
// If available, attempt to determine the available virtual memory.
// As `commitLimit` is not guaranteed to be larger than `committedAS`,
// we return `0` in case the commit limit has already been exceeded.
uint64_t availablePageFile = (committedAS.value <= commitLimit.value)
? (commitLimit.value - committedAS.value)
: 0;
WriteMemoryAnnotation(aTable, Annotation::AvailablePageFile,
availablePageFile);
}
if (memTotal.found && swapTotal.found) {
// If available, attempt to determine the available virtual memory.
WriteMemoryAnnotation(aTable, Annotation::TotalPageFile,
memTotal.value + swapTotal.value);
}
}
#else
static void AnnotateMemoryStatus(AnnotationTable&) {
static void WriteMemoryStatus(AnnotationWriter& aWriter) {
// No memory data for other platforms yet.
}
#endif // XP_WIN || XP_MACOSX || XP_LINUX || else
#endif // XP_WIN || XP_MACOSX || else
#if !defined(MOZ_WIDGET_ANDROID)
@ -1342,6 +1052,7 @@ static void WriteAnnotationsForMainProcessCrash(PlatformWriter& pw,
# endif
#endif // XP_WIN
WriteMemoryStatus(writer);
WriteMozCrashReason(writer);
char oomAllocationSizeBuffer[32] = "";
@ -1637,6 +1348,9 @@ static void PrepareChildExceptionTimeAnnotations(
apiData.OpenHandle(f);
BinaryAnnotationWriter writer(apiData);
// ...and write out any annotations.
WriteMemoryStatus(writer);
char oomAllocationSizeBuffer[32] = "";
if (gOOMAllocationSize) {
XP_STOA(gOOMAllocationSize, oomAllocationSizeBuffer);
@ -2434,8 +2148,6 @@ static void MergeContentCrashAnnotations(AnnotationTable& aDst,
// Adds crash time, uptime and memory report annotations
static void AddCommonAnnotations(AnnotationTable& aAnnotations) {
AnnotateMemoryStatus(aAnnotations);
nsAutoCString crashTime;
crashTime.AppendInt((uint64_t)time(nullptr));
aAnnotations[Annotation::CrashTime] = crashTime;

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

@ -1,68 +0,0 @@
add_task(async function run_test() {
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
dump(
"INFO | test_crash_oom.js | Can't test crashreporter in a non-libxul build.\n"
);
return;
}
await do_content_crash(
function() {
crashType = CrashTestUtils.CRASH_OOM;
crashReporter.annotateCrashReport("TestKey", "Yes");
},
function(mdump, extra) {
ChromeUtils.import("resource://gre/modules/AppConstants.jsm", this);
Assert.equal(extra.TestKey, "Yes");
// A list of pairs [annotation name, must be > 0]
let annotations;
switch (AppConstants.platform) {
case "win":
annotations = [
["OOMAllocationSize", true],
["SystemMemoryUsePercentage", false],
["TotalVirtualMemory", true],
["AvailableVirtualMemory", false],
["TotalPageFile", false],
["AvailablePageFile", false],
["TotalPhysicalMemory", true],
["AvailablePhysicalMemory", false],
];
break;
case "linux":
annotations = [
["AvailablePageFile", false],
["AvailablePhysicalMemory", false],
["AvailableSwapMemory", false],
["AvailableVirtualMemory", false],
["TotalPageFile", false],
["TotalPhysicalMemory", true],
];
break;
case "macosx":
annotations = [
["AvailablePhysicalMemory", false],
["AvailableSwapMemory", false],
["PurgeablePhysicalMemory", false],
["TotalPhysicalMemory", true],
];
break;
default:
annotations = [];
}
for (let [label, shouldBeGreaterThanZero] of annotations) {
Assert.ok(label in extra, `Annotation ${label} is present`);
// All these annotations should represent non-negative numbers.
// A few of them (e.g. physical memory) are guaranteed to be positive.
if (shouldBeGreaterThanZero) {
Assert.ok(Number(extra[label]) > 0);
} else {
Assert.ok(Number(extra[label]) >= 0);
}
}
},
true
);
});

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

@ -0,0 +1,28 @@
add_task(async function run_test() {
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
dump(
"INFO | test_crash_oom.js | Can't test crashreporter in a non-libxul build.\n"
);
return;
}
await do_crash(
function() {
crashType = CrashTestUtils.CRASH_OOM;
crashReporter.annotateCrashReport("TestKey", "Yes");
},
function(mdump, extra) {
Assert.equal(extra.TestKey, "Yes");
Assert.ok("OOMAllocationSize" in extra);
Assert.ok(Number(extra.OOMAllocationSize) > 0);
Assert.ok("SystemMemoryUsePercentage" in extra);
Assert.ok("TotalVirtualMemory" in extra);
Assert.ok("AvailableVirtualMemory" in extra);
Assert.ok("TotalPageFile" in extra);
Assert.ok("AvailablePageFile" in extra);
Assert.ok("TotalPhysicalMemory" in extra);
Assert.ok("AvailablePhysicalMemory" in extra);
},
true
);
});

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

@ -15,7 +15,8 @@ support-files =
[test_crash_after_js_large_allocation_failure.js]
[test_crash_after_js_large_allocation_failure_reporting.js]
[test_crash_oom.js]
[test_oom_annotation.js]
[test_oom_annotation_windows.js]
skip-if = os != 'win'
[test_crash_abort.js]
skip-if = os == 'win'