Bug 1034566 - Add GC tracing for object types r=terrence

This commit is contained in:
Jon Coppeard 2014-07-08 14:05:24 +01:00
parent 15ece3a4fa
commit e9933ba3c1
5 changed files with 203 additions and 25 deletions

View File

@ -55,16 +55,17 @@ enum State
typedef uint64_t address;
typedef uint8_t AllocKind;
typedef uint8_t ClassId;
typedef uint64_t TypeId;
struct AllocInfo
{
const uint64_t serial;
const AllocKind kind;
const Heap initialHeap;
ClassId classId;
TypeId typeId;
AllocInfo(uint64_t allocCount, uint8_t kind, Heap loc)
: serial(allocCount), kind(kind), initialHeap(loc), classId(0)
: serial(allocCount), kind(kind), initialHeap(loc), typeId(0)
{
assert(kind < AllocKinds);
assert(initialHeap < HeapKinds);
@ -82,15 +83,38 @@ struct ClassInfo
: id(id), name(name), flags(flags), hasFinalizer(hasFinalizer) {}
};
struct TypeInfo
{
const TypeId id;
const ClassId classId;
const uint32_t flags;
const char *name;
TypeInfo(TypeId id, ClassId classId, uint32_t flags)
: id(id), classId(classId), flags(flags), name(nullptr) {}
const char *getName() {
if (name)
return name;
static char buffer[32];
sprintf(buffer, "type %ld", id);
return buffer;
}
};
typedef std::unordered_map<address, AllocInfo> AllocMap;
typedef std::unordered_map<address, ClassId> ClassMap;
typedef std::vector<ClassInfo> ClassVector;
typedef std::unordered_map<address, TypeId> TypeMap;
typedef std::vector<TypeInfo> TypeVector;
uint64_t thingSizes[AllocKinds];
AllocMap nurseryThings;
AllocMap tenuredThings;
ClassMap classMap;
ClassVector classes;
TypeMap typeMap;
TypeVector types;
uint64_t allocCount = 0;
// Collected data
@ -108,10 +132,13 @@ const unsigned LifetimeBinTotal = MaxLifetimeBins;
const unsigned AugClasses = MaxClasses + 1;
const unsigned ClassTotal = MaxClasses;
struct EmptyArrayTag {};
template <typename T, size_t length>
struct Array
{
Array() {}
Array(const EmptyArrayTag&) { zero(); }
void zero() { memset(&elements, 0, sizeof(elements)); }
T &operator[](size_t index) {
assert(index < length);
@ -130,10 +157,12 @@ Array<Array<uint64_t, AllocKinds>, HeapKinds> allocCountByHeapAndKind;
Array<Array<uint64_t, MaxLifetimeBins>, HeapKinds> allocCountByHeapAndLifetime;
Array<Array<Array<uint64_t, MaxLifetimeBins>, AllocKinds>, HeapKinds> allocCountByHeapKindAndLifetime;
Array<uint64_t, MaxClasses> objectCountByClass;
std::vector<uint64_t> objectCountByType;
Array<Array<uint64_t, MaxClasses>, HeapKinds> objectCountByHeapAndClass;
Array<Array<Array<uint64_t, MaxLifetimeBins>, MaxClasses>, HeapKinds> objectCountByHeapClassAndLifetime;
Array<Array<uint64_t, MaxLifetimeBins>, FinalizerKinds> heapObjectCountByFinalizerAndLifetime;
Array<Array<uint64_t, MaxLifetimeBins>, MaxClasses> finalizedHeapObjectCountByClassAndLifetime;
std::vector<Array<Array<uint64_t, MaxLifetimeBins>, HeapKinds> > objectCountByTypeHeapAndLifetime;
static void
die(const char *format, ...)
@ -405,6 +434,41 @@ outputLifetimeByClass(FILE *file, unsigned initialHeap)
}
}
static void
outputLifetimeByType(FILE *file, unsigned initialHeap)
{
assert(initialHeap < AugHeapKinds);
fprintf(file, "# Lifetime of %s things (in log2 bins) by type\n", heapName(initialHeap));
fprintf(file, "# NB invalid unless execution was traced with appropriate zeal\n");
fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
// There are many types but few are frequently used.
const size_t minObjectCount = 1;
const size_t outputEntries = 10;
std::vector<TypeId> topTypes;
for (size_t i = 0; i < types.size(); ++i) {
if (objectCountByType.at(i) > minObjectCount)
topTypes.push_back(i);
}
std::sort(topTypes.begin(), topTypes.end(),
[] (TypeId a, TypeId b) { return objectCountByType.at(a) > objectCountByType.at(b); });
size_t count = std::min(outputEntries, topTypes.size());
fprintf(file, "Lifetime");
for (unsigned i = 0; i < count; ++i)
fprintf(file, ", %15s", types[topTypes[i]].getName());
fprintf(file, "\n");
for (unsigned i = 0; i < lifetimeBins; ++i) {
fprintf(file, "%8d", binLimit(i));
for (unsigned j = 0; j < count; ++j)
fprintf(file, ", %8" PRIu64,
objectCountByTypeHeapAndLifetime.at(topTypes[j])[initialHeap][i]);
fprintf(file, "\n");
}
}
static void
processAlloc(const AllocInfo &info, uint64_t finalizeTime)
{
@ -419,14 +483,18 @@ processAlloc(const AllocInfo &info, uint64_t finalizeTime)
++allocCountByHeapKindAndLifetime[info.initialHeap][info.kind][lifetimeBin];
if (info.kind <= LastObjectAllocKind) {
++objectCountByClass[info.classId];
++objectCountByHeapAndClass[info.initialHeap][info.classId];
++objectCountByHeapClassAndLifetime[info.initialHeap][info.classId][lifetimeBin];
const TypeInfo &typeInfo = types[info.typeId];
const ClassInfo &classInfo = classes[typeInfo.classId];
++objectCountByType.at(typeInfo.id);
++objectCountByClass[classInfo.id];
++objectCountByHeapAndClass[info.initialHeap][classInfo.id];
++objectCountByHeapClassAndLifetime[info.initialHeap][classInfo.id][lifetimeBin];
++objectCountByTypeHeapAndLifetime.at(typeInfo.id)[info.initialHeap][lifetimeBin];
if (info.initialHeap == TenuredHeap) {
FinalizerKind f = classes[info.classId].hasFinalizer;
FinalizerKind f = classes[classInfo.id].hasFinalizer;
++heapObjectCountByFinalizerAndLifetime[f][lifetimeBin];
if (f == HasFinalizer)
++finalizedHeapObjectCountByClassAndLifetime[info.classId][lifetimeBin];
++finalizedHeapObjectCountByClassAndLifetime[classInfo.id][lifetimeBin];
}
}
@ -532,6 +600,53 @@ readClassInfo(FILE *file, address clasp)
createClassInfo(name, flags, hasFinalizer, clasp);
}
static ClassId
lookupClassId(address clasp)
{
auto i = classMap.find(clasp);
assert(i != classMap.end());
ClassId id = i->second;
assert(id < classes.size());
return id;
}
static void
createTypeInfo(ClassId classId, uint32_t flags, address typeObject = 0)
{
TypeId id = types.size();
types.push_back(TypeInfo(id, classId, flags));
if (typeObject)
typeMap.emplace(typeObject, id);
objectCountByType.push_back(0);
objectCountByTypeHeapAndLifetime.push_back(EmptyArrayTag());
}
static void
readTypeInfo(FILE *file, address typeObject)
{
assert(typeObject);
address clasp = expectDataAddress(file);
uint32_t flags = expectDataInt(file);
createTypeInfo(lookupClassId(clasp), flags, typeObject);
}
static TypeId
lookupTypeId(address typeObject)
{
auto i = typeMap.find(typeObject);
assert(i != typeMap.end());
TypeId id = i->second;
assert(id < types.size());
return id;
}
static void
setTypeName(address typeObject, const char *name)
{
TypeId id = lookupTypeId(typeObject);
types[id].name = name;
}
static void
allocHeapThing(address thing, AllocKind kind)
{
@ -547,20 +662,15 @@ allocNurseryThing(address thing, AllocKind kind)
}
static void
setObjectClass(address obj, address clasp)
setObjectType(address obj, address typeObject)
{
auto i = classMap.find(clasp);
assert(i != classMap.end());
ClassId id = i->second;
assert(id < classes.size());
auto j = nurseryThings.find(obj);
if (j == nurseryThings.end()) {
j = tenuredThings.find(obj);
if (j == tenuredThings.end())
die("Can't find allocation for object %p", obj);
}
j->second.classId = id;
j->second.typeId = lookupTypeId(typeObject);
}
static void
@ -642,6 +752,8 @@ processTraceFile(const char *filename)
assert(lifetimeBins <= MaxLifetimeBins);
createClassInfo("unknown", 0, NoFinalizer);
createTypeInfo(0, 0);
types[0].name = "unknown";
State state = StateMutator;
while (readTrace(file, trace)) {
@ -659,9 +771,17 @@ processTraceFile(const char *filename)
assert(state == StateMutator);
readClassInfo(file, getTracePayload(trace));
break;
case TraceEventTypeInfo:
assert(state == StateMutator);
readTypeInfo(file, getTracePayload(trace));
break;
case TraceEventTypeNewScript:
assert(state == StateMutator);
setTypeName(getTracePayload(trace), expectDataString(file));
break;
case TraceEventCreateObject:
assert(state == StateMutator);
setObjectClass(getTracePayload(trace), expectDataAddress(file));
setObjectType(getTracePayload(trace), expectDataAddress(file));
break;
case TraceEventMinorGCStart:
assert(state == StateMutator);
@ -744,5 +864,9 @@ main(int argc, const char *argv[])
outputLifetimeByHasFinalizer);
withOutputFile(outputBase, "finalizedHeapObjectlifetimeByClass",
outputFinalizedHeapObjectLifetimeByClass);
withOutputFile(outputBase, "lifetimeByTypeForNursery",
std::bind(outputLifetimeByType, _1, Nursery));
withOutputFile(outputBase, "lifetimeByTypeForHeap",
std::bind(outputLifetimeByType, _1, TenuredHeap));
return 0;
}

