mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1034566 - Add GC tracing for object types r=terrence
This commit is contained in:
parent
15ece3a4fa
commit
e9933ba3c1
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -23,6 +23,8 @@ enum GCTraceEvent {
|
||||
TraceEventNurseryAlloc,
|
||||
TraceEventTenuredAlloc,
|
||||
TraceEventClassInfo,
|
||||
TraceEventTypeInfo,
|
||||
TraceEventTypeNewScript,
|
||||
TraceEventCreateObject,
|
||||
TraceEventMinorGCStart,
|
||||
TraceEventPromoteToTenured,
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user