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:
Justin Lebar 2013-08-05 16:33:00 -07:00
parent f2f063a8e3
commit fb5630ed11
3 changed files with 496 additions and 150 deletions

View File

@ -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.

View File

@ -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];

View File

@ -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;
}