blob: 9b7d22247aef1e6d8ce31b98fb004743d599a73f [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=78:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Read and process GC trace logs.
*/
#include "gc/GCTraceFormat.h"
#define __STDC_FORMAT_MACROS
#include <assert.h>
#include <inttypes.h>
#include <math.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <functional>
#include <unordered_map>
#include <vector>
// State of the program
enum Heap
{
Nursery,
TenuredHeap,
HeapKinds
};
enum FinalizerKind
{
NoFinalizer,
HasFinalizer,
FinalizerKinds
};
enum State
{
StateMutator,
StateMinorGC,
StateMajorGC
};
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;
TypeId typeId;
AllocInfo(uint64_t allocCount, uint8_t kind, Heap loc)
: serial(allocCount), kind(kind), initialHeap(loc), typeId(0)
{
assert(kind < AllocKinds);
assert(initialHeap < HeapKinds);
}
};
struct ClassInfo
{
const ClassId id;
const char* name;
const uint32_t flags;
const FinalizerKind hasFinalizer;
ClassInfo(ClassId id, const char* name, uint32_t flags, FinalizerKind hasFinalizer)
: 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
const unsigned MaxClasses = 128;
const unsigned LifetimeBinLog = 10;
const unsigned MaxLifetimeBins = 40;
const unsigned AugHeapKinds = HeapKinds + 1;
const unsigned HeapTotal = HeapKinds;
const unsigned AugAllocKinds = AllocKinds + 1;
const unsigned AllocKindTotal = AllocKinds;
const unsigned AugLifetimeBins = MaxLifetimeBins + 1;
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);
return elements[index];
}
private:
T elements[length];
};
unsigned timesliceSize;
unsigned lifetimeBins;
std::vector<uint64_t> gcBytesAllocatedInSlice;
std::vector<uint64_t> gcBytesFreedInSlice;
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, ...)
{
va_list va;
va_start(va, format);
vfprintf(stderr, format, va);
fprintf(stderr, "\n");
va_end(va);
exit(1);
}
const uint64_t FirstBinSize = 100;
const unsigned BinLog = 2;
static unsigned
getBin(uint64_t lifetime)
{
/*
* Calculate a bin number for a given lifetime.
*
* We use a logarithmic scale, starting with a bin size of 100 and doubling
* from there.
*/
static double logDivisor = log(BinLog);
if (lifetime < FirstBinSize)
return 0;
return unsigned(log(lifetime / FirstBinSize) / logDivisor) + 1;
}
static unsigned
binLimit(unsigned bin)
{
return unsigned(pow(BinLog, bin) * FirstBinSize);
}
static void
testBinning()
{
assert(getBin(0) == 0);
assert(getBin(FirstBinSize - 1) == 0);
assert(getBin(FirstBinSize) == 1);
assert(getBin(2 * FirstBinSize - 1) == 1);
assert(getBin(2 * FirstBinSize) == 2);
assert(getBin(4 * FirstBinSize - 1) == 2);
assert(getBin(4 * FirstBinSize) == 3);
assert(binLimit(0) == FirstBinSize);
assert(binLimit(1) == 2 * FirstBinSize);
assert(binLimit(2) == 4 * FirstBinSize);
assert(binLimit(3) == 8 * FirstBinSize);
}
static const char*
allocKindName(AllocKind kind)
{
static const char* AllocKindNames[] = {
"Object0",
"Object0Bg",
"Object2",
"Object2Bg",
"Object4",
"Object4Bg",
"Object8",
"Object8Bg",
"Object12",
"Object12Bg",
"Object16",
"Object16Bg",
"Script",
"LazyScript",
"Shape",
"BaseShape",
"TypeObject",
"FatInlineString",
"String",
"ExternalString",
"Symbol",
"JitCode",
"Total"
};
assert(sizeof(AllocKindNames) / sizeof(const char*) == AugAllocKinds);
assert(kind < AugAllocKinds);
return AllocKindNames[kind];
}
static const char*
heapName(unsigned heap)
{
static const char* HeapNames[] = {
"nursery",
"tenured heap",
"all"
};
assert(heap < AugHeapKinds);
return HeapNames[heap];
}
static const char*
heapLabel(unsigned heap)
{
static const char* HeapLabels[] = {
"Nursery",
"Tenured heap",
"Total"
};
assert(heap < AugHeapKinds);
return HeapLabels[heap];
}
static void
outputGcBytesAllocated(FILE* file)
{
fprintf(file, "# Total GC bytes allocated by timeslice\n");
fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
fprintf(file, "Time, GCBytesAllocated\n");
uint64_t timesliceCount = allocCount / timesliceSize + 1;
uint64_t total = 0;
for (uint64_t i = 0; i < timesliceCount; ++i) {
total += gcBytesAllocatedInSlice[i];
fprintf(file, "%12" PRIu64 ", %12" PRIu64 "\n", i * timesliceSize, total);
}
}
static void
outputGcBytesUsed(FILE* file)
{
fprintf(file, "# Total GC bytes used by timeslice\n");
fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
fprintf(file, "Time, GCBytesUsed\n");
uint64_t timesliceCount = allocCount / timesliceSize + 1;
uint64_t total = 0;
for (uint64_t i = 0; i < timesliceCount; ++i) {
total += gcBytesAllocatedInSlice[i] - gcBytesFreedInSlice[i];
fprintf(file, "%12" PRIu64 ", %12" PRIu64 "\n", i * timesliceSize, total);
}
}
static void
outputThingCounts(FILE* file)
{
fprintf(file, "# GC thing allocation count in nursery and tenured heap by kind\n");
fprintf(file, "# This shows what kind of things we are allocating in the nursery\n");
fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
fprintf(file, "Kind, Nursery, Tenured heap\n");
for (unsigned i = 0; i < AllocKinds; ++i) {
fprintf(file, "%15s, %8" PRIu64 ", %8" PRIu64 "\n", allocKindName(i),
allocCountByHeapAndKind[Nursery][i],
allocCountByHeapAndKind[TenuredHeap][i]);
}
}
static void
outputObjectCounts(FILE* file)
{
fprintf(file, "# Object allocation count in nursery and tenured heap by class\n");
fprintf(file, "# This shows what kind of objects we are allocating in the nursery\n");
fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
fprintf(file, "Class, Nursery, Tenured heap, Total\n");
for (unsigned i = 0; i < classes.size(); ++i) {
fprintf(file, "%30s, %8" PRIu64 ", %8" PRIu64 ", %8" PRIu64 "\n",
classes[i].name,
objectCountByHeapAndClass[Nursery][i],
objectCountByHeapAndClass[TenuredHeap][i],
objectCountByClass[i]);
}
}
static void
outputLifetimeByHeap(FILE* file)
{
fprintf(file, "# Lifetime of all things (in log2 bins) by initial heap\n");
fprintf(file, "# NB invalid unless execution was traced with appropriate zeal\n");
fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
fprintf(file, "Lifetime");
for (unsigned i = 0; i < HeapKinds; ++i)
fprintf(file, ", %s", heapLabel(i));
fprintf(file, "\n");
for (unsigned i = 0; i < lifetimeBins; ++i) {
fprintf(file, "%8d", binLimit(i));
for (unsigned j = 0; j < HeapKinds; ++j)
fprintf(file, ", %8" PRIu64, allocCountByHeapAndLifetime[j][i]);
fprintf(file, "\n");
}
}
static void
outputLifetimeByHasFinalizer(FILE* file)
{
fprintf(file, "# Lifetime of heap allocated objects by prescence of finalizer\n");
fprintf(file, "# NB invalid unless execution was traced with appropriate zeal\n");
fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
fprintf(file, "Lifetime, NoFinalizer, HasFinalizer\n");
for (unsigned i = 0; i < lifetimeBins; ++i) {
fprintf(file, "%8d", binLimit(i));
for (unsigned j = 0; j < FinalizerKinds; ++j)
fprintf(file, ", %8" PRIu64,
heapObjectCountByFinalizerAndLifetime[j][i]);
fprintf(file, "\n");
}
}
static void
outputFinalizedHeapObjectLifetimeByClass(FILE* file)
{
fprintf(file, "# Lifetime of finalized heap objects by class\n");
fprintf(file, "# NB invalid unless execution was traced with appropriate zeal\n");
fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
fprintf(file, "Lifetime");
for (unsigned i = 0; i < classes.size(); ++i)
fprintf(file, ", %15s", classes[i].name);
fprintf(file, "\n");
for (unsigned i = 0; i < lifetimeBins; ++i) {
fprintf(file, "%8d", binLimit(i));
for (unsigned j = 0; j < classes.size(); ++j) {
fprintf(file, ", %8" PRIu64,
finalizedHeapObjectCountByClassAndLifetime[j][i]);
}
fprintf(file, "\n");
}
}
static void
outputLifetimeByKind(FILE* file, unsigned initialHeap)
{
assert(initialHeap < AugHeapKinds);
fprintf(file, "# Lifetime of %s things (in log2 bins) by kind\n", heapName(initialHeap));
fprintf(file, "# NB invalid unless execution was traced with appropriate zeal\n");
fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
fprintf(file, "Lifetime");
for (unsigned i = 0; i < AllocKinds; ++i)
fprintf(file, ", %15s", allocKindName(i));
fprintf(file, "\n");
for (unsigned i = 0; i < lifetimeBins; ++i) {
fprintf(file, "%8d", binLimit(i));
for (unsigned j = 0; j < AllocKinds; ++j)
fprintf(file, ", %8" PRIu64,
allocCountByHeapKindAndLifetime[initialHeap][j][i]);
fprintf(file, "\n");
}
}
static void
outputLifetimeByClass(FILE* file, unsigned initialHeap)
{
assert(initialHeap < AugHeapKinds);
fprintf(file, "# Lifetime of %s things (in log2 bins) by class\n", heapName(initialHeap));
fprintf(file, "# NB invalid unless execution was traced with appropriate zeal\n");
fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
fprintf(file, "Lifetime");
for (unsigned i = 0; i < classes.size(); ++i)
fprintf(file, ", %15s", classes[i].name);
fprintf(file, "\n");
for (unsigned i = 0; i < lifetimeBins; ++i) {
fprintf(file, "%8d", binLimit(i));
for (unsigned j = 0; j < classes.size(); ++j)
fprintf(file, ", %8" PRIu64,
objectCountByHeapClassAndLifetime[initialHeap][j][i]);
fprintf(file, "\n");
}
}
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)
{
uint64_t lifetime = finalizeTime - info.serial;
unsigned timeslice = info.serial / timesliceSize;
unsigned lifetimeBin = getBin(lifetime);
assert(lifetimeBin < lifetimeBins);
++allocCountByHeapAndKind[info.initialHeap][info.kind];
++allocCountByHeapAndLifetime[info.initialHeap][lifetimeBin];
++allocCountByHeapKindAndLifetime[info.initialHeap][info.kind][lifetimeBin];
if (info.kind <= LastObjectAllocKind) {
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[classInfo.id].hasFinalizer;
++heapObjectCountByFinalizerAndLifetime[f][lifetimeBin];
if (f == HasFinalizer)
++finalizedHeapObjectCountByClassAndLifetime[classInfo.id][lifetimeBin];
}
}
uint64_t size = thingSizes[info.kind];
gcBytesAllocatedInSlice[timeslice] += size;
gcBytesFreedInSlice[finalizeTime / timesliceSize] += size;
}
static bool
readTrace(FILE* file, uint64_t& trace)
{
if (fread(&trace, sizeof(trace), 1, file) != 1) {
if (feof(file))
return false;
else
die("Error reading input");
}
return true;
}
static GCTraceEvent
getTraceEvent(uint64_t trace)
{
uint64_t event = trace >> TraceEventShift;
assert(event < GCTraceEventCount);
return (GCTraceEvent)event;
}
static uint64_t
getTracePayload(uint64_t trace)
{
return trace & ((1lu << TracePayloadBits) - 1);
}
static uint8_t
getTraceExtra(uint64_t trace)
{
uint64_t extra = (trace >> TraceExtraShift) & ((1 << TraceExtraBits) - 1);
assert(extra < 256);
return (uint8_t)extra;
}
static uint64_t
expectTrace(FILE* file, GCTraceEvent event)
{
uint64_t trace;
if (!readTrace(file, trace))
die("End of file while expecting trace %d", event);
if (getTraceEvent(trace) != event)
die("Expected trace %d but got trace %d", event, getTraceEvent(trace));
return getTracePayload(trace);
}
static uint64_t
expectDataAddress(FILE* file)
{
return expectTrace(file, TraceDataAddress);
}
static uint32_t
expectDataInt(FILE* file)
{
return (uint32_t)expectTrace(file, TraceDataInt);
}
static char*
expectDataString(FILE* file)
{
uint64_t length = expectTrace(file, TraceDataString);
assert(length < 256); // Sanity check
char* string = static_cast<char*>(malloc(length + 1));
if (!string)
die("Out of memory while reading string data");
const unsigned charsPerWord = sizeof(uint64_t);
unsigned wordCount = (length + charsPerWord - 1) / charsPerWord;
for (unsigned i = 0; i < wordCount; ++i) {
if (fread(&string[i * charsPerWord], sizeof(char), charsPerWord, file) != charsPerWord)
die("Error or EOF while reading string data");
}
string[length] = 0;
return string;
}
static void
createClassInfo(const char* name, uint32_t flags, FinalizerKind hasFinalizer,
address clasp = 0)
{
ClassId id = classes.size();
classes.push_back(ClassInfo(id, name, flags, hasFinalizer));
if (clasp)
classMap.emplace(clasp, id);
}
static void
readClassInfo(FILE* file, address clasp)
{
assert(clasp);
char* name = expectDataString(file);
uint32_t flags = expectDataInt(file);
FinalizerKind hasFinalizer = expectDataInt(file) != 0 ? HasFinalizer : NoFinalizer;
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)
{
uint64_t allocTime = allocCount++;
tenuredThings.emplace(thing, AllocInfo(allocTime, kind, TenuredHeap));
}
static void
allocNurseryThing(address thing, AllocKind kind)
{
uint64_t allocTime = allocCount++;
nurseryThings.emplace(thing, AllocInfo(allocTime, kind, Nursery));
}
static void
setObjectType(address obj, address typeObject)
{
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.typeId = lookupTypeId(typeObject);
}
static void
promoteToTenured(address src, address dst)
{
auto i = nurseryThings.find(src);
assert(i != nurseryThings.end());
AllocInfo alloc = i->second;
tenuredThings.emplace(dst, alloc);
nurseryThings.erase(i);
}
static void
finalizeThing(const AllocInfo& info)
{
processAlloc(info, allocCount);
}
static void
sweepNursery()
{
for (auto i = nurseryThings.begin(); i != nurseryThings.end(); ++i) {
finalizeThing(i->second);
}
nurseryThings.clear();
}
static void
finalizeTenuredThing(address thing)
{
auto i = tenuredThings.find(thing);
assert(i != tenuredThings.end());
finalizeThing(i->second);
tenuredThings.erase(i);
}
static void
updateTimeslices(std::vector<uint64_t>& data, uint64_t lastTime, uint64_t currentTime, uint64_t value)
{
unsigned firstSlice = (lastTime / timesliceSize) + 1;
unsigned lastSlice = currentTime / timesliceSize;
for (unsigned i = firstSlice; i <= lastSlice; ++i)
data[i] = value;
}
static void
processTraceFile(const char* filename)
{
FILE* file;
file = fopen(filename, "r");
if (!file)
die("Can't read file: %s", filename);
// Get a conservative estimate of the total number of allocations so we can
// allocate buffers in advance.
fseek(file, 0, SEEK_END);
size_t length = ftell(file);
fseek(file, 0, SEEK_SET);
size_t maxTraces = length / sizeof(uint64_t);
uint64_t trace;
if (!readTrace(file, trace))
die("Empty input file");
if (getTraceEvent(trace) != TraceEventInit)
die("Can't parse input file");
if (getTraceExtra(trace) != TraceFormatVersion)
die("Unexpected format version %d", getTraceExtra(trace));
for (unsigned kind = 0; kind < AllocKinds; ++kind)
thingSizes[kind] = expectTrace(file, TraceEventThingSize);
timesliceSize = 1000;
while ((maxTraces / timesliceSize ) > 1000)
timesliceSize *= 2;
size_t maxTimeslices = maxTraces / timesliceSize;
gcBytesAllocatedInSlice.reserve(maxTimeslices);
gcBytesFreedInSlice.reserve(maxTimeslices);
lifetimeBins = getBin(maxTraces) + 1;
assert(lifetimeBins <= MaxLifetimeBins);
createClassInfo("unknown", 0, NoFinalizer);
createTypeInfo(0, 0);
types[0].name = "unknown";
State state = StateMutator;
while (readTrace(file, trace)) {
GCTraceEvent event = getTraceEvent(trace);
switch (event) {
case TraceEventNurseryAlloc:
assert(state == StateMutator);
allocNurseryThing(getTracePayload(trace), getTraceExtra(trace));
break;
case TraceEventTenuredAlloc:
assert(state == StateMutator);
allocHeapThing(getTracePayload(trace), getTraceExtra(trace));
break;
case TraceEventClassInfo:
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);
setObjectType(getTracePayload(trace), expectDataAddress(file));
break;
case TraceEventMinorGCStart:
assert(state == StateMutator);
state = StateMinorGC;
break;
case TraceEventPromoteToTenured:
assert(state == StateMinorGC);
promoteToTenured(getTracePayload(trace), expectDataAddress(file));
break;
case TraceEventMinorGCEnd:
assert(state == StateMinorGC);
sweepNursery();
state = StateMutator;
break;
case TraceEventMajorGCStart:
assert(state == StateMutator);
state = StateMajorGC;
break;
case TraceEventTenuredFinalize:
assert(state == StateMajorGC);
finalizeTenuredThing(getTracePayload(trace));
break;
case TraceEventMajorGCEnd:
assert(state == StateMajorGC);
state = StateMutator;
break;
default:
assert(false);
die("Unexpected trace event %d", event);
break;
}
}
// Correct number of lifetime bins now we know the real allocation count.
assert(allocCount < maxTraces);
lifetimeBins = getBin(allocCount) + 1;
assert(lifetimeBins <= MaxLifetimeBins);
fclose(file);
}
template <class func>
void withOutputFile(const char* base, const char* name, func f)
{
const size_t bufSize = 256;
char filename[bufSize];
int r = snprintf(filename, bufSize, "%s-%s.csv", base, name);
assert(r > 0 && r < bufSize);
FILE* file = fopen(filename, "w");
if (!file)
die("Can't write to %s", filename);
f(file);
fclose(file);
}
int
main(int argc, const char* argv[])
{
testBinning();
if (argc != 3)
die("usage: gctrace INPUT_FILE OUTPUT_BASE");
const char* inputFile = argv[1];
const char* outputBase = argv[2];
processTraceFile(inputFile);
using namespace std::placeholders;
withOutputFile(outputBase, "bytesAllocatedBySlice", outputGcBytesAllocated);
withOutputFile(outputBase, "bytesUsedBySlice", outputGcBytesUsed);
withOutputFile(outputBase, "thingCounts", outputThingCounts);
withOutputFile(outputBase, "objectCounts", outputObjectCounts);
withOutputFile(outputBase, "lifetimeByClassForNursery",
std::bind(outputLifetimeByClass, _1, Nursery));
withOutputFile(outputBase, "lifetimeByKindForHeap",
std::bind(outputLifetimeByKind, _1, TenuredHeap));
withOutputFile(outputBase, "lifetimeByHeap", outputLifetimeByHeap);
withOutputFile(outputBase, "lifetimeByHasFinalizer",
outputLifetimeByHasFinalizer);
withOutputFile(outputBase, "finalizedHeapObjectlifetimeByClass",
outputFinalizedHeapObjectLifetimeByClass);
withOutputFile(outputBase, "lifetimeByTypeForNursery",
std::bind(outputLifetimeByType, _1, Nursery));
withOutputFile(outputBase, "lifetimeByTypeForHeap",
std::bind(outputLifetimeByType, _1, TenuredHeap));
return 0;
}