Bug 972712 (part 3) - Rework notable string reporting. r=till.

--HG--
extra : rebase_source : 274c2ee9beafca5e464234f37e894967d20abb25
This commit is contained in:
Nicholas Nethercote 2014-02-26 18:11:01 -08:00
parent 3cd29dbeac
commit fdb45bb355
3 changed files with 181 additions and 194 deletions

View File

@ -92,6 +92,9 @@ struct InefficientNonFlatteningStringHashPolicy
#define ZERO_SIZE(kind, gc, mSize) mSize(0), #define ZERO_SIZE(kind, gc, mSize) mSize(0),
#define COPY_OTHER_SIZE(kind, gc, mSize) mSize(other.mSize), #define COPY_OTHER_SIZE(kind, gc, mSize) mSize(other.mSize),
#define ADD_OTHER_SIZE(kind, gc, mSize) mSize += other.mSize; #define ADD_OTHER_SIZE(kind, gc, mSize) mSize += other.mSize;
#define SUB_OTHER_SIZE(kind, gc, mSize) MOZ_ASSERT(mSize >= other.mSize); \
mSize -= other.mSize;
#define ADD_SIZE_TO_N(kind, gc, mSize) n += mSize;
#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(kind, gc, mSize) n += (js::gc) ? mSize : 0; #define ADD_SIZE_TO_N_IF_LIVE_GC_THING(kind, gc, mSize) n += (js::gc) ? mSize : 0;
#define ADD_TO_TAB_SIZES(kind, gc, mSize) sizes->add(JS::TabSizes::kind, mSize); #define ADD_TO_TAB_SIZES(kind, gc, mSize) sizes->add(JS::TabSizes::kind, mSize);
@ -101,48 +104,6 @@ enum {
IsLiveGCThing = true IsLiveGCThing = true
}; };
struct ZoneStatsPod
{
#define FOR_EACH_SIZE(macro) \
macro(Other, NotLiveGCThing, gcHeapArenaAdmin) \
macro(Other, NotLiveGCThing, unusedGCThings) \
macro(Other, IsLiveGCThing, lazyScriptsGCHeap) \
macro(Other, NotLiveGCThing, lazyScriptsMallocHeap) \
macro(Other, IsLiveGCThing, jitCodesGCHeap) \
macro(Other, IsLiveGCThing, typeObjectsGCHeap) \
macro(Other, NotLiveGCThing, typeObjectsMallocHeap) \
macro(Other, NotLiveGCThing, typePool) \
macro(Strings, IsLiveGCThing, stringsGCHeap) \
macro(Strings, NotLiveGCThing, stringsMallocHeap)
ZoneStatsPod()
: FOR_EACH_SIZE(ZERO_SIZE)
extra()
{}
void add(const ZoneStatsPod &other) {
FOR_EACH_SIZE(ADD_OTHER_SIZE)
// Do nothing with |extra|.
}
size_t sizeOfLiveGCThings() const {
size_t n = 0;
FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
// Do nothing with |extra|.
return n;
}
void addToTabSizes(JS::TabSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
// Do nothing with |extra|.
}
FOR_EACH_SIZE(DECL_SIZE)
void *extra; // This field can be used by embedders.
#undef FOR_EACH_SIZE
};
} // namespace js } // namespace js
namespace JS { namespace JS {
@ -240,45 +201,57 @@ struct GCSizes
// is not. // is not.
struct StringInfo struct StringInfo
{ {
#define FOR_EACH_SIZE(macro) \
macro(Strings, IsLiveGCThing, gcHeap) \
macro(Strings, NotLiveGCThing, mallocHeap) \
StringInfo() StringInfo()
: numCopies(0), : FOR_EACH_SIZE(ZERO_SIZE)
gcHeap(0), numCopies(0)
mallocHeap(0)
{} {}
StringInfo(size_t gcSize, size_t mallocSize) void add(const StringInfo &other) {
: numCopies(1), FOR_EACH_SIZE(ADD_OTHER_SIZE);
gcHeap(gcSize),
mallocHeap(mallocSize)
{}
void add(size_t gcSize, size_t mallocSize) {
numCopies++; numCopies++;
gcHeap += gcSize;
mallocHeap += mallocSize;
} }
void add(const StringInfo& info) { void subtract(const StringInfo &other) {
numCopies += info.numCopies; FOR_EACH_SIZE(SUB_OTHER_SIZE);
gcHeap += info.gcHeap; numCopies--;
mallocHeap += info.mallocHeap;
} }
bool isNotable() const {
static const size_t NotabilityThreshold = 16 * 1024;
size_t n = 0;
FOR_EACH_SIZE(ADD_SIZE_TO_N)
return n >= NotabilityThreshold;
}
size_t sizeOfLiveGCThings() const {
size_t n = 0;
FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
return n;
}
void addToTabSizes(TabSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
}
FOR_EACH_SIZE(DECL_SIZE)
uint32_t numCopies; // How many copies of the string have we seen? uint32_t numCopies; // How many copies of the string have we seen?
// These are all totals across all copies of the string we've seen. #undef FOR_EACH_SIZE
size_t gcHeap;
size_t mallocHeap;
}; };
// Holds data about a notable string (one which uses more than // Holds data about a notable string (one which, counting all duplicates, uses
// NotableStringInfo::notableSize() bytes of memory), so we can report it // more than a certain amount of memory) so we can report it individually.
// individually.
// //
// Essentially the only difference between this class and StringInfo is that // The only difference between this class and StringInfo is that
// NotableStringInfo holds a copy of the string's chars. // NotableStringInfo holds a copy of some or all of the string's chars.
struct NotableStringInfo : public StringInfo struct NotableStringInfo : public StringInfo
{ {
static const size_t MAX_SAVED_CHARS = 1024;
NotableStringInfo(); NotableStringInfo();
NotableStringInfo(JSString *str, const StringInfo &info); NotableStringInfo(JSString *str, const StringInfo &info);
NotableStringInfo(NotableStringInfo &&info); NotableStringInfo(NotableStringInfo &&info);
@ -288,12 +261,6 @@ struct NotableStringInfo : public StringInfo
js_free(buffer); js_free(buffer);
} }
// A string needs to take up this many bytes of storage before we consider
// it to be "notable".
static size_t notableSize() {
return js::MemoryReportingSundriesThreshold();
}
char *buffer; char *buffer;
size_t length; size_t length;
@ -331,63 +298,89 @@ struct RuntimeSizes
#undef FOR_EACH_SIZE #undef FOR_EACH_SIZE
}; };
struct ZoneStats : js::ZoneStatsPod struct ZoneStats
{ {
#define FOR_EACH_SIZE(macro) \
macro(Other, NotLiveGCThing, gcHeapArenaAdmin) \
macro(Other, NotLiveGCThing, unusedGCThings) \
macro(Other, IsLiveGCThing, lazyScriptsGCHeap) \
macro(Other, NotLiveGCThing, lazyScriptsMallocHeap) \
macro(Other, IsLiveGCThing, jitCodesGCHeap) \
macro(Other, IsLiveGCThing, typeObjectsGCHeap) \
macro(Other, NotLiveGCThing, typeObjectsMallocHeap) \
macro(Other, NotLiveGCThing, typePool) \
ZoneStats() ZoneStats()
: strings(nullptr) : FOR_EACH_SIZE(ZERO_SIZE)
stringInfo(),
extra(),
allStrings(nullptr),
notableStrings(),
isTotals(true)
{} {}
ZoneStats(ZoneStats &&other) ZoneStats(ZoneStats &&other)
: ZoneStatsPod(mozilla::Move(other)), : FOR_EACH_SIZE(COPY_OTHER_SIZE)
strings(other.strings), stringInfo(mozilla::Move(other.stringInfo)),
notableStrings(mozilla::Move(other.notableStrings)) extra(other.extra),
allStrings(other.allStrings),
notableStrings(mozilla::Move(other.notableStrings)),
isTotals(other.isTotals)
{ {
other.strings = nullptr; other.allStrings = nullptr;
MOZ_ASSERT(!other.isTotals);
}
~ZoneStats() {
// |allStrings| is usually deleted and set to nullptr before this
// destructor runs. But there are failure cases due to OOMs that may
// prevent that, so it doesn't hurt to try again here.
js_delete(allStrings);
} }
bool initStrings(JSRuntime *rt); bool initStrings(JSRuntime *rt);
// Add |other|'s numbers to this object's numbers. The strings data isn't void addSizes(const ZoneStats &other) {
// touched. MOZ_ASSERT(isTotals);
void addIgnoringStrings(const ZoneStats &other) { FOR_EACH_SIZE(ADD_OTHER_SIZE)
ZoneStatsPod::add(other); stringInfo.add(other.stringInfo);
}
// Add |other|'s strings data to this object's strings data. (We don't do
// anything with notableStrings.)
void addStrings(const ZoneStats &other) {
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());
}
}
} }
size_t sizeOfLiveGCThings() const { size_t sizeOfLiveGCThings() const {
size_t n = ZoneStatsPod::sizeOfLiveGCThings(); MOZ_ASSERT(isTotals);
for (size_t i = 0; i < notableStrings.length(); i++) { size_t n = 0;
const JS::NotableStringInfo& info = notableStrings[i]; FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
n += info.gcHeap; n += stringInfo.sizeOfLiveGCThings();
}
return n; return n;
} }
typedef js::HashMap<JSString*, void addToTabSizes(JS::TabSizes *sizes) const {
StringInfo, MOZ_ASSERT(isTotals);
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
stringInfo.addToTabSizes(sizes);
}
// These string measurements are initially for all strings. At the end,
// if the measurement granularity is FineGrained, we subtract the
// measurements of the notable script sources and move them into
// |notableStrings|.
FOR_EACH_SIZE(DECL_SIZE)
StringInfo stringInfo;
void *extra; // This field can be used by embedders.
typedef js::HashMap<JSString*, StringInfo,
js::InefficientNonFlatteningStringHashPolicy, js::InefficientNonFlatteningStringHashPolicy,
js::SystemAllocPolicy> StringsHashMap; js::SystemAllocPolicy> StringsHashMap;
// |strings| is only used transiently. During the zone traversal it is // |allStrings| is only used transiently. During the zone traversal it is
// filled with info about every string in the zone. It's then used to fill // filled with info about every string in the zone. It's then used to fill
// in |notableStrings| (which actually gets reported), and immediately // in |notableStrings| (which actually gets reported), and immediately
// discarded afterwards. // discarded afterwards.
StringsHashMap *strings; StringsHashMap *allStrings;
js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings; js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings;
bool isTotals;
#undef FOR_EACH_SIZE
}; };
struct CompartmentStats struct CompartmentStats
@ -564,6 +557,8 @@ AddSizeOfTab(JSRuntime *rt, JS::HandleObject obj, mozilla::MallocSizeOf mallocSi
#undef ZERO_SIZE #undef ZERO_SIZE
#undef COPY_OTHER_SIZE #undef COPY_OTHER_SIZE
#undef ADD_OTHER_SIZE #undef ADD_OTHER_SIZE
#undef SUB_OTHER_SIZE
#undef ADD_SIZE_TO_N
#undef ADD_SIZE_TO_N_IF_LIVE_GC_THING #undef ADD_SIZE_TO_N_IF_LIVE_GC_THING
#undef ADD_TO_TAB_SIZES #undef ADD_TO_TAB_SIZES

View File

@ -94,7 +94,8 @@ InefficientNonFlatteningStringHashPolicy::match(const JSString *const &k, const
namespace JS { namespace JS {
NotableStringInfo::NotableStringInfo() NotableStringInfo::NotableStringInfo()
: buffer(0), : StringInfo(),
buffer(0),
length(0) length(0)
{ {
} }
@ -103,7 +104,7 @@ NotableStringInfo::NotableStringInfo(JSString *str, const StringInfo &info)
: StringInfo(info), : StringInfo(info),
length(str->length()) length(str->length())
{ {
size_t bufferSize = Min(str->length() + 1, size_t(4096)); size_t bufferSize = Min(str->length() + 1, size_t(MAX_SAVED_CHARS));
buffer = js_pod_malloc<char>(bufferSize); buffer = js_pod_malloc<char>(bufferSize);
if (!buffer) { if (!buffer) {
MOZ_CRASH("oom"); MOZ_CRASH("oom");
@ -119,7 +120,7 @@ NotableStringInfo::NotableStringInfo(JSString *str, const StringInfo &info)
chars = ownedChars; chars = ownedChars;
} }
// We might truncate |str| even if it's much shorter than 4096 chars, if // We might truncate |str| even if it's much shorter than 1024 chars, if
// |str| contains unicode chars. Since this is just for a memory reporter, // |str| contains unicode chars. Since this is just for a memory reporter,
// we don't care. // we don't care.
PutEscapedString(buffer, bufferSize, chars, str->length(), /* quote */ 0); PutEscapedString(buffer, bufferSize, chars, str->length(), /* quote */ 0);
@ -181,7 +182,8 @@ StatsZoneCallback(JSRuntime *rt, void *data, Zone *zone)
// CollectRuntimeStats reserves enough space. // CollectRuntimeStats reserves enough space.
MOZ_ALWAYS_TRUE(rtStats->zoneStatsVector.growBy(1)); MOZ_ALWAYS_TRUE(rtStats->zoneStatsVector.growBy(1));
ZoneStats &zStats = rtStats->zoneStatsVector.back(); ZoneStats &zStats = rtStats->zoneStatsVector.back();
zStats.initStrings(rt); if (!zStats.initStrings(rt))
MOZ_CRASH("oom");
rtStats->initExtraZoneStats(zone, &zStats); rtStats->initExtraZoneStats(zone, &zStats);
rtStats->currZoneStats = &zStats; rtStats->currZoneStats = &zStats;
@ -280,21 +282,22 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin
case JSTRACE_STRING: { case JSTRACE_STRING: {
JSString *str = static_cast<JSString *>(thing); JSString *str = static_cast<JSString *>(thing);
size_t strCharsSize = str->sizeOfExcludingThis(rtStats->mallocSizeOf_); JS::StringInfo info;
info.gcHeap = thingSize;
info.mallocHeap = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
info.numCopies = 1;
zStats->stringsGCHeap += thingSize; zStats->stringInfo.add(info);
zStats->stringsMallocHeap += strCharsSize;
if (granularity == FineGrained) { if (granularity == FineGrained) {
ZoneStats::StringsHashMap::AddPtr p = zStats->strings->lookupForAdd(str); ZoneStats::StringsHashMap::AddPtr p = zStats->allStrings->lookupForAdd(str);
if (!p) { if (!p) {
JS::StringInfo info(thingSize, strCharsSize); // Ignore failure -- we just won't record the string as notable.
zStats->strings->add(p, str, info); (void)zStats->allStrings->add(p, str, info);
} else { } else {
p->value().add(thingSize, strCharsSize); p->value().add(info);
} }
} }
break; break;
} }
@ -379,47 +382,48 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin
zStats->unusedGCThings -= thingSize; zStats->unusedGCThings -= thingSize;
} }
static void static bool
FindNotableStrings(ZoneStats &zStats) FindNotableStrings(ZoneStats &zStats)
{ {
using namespace JS; using namespace JS;
// You should only run FindNotableStrings once per ZoneStats object // We 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()); MOZ_ASSERT(zStats.notableStrings.empty());
for (ZoneStats::StringsHashMap::Range r = zStats.strings->all(); !r.empty(); r.popFront()) { for (ZoneStats::StringsHashMap::Range r = zStats.allStrings->all(); !r.empty(); r.popFront()) {
JSString *str = r.front().key(); JSString *str = r.front().key();
StringInfo &info = r.front().value(); StringInfo &info = r.front().value();
// If this string is too small, or if we can't grow the notableStrings if (!info.isNotable())
// vector, skip this string.
if (info.gcHeap + info.mallocHeap < NotableStringInfo::notableSize() ||
!zStats.notableStrings.growBy(1))
continue; continue;
if (!zStats.notableStrings.growBy(1))
return false;
zStats.notableStrings.back() = NotableStringInfo(str, info); zStats.notableStrings.back() = NotableStringInfo(str, info);
// We're moving this string from a non-notable to a notable bucket, so // We're moving this string from a non-notable to a notable bucket, so
// subtract it out of the non-notable tallies. // subtract it out of the non-notable tallies.
MOZ_ASSERT(zStats.stringsGCHeap >= info.gcHeap); zStats.stringInfo.subtract(info);
MOZ_ASSERT(zStats.stringsMallocHeap >= info.mallocHeap);
zStats.stringsGCHeap -= info.gcHeap;
zStats.stringsMallocHeap -= info.mallocHeap;
} }
// Delete |allStrings| now, rather than waiting for zStats's destruction,
// to reduce peak memory consumption during reporting.
js_delete(zStats.allStrings);
zStats.allStrings = nullptr;
return true;
} }
bool bool
ZoneStats::initStrings(JSRuntime *rt) ZoneStats::initStrings(JSRuntime *rt)
{ {
strings = rt->new_<StringsHashMap>(); isTotals = false;
if (!strings) allStrings = rt->new_<StringsHashMap>();
if (!allStrings)
return false; return false;
if (!strings->init()) { if (!allStrings->init()) {
js_delete(strings); js_delete(allStrings);
strings = nullptr; allStrings = nullptr;
return false; return false;
} }
return true; return true;
@ -456,41 +460,17 @@ JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisit
ZoneStatsVector &zs = rtStats->zoneStatsVector; ZoneStatsVector &zs = rtStats->zoneStatsVector;
ZoneStats &zTotals = rtStats->zTotals; ZoneStats &zTotals = rtStats->zTotals;
// For each zone: // We don't look for notable strings for zTotals. So we first sum all the
// - sum everything except its strings data into zTotals, and // zones' measurements to get the totals. Then we find the notable strings
// - find its notable strings. // within each zone.
// Also, record which zone had the biggest |strings| hashtable -- to save for (size_t i = 0; i < zs.length(); i++)
// time and memory, we will re-use that hashtable to find the notable zTotals.addSizes(zs[i]);
// strings for zTotals.
size_t iMax = 0;
for (size_t i = 0; i < zs.length(); i++) {
zTotals.addIgnoringStrings(zs[i]);
FindNotableStrings(zs[i]);
if (zs[i].strings->count() > zs[iMax].strings->count())
iMax = i;
}
// Transfer the biggest strings table to zTotals. We can do this because: for (size_t i = 0; i < zs.length(); i++)
// (a) we've found the notable strings for zs[IMax], and so don't need it if (!FindNotableStrings(zs[i]))
// any more for zs, and return false;
// (b) zs[iMax].strings contains a subset of the values that will end up in
// zTotals.strings.
MOZ_ASSERT(!zTotals.strings);
zTotals.strings = zs[iMax].strings;
zs[iMax].strings = nullptr;
// Add the remaining strings hashtables to zTotals, and then get the MOZ_ASSERT(!zTotals.allStrings);
// notable strings for zTotals.
for (size_t i = 0; i < zs.length(); i++) {
if (i != iMax) {
zTotals.addStrings(zs[i]);
js_delete(zs[i].strings);
zs[i].strings = nullptr;
}
}
FindNotableStrings(zTotals);
js_delete(zTotals.strings);
zTotals.strings = nullptr;
for (size_t i = 0; i < rtStats->compartmentStatsVector.length(); i++) { for (size_t i = 0; i < rtStats->compartmentStatsVector.length(); i++) {
CompartmentStats &cStats = rtStats->compartmentStatsVector[i]; CompartmentStats &cStats = rtStats->compartmentStatsVector[i];
@ -598,7 +578,7 @@ AddSizeOfTab(JSRuntime *rt, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectP
StatsCellCallback<CoarseGrained>); StatsCellCallback<CoarseGrained>);
JS_ASSERT(rtStats.zoneStatsVector.length() == 1); JS_ASSERT(rtStats.zoneStatsVector.length() == 1);
rtStats.zTotals.add(rtStats.zoneStatsVector[0]); rtStats.zTotals.addSizes(rtStats.zoneStatsVector[0]);
for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) { for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
CompartmentStats &cStats = rtStats.compartmentStatsVector[i]; CompartmentStats &cStats = rtStats.compartmentStatsVector[i];

View File

@ -1759,6 +1759,8 @@ ReportZoneStats(const JS::ZoneStats &zStats,
const nsAutoCString& pathPrefix = extras.pathPrefix; const nsAutoCString& pathPrefix = extras.pathPrefix;
size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0; size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
MOZ_ASSERT(!gcTotalOut == zStats.isTotals);
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-arena-admin"), ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-arena-admin"),
zStats.gcHeapArenaAdmin, zStats.gcHeapArenaAdmin,
"Bookkeeping information and alignment padding within GC arenas."); "Bookkeeping information and alignment padding within GC arenas.");
@ -1802,6 +1804,8 @@ ReportZoneStats(const JS::ZoneStats &zStats,
for (size_t i = 0; i < zStats.notableStrings.length(); i++) { for (size_t i = 0; i < zStats.notableStrings.length(); i++) {
const JS::NotableStringInfo& info = zStats.notableStrings[i]; const JS::NotableStringInfo& info = zStats.notableStrings[i];
MOZ_ASSERT(!zStats.isTotals);
nsDependentCString notableString(info.buffer); nsDependentCString notableString(info.buffer);
// Viewing about:memory generates many notable strings which contain // Viewing about:memory generates many notable strings which contain
@ -1810,8 +1814,7 @@ ReportZoneStats(const JS::ZoneStats &zStats,
// there's a GC in the meantime), and so on ad infinitum. // there's a GC in the meantime), and so on ad infinitum.
// //
// To avoid cluttering up about:memory like this, we stick notable // To avoid cluttering up about:memory like this, we stick notable
// strings which contain "strings/notable/string(length=" into their own // strings which contain "string(length=" into their own bucket.
// bucket.
# define STRING_LENGTH "string(length=" # define STRING_LENGTH "string(length="
if (FindInReadable(NS_LITERAL_CSTRING(STRING_LENGTH), notableString)) { if (FindInReadable(NS_LITERAL_CSTRING(STRING_LENGTH), notableString)) {
stringsNotableAboutMemoryGCHeap += info.gcHeap; stringsNotableAboutMemoryGCHeap += info.gcHeap;
@ -1828,48 +1831,57 @@ ReportZoneStats(const JS::ZoneStats &zStats,
bool truncated = notableString.Length() < info.length; bool truncated = notableString.Length() < info.length;
nsCString path = pathPrefix + nsCString path = pathPrefix +
nsPrintfCString("strings/notable/" STRING_LENGTH "%d, copies=%d, \"%s\"%s)/", nsPrintfCString("strings/" STRING_LENGTH "%d, copies=%d, \"%s\"%s)/",
info.length, info.numCopies, escapedString.get(), info.length, info.numCopies, escapedString.get(),
truncated ? " (truncated)" : ""); truncated ? " (truncated)" : "");
REPORT_BYTES(path + NS_LITERAL_CSTRING("gc-heap"), if (info.gcHeap > 0) {
KIND_NONHEAP, info.gcHeap, REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap"),
"A notable string, i.e. one whose copies together use a lot of " info.gcHeap,
"GC heap and malloc heap memory. " MAYBE_INLINE); "Strings. " MAYBE_INLINE);
gcTotal += info.gcHeap; }
if (info.mallocHeap > 0) { if (info.mallocHeap > 0) {
REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap"), REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap"),
KIND_HEAP, info.mallocHeap, KIND_HEAP, info.mallocHeap,
"Non-inline string characters of a notable string. " "Non-inline string characters. " MAYBE_OVERALLOCATED);
MAYBE_OVERALLOCATED);
} }
} }
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/non-notable/gc-heap"), nsCString nonNotablePath = pathPrefix;
zStats.stringsGCHeap, nonNotablePath += zStats.isTotals
"Non-notable strings. " MAYBE_INLINE); ? NS_LITERAL_CSTRING("strings/")
: NS_LITERAL_CSTRING("strings/string(<non-notable strings>)/");
ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/non-notable/malloc-heap"), if (zStats.stringInfo.gcHeap > 0) {
zStats.stringsMallocHeap, REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap"),
"Non-inline string characters of non-notable strings. " zStats.stringInfo.gcHeap,
MAYBE_OVERALLOCATED); "Strings. " MAYBE_INLINE);
}
if (zStats.stringInfo.mallocHeap > 0) {
REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap"),
KIND_HEAP, zStats.stringInfo.mallocHeap,
"Non-inline string characters. " MAYBE_OVERALLOCATED);
}
if (stringsNotableAboutMemoryGCHeap > 0) { if (stringsNotableAboutMemoryGCHeap > 0) {
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/notable/about-memory/gc-heap"), MOZ_ASSERT(!zStats.isTotals);
REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/gc-heap"),
stringsNotableAboutMemoryGCHeap, stringsNotableAboutMemoryGCHeap,
"Notable strings that contain the characters '" STRING_LENGTH "', " "Strings that contain the characters '" STRING_LENGTH "', which "
"which are probably from about:memory itself." MAYBE_INLINE "are probably from about:memory itself." MAYBE_INLINE
" We filter them out rather than display them, because displaying " " We filter them out rather than display them, because displaying "
"them would create even more such strings every time about:memory " "them would create even more such strings every time about:memory "
"is refreshed."); "is refreshed.");
} }
if (stringsNotableAboutMemoryMallocHeap > 0) { if (stringsNotableAboutMemoryMallocHeap > 0) {
ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/notable/about-memory/malloc-heap"), MOZ_ASSERT(!zStats.isTotals);
stringsNotableAboutMemoryMallocHeap, REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/malloc-heap"),
"Non-inline string characters of notable strings that contain " KIND_HEAP, stringsNotableAboutMemoryMallocHeap,
"the characters '" STRING_LENGTH "', which are probably from " "Non-inline string characters of strings that contain the "
"characters '" STRING_LENGTH "', which are probably from "
"about:memory itself. " MAYBE_OVERALLOCATED "about:memory itself. " MAYBE_OVERALLOCATED
" We filter them out rather than display them, because displaying " " We filter them out rather than display them, because displaying "
"them would create even more such strings every time about:memory " "them would create even more such strings every time about:memory "