View File

@ -17,6 +17,7 @@
using namespace js;
using namespace js::gc;
using namespace js::types;
JS_STATIC_ASSERT(AllocKinds == FINALIZE_LIMIT);
JS_STATIC_ASSERT(LastObjectAllocKind == FINALIZE_OBJECT_LAST);
@ -24,6 +25,7 @@ JS_STATIC_ASSERT(LastObjectAllocKind == FINALIZE_OBJECT_LAST);
static FILE *gcTraceFile = nullptr;
static HashSet<const Class *, DefaultHasher<const Class *>, SystemAllocPolicy> tracedClasses;
static HashSet<const TypeObject *, DefaultHasher<const TypeObject *>, SystemAllocPolicy> tracedTypes;
static inline void
WriteWord(uint64_t data)
@ -77,19 +79,23 @@ TraceString(const char* string)
bool
js::gc::InitTrace(GCRuntime &gc)
{
/* This currently does not support multiple runtimes. */
MOZ_ALWAYS_TRUE(!gcTraceFile);
char *filename = getenv("JS_GC_TRACE");
if (!filename)
return true;
if (!tracedClasses.init())
if (!tracedClasses.init() || !tracedTypes.init()) {
FinishTrace();
return false;
/* This currently does not support multiple runtimes. */
JS_ASSERT(!gcTraceFile);
}
gcTraceFile = fopen(filename, "w");
if (!gcTraceFile)
if (!gcTraceFile) {
FinishTrace();
return false;
}
TraceEvent(TraceEventInit, 0, TraceFormatVersion);
@ -103,8 +109,12 @@ js::gc::InitTrace(GCRuntime &gc)
void
js::gc::FinishTrace()
{
if (gcTraceFile)
if (gcTraceFile) {
fclose(gcTraceFile);
gcTraceFile = nullptr;
}
tracedClasses.finish();
tracedTypes.finish();
}
bool
@ -141,7 +151,41 @@ MaybeTraceClass(const Class *clasp)
TraceString(clasp->name);
TraceInt(clasp->flags);
TraceInt(clasp->finalize != nullptr);
tracedClasses.put(clasp);
MOZ_ALWAYS_TRUE(tracedClasses.put(clasp));
}
static void
MaybeTraceType(TypeObject *type)
{
if (tracedTypes.has(type))
return;
MaybeTraceClass(type->clasp());
TraceEvent(TraceEventTypeInfo, uint64_t(type));
TraceAddress(type->clasp());
TraceInt(type->flags());
MOZ_ALWAYS_TRUE(tracedTypes.put(type));
}
void
js::gc::TraceTypeNewScript(TypeObject *type)
{
const size_t bufLength = 128;
static char buffer[bufLength];
JS_ASSERT(type->hasNewScript());
JSAtom *funName = type->newScript()->fun->displayAtom();
if (!funName)
return;
size_t length = funName->length();
MOZ_ALWAYS_TRUE(length < bufLength);
CopyChars(reinterpret_cast<Latin1Char *>(buffer), *funName);
buffer[length] = 0;
TraceEvent(TraceEventTypeNewScript, uint64_t(type));
TraceString(buffer);
}
void
@ -150,10 +194,10 @@ js::gc::TraceCreateObject(JSObject* object)
if (!gcTraceFile)
return;
const Class *clasp = object->type()->clasp();
MaybeTraceClass(clasp);
TypeObject *type = object->type();
MaybeTraceType(type);
TraceEvent(TraceEventCreateObject, uint64_t(object));
TraceAddress(clasp);
TraceAddress(type);
}
void
@ -184,6 +228,10 @@ js::gc::TraceMajorGCStart()
void
js::gc::TraceTenuredFinalize(Cell *thing)
{
if (!gcTraceFile)
return;
if (thing->tenuredGetAllocKind() == FINALIZE_TYPE_OBJECT)
tracedTypes.remove(static_cast<const TypeObject *>(thing));
TraceEvent(TraceEventTenuredFinalize, uint64_t(thing));
}

View File

@ -29,6 +29,7 @@ extern void TraceMinorGCEnd();
extern void TraceMajorGCStart();
extern void TraceTenuredFinalize(Cell *thing);
extern void TraceMajorGCEnd();
extern void TraceTypeNewScript(js::types::TypeObject *type);
#else
@ -44,6 +45,7 @@ inline void TraceMinorGCEnd() {}
inline void TraceMajorGCStart() {}
inline void TraceTenuredFinalize(Cell *thing) {}
inline void TraceMajorGCEnd() {}
inline void TraceTypeNewScript(js::types::TypeObject *type) {}
#endif

View File

@ -23,6 +23,8 @@ enum GCTraceEvent {
TraceEventNurseryAlloc,
TraceEventTenuredAlloc,
TraceEventClassInfo,
TraceEventTypeInfo,
TraceEventTypeNewScript,
TraceEventCreateObject,
TraceEventMinorGCStart,
TraceEventPromoteToTenured,

View File

@ -3504,6 +3504,8 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun)
PodCopy(newScript->initializerList,
initializerList.begin(),
initializerList.length());
js::gc::TraceTypeNewScript(type);
#endif // JS_ION
}