/*
* Copyright (c) 2009 Apple Inc. All rights reserved.
*
* @APPLE_APACHE_LICENSE_HEADER_START@
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @APPLE_APACHE_LICENSE_HEADER_END@
*/
//
// dump.m
// gctests
//
// Created by Blaine Garst on 10/22/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
#import </usr/local/include/auto_zone.h>
#import <objc/objc.h>
#import <objc/objc-auto.h>
#import </usr/local/include/objc/objc-auto-dump.h>
#include <objc/runtime.h>
#if 0
extern void auto_zone_dump(auto_zone_t *zone,
void (^stack_dump)(const void *base, unsigned long byte_size),
void (^register_dump)(const void *base, unsigned long byte_size),
void (^thread_local_node_dump)(const void *address, unsigned long size, unsigned int layout, unsigned long refcount),
void (^root_dump)(const void **address),
void (^global_node_dump)(const void *address, unsigned long size, unsigned int layout, unsigned long refcount),
void (^weak_dump)(const void **address, const void *item)
);
#endif
__weak id Y = nil;
/*
* Simple zone printer
*/
void printLiveDump(auto_zone_t *zone) {
auto_zone_dump(zone,
^(const void *base, unsigned long byte_size)
{ printf("stack base ^(const void *base, unsigned long byte_size)
{ printf("register base ^(const void *address, unsigned long size, unsigned int layout, unsigned long refcount)
{ printf("thread local ^(const void **address)
{ printf("global root at ^(const void *address, unsigned long size, unsigned int layout, unsigned long refcount)
{ printf("non-local ^(const void **address, const void *item)
{ printf("weak ref located at );
}
#if 0
/*
* Raw file format definitions
*/
// must be unique in first letter...
// RAW FORMAT
#define HEADER "dumpster"
#define THREAD 't'
#define LOCAL 'l'
#define NODE 'n'
#define REGISTER 'r'
#define ROOT 'g'
#define WEAK 'w'
#define CLASS 'c'
#define END 'e'
#define SixtyFour 1
#define Little 2
/*
Raw format, not that anyone should really care.
<rawfile := <header> <arch> <middle>* <end>
<header> := 'd' 'u' 'm' 'p' 's' 't' 'e' 'r' ; the HEADER string
<arch> := SixtyFour? + Little? ; architecture
<middle> := <thread> | <root> | <node> | <weak> | <class>
<thread> := <register> <stack> <local>* ; the triple
<register> := 'r' longLength [bytes] ; the register bank
<stack> := 't' longLength [bytes] ; the stack
<local> := 'l' [long] ; a thread local node
<root> := 'g' longAddress longValue
<node> := 'n' longAddress longSize intLayout longRefcount longIsa?
<weak> := 'w' longAddress longValue
<class> := 'c' longAddress <name> <strongLayout> <weakLayout>
<name> := intLength [bytes] ; no null byte
<strongLayout> := intLength [bytes] ; including 0 byte at end
<weakLayout> := intLength [bytes] ; including 0 byte at end
<end> := 'e'
*/
#endif
// COOKED FORMAT
#define HEADER2 "dumpfile"
/*
* Utilities
*/
static char myType() {
char type = 0;
if (sizeof(void *) == 8) type |= SixtyFour;
#if __LITTLE_ENDIAN__
type |= Little;
#endif
return type;
}
#if 0
/*
* Sigh, a mutable set.
*/
typedef struct {
long *items;
long count;
long capacity;
} pointer_set_t;
static pointer_set_t *new_pointer_set() {
pointer_set_t *result = malloc(sizeof(pointer_set_t));
result->items = calloc(64, sizeof(long));
result->count = 0;
result->capacity = 63; // last valid ptr, also mask
return result;
}
static void pointer_set_grow(pointer_set_t *set);
static void pointer_set_add(pointer_set_t *set, long ptr) {
long hash = ptr & set->capacity;
while (true) {
if (!set->items[hash]) {
set->items[hash] = ptr;
++set->count;
if (set->count*3 > set->capacity*2)
pointer_set_grow(set);
return;
}
if (set->items[hash] == ptr) return;
hash = (hash + 1) & set->capacity;
}
}
static void pointer_set_grow(pointer_set_t *set) {
long oldCapacity = set->capacity;
long *oldItems = set->items;
set->count = 0;
set->capacity = 2*(oldCapacity+1)-1;
set->items = calloc(2*(oldCapacity+1), sizeof(long));
for (long i = 0; i < oldCapacity; ++i)
if (oldItems[i]) pointer_set_add(set, oldItems[i]);
free(oldItems);
}
static void pointer_set_iterate(pointer_set_t *set, void (^block)(long item)) {
for (long i = 0; i < set->capacity; ++i)
if (set->items[i]) block(set->items[i]);
}
static pointer_set_dispose(pointer_set_t *set) {
free(set->items);
free(set);
}
/*
Quickly dump heap to a named file in a pretty raw format.
*/
void auto_zone_create_dump_file(auto_zone_t *zone, const char *filename) {
// just write interesting info to disk
FILE *fp = fopen(filename, "w");
if (fp == NULL) {
printf("couldn't open test file: return;
}
fwrite(HEADER, strlen(HEADER), 1, fp);
char type = myType();
fwrite(&type, 1, 1, fp);
// for each thread...
// do registers first
void (^dump_registers)(const void *, unsigned long) = ^(const void *base, unsigned long byte_size) {
char type = REGISTER;
fwrite(&type, 1, 1, fp);
//fwrite(REGISTER, strlen(REGISTER), 1, fp);
fwrite(&byte_size, sizeof(byte_size), 1, fp);
fwrite(base, byte_size, 1, fp);
};
// then stacks
void (^dump_stack)(const void *, unsigned long) = ^(const void *base, unsigned long byte_size) {
char type = THREAD;
fwrite(&type, 1, 1, fp);
//fwrite(THREAD, strlen(THREAD), 1, fp);
fwrite(&byte_size, sizeof(byte_size), 1, fp);
fwrite(base, byte_size, 1, fp);
};
// then locals
void (^dump_local)(const void *, unsigned long, unsigned int, unsigned long) =
^(const void *address, unsigned long size, unsigned int layout, unsigned long refcount) {
// just write the value - rely on it showing up again as a node later
char type = LOCAL;
fwrite(&type, 1, 1, fp);
fwrite(&address, sizeof(address), 1, fp);
};
// roots
void (^dump_root)(const void **) = ^(const void **address) {
char type = ROOT;
fwrite(&type, 1, 1, fp);
// write the address so that we can catch misregistered globals
fwrite(&address, sizeof(address), 1, fp);
// write content, even (?) if zero
fwrite(address, sizeof(*address), 1, fp);
};
// the nodes
pointer_set_t *classes = new_pointer_set();
void (^dump_node)(const void *, unsigned long, unsigned int, unsigned long) =
^(const void *address, unsigned long size, unsigned int layout, unsigned long refcount) {
char type = NODE;
fwrite(&type, 1, 1, fp);
fwrite(&address, sizeof(address), 1, fp);
fwrite(&size, sizeof(size), 1, fp);
fwrite(&layout, sizeof(layout), 1, fp);
fwrite(&refcount, sizeof(refcount), 1, fp);
if ((layout & AUTO_UNSCANNED) != AUTO_UNSCANNED) {
// now the nodes unfiltered content
fwrite(address, size, 1, fp);
}
if ((layout & AUTO_OBJECT) == AUTO_OBJECT) {
#if 0
id theClass = *(id *)address;
if (theClass) [classes addObject:theClass];
#else
long theClass = *(long *)address;
if (theClass) pointer_set_add(classes, theClass);
#endif
}
};
// weak
void (^dump_weak)(const void **, const void *) = ^(const void **address, const void *item) {
char type = WEAK;
fwrite(&type, 1, 1, fp);
fwrite(&address, sizeof(address), 1, fp);
fwrite(&item, sizeof(item), 1, fp);
};
auto_zone_dump(zone, dump_stack, dump_registers, dump_local, dump_root, dump_node, dump_weak);
//for (Class class in classes) {
pointer_set_iterate(classes, ^(long class) {
char type = CLASS;
fwrite(&type, 1, 1, fp);
fwrite(&class, sizeof(class), 1, fp); // write address so that we can map it from node isa's
// classname (for grins)
const char *className = class_getName(class);
unsigned int length = strlen(className);
fwrite(&length, sizeof(length), 1, fp); // n
fwrite(className, length, 1, fp); // n bytes
// strong layout
const char *layout = class_getIvarLayout(class);
length = layout ? strlen(layout)+1 : 0; // format is <skipnibble><count nibble> ending with <0><0>
fwrite(&length, sizeof(length), 1, fp); // n
fwrite(layout, length, 1, fp); // n bytes
// weak layout
layout = class_getWeakIvarLayout(class);
length = layout ? strlen(layout)+1 : 0; // format is <skipnibble><count nibble> ending with <0><0>
fwrite(&length, sizeof(length), 1, fp); // n
fwrite(layout, length, 1, fp); // n bytes
});
{
// end
char type = END;
fwrite(&type, 1, 1, fp);
fclose(fp);
}
}
#endif
/*
* Primitives for reading raw file
*/
unsigned long readLong(FILE *fp) {
unsigned long result;
size_t len = fread(&result, sizeof(result), 1, fp);
if (len != 1) {
printf("error reading long\n");
exit(1);
}
return result;
}
void *readPointer(FILE *fp) {
void *result;
size_t len = fread(&result, sizeof(result), 1, fp);
if (len != 1) {
printf("error reading pointer\n");
exit(1);
}
return result;
}
int readInt(FILE *fp) {
int result;
size_t len = fread(&result, sizeof(result), 1, fp);
if (len != 1) {
printf("error reading int\n");
exit(1);
}
return result;
}
__strong char *readString(FILE *fp) {
int length;
size_t len = fread(&length, sizeof(length), 1, fp);
if (len != 1) {
printf("error reading length\n");
exit(1);
}
if (length == 0) return NULL;
char buffer[length+1];
len = fread(buffer, length, 1, fp);
if (len != 1) {
printf("error reading string content\n");
exit(1);
}
char *result = NSAllocateCollectable(length+1, 0);
memmove(result, buffer, length);
result[length] = 0;
return result;
}
__strong char *readStringSize(FILE *fp, uint32_t length) {
if (length == 0) return NULL;
char buffer[length+1];
int len = fread(buffer, length, 1, fp);
if (len != 1) {
printf("error reading string content\n");
exit(1);
}
char *result = NSAllocateCollectable(length+1, 0);
memmove(result, buffer, length);
result[length] = 0;
return result;
}
FILE *openDump(const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) {
printf("Whoops, can't open return NULL;
}
char header[strlen(HEADER)+1];
fread(header, strlen(HEADER), 1, fp);
if (memcmp(header, HEADER, strlen(HEADER))) {
header[strlen(HEADER)] = 0;
printf("Whoops, header not recognized: return NULL;
}
fgetc(fp); // skip type for now
return fp;
}
FILE *openCooked(const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) {
printf("Whoops, can't open return NULL;
}
char header[strlen(HEADER2)+1];
fread(header, strlen(HEADER2), 1, fp);
if (memcmp(header, HEADER2, strlen(HEADER2))) {
header[strlen(HEADER2)] = 0;
printf("Whoops, header not recognized: return NULL;
}
return fp;
}
/*
Read raw dump and print out its contents without much processing.
Just a utility printer - or base for better efforts
*/
void printRawDumpFile(const char *filename) {
FILE *fp = openDump(filename);
if (!fp) {
printf("Whoops, can't open return;
}
while (true) {
int ch = fgetc(fp);
switch (ch) {
case EOF: {
printf("unexpected end of input\n");
return;
}
case THREAD: {
unsigned long byte_size = readLong(fp);
printf("thread of size fseek(fp, byte_size, SEEK_CUR);
break;
}
case REGISTER: {
unsigned long byte_size = readLong(fp);
printf("registers of size fseek(fp, byte_size, SEEK_CUR);
break;
}
case LOCAL: {
void *address = readPointer(fp);
printf("local node at break;
}
case ROOT: {
void *address = readPointer(fp);
void *value = readPointer(fp);
printf("root at break;
}
case NODE: {
void *address = readPointer(fp);
unsigned long size = readLong(fp);
int layout = readInt(fp);
unsigned long refcount = readLong(fp);
printf("node at if ((layout & AUTO_UNSCANNED) != AUTO_UNSCANNED) {
fseek(fp, size, SEEK_CUR);
}
break;
}
case WEAK: {
void *address = readPointer(fp);
void *value = readPointer(fp);
printf("weak at break;
}
case CLASS: {
void *address = readPointer(fp);
char *name = readString(fp);
printf("class char *layout = readString(fp);
if (layout) printf(" hasStrong");
layout = readString(fp);
if (layout) printf(" hasWeak");
printf("\n");
break;
}
case END: {
fclose(fp);
return;
}
default:
printf("unrecognized key: exit(1);
}
}
}
#pragma mark dump crunching
/*
Define structures for dealing with processed data.
*/
typedef struct {
uint32_t class_index;
uint32_t nameOffset, nameLen;
uint32_t strongOffset, strongLen;
uint32_t weakOffset, weakLen;
} class_descriptor_t;
typedef struct {
void *original;
uint32_t size, layout, refcount;
uint32_t nitems;
struct {
uint32_t offset; // in words
uint32_t index;
} items[];
} node_descriptor_t;
typedef struct {
uint32_t nitems;
struct {
uint32_t offset; // in words
uint32_t index;
} items[];
} stack_descriptor_t;
typedef struct thread_descriptor {
stack_descriptor_t *__strong stack_descriptor;
uint32_t nLocals;
uint32_t *locals;
uint32_t nRegisters;
uint32_t *registers;
} thread_descriptor_t;
node_descriptor_t *getNodeDescriptor(char *buffer, uint32_t size, char *nibbles, NSMapTable *nodeIndex) {
char *nibbler = nibbles;
id *fields = (id *)buffer;
uint32_t limit = size/sizeof(id);
uint32_t counter = 0;
uint32_t nitems = 0;
// allocate worst case.
node_descriptor_t *descriptor = NSAllocateCollectable(sizeof(node_descriptor_t)+limit*2*sizeof(uint32_t), 0);
while (counter < limit) {
if (nibbler && *nibbler) {
int skip = ((*nibbler) >> 4 & 0xf);
counter += skip;
int use = (*nibbler) & 0xf;
while (use) {
long index = (long)[nodeIndex objectForKey:(id)fields[counter]];
if (index) {
descriptor->items[nitems].offset = counter;
descriptor->items[nitems].index = index - 1;
++nitems;
}
--use;
++counter;
}
nibbler++;
}
else {
long index = (long)[nodeIndex objectForKey:(id)fields[counter]];
if (index) {
descriptor->items[nitems].offset = counter;
descriptor->items[nitems].index = index - 1;
++nitems;
}
++counter;
}
}
descriptor->nitems = nitems;
return descriptor;
}
/*
* raw file reader
* cooked file writer
*/
@interface Dumpster : NSObject {
NSPointerArray *classArray;
NSPointerArray *nodeArray;
NSPointerArray *rootArray;
NSPointerArray *stackArray;
}
- initWithRawFilename:(const char *)filename verbose:(bool) verbose;
- (void)writeCookedToFilename:(const char *)filename;
@end
@implementation Dumpster
/*
Read raw format from disk & canonicalize the class & node references.
We assume less than 4G nodes so we use uint_32 indexes to represent them.
Ergo #classes too.
Thread stacks are represented as { int32_t skip, int32_t index }.
This does not preserve stale references to free nodes.
*/
- initWithRawFilename:(const char *)filename verbose:(bool) verbose {
FILE *fp = openDump(filename);
if (!fp) {
printf("Whoops, can't open return nil;
}
// set up class list & mapping tables
// first, a structure thing...
NSPointerFunctions *structDescriptorFunctions =
[NSPointerFunctions pointerFunctionsWithOptions:
NSPointerFunctionsStrongMemory
|NSPointerFunctionsStructPersonality];
// the array...
classArray = [NSPointerArray pointerArrayWithPointerFunctions:structDescriptorFunctions];
// now the pointer keys...
NSPointerFunctions *pointerFunctions =
[NSPointerFunctions pointerFunctionsWithOptions:
NSPointerFunctionsOpaqueMemory
|NSPointerFunctionsOpaquePersonality];
// now the maptable...
NSMapTable *classMap = [[NSMapTable alloc] initWithKeyPointerFunctions:pointerFunctions valuePointerFunctions:structDescriptorFunctions capacity:32];
// set up mapping of nodes to their next life index
NSMapTable *nodeIndex = [[NSMapTable alloc] initWithKeyPointerFunctions:pointerFunctions valuePointerFunctions:pointerFunctions capacity:32];
// set up a hashTable (set) of local nodes
NSHashTable *locals = [[NSHashTable alloc] initWithPointerFunctions:pointerFunctions capacity:32];
nodeArray = [NSPointerArray pointerArrayWithPointerFunctions:structDescriptorFunctions];
rootArray = [NSPointerArray pointerArrayWithPointerFunctions:pointerFunctions];
stackArray = [NSPointerArray pointerArrayWithPointerFunctions:structDescriptorFunctions];
// pass one, establish nodes in heap, set up class table
// pass two, process node references + everything else
int pass = 1;
int nthreads = 0;
thread_descriptor_t *thread_descriptor = NULL;
while (true) {
int ch = fgetc(fp);
switch (ch) {
case EOF: {
printf("unexpected end of input\n");
exit(1);
}
case REGISTER: { // REGISTER
unsigned long byte_size = readLong(fp);
if (pass == 1) {
fseek(fp, byte_size, SEEK_CUR);
thread_descriptor = NSAllocateCollectable(sizeof(thread_descriptor_t), NSScannedOption);
[stackArray addPointer:thread_descriptor];
break;
}
thread_descriptor = (thread_descriptor_t *)[stackArray pointerAtIndex:nthreads++];
uint32_t nitems = byte_size/sizeof(void *);
thread_descriptor->nRegisters = nitems;
thread_descriptor->registers = (uint32_t *)NSAllocateCollectable(nitems*sizeof(uint32_t), 0);
for (uint32_t i = 0; i < nitems; ++i) {
void *pointer = (void *)readLong(fp);
long index = (long)[nodeIndex objectForKey:pointer];
thread_descriptor->registers[i] = index; // +1 for sanity
}
break;
}
case THREAD: {
// XXX not complete thread description - need register state also
unsigned long byte_size = readLong(fp);
if (pass == 1) {
fseek(fp, byte_size, SEEK_CUR);
//thread_descriptor = NSAllocateCollectable(sizeof(thread_descriptor_t), NSScannedOption);
//[stackArray addPointer:thread_descriptor];
break;
}
printf("initWithRaw: thread of size long *stack = (long *)malloc(byte_size);
fread(stack, byte_size, 1, fp);
// pass one, fill each "long" with index+1 of node it references, if any
uint32_t limit = byte_size/sizeof(long);
uint32_t nitems = 0;
for (int i = 0; i < limit; ++i) {
bool isLocal = [locals member:(id)stack[i]];
stack[i] = (long)[nodeIndex objectForKey:(id)stack[i]];
if (isLocal) stack[i] = -stack[i]; // negate index for locals
if (stack[i]) ++nitems;
}
printf("thread size // now allocate an appropriately sized stack descriptor
unsigned long size = nitems*2*sizeof(uint32_t) + sizeof(stack_descriptor_t);
stack_descriptor_t *descriptor = (stack_descriptor_t *)NSAllocateCollectable(size, 0);
// and fill it up
descriptor->nitems = nitems;
int stackWalker = 0;
int descriptorWalker = 0;
for (; stackWalker < limit; ++stackWalker) {
if (stack[stackWalker]) {
descriptor->items[descriptorWalker].offset = stackWalker;
descriptor->items[descriptorWalker].index = stack[stackWalker] - 1;
++descriptorWalker;
}
}
if (descriptorWalker != nitems) {
printf("whoops, we set up return nil;
}
free(stack);
if (verbose) printf("stack descriptor
//thread_descriptor = (thread_descriptor_t *)[stackArray pointerAtIndex:nthreads++];
thread_descriptor->stack_descriptor = descriptor;
break;
}
case LOCAL: {
void *address = readPointer(fp);
printf("initWithRaw: local node at if (pass == 1) {
[locals addObject:(id)address];
thread_descriptor->nLocals++;
break;
}
if (!thread_descriptor->locals) {
thread_descriptor->locals = NSAllocateCollectable(thread_descriptor->nLocals*sizeof(uint32_t), 0);
thread_descriptor->nLocals = 0;
}
long index = (long)[nodeIndex objectForKey:(id)address];
thread_descriptor->locals[thread_descriptor->nLocals++] = index;
break;
}
case ROOT: {
void *address = readPointer(fp);
void *value = readPointer(fp);
if (pass == 1) break; // wait till pass 2
long index = (long)[nodeIndex objectForKey:(id)value];
if (verbose > 1) printf("root at if (index == 0) {
printf("Hmm, root points to non-node ??\n");
}
else {
[rootArray addPointer:(void *)(index-1)];
}
break;
}
case NODE: {
void *address = readPointer(fp);
unsigned long size = readLong(fp);
int layout = readInt(fp);
unsigned long refcount = readLong(fp);
if (pass == 1) {
// use +1 so that node 0 shows up; this is internal only, won't go to file that way
[nodeIndex setObject:(id)(1 + [nodeIndex count]) forKey:(id)address];
//printf("node at if ((layout & AUTO_UNSCANNED) != AUTO_UNSCANNED) {
fseek(fp, size, SEEK_CUR);
}
}
else {
if (verbose > 1) printf("node at node_descriptor_t *nodeDescriptor = NULL;
if ((layout & AUTO_UNSCANNED) != AUTO_UNSCANNED) {
char buffer[size];
fread(buffer, size, 1, fp);
char *nibbles = NULL;
if ((layout & AUTO_OBJECT) == AUTO_OBJECT) {
id class;
memmove(&class, buffer, sizeof(class));
class_descriptor_t *descriptor = (class_descriptor_t *)[classMap objectForKey:class];
layout |= (descriptor->class_index << 8);
if (!descriptor) {
printf("Hmm, object doesn't have a known class\n");
}
else if (descriptor->strongLen) {
nibbles = ((char *)descriptor) + descriptor->strongOffset; // XXX must write null byte
}
}
nodeDescriptor = getNodeDescriptor(buffer, size, nibbles, nodeIndex);
}
else {
nodeDescriptor = NSAllocateCollectable(sizeof(node_descriptor_t), 0);
nodeDescriptor->nitems = 0;
}
nodeDescriptor->original = address;
nodeDescriptor->size = size;
if ([locals member:(id)address]) {
printf("**** found layout |= 128; // mark it as a local
}
nodeDescriptor->layout = layout;
nodeDescriptor->refcount = refcount;
//printf("adding node descriptor [nodeArray addPointer:nodeDescriptor];
}
break;
}
case WEAK: {
void *address = readPointer(fp);
void *value = readPointer(fp);
if (verbose) printf("weak at break;
}
case CLASS: {
void *address = readPointer(fp);
char *name = readString(fp);
char *slayout = readString(fp);
char *wlayout = readString(fp);
if (pass > 1) break;
if (verbose) printf("class uint32_t dlen = sizeof(class_descriptor_t) + strlen(name);
if (slayout) if (verbose) printf(" hasStrong");
if (slayout) dlen += strlen(slayout);
if (wlayout) if (verbose) printf(" hasWeak");
if (wlayout) dlen += strlen(wlayout);
if (verbose) printf("\n");
class_descriptor_t *descriptor = NSAllocateCollectable(sizeof(class_descriptor_t)+dlen, 0);
descriptor->class_index = [classMap count]; // used in node_descriptor
descriptor->nameOffset = sizeof(class_descriptor_t);
descriptor->nameLen = strlen(name)+1;
descriptor->strongOffset = descriptor->nameOffset + descriptor->nameLen;
descriptor->strongLen = slayout ? (strlen(slayout)+1) : 0;
descriptor->weakOffset = descriptor->strongOffset + descriptor->strongLen;
descriptor->weakLen = wlayout ? (strlen(wlayout)+1) : 0;
memmove(((char *)descriptor) + descriptor->nameOffset, name, descriptor->nameLen);
if (slayout) memmove(((char *)descriptor) + descriptor->strongOffset, slayout, descriptor->strongLen);
if (wlayout) memmove(((char *)descriptor) + descriptor->weakOffset, wlayout, descriptor->weakLen);
[classArray addPointer:(id)descriptor];
[classMap setObject:(id)descriptor forKey:address];
break;
}
case END: {
if (pass == 1) {
fclose(fp);
fp = openDump(filename);
if (!fp) {
printf("Whoops, can't reopen return nil;
}
++pass;
if (verbose) printf("!!!! done with first pass!!!!\n");
}
else {
if (verbose) printf("!!!! done with second pass!!!!\n");
fclose(fp);
return self;
}
break;
}
default: {
printf("unrecognized key: exit(1);
}
}
}
}
- (void)writeCookedToFilename:(const char *)filename {
FILE *fp = fopen(filename, "w");
if (!fp) {
printf("could not open exit(1);
return;
}
// write header
fwrite(HEADER2, strlen(HEADER2), 1, fp);
// write ObjC class info
// first.. count!
uint32_t count = [classArray count];
fwrite(&count, sizeof(count), 1, fp);
// and now 'count' of them
for (unsigned int index = 0; index < [classArray count]; ++index) {
class_descriptor_t *descriptor = (class_descriptor_t *)[classArray pointerAtIndex:index];
uint32_t len = sizeof(class_descriptor_t) + descriptor->nameLen + descriptor->strongLen + descriptor->weakLen;
fwrite(descriptor, len, 1, fp);
}
// write node info
// first the count
count = [nodeArray count];
fwrite(&count, sizeof(count), 1, fp);
// and now the nodes
for (unsigned int index = 0; index < [nodeArray count]; ++index) {
node_descriptor_t *descriptor = (node_descriptor_t *)[nodeArray pointerAtIndex:index];
//uint32_t len = sizeof(node_descriptor_t) + descriptor->nitems*2*sizeof(uint32_t);
uint32_t len = sizeof(node_descriptor_t);
//printf("writing cooked [ fwrite(descriptor, len, 1, fp);
}
// write node links info
for (unsigned int index = 0; index < [nodeArray count]; ++index) {
node_descriptor_t *descriptor = (node_descriptor_t *)[nodeArray pointerAtIndex:index];
uint32_t len = descriptor->nitems*2*sizeof(uint32_t);
fwrite(&descriptor->items[0], len, 1, fp);
}
// write roots info
// first the count
count = [rootArray count];
fwrite(&count, sizeof(count), 1, fp);
// and now the nodes
for (unsigned int counter = 0; counter < [rootArray count]; ++counter) {
long index = (long)[rootArray pointerAtIndex:counter];
uint32_t intIndex = index;
fwrite(&intIndex, sizeof(intIndex), 1, fp);
}
// write thread info
count = [stackArray count];
fwrite(&count, sizeof(count), 1, fp);
// and now the stacks
for (unsigned int index = 0; index < [stackArray count]; ++index) {
thread_descriptor_t *thread_descriptor = (thread_descriptor_t *)[stackArray pointerAtIndex:index];
stack_descriptor_t *descriptor = thread_descriptor->stack_descriptor;
uint32_t len = sizeof(stack_descriptor_t) + descriptor->nitems*2*sizeof(uint32_t);
//printf("descriptor fwrite(descriptor, len, 1, fp);
// now the locals
len = thread_descriptor->nLocals;
fwrite(&len, sizeof(uint32_t), 1, fp);
fwrite(thread_descriptor->locals, len*sizeof(uint32_t), 1, fp);
// now the registers
len = thread_descriptor->nRegisters;
fwrite(&len, sizeof(uint32_t), 1, fp);
fwrite(thread_descriptor->registers, len*sizeof(uint32_t), 1, fp);
}
fclose(fp);
}
@end
/*
* cooked file reader
*/
struct class_map_entry {
char *className;
class_descriptor_t diskClass;
};
@interface Heap : NSObject {
int nClasses;
class_descriptor_t *__strong *__strong classes;
int nNodes;
node_descriptor_t *__strong *__strong node_descriptors;
int nRoots;
uint32_t *roots;
int nThreads;
thread_descriptor_t *__strong thread_descriptors;
// weak
// associations
auto_zone_t *collector;
}
@end
@implementation Heap
- initWithCookedFilename:(const char *)cookedFile {
FILE *fp = openCooked(cookedFile);
if (!fp) {
printf("Couldn't open cooked file: exit(1);
}
/*
* Classes first
*/
nClasses = readInt(fp);
// allocate space for the pointers
printf("Cooked: classes = (class_descriptor_t **)NSAllocateCollectable(nClasses*sizeof(struct class_descriptor_t *), NSScannedOption);
// now read in size & then details for each class
for (uint32_t i = 0; i < nClasses; ++i) {
class_descriptor_t fixed;
fread(&fixed, sizeof(class_descriptor_t), 1, fp);
uint32_t stringsize = fixed.nameLen + fixed.strongLen + fixed.weakLen;
uint32_t totalsize = sizeof(class_descriptor_t) + stringsize;
classes[i] = (class_descriptor_t *)NSAllocateCollectable(totalsize, 0);
memcpy(classes[i], &fixed, sizeof(class_descriptor_t));
// now pack the 3 strings in right afterwards
fread(((char *)classes[i])+fixed.nameOffset, stringsize, 1, fp);
//printf("read class }
/*
* Nodes next
*/
nNodes = readInt(fp);
printf("Cooked: node_descriptors = (node_descriptor_t **)NSAllocateCollectable(nNodes*sizeof(node_descriptor_t *), NSScannedOption);
for (uint32_t i = 0; i < nNodes; ++i) {
node_descriptor_t fixed;
fread(&fixed, sizeof(node_descriptor_t), 1, fp);
uint32_t totalsize = sizeof(node_descriptor_t) + fixed.nitems * 2 * sizeof(uint32_t);
node_descriptors[i] = (node_descriptor_t *)NSAllocateCollectable(totalsize, 0);
memcpy(node_descriptors[i], &fixed, sizeof(node_descriptor_t));
}
for (uint32_t i = 0; i < nNodes; ++i) {
uint32_t nitems = node_descriptors[i]->nitems;
uint32_t size = nitems * 2 * sizeof(uint32_t);
//printf("reading if (nitems) fread(((char *)node_descriptors[i])+sizeof(node_descriptor_t), size, 1, fp);
}
/*
* roots
*/
nRoots = readInt(fp);
roots = (uint32_t *)NSAllocateCollectable(nRoots*sizeof(uint32_t), 0);
printf("Cooked: reading fread(roots, nRoots, sizeof(uint32_t), fp);
/*
* threads
*/
nThreads = readInt(fp);
thread_descriptors = (thread_descriptor_t *)NSAllocateCollectable(nThreads*sizeof(thread_descriptor_t), NSScannedOption);
for (uint32_t i = 0; i < nThreads; ++i) {
/* do stack descriptor part only */
stack_descriptor_t stack_descriptor, *descriptorp;
fread(&stack_descriptor, sizeof(stack_descriptor), 1, fp);
uint32_t variablesize = stack_descriptor.nitems*2*sizeof(uint32_t);
uint32_t totalsize = sizeof(stack_descriptor_t) + variablesize;
descriptorp = (stack_descriptor_t *)NSAllocateCollectable(totalsize, 0);
memcpy(descriptorp, &stack_descriptor, sizeof(stack_descriptor));
fread(((char *)descriptorp)+sizeof(stack_descriptor_t), variablesize, 1, fp);
thread_descriptors[i].stack_descriptor = descriptorp;
/* now the locals */
thread_descriptors[i].nLocals = readInt(fp);
uint32_t size = thread_descriptors[i].nLocals*sizeof(uint32_t);
thread_descriptors[i].locals = NSAllocateCollectable(size, 0);
fread(thread_descriptors[i].locals, size, 1, fp);
/* now the registers */
thread_descriptors[i].nRegisters = readInt(fp);
size = thread_descriptors[i].nRegisters*sizeof(uint32_t);
thread_descriptors[i].registers = NSAllocateCollectable(size, 0);
fread(thread_descriptors[i].registers, size, 1, fp);
}
}
@end
void printCookedFilename(const char *filename) {
FILE *fp = openCooked(filename);
if (!fp) {
printf("Couldn't open cooked file: exit(1);
}
uint32_t count = readInt(fp);
printf(" struct class_map_entry *classMap = NSAllocateCollectable(count*sizeof(struct class_map_entry), NSScannedOption);
for (uint32_t i = 0; i < count; ++i) {
// if we memory mapped the file and put the string info in its own section
// after the constant sized class info we could simply patch the offset
// with the base pointer, or something, thinking about longs vs uint32_t..
fread(&classMap[i].diskClass, sizeof(class_descriptor_t), 1, fp);
classMap[i].className = readStringSize(fp, classMap[i].diskClass.nameLen);
readStringSize(fp, classMap[i].diskClass.strongLen);
readStringSize(fp, classMap[i].diskClass.weakLen);
printf("read class }
// now do the nodes...
count = readInt(fp);
printf(" node_descriptor_t *node_descriptors = (node_descriptor_t *)NSAllocateCollectable(count*sizeof(node_descriptor_t), 1);
for (uint32_t i = 0; i < count; ++i) {
node_descriptor_t *node_descriptor = &node_descriptors[i];
fread(node_descriptor, sizeof(node_descriptor_t), 1, fp);
//printf("node[ }
for (uint32_t i = 0; i < count; ++i) {
node_descriptor_t *node_descriptor = &node_descriptors[i];
printf("node[ uint32_t classIndex = node_descriptor->layout >> 8;
if ((node_descriptor->layout & AUTO_OBJECT) == AUTO_OBJECT) {
printf(", class: }
if ((node_descriptor->layout & 128) == 128) {
printf(", local");
}
printf("\n");
uint32_t nitems = node_descriptors[i].nitems;
struct {
uint32_t offset;
uint32_t index;
} items[nitems];
if (nitems) fread(&items[0], sizeof(items), 1, fp);
for (uint32_t j = 0; j < nitems; ++j) {
printf(" [ }
}
// now do the roots...
count = readInt(fp);
printf(" uint32_t roots[count];
fread(roots, sizeof(roots), 1, fp);
for (uint32_t i = 0; i < count; ++i) {
printf("root [ }
// now do the stacks...
count = readInt(fp);
printf(" for (uint32_t i = 0; i < count; ++i) {
stack_descriptor_t stack_descriptor;
fread(&stack_descriptor, sizeof(stack_descriptor), 1, fp);
printf("stack struct {
uint32_t offset;
uint32_t index;
} *items = NSAllocateCollectable(stack_descriptor.nitems*2*sizeof(uint32_t), 0);
fread(&items[0], stack_descriptor.nitems*2*sizeof(uint32_t), 1, fp);
for (uint32_t j = 0; j < stack_descriptor.nitems; ++j) {
// need to use absolute value - 1
int32_t index = items[j].index;
bool isLocal = index < 0;
if (isLocal) index = -index;
printf(" [ }
printf("Locals\n");
uint32_t nitems = readInt(fp);
uint32_t *memory = malloc(nitems*sizeof(uint32_t));
fread(memory, nitems, sizeof(uint32_t), fp);
for (uint32_t j = 0; j < nitems; ++j) {
// -1 bias?
printf("local node[ }
free(memory);
printf("Registers\n");
nitems = readInt(fp);
memory = malloc(nitems*sizeof(uint32_t));
fread(memory, nitems, sizeof(uint32_t), fp);
for (uint32_t j = 0; j < nitems; ++j) {
// adjust for -1 bias
if (memory[j]) printf("register node[ }
free(memory);
}
}
char RawName[1024];
void takeRawSnapshot() {
static id X = nil;
if (!X) X = [NSObject new];
printf("Should see //static __weak id Y = nil;
Y = X;
printf("Should see id local = [NSObject new];
printf("Should see //printLiveDump((auto_zone_t *)NSDefaultMallocZone());
//auto_zone_create_dump_file((auto_zone_t *)NSDefaultMallocZone(), rawName);
objc_dumpHeap(RawName, sizeof(RawName));
//printRawDumpFile("/private/tmp/test.dumpster");
}
void cook(const char *rawName, const char *cookedName) {
Dumpster *dumpster = [[Dumpster alloc] initWithRawFilename:rawName verbose:false];
[dumpster writeCookedToFilename:cookedName];
}
void enliven(const char *cookedName) {
}
int main(int argc, char *argv[]) {
//printLiveDump();
char cookedName[1024];
if (0) {
takeRawSnapshot();
printf("raw file written to sprintf(cookedName, " }
else {
strcpy(RawName, argv[1]);
sprintf(cookedName, " }
cook(RawName, cookedName);
//printCookedFilename(cookedName);
Heap *heap = [[Heap alloc] initWithCookedFilename:cookedName];
return 0;
}