3

I have been trying to read a .trace file, which I had generated using a custom instruments template(instruments: Automator, Allocations, Leaks) using Instruments.

The best help I found in this stackoverflow answer. Basically the author created a custom Objective-C program(Traced) to read a specific type of Apples .trace file(instrument: OpenGL ES Driver). His answer is geared towards XCode 4.6.

The code still works with XCode 6.1, but the trace-file seems to have changed slightly. You have to find the *.run.zip file within the .trace package and unzip it. In the extracted folder you now have to find the *.run file. There are several *.run.zip files in a .trace package; one per used instrument.

Simply running the Traced program got me a uncaught exception 'NSArchiverArchiveInconsistency', reason: '*** class error for 'XRObjectAllocRun'-error.

This error was initially easy to figure out. All I had to do was implement the missing class XRObjectAllocRun; parallel to the example XRRun or XRVideoCardRun classes in XRRun.m.

This is how far I got and where I got stuck:

#import "XRObjectAllocRun.h"

@implementation XRObjectAllocRun
- (id)initWithCoder:(NSCoder *)decoder
{
    if((self = [super init]))
    {   
        NSObject *a = [decoder decodeObject];
        NSObject *b = [decoder decodeObject];
        NSObject *c = [decoder decodeObject];
        NSObject *d = [decoder decodeObject];
        NSObject *e = [decoder decodeObject];
        NSObject *f = [decoder decodeObject];
        NSObject *g = [decoder decodeObject];
        NSObject *h = [decoder decodeObject];
        NSObject *i = [decoder decodeObject];
//        NSObject *j = [decoder decodeObject];
//        NSObject *k = [decoder decodeObject];   
        NSLog(@"test");
    }
    return self;
}
@end

Basically I am stuck reverse-engineering the XRObjectAllocRun class. But no matter how many or little objects I decode I always receive the following exception: uncaught exception 'NSArchiverArchiveInconsistency', reason: '*** NSUnarchiver: inconsistency between written and read data for object 0x100112750'

If you uncomment the last two decode statements the program will crash with this exception: uncaught exception 'NSArchiverArchiveInconsistency', reason: '*** file inconsistency: read 'i', expecting '@''.

Does anyone know the signature of Apples XRObjectAllocRun class? This class is used for the Allocations instrument.

Any help would be great!

Update

I played around with Swift and translated the entire *.trace-reader - it fails with exactly the same error(s):

import Foundation
import Cocoa

@objc(XRObjectAllocRun)
class XRObjectAllocRun: NSObject {
    func initWithCoder(decoder:NSCoder){
        var x = decoder.decodeObject()
        // this is where things start breaking...
    }
}

@objc(XRRun)
class XRRun: NSObject {
    // to be implemented    
}

@objc(XRTrackSegment)
class XRTrackSegment: NSObject {
    func initWithCoder(decoder:NSCoder)->NSString{
        var a = decoder.decodeObject()?.integerValue
        var b = decoder.decodeObject()?.integerValue
        var c = decoder.decodeObject()?.integerValue
        var d = decoder.decodeObject()?.integerValue
        var e = decoder.decodeObject()

        return "test"
    }
}

@objc(PFTTrackSegment)
class PFTTrackSegment: NSObject {
    func initWithCoder(decoder:NSCoder){
        var a = decoder.decodeObject()?.integerValue
        var b = decoder.decodeObject()?.integerValue
        var c = decoder.decodeObject()?.integerValue
        var d = decoder.decodeObject()?.integerValue
        var e = decoder.decodeObject()?.integerValue
        var f = decoder.decodeObject()?.integerValue
    }
}

// parse command line
var traceFilePath = Process.arguments[1]
println("input: \(traceFilePath)")

var traceFile = NSURL(fileURLWithPath: traceFilePath)
var error:NSError?

// check if the file exists
if (traceFile?.checkResourceIsReachableAndReturnError(&error) == false){
    // file does not exist or cannot be accessed
    println("\(error)")
    exit(1)
}

var rawData = NSData(contentsOfURL: traceFile!)
var data = NSUnarchiver(forReadingWithData: rawData!)
var decodedObject: AnyObject? = data?.decodeObject()
println("\(decodedObject)")
Community
  • 1
  • 1
m3o
  • 3,881
  • 3
  • 35
  • 56
  • Try to look here: https://github.com/JustSid/Traced and here: http://stackoverflow.com/questions/16737621/trying-to-read-a-xcode-instruments-trace-file-what-is-the-file-format-of-a-tr Hope this help! – weso Jan 14 '15 at 20:08
  • Thanks, but this is where I started from. I am using the code-example and describe how one can adapt it. Unfortunately I cannot figure out the signature as [JustSid](http://stackoverflow.com/users/350272/justsid) did. – m3o Jan 14 '15 at 20:10
  • @MarcoPashkov did you ever figured out how to parse the Allocations trace? I'm also stuck with same exceptions. – Ricardo B. Jan 21 '15 at 21:06
  • Unfortunately not yet. I made a bit more progress in **swift** - but it's not there yet. Once I figured it out I will post the answer. Also, since I am not working full-time on this issue I can't promise of reaching it soon. – m3o Jan 21 '15 at 21:27

2 Answers2

1

Here is the signature of the XRObjectAllocRun class

#import "XRRun.h"

#import "SymbolAwareRun.h"
#import "XRCallTreeDataSource.h"
#import "XRSourceQuery.h"

@class NSMutableArray, NSMutableDictionary, NSString, XRHeapGeneration, XROAEventSummary, XRObjectAllocRunSharedData;

@interface XRObjectAllocRun : XRRun <SymbolAwareRun, XRSourceQuery, XRCallTreeDataSource>
{
    XRObjectAllocRunSharedData *_sharedData;
    NSMutableArray *_allStats;
    NSMutableDictionary *_statsForCategory;
    NSMutableDictionary *_categoryIDForName;
    XROAEventSummary *_scaleStats;
    NSMutableArray *_generations;
    struct XRTimeRange _filterTimeRange;
    unsigned int _filterMinEventID;
    unsigned int _filterMaxEventID;
    unsigned long long _nextGenNumber;
    NSMutableDictionary *_samplesByCategoryNumber;
    unsigned long long _catNumIndex;
    struct XRTimeRange _currentStatsFilterRange;
    int _lifecycleFilter;
    int _allocationTypeFilter;
    unsigned int *_quickEventCacheIds;
    id *_quickEventCache;
    XRHeapGeneration *_activeGeneration;
}

+ (void)initialize;
- (id)operation:(id)arg1 commentsForSymbol:(id)arg2 inSourceManager:(id)arg3 callTreeInformation:(id)arg4;
- (id)provideCategories;
- (id)backtracesForCategory:(id)arg1 timeRange:(struct XRTimeRange)arg2 savedIndex:(unsigned long long *)arg3;
- (void)_configureCallTreeForAllocationType:(int)arg1;
- (id)symbolsForEvent:(id)arg1 reverseOrder:(BOOL)arg2;
- (id)backtraceRepository;
- (BOOL)eventIsLiveInCurrentTimeRange:(id)arg1;
- (unsigned int)uncategorizedCount;
- (unsigned int)countOfObjectEventsForCategory:(unsigned int)arg1;
- (void)enumerateObjectEventsForCategory:(unsigned int)arg1 skipToIndex:(unsigned int)arg2 withBlock:(CDUnknownBlockType)arg3;
- (BOOL)_applyLifecycleFilterToEvent:(id)arg1;
- (id)zombieEvent;
- (id)eventForIdentifier:(unsigned int)arg1;
- (BOOL)loadDTPerformanceSessionDataFromPaths:(id)arg1 error:(id *)arg2;
- (void)updateGenerations;
- (void)deleteGeneration:(id)arg1;
- (void)moveGeneration:(id)arg1 toTime:(unsigned long long)arg2;
- (void)setActiveGeneration:(id)arg1;
- (id)generationAtTime:(unsigned long long)arg1;
- (id)generations;
- (id)nextGenerationIdentifier;
- (void)createGenerationAtTime:(unsigned long long)arg1;
- (void)removeFlag:(id)arg1;
- (struct XRTimeRange)_displayTimeFilter;
- (BOOL)_isTimeScoped;
- (BOOL)useTypeFilteringRules:(id)arg1;
- (void)setAllocationTypeFilter:(int)arg1;
- (void)setLifecycleFilter:(int)arg1;
- (struct XRTimeRange)selectedTimeRange;
- (void)setSelectedTimeRange:(struct XRTimeRange)arg1;
- (id)categoryNameForIdentifier:(unsigned int)arg1;
- (id)globalStats;
- (id)scalingStats;
- (void)_clearStats;
- (void)allowEventReuse;
- (void)refreshStatsForActiveTimeFilter;
- (void)_updateStatsWithEventIdentifier:(unsigned int)arg1 category:(unsigned int)arg2 type:(unsigned int)arg3 size:(int)arg4 pastEvent:(unsigned int)arg5 summaryMap:(id *)arg6 maxCat:(unsigned int)arg7;
- (id)_statsObjectForCategoryID:(unsigned int)arg1;
- (void)_changeStatsByTimestampRange:(struct XRTimeRange)arg1 overallRange:(struct XRTimeRange)arg2 startID:(unsigned int)arg3 endID:(unsigned int)arg4;
- (id *)_createCategorySummaryMapWithMaximum:(unsigned int)arg1;
- (void)_validateGlobalStatsForTimeRange:(struct XRTimeRange)arg1;
- (void)_recomputeGlobalStats;
- (BOOL)discardsLifeCycleComplete;
- (unsigned long long)lastTimestamp;
- (id)sharedData;
- (void)setRecordMode:(int)arg1;
- (void)setDiscardsLifeCycleComplete:(BOOL)arg1;
- (void)setTargetDevice:(id)arg1 pid:(int)arg2 repository:(id)arg3;
- (id)initWithCoder:(id)arg1;
- (void)encodeWithCoder:(id)arg1;
- (void)dealloc;
- (id)init;

// Remaining properties
@property(readonly, copy) NSString *debugDescription;
@property(readonly, copy) NSString *description;
@property(readonly) unsigned long long hash;
@property(readonly) Class superclass;

@end

I've uploaded signatures of other classes here if you need them.

Instead of declaring required classes in Swift, you can simply import headers from the archive to the bridging header.

bzz
  • 5,556
  • 24
  • 26
  • looks pretty good on an initial glance - I will take a deeper look over the weekend! – m3o Jan 30 '15 at 22:53
  • where did you find the headers? – m3o Jan 30 '15 at 22:54
  • 1
    @MarcoPashkov I used [class-dump](http://stevenygard.com/projects/class-dump/) to extract them from the binaries. – bzz Jan 30 '15 at 22:55
  • @MarcoPashkov were you able to parse the allocation data from the trace files? If so, can you give us an update? Thanks! – Ricardo B. Jul 13 '15 at 23:45
  • I am sorry, but I had to switch gears and abandon this project. But this seemed like it should work, since the API can be reverse reverse engineered by the tool that @bzz suggested. If you include the generated files you should be able to uncompress the data and read the traces. – m3o Jul 13 '15 at 23:50
0

I made it work. Here is the git project.

Bogus
  • 263
  • 1
  • 2
  • 11