mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 22:32:46 +00:00
Bug 972712 (part 3) - Rework notable string reporting. r=till.
--HG-- extra : rebase_source : 274c2ee9beafca5e464234f37e894967d20abb25
This commit is contained in:
parent
3cd29dbeac
commit
fdb45bb355
@ -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
|
||||||
|
|
||||||
|
@ -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];
|
||||||
|
@ -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 "
|
||||||
|
Loading…
Reference in New Issue
Block a user