mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 893222 - Part 3: Modify the JS memory reporter to consider a string as "notable" if we have many small copies of it. r=njn
This commit is contained in:
parent
f2f063a8e3
commit
fb5630ed11
@ -11,12 +11,14 @@
|
||||
// change in the future. Depend on them at your own risk.
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "jsalloc.h"
|
||||
#include "jspubtd.h"
|
||||
|
||||
#include "js/HashTable.h"
|
||||
#include "js/Utility.h"
|
||||
#include "js/Vector.h"
|
||||
|
||||
@ -30,9 +32,61 @@ namespace js {
|
||||
// MemoryReportingSundriesThreshold() bytes.
|
||||
//
|
||||
// We need to define this value here, rather than in the code which actually
|
||||
// generates the memory reports, because HugeStringInfo uses this value.
|
||||
// generates the memory reports, because NotableStringInfo uses this value.
|
||||
JS_FRIEND_API(size_t) MemoryReportingSundriesThreshold();
|
||||
|
||||
struct StringHashPolicy
|
||||
{
|
||||
typedef JSString *Lookup;
|
||||
static HashNumber hash(const Lookup &l);
|
||||
static bool match(const JSString *const &k, const Lookup &l);
|
||||
};
|
||||
|
||||
struct ZoneStatsPod
|
||||
{
|
||||
ZoneStatsPod() {
|
||||
mozilla::PodZero(this);
|
||||
}
|
||||
|
||||
void add(const ZoneStatsPod &other) {
|
||||
#define ADD(x) this->x += other.x
|
||||
|
||||
ADD(gcHeapArenaAdmin);
|
||||
ADD(gcHeapUnusedGcThings);
|
||||
|
||||
ADD(gcHeapStringsNormal);
|
||||
ADD(gcHeapStringsShort);
|
||||
ADD(gcHeapLazyScripts);
|
||||
ADD(gcHeapTypeObjects);
|
||||
ADD(gcHeapIonCodes);
|
||||
|
||||
ADD(stringCharsNonNotable);
|
||||
ADD(lazyScripts);
|
||||
ADD(typeObjects);
|
||||
ADD(typePool);
|
||||
|
||||
#undef ADD
|
||||
}
|
||||
|
||||
// This field can be used by embedders.
|
||||
void *extra;
|
||||
|
||||
size_t gcHeapArenaAdmin;
|
||||
size_t gcHeapUnusedGcThings;
|
||||
|
||||
size_t gcHeapStringsNormal;
|
||||
size_t gcHeapStringsShort;
|
||||
|
||||
size_t gcHeapLazyScripts;
|
||||
size_t gcHeapTypeObjects;
|
||||
size_t gcHeapIonCodes;
|
||||
|
||||
size_t stringCharsNonNotable;
|
||||
size_t lazyScripts;
|
||||
size_t typeObjects;
|
||||
size_t typePool;
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
@ -103,26 +157,89 @@ struct CodeSizes
|
||||
CodeSizes() { memset(this, 0, sizeof(CodeSizes)); }
|
||||
};
|
||||
|
||||
// Holds data about a huge string (one which uses more HugeStringInfo::MinSize
|
||||
// bytes of memory), so we can report it individually.
|
||||
struct HugeStringInfo
|
||||
|
||||
// This class holds information about the memory taken up by identical copies of
|
||||
// a particular string. Multiple JSStrings may have their sizes aggregated
|
||||
// together into one StringInfo object.
|
||||
struct StringInfo
|
||||
{
|
||||
HugeStringInfo() : length(0), size(0) { memset(&buffer, 0, sizeof(buffer)); }
|
||||
StringInfo()
|
||||
: length(0), numCopies(0), sizeOfShortStringGCThings(0),
|
||||
sizeOfNormalStringGCThings(0), sizeOfAllStringChars(0)
|
||||
{}
|
||||
|
||||
StringInfo(size_t len, size_t shorts, size_t normals, size_t chars)
|
||||
: length(len),
|
||||
numCopies(1),
|
||||
sizeOfShortStringGCThings(shorts),
|
||||
sizeOfNormalStringGCThings(normals),
|
||||
sizeOfAllStringChars(chars)
|
||||
{}
|
||||
|
||||
void add(size_t shorts, size_t normals, size_t chars) {
|
||||
sizeOfShortStringGCThings += shorts;
|
||||
sizeOfNormalStringGCThings += normals;
|
||||
sizeOfAllStringChars += chars;
|
||||
numCopies++;
|
||||
}
|
||||
|
||||
void add(const StringInfo& info) {
|
||||
MOZ_ASSERT(length == info.length);
|
||||
|
||||
sizeOfShortStringGCThings += info.sizeOfShortStringGCThings;
|
||||
sizeOfNormalStringGCThings += info.sizeOfNormalStringGCThings;
|
||||
sizeOfAllStringChars += info.sizeOfAllStringChars;
|
||||
numCopies += info.numCopies;
|
||||
}
|
||||
|
||||
size_t totalSizeOf() const {
|
||||
return sizeOfShortStringGCThings + sizeOfNormalStringGCThings + sizeOfAllStringChars;
|
||||
}
|
||||
|
||||
size_t totalGCThingSizeOf() const {
|
||||
return sizeOfShortStringGCThings + sizeOfNormalStringGCThings;
|
||||
}
|
||||
|
||||
// The string's length, excluding the null-terminator.
|
||||
size_t length;
|
||||
|
||||
// How many copies of the string have we seen?
|
||||
size_t numCopies;
|
||||
|
||||
// These are all totals across all copies of the string we've seen.
|
||||
size_t sizeOfShortStringGCThings;
|
||||
size_t sizeOfNormalStringGCThings;
|
||||
size_t sizeOfAllStringChars;
|
||||
};
|
||||
|
||||
// Holds data about a notable string (one which uses more than
|
||||
// NotableStringInfo::notableSize() bytes of memory), so we can report it
|
||||
// individually.
|
||||
//
|
||||
// Essentially the only difference between this class and StringInfo is that
|
||||
// NotableStringInfo holds a copy of the string's chars.
|
||||
struct NotableStringInfo : public StringInfo
|
||||
{
|
||||
NotableStringInfo();
|
||||
NotableStringInfo(JSString *str, const StringInfo &info);
|
||||
NotableStringInfo(const NotableStringInfo& info);
|
||||
NotableStringInfo(mozilla::MoveRef<NotableStringInfo> info);
|
||||
NotableStringInfo &operator=(mozilla::MoveRef<NotableStringInfo> info);
|
||||
|
||||
~NotableStringInfo() {
|
||||
js_free(buffer);
|
||||
}
|
||||
|
||||
// A string needs to take up this many bytes of storage before we consider
|
||||
// it to be "huge".
|
||||
static size_t MinSize() {
|
||||
// it to be "notable".
|
||||
static size_t notableSize() {
|
||||
return js::MemoryReportingSundriesThreshold();
|
||||
}
|
||||
|
||||
// A string's size in memory is not necessarily equal to twice its length
|
||||
// because the allocator and the JS engine both may round up.
|
||||
size_t length;
|
||||
size_t size;
|
||||
|
||||
// We record the first 32 chars of the escaped string here. (We escape the
|
||||
// string so we can use a char[] instead of a jschar[] here.
|
||||
char buffer[32];
|
||||
// The amount of memory we requested for |buffer|; i.e.
|
||||
// buffer = malloc(bufferSize).
|
||||
size_t bufferSize;
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
// These measurements relate directly to the JSRuntime, and not to
|
||||
@ -146,84 +263,46 @@ struct RuntimeSizes
|
||||
CodeSizes code;
|
||||
};
|
||||
|
||||
struct ZoneStats
|
||||
struct ZoneStats : js::ZoneStatsPod
|
||||
{
|
||||
ZoneStats()
|
||||
: extra(NULL),
|
||||
gcHeapArenaAdmin(0),
|
||||
gcHeapUnusedGcThings(0),
|
||||
gcHeapStringsNormal(0),
|
||||
gcHeapStringsShort(0),
|
||||
gcHeapLazyScripts(0),
|
||||
gcHeapTypeObjects(0),
|
||||
gcHeapIonCodes(0),
|
||||
stringCharsNonHuge(0),
|
||||
lazyScripts(0),
|
||||
typeObjects(0),
|
||||
typePool(0),
|
||||
hugeStrings()
|
||||
ZoneStats() {
|
||||
strings.init();
|
||||
}
|
||||
|
||||
ZoneStats(mozilla::MoveRef<ZoneStats> other)
|
||||
: ZoneStatsPod(other),
|
||||
strings(mozilla::Move(other->strings)),
|
||||
notableStrings(mozilla::Move(other->notableStrings))
|
||||
{}
|
||||
|
||||
ZoneStats(const ZoneStats &other)
|
||||
: extra(other.extra),
|
||||
gcHeapArenaAdmin(other.gcHeapArenaAdmin),
|
||||
gcHeapUnusedGcThings(other.gcHeapUnusedGcThings),
|
||||
gcHeapStringsNormal(other.gcHeapStringsNormal),
|
||||
gcHeapStringsShort(other.gcHeapStringsShort),
|
||||
gcHeapLazyScripts(other.gcHeapLazyScripts),
|
||||
gcHeapTypeObjects(other.gcHeapTypeObjects),
|
||||
gcHeapIonCodes(other.gcHeapIonCodes),
|
||||
stringCharsNonHuge(other.stringCharsNonHuge),
|
||||
lazyScripts(other.lazyScripts),
|
||||
typeObjects(other.typeObjects),
|
||||
typePool(other.typePool),
|
||||
hugeStrings()
|
||||
{
|
||||
hugeStrings.appendAll(other.hugeStrings);
|
||||
// Add other's numbers to this object's numbers. Both objects'
|
||||
// notableStrings vectors must be empty at this point, because we can't
|
||||
// merge them. (A NotableStringInfo contains only a prefix of the string,
|
||||
// so we can't tell whether two NotableStringInfo objects correspond to the
|
||||
// same string.)
|
||||
void add(const ZoneStats &other) {
|
||||
ZoneStatsPod::add(other);
|
||||
|
||||
MOZ_ASSERT(notableStrings.empty());
|
||||
MOZ_ASSERT(other.notableStrings.empty());
|
||||
|
||||
for (StringsHashMap::Range r = other.strings.all(); !r.empty(); r.popFront()) {
|
||||
StringsHashMap::AddPtr p = strings.lookupForAdd(r.front().key);
|
||||
if (p) {
|
||||
// We've seen this string before; add its size to our tally.
|
||||
p->value.add(r.front().value);
|
||||
} else {
|
||||
// We haven't seen this string before; add it to the hashtable.
|
||||
strings.add(p, r.front().key, r.front().value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add other's numbers to this object's numbers.
|
||||
void add(ZoneStats &other) {
|
||||
#define ADD(x) this->x += other.x
|
||||
typedef js::HashMap<JSString*, StringInfo, js::StringHashPolicy, js::SystemAllocPolicy>
|
||||
StringsHashMap;
|
||||
|
||||
ADD(gcHeapArenaAdmin);
|
||||
ADD(gcHeapUnusedGcThings);
|
||||
|
||||
ADD(gcHeapStringsNormal);
|
||||
ADD(gcHeapStringsShort);
|
||||
ADD(gcHeapLazyScripts);
|
||||
ADD(gcHeapTypeObjects);
|
||||
ADD(gcHeapIonCodes);
|
||||
|
||||
ADD(stringCharsNonHuge);
|
||||
ADD(lazyScripts);
|
||||
ADD(typeObjects);
|
||||
ADD(typePool);
|
||||
|
||||
#undef ADD
|
||||
|
||||
hugeStrings.appendAll(other.hugeStrings);
|
||||
}
|
||||
|
||||
// This field can be used by embedders.
|
||||
void *extra;
|
||||
|
||||
size_t gcHeapArenaAdmin;
|
||||
size_t gcHeapUnusedGcThings;
|
||||
|
||||
size_t gcHeapStringsNormal;
|
||||
size_t gcHeapStringsShort;
|
||||
|
||||
size_t gcHeapLazyScripts;
|
||||
size_t gcHeapTypeObjects;
|
||||
size_t gcHeapIonCodes;
|
||||
|
||||
size_t stringCharsNonHuge;
|
||||
size_t lazyScripts;
|
||||
size_t typeObjects;
|
||||
size_t typePool;
|
||||
|
||||
js::Vector<HugeStringInfo, 0, js::SystemAllocPolicy> hugeStrings;
|
||||
StringsHashMap strings;
|
||||
js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings;
|
||||
|
||||
// The size of all the live things in the GC heap that don't belong to any
|
||||
// compartment.
|
||||
|
@ -19,10 +19,14 @@
|
||||
#include "vm/Runtime.h"
|
||||
#include "vm/Shape.h"
|
||||
#include "vm/WrapperObject.h"
|
||||
#include "vm/String.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using mozilla::DebugOnly;
|
||||
using mozilla::Move;
|
||||
using mozilla::MoveRef;
|
||||
using mozilla::PodEqual;
|
||||
|
||||
using namespace js;
|
||||
|
||||
@ -31,12 +35,114 @@ using JS::ObjectPrivateVisitor;
|
||||
using JS::ZoneStats;
|
||||
using JS::CompartmentStats;
|
||||
|
||||
namespace js {
|
||||
|
||||
JS_FRIEND_API(size_t)
|
||||
js::MemoryReportingSundriesThreshold()
|
||||
MemoryReportingSundriesThreshold()
|
||||
{
|
||||
return 8 * 1024;
|
||||
}
|
||||
|
||||
/* static */ HashNumber
|
||||
StringHashPolicy::hash(const Lookup &l)
|
||||
{
|
||||
const jschar *chars = l->maybeChars();
|
||||
ScopedJSFreePtr<jschar> ownedChars;
|
||||
if (!chars) {
|
||||
if (!l->getCharsNonDestructive(/* tcx */ NULL, ownedChars))
|
||||
MOZ_CRASH("oom");
|
||||
chars = ownedChars;
|
||||
}
|
||||
|
||||
return mozilla::HashString(chars, l->length());
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
StringHashPolicy::match(const JSString *const &k, const Lookup &l)
|
||||
{
|
||||
// We can't use js::EqualStrings, because that flattens our strings.
|
||||
if (k->length() != l->length())
|
||||
return false;
|
||||
|
||||
const jschar *c1 = k->maybeChars();
|
||||
ScopedJSFreePtr<jschar> ownedChars1;
|
||||
if (!c1) {
|
||||
if (!k->getCharsNonDestructive(/* tcx */ NULL, ownedChars1))
|
||||
MOZ_CRASH("oom");
|
||||
c1 = ownedChars1;
|
||||
}
|
||||
|
||||
const jschar *c2 = l->maybeChars();
|
||||
ScopedJSFreePtr<jschar> ownedChars2;
|
||||
if (!c2) {
|
||||
if (!l->getCharsNonDestructive(/* tcx */ NULL, ownedChars2))
|
||||
MOZ_CRASH("oom");
|
||||
c2 = ownedChars2;
|
||||
}
|
||||
|
||||
return PodEqual(c1, c2, k->length());
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
namespace JS
|
||||
{
|
||||
|
||||
NotableStringInfo::NotableStringInfo()
|
||||
: bufferSize(0),
|
||||
buffer(0)
|
||||
{}
|
||||
|
||||
NotableStringInfo::NotableStringInfo(JSString *str, const StringInfo &info)
|
||||
: StringInfo(info)
|
||||
{
|
||||
bufferSize = Min(str->length() + 1, size_t(4096));
|
||||
buffer = js_pod_malloc<char>(bufferSize);
|
||||
if (!buffer) {
|
||||
MOZ_CRASH("oom");
|
||||
}
|
||||
|
||||
const jschar* chars = str->maybeChars();
|
||||
ScopedJSFreePtr<jschar> ownedChars;
|
||||
if (!chars) {
|
||||
if (!str->getCharsNonDestructive(/* tcx */ NULL, ownedChars))
|
||||
MOZ_CRASH("oom");
|
||||
chars = ownedChars;
|
||||
}
|
||||
|
||||
// We might truncate |str| even if it's much shorter than 4096 chars, if
|
||||
// |str| contains unicode chars. Since this is just for a memory reporter,
|
||||
// we don't care.
|
||||
PutEscapedString(buffer, bufferSize, chars, str->length(), /* quote */ 0);
|
||||
}
|
||||
|
||||
NotableStringInfo::NotableStringInfo(const NotableStringInfo& info)
|
||||
: StringInfo(info),
|
||||
bufferSize(info.bufferSize)
|
||||
{
|
||||
buffer = js_pod_malloc<char>(bufferSize);
|
||||
if (!buffer)
|
||||
MOZ_CRASH("oom");
|
||||
|
||||
strcpy(buffer, info.buffer);
|
||||
}
|
||||
|
||||
NotableStringInfo::NotableStringInfo(MoveRef<NotableStringInfo> info)
|
||||
: StringInfo(info)
|
||||
{
|
||||
buffer = info->buffer;
|
||||
info->buffer = NULL;
|
||||
}
|
||||
|
||||
NotableStringInfo &NotableStringInfo::operator=(MoveRef<NotableStringInfo> info)
|
||||
{
|
||||
this->~NotableStringInfo();
|
||||
new (this) NotableStringInfo(info);
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace JS
|
||||
|
||||
typedef HashSet<ScriptSource *, DefaultHasher<ScriptSource *>, SystemAllocPolicy> SourceSet;
|
||||
|
||||
struct IteratorClosure
|
||||
@ -201,23 +307,25 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin
|
||||
case JSTRACE_STRING: {
|
||||
JSString *str = static_cast<JSString *>(thing);
|
||||
|
||||
size_t strSize = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
|
||||
size_t strCharsSize = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
|
||||
MOZ_ASSERT_IF(str->isShort(), strCharsSize == 0);
|
||||
|
||||
// If we can't grow hugeStrings, let's just call this string non-huge.
|
||||
// We're probably about to OOM anyway.
|
||||
if (strSize >= JS::HugeStringInfo::MinSize() && zStats->hugeStrings.growBy(1)) {
|
||||
zStats->gcHeapStringsNormal += thingSize;
|
||||
JS::HugeStringInfo &info = zStats->hugeStrings.back();
|
||||
info.length = str->length();
|
||||
info.size = strSize;
|
||||
PutEscapedString(info.buffer, sizeof(info.buffer), &str->asLinear(), 0);
|
||||
} else if (str->isShort()) {
|
||||
MOZ_ASSERT(strSize == 0);
|
||||
zStats->gcHeapStringsShort += thingSize;
|
||||
size_t shortStringThingSize = str->isShort() ? thingSize : 0;
|
||||
size_t normalStringThingSize = !str->isShort() ? thingSize : 0;
|
||||
|
||||
ZoneStats::StringsHashMap::AddPtr p = zStats->strings.lookupForAdd(str);
|
||||
if (!p) {
|
||||
JS::StringInfo info(str->length(), shortStringThingSize,
|
||||
normalStringThingSize, strCharsSize);
|
||||
zStats->strings.add(p, str, info);
|
||||
} else {
|
||||
zStats->gcHeapStringsNormal += thingSize;
|
||||
zStats->stringCharsNonHuge += strSize;
|
||||
p->value.add(shortStringThingSize, normalStringThingSize, strCharsSize);
|
||||
}
|
||||
|
||||
zStats->gcHeapStringsShort += shortStringThingSize;
|
||||
zStats->gcHeapStringsNormal += normalStringThingSize;
|
||||
zStats->stringCharsNonNotable += strCharsSize;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -300,6 +408,44 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin
|
||||
zStats->gcHeapUnusedGcThings -= thingSize;
|
||||
}
|
||||
|
||||
static void
|
||||
FindNotableStrings(ZoneStats &zStats)
|
||||
{
|
||||
using namespace JS;
|
||||
|
||||
// You should only run FindNotableStrings once per ZoneStats object
|
||||
// (although it's not going to break anything if you run it more than once,
|
||||
// unless you add to |strings| in the meantime).
|
||||
MOZ_ASSERT(zStats.notableStrings.empty());
|
||||
|
||||
for (ZoneStats::StringsHashMap::Range r = zStats.strings.all(); !r.empty(); r.popFront()) {
|
||||
|
||||
JSString *str = r.front().key;
|
||||
StringInfo &info = r.front().value;
|
||||
|
||||
// If this string is too small, or if we can't grow the notableStrings
|
||||
// vector, skip this string.
|
||||
if (info.totalSizeOf() < NotableStringInfo::notableSize() ||
|
||||
!zStats.notableStrings.growBy(1))
|
||||
continue;
|
||||
|
||||
zStats.notableStrings.back() = Move(NotableStringInfo(str, info));
|
||||
|
||||
// We're moving this string from a non-notable to a notable bucket, so
|
||||
// subtract it out of the non-notable tallies.
|
||||
MOZ_ASSERT(zStats.gcHeapStringsShort >= info.sizeOfShortStringGCThings);
|
||||
MOZ_ASSERT(zStats.gcHeapStringsNormal >= info.sizeOfNormalStringGCThings);
|
||||
MOZ_ASSERT(zStats.stringCharsNonNotable >= info.sizeOfAllStringChars);
|
||||
zStats.gcHeapStringsShort -= info.sizeOfShortStringGCThings;
|
||||
zStats.gcHeapStringsNormal -= info.sizeOfNormalStringGCThings;
|
||||
zStats.stringCharsNonNotable -= info.sizeOfAllStringChars;
|
||||
}
|
||||
|
||||
// zStats.strings holds unrooted JSString pointers, which we don't want to
|
||||
// expose out into the dangerous land where we might GC.
|
||||
zStats.strings.clear();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv)
|
||||
{
|
||||
@ -340,8 +486,14 @@ JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisit
|
||||
#ifdef DEBUG
|
||||
totalArenaSize += zStats.gcHeapArenaAdmin + zStats.gcHeapUnusedGcThings;
|
||||
#endif
|
||||
|
||||
// Move any strings which take up more than the sundries threshold
|
||||
// (counting all of their copies together) into notableStrings.
|
||||
FindNotableStrings(zStats);
|
||||
}
|
||||
|
||||
FindNotableStrings(rtStats->zTotals);
|
||||
|
||||
for (size_t i = 0; i < rtStats->compartmentStatsVector.length(); i++) {
|
||||
CompartmentStats &cStats = rtStats->compartmentStatsVector[i];
|
||||
|
||||
|
@ -1575,13 +1575,35 @@ NS_MEMORY_REPORTER_IMPLEMENT(JSMainRuntimeTemporaryPeak,
|
||||
#define REPORT(_path, _kind, _units, _amount, _desc) \
|
||||
do { \
|
||||
nsresult rv; \
|
||||
rv = cb->Callback(EmptyCString(), _path, _kind, _units, _amount, \
|
||||
NS_LITERAL_CSTRING(_desc), closure); \
|
||||
rv = cb->Callback(EmptyCString(), _path, \
|
||||
nsIMemoryReporter::_kind, \
|
||||
nsIMemoryReporter::_units, \
|
||||
_amount, \
|
||||
NS_LITERAL_CSTRING(_desc), \
|
||||
closure); \
|
||||
NS_ENSURE_SUCCESS(rv, rv); \
|
||||
} while (0)
|
||||
|
||||
#define REPORT_BYTES(_path, _kind, _amount, _desc) \
|
||||
REPORT(_path, _kind, nsIMemoryReporter::UNITS_BYTES, _amount, _desc);
|
||||
REPORT(_path, _kind, UNITS_BYTES, _amount, _desc);
|
||||
|
||||
// REPORT2 and REPORT_BYTES2 are just like REPORT and REPORT_BYTES, except the
|
||||
// description is an nsCString, instead of a literal string.
|
||||
|
||||
#define REPORT2(_path, _kind, _units, _amount, _desc) \
|
||||
do { \
|
||||
nsresult rv; \
|
||||
rv = cb->Callback(EmptyCString(), _path, \
|
||||
nsIMemoryReporter::_kind, \
|
||||
nsIMemoryReporter::_units, \
|
||||
_amount, \
|
||||
_desc, \
|
||||
closure); \
|
||||
NS_ENSURE_SUCCESS(rv, rv); \
|
||||
} while (0)
|
||||
|
||||
#define REPORT_BYTES2(_path, _kind, _amount, _desc) \
|
||||
REPORT2(_path, _kind, UNITS_BYTES, _amount, _desc);
|
||||
|
||||
#define REPORT_GC_BYTES(_path, _amount, _desc) \
|
||||
do { \
|
||||
@ -1679,21 +1701,6 @@ ReportZoneStats(const JS::ZoneStats &zStats,
|
||||
"heap taken by empty GC thing slots within non-empty "
|
||||
"arenas.");
|
||||
|
||||
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/strings/normal"),
|
||||
zStats.gcHeapStringsNormal,
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap that holds normal string headers. String headers contain "
|
||||
"various pieces of information about a string, but do not "
|
||||
"contain (except in the case of very short strings) the "
|
||||
"string characters; characters in longer strings are "
|
||||
"counted under 'gc-heap/string-chars' instead.");
|
||||
|
||||
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/strings/short"),
|
||||
zStats.gcHeapStringsShort,
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap that holds over-sized string headers, in which "
|
||||
"string characters are stored inline.");
|
||||
|
||||
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/lazy-scripts"),
|
||||
zStats.gcHeapLazyScripts,
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
@ -1724,8 +1731,121 @@ ReportZoneStats(const JS::ZoneStats &zStats,
|
||||
zStats.typePool,
|
||||
"Memory holding contents of type sets and related data.");
|
||||
|
||||
ZCREPORT_BYTES2(pathPrefix + NS_LITERAL_CSTRING("string-chars/non-huge"),
|
||||
zStats.stringCharsNonHuge, nsPrintfCString(
|
||||
// Our notable string reporter needs to change these values, so copy them
|
||||
// here.
|
||||
size_t gcHeapStringsShort = zStats.gcHeapStringsShort;
|
||||
size_t gcHeapStringsNormal = zStats.gcHeapStringsNormal;
|
||||
size_t stringCharsNonNotable = zStats.stringCharsNonNotable;
|
||||
size_t gcHeapStringsAboutMemory = 0;
|
||||
size_t stringCharsAboutMemory = 0;
|
||||
|
||||
for (size_t i = 0; i < zStats.notableStrings.length(); i++) {
|
||||
const JS::NotableStringInfo &info = zStats.notableStrings[i];
|
||||
|
||||
nsDependentCString notableString(info.buffer);
|
||||
|
||||
// Viewing about:memory generates many notable strings which contain
|
||||
// "string(length=". If we report these as notable, then we'll create
|
||||
// even more notable strings the next time we open about:memory (unless
|
||||
// there's a GC in the meantime), and so on ad infinitum.
|
||||
//
|
||||
// To avoid cluttering up about:memory like this, we stick notable
|
||||
// strings which contain "string(length=" into their own bucket.
|
||||
# define STRING_LENGTH "string(length="
|
||||
if (FindInReadable(NS_LITERAL_CSTRING(STRING_LENGTH), notableString)) {
|
||||
gcHeapStringsAboutMemory += info.totalGCThingSizeOf();
|
||||
stringCharsAboutMemory += info.sizeOfAllStringChars;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Escape / to \ before we put notableString into the memory reporter
|
||||
// path, because we don't want any forward slashes in the string to
|
||||
// count as path separators.
|
||||
nsCString escapedString(notableString);
|
||||
escapedString.ReplaceSubstring("/", "\\");
|
||||
|
||||
bool truncated = notableString.Length() < info.length;
|
||||
|
||||
nsCString notableBlurb =
|
||||
nsPrintfCString("notable/" STRING_LENGTH "%d, copies=%d, \"%s\"%s)",
|
||||
info.length, info.numCopies, escapedString.get(),
|
||||
truncated ? " (truncated)" : "");
|
||||
# undef STRING_LENGTH
|
||||
|
||||
// SpiderMonkey considers a string to be notable if all of its copies
|
||||
// together take up more than notableSize() bytes of memory, counting
|
||||
// both JS GC heap memory and string-chars. Most notable strings will
|
||||
// use either a lot of gc-heap memory or a lot of string-chars memory,
|
||||
// but only strings of a certain sweet-spot length will have their
|
||||
// memory usage spread roughly equally between the two types of memory.
|
||||
//
|
||||
// We don't want to clutter up 'gc-heap/strings' with entries for
|
||||
// notable strings which use relatively little GC memory, so instead we
|
||||
// report a notable string in gc-heap/strings only if it uses
|
||||
// |notableSize() / 2| or more bytes of GC memory. We do the same for
|
||||
// string-chars. This ensures that every notable string is reported
|
||||
// under at least one of gc-heap/strings or string-chars.
|
||||
|
||||
MOZ_ASSERT(info.totalGCThingSizeOf() >=
|
||||
JS::NotableStringInfo::notableSize() / 2 ||
|
||||
info.sizeOfAllStringChars >=
|
||||
JS::NotableStringInfo::notableSize() / 2);
|
||||
|
||||
if (info.totalGCThingSizeOf() >= JS::NotableStringInfo::notableSize() / 2) {
|
||||
REPORT_BYTES2(pathPrefix + NS_LITERAL_CSTRING("gc-heap/strings/") + notableBlurb,
|
||||
KIND_NONHEAP,
|
||||
info.totalGCThingSizeOf(),
|
||||
nsPrintfCString("Memory allocated to hold headers for copies of "
|
||||
"the given notable string. A string is notable if all of its copies "
|
||||
"together use more than %d bytes total of JS GC heap and malloc heap "
|
||||
"memory.\n\n"
|
||||
"These headers may contain the string data itself, if the string "
|
||||
"is short enough. If so, the string won't have any memory reported "
|
||||
"under 'string-chars/.",
|
||||
JS::NotableStringInfo::notableSize()));
|
||||
|
||||
// Don't add to gcTotal if we're adding to
|
||||
// gcHeapStrings{Short,Normal}; when we report those values,
|
||||
// they'll get added into gcTotal.
|
||||
gcTotal += info.totalGCThingSizeOf();
|
||||
} else {
|
||||
gcHeapStringsShort += info.sizeOfShortStringGCThings;
|
||||
gcHeapStringsNormal += info.sizeOfNormalStringGCThings;
|
||||
}
|
||||
|
||||
if (info.sizeOfAllStringChars >= JS::NotableStringInfo::notableSize() / 2) {
|
||||
REPORT_BYTES2(pathPrefix + NS_LITERAL_CSTRING("string-chars/") + notableBlurb,
|
||||
KIND_HEAP,
|
||||
info.sizeOfAllStringChars,
|
||||
nsPrintfCString("Memory allocated to hold string data for copies "
|
||||
"of the given notable string. A string is notable if all of its "
|
||||
"copies together use more than %d bytes total of JS GC heap and "
|
||||
"malloc heap memory.\n\n",
|
||||
JS::NotableStringInfo::notableSize()));
|
||||
} else {
|
||||
stringCharsNonNotable += info.sizeOfAllStringChars;
|
||||
}
|
||||
}
|
||||
|
||||
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/strings/short"),
|
||||
gcHeapStringsShort,
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap that holds over-sized string headers, in which "
|
||||
"string characters are stored inline.");
|
||||
|
||||
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/strings/normal"),
|
||||
gcHeapStringsNormal,
|
||||
"Memory on the garbage-collected JavaScript "
|
||||
"heap that holds headers for strings that are not short "
|
||||
"and that don't appear under 'gc-heap/strings/notable*'.\n\n "
|
||||
"String headers contain various pieces of information "
|
||||
"about a string, but do not contain (except in the case of "
|
||||
"very short strings) the string characters; characters "
|
||||
"in longer strings are counted under 'gc-heap/string-chars' "
|
||||
"instead.");
|
||||
|
||||
ZCREPORT_BYTES2(pathPrefix + NS_LITERAL_CSTRING("string-chars/non-notable"),
|
||||
stringCharsNonNotable, nsPrintfCString(
|
||||
"Memory allocated to hold characters of strings whose "
|
||||
"characters take up less than than %d bytes of memory.\n\n"
|
||||
"Sometimes more memory is allocated than necessary, to "
|
||||
@ -1733,31 +1853,29 @@ ReportZoneStats(const JS::ZoneStats &zStats,
|
||||
"header which is stored on the compartment's JavaScript heap; "
|
||||
"that header is not counted here, but in 'gc-heap/strings' "
|
||||
"instead.",
|
||||
JS::HugeStringInfo::MinSize()));
|
||||
JS::NotableStringInfo::notableSize()));
|
||||
|
||||
for (size_t i = 0; i < zStats.hugeStrings.length(); i++) {
|
||||
const JS::HugeStringInfo& info = zStats.hugeStrings[i];
|
||||
if (gcHeapStringsAboutMemory > 0) {
|
||||
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/strings/notable/about-memory"),
|
||||
gcHeapStringsAboutMemory,
|
||||
"Memory allocated on the garbage-collected JavaScript "
|
||||
"heap that holds headers for notable strings which "
|
||||
"contain the string 'string(length='. These strings are "
|
||||
"likely from about:memory itself. We filter them out "
|
||||
"rather than display them, because displaying them would "
|
||||
"create even more strings every time you refresh "
|
||||
"about:memory.");
|
||||
}
|
||||
|
||||
nsDependentCString hugeString(info.buffer);
|
||||
|
||||
// Escape / to \ before we put hugeString into the memory reporter
|
||||
// path, because we don't want any forward slashes in the string to
|
||||
// count as path separators.
|
||||
nsCString escapedString(hugeString);
|
||||
escapedString.ReplaceSubstring("/", "\\");
|
||||
|
||||
ZCREPORT_BYTES2(
|
||||
pathPrefix +
|
||||
nsPrintfCString("string-chars/huge/string(length=%d, \"%s...\")",
|
||||
info.length, escapedString.get()),
|
||||
info.size,
|
||||
nsPrintfCString("Memory allocated to hold characters of "
|
||||
"a length-%d string which begins \"%s\".\n\n"
|
||||
"Sometimes more memory is allocated than necessary, to simplify "
|
||||
"string concatenation. Each string also includes a header which is "
|
||||
"stored on the compartment's JavaScript heap; that header is not "
|
||||
"counted here, but in 'gc-heap/strings' instead.",
|
||||
info.length, hugeString.get()));
|
||||
if (stringCharsAboutMemory > 0) {
|
||||
ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("string-chars/notable/about-memory"),
|
||||
stringCharsAboutMemory,
|
||||
"Memory allocated to hold characters of notable strings "
|
||||
"which contain the string 'string(length='. These strings "
|
||||
"are likely from about:memory itself. We filter them out "
|
||||
"rather than display them, because displaying them would "
|
||||
"create even more strings every time you refresh "
|
||||
"about:memory.");
|
||||
}
|
||||
|
||||
if (gcHeapSundries > 0) {
|
||||
@ -2050,7 +2168,7 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
|
||||
size_t gcTotal = 0;
|
||||
|
||||
for (size_t i = 0; i < rtStats.zoneStatsVector.length(); i++) {
|
||||
JS::ZoneStats zStats = rtStats.zoneStatsVector[i];
|
||||
const JS::ZoneStats &zStats = rtStats.zoneStatsVector[i];
|
||||
const xpc::ZoneStatsExtras *extras =
|
||||
static_cast<const xpc::ZoneStatsExtras*>(zStats.extra);
|
||||
rv = ReportZoneStats(zStats, *extras, cb, closure, &gcTotal);
|
||||
@ -2240,10 +2358,7 @@ class JSCompartmentsMultiReporter MOZ_FINAL : public nsIMemoryMultiReporter
|
||||
// Report.
|
||||
for (size_t i = 0; i < paths.length(); i++)
|
||||
// These ones don't need a description, hence the "".
|
||||
REPORT(nsCString(paths[i]),
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
nsIMemoryReporter::UNITS_COUNT,
|
||||
1, "");
|
||||
REPORT(nsCString(paths[i]), KIND_OTHER, UNITS_COUNT, 1, "");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user