Add basic crash log parsing support.

git-svn-id: https://plcrashreporter.googlecode.com/svn/trunk@170 25172300-ee46-11dd-abe2-393a09110dd0
This commit is contained in:
landonf@OFFICE.PLAUSIBLELABS.COM 2008-12-17 08:24:39 +00:00
Родитель 860bb8493d
Коммит 3d99ecb9cc
9 изменённых файлов: 313 добавлений и 11 удалений

Просмотреть файл

@ -140,6 +140,12 @@
05F411AD0EF8DE68008050CF /* PLCrashLogTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 05F411AC0EF8DE68008050CF /* PLCrashLogTests.m */; };
05F411AE0EF8DE68008050CF /* PLCrashLogTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 05F411AC0EF8DE68008050CF /* PLCrashLogTests.m */; };
05F411AF0EF8DE68008050CF /* PLCrashLogTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 05F411AC0EF8DE68008050CF /* PLCrashLogTests.m */; };
05F411F30EF8DFD3008050CF /* crash_report.proto in Sources */ = {isa = PBXBuildFile; fileRef = 059670C70EEFAC3A008A0601 /* crash_report.proto */; };
05F411F40EF8DFDA008050CF /* crash_report.proto in Sources */ = {isa = PBXBuildFile; fileRef = 059670C70EEFAC3A008A0601 /* crash_report.proto */; };
05F411F50EF8DFE4008050CF /* crash_report.proto in Sources */ = {isa = PBXBuildFile; fileRef = 059670C70EEFAC3A008A0601 /* crash_report.proto */; };
05F411F70EF8E001008050CF /* protobuf-c.c in Sources */ = {isa = PBXBuildFile; fileRef = 05F40F830EF850FC008050CF /* protobuf-c.c */; };
05F411F90EF8E013008050CF /* protobuf-c.c in Sources */ = {isa = PBXBuildFile; fileRef = 05F40F830EF850FC008050CF /* protobuf-c.c */; };
05F411FB0EF8E023008050CF /* protobuf-c.c in Sources */ = {isa = PBXBuildFile; fileRef = 05F40F830EF850FC008050CF /* protobuf-c.c */; };
/* End PBXBuildFile section */
/* Begin PBXBuildRule section */
@ -179,6 +185,42 @@
);
script = "cd \"${INPUT_FILE_DIR}\" && \"${SRCROOT}/Dependencies/protobuf-2.0.3/bin/protoc-c\" --c_out=\"${DERIVED_FILES_DIR}\" \"${INPUT_FILE_NAME}\"";
};
05F411F10EF8DF79008050CF /* PBXBuildRule */ = {
isa = PBXBuildRule;
compilerSpec = com.apple.compilers.proxy.script;
filePatterns = "*.proto";
fileType = pattern.proxy;
isEditable = 1;
outputFiles = (
"$(INPUT_FILE_BASE).pb-c.c",
"$(INPUT_FILE_BASE).pb-c.h",
);
script = "cd \"${INPUT_FILE_DIR}\" && \"${SRCROOT}/Dependencies/protobuf-2.0.3/bin/protoc-c\" --c_out=\"${DERIVED_FILES_DIR}\" \"${INPUT_FILE_NAME}\"";
};
05F411FE0EF8E070008050CF /* PBXBuildRule */ = {
isa = PBXBuildRule;
compilerSpec = com.apple.compilers.proxy.script;
filePatterns = "*.proto";
fileType = pattern.proxy;
isEditable = 1;
outputFiles = (
"$(INPUT_FILE_BASE).pb-c.c",
"$(INPUT_FILE_BASE).pb-c.h",
);
script = "cd \"${INPUT_FILE_DIR}\" && \"${SRCROOT}/Dependencies/protobuf-2.0.3/bin/protoc-c\" --c_out=\"${DERIVED_FILES_DIR}\" \"${INPUT_FILE_NAME}\"";
};
05F411FF0EF8E070008050CF /* PBXBuildRule */ = {
isa = PBXBuildRule;
compilerSpec = com.apple.compilers.proxy.script;
filePatterns = "*.proto";
fileType = pattern.proxy;
isEditable = 1;
outputFiles = (
"$(INPUT_FILE_BASE).pb-c.c",
"$(INPUT_FILE_BASE).pb-c.h",
);
script = "cd \"${INPUT_FILE_DIR}\" && \"${SRCROOT}/Dependencies/protobuf-2.0.3/bin/protoc-c\" --c_out=\"${DERIVED_FILES_DIR}\" \"${INPUT_FILE_NAME}\"";
};
/* End PBXBuildRule section */
/* Begin PBXContainerItemProxy section */
@ -695,6 +737,7 @@
05CD321E0EE93B59000FDE88 /* Create Framework */,
);
buildRules = (
05F411FE0EF8E070008050CF /* PBXBuildRule */,
);
dependencies = (
05CD31720EE939DC000FDE88 /* PBXTargetDependency */,
@ -714,6 +757,7 @@
05CD32200EE93B72000FDE88 /* Create Framework */,
);
buildRules = (
05F411FF0EF8E070008050CF /* PBXBuildRule */,
);
dependencies = (
05CD31740EE939DF000FDE88 /* PBXTargetDependency */,
@ -812,6 +856,7 @@
8DC2EF560486A6940098B216 /* Frameworks */,
);
buildRules = (
05F411F10EF8DF79008050CF /* PBXBuildRule */,
);
dependencies = (
);
@ -966,6 +1011,8 @@
05CD36D40EF25717000FDE88 /* PLCrashLogWriterEncoding.c in Sources */,
05F40ACC0EF7379F008050CF /* PLCrashReporter.m in Sources */,
05F411A90EF8DA31008050CF /* PLCrashLog.m in Sources */,
05F411F40EF8DFDA008050CF /* crash_report.proto in Sources */,
05F411FB0EF8E023008050CF /* protobuf-c.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -983,6 +1030,8 @@
05CD36D20EF25717000FDE88 /* PLCrashLogWriterEncoding.c in Sources */,
05F40ACB0EF7379F008050CF /* PLCrashReporter.m in Sources */,
05F411A70EF8DA31008050CF /* PLCrashLog.m in Sources */,
05F411F70EF8E001008050CF /* protobuf-c.c in Sources */,
05F411F50EF8DFE4008050CF /* crash_report.proto in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1081,6 +1130,8 @@
05CD36D60EF25717000FDE88 /* PLCrashLogWriterEncoding.c in Sources */,
05F40ACD0EF7379F008050CF /* PLCrashReporter.m in Sources */,
05F411AB0EF8DA31008050CF /* PLCrashLog.m in Sources */,
05F411F30EF8DFD3008050CF /* crash_report.proto in Sources */,
05F411F90EF8E013008050CF /* protobuf-c.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

Просмотреть файл

@ -4,8 +4,10 @@
# Created by Landon Fuller on 12/5/08.
# Copyright 2008 Plausible Labs Cooperative, Inc. All rights reserved.
# CrashReporter
# PLCrashReporter
_PLCrashReporterException
_PLCrashReporterErrorDomain
.objc_class_name_PLCrashReporter
# PLCrashLog
.objc_class_name_PLCrashLog

Просмотреть файл

@ -15,11 +15,16 @@
#endif
#import "PLCrashReporter.h"
#import "PLCrashLog.h"
/**
* @defgroup functions Crash Reporter Functions Reference
*/
/**
* @defgroup types Crash Reporter Data Types Reference
*/
/**
* @defgroup constants Crash Reporter Constants Reference
*/
@ -61,6 +66,9 @@ typedef enum {
/** An Mach or POSIX operating system error has occured. The underlying NSError cause may be fetched from the userInfo
* dictionary using the NSUnderlyingErrorKey key. */
PLCrashReporterErrorOperatingSystem = 1,
/** The crash report log file is corrupt or invalid */
PLCrashReporterErrorCrashReportInvalid = 2,
} PLCrashReporterError;

Просмотреть файл

@ -6,10 +6,45 @@
*/
#import <Foundation/Foundation.h>
/**
* @ingroup constants
* Crash file magic identifier */
#define PLCRASH_LOG_FILE_MAGIC "plcrash"
/**
* @ingroup constants
* Crash format version byte identifier. Will not change outside of the introduction of
* an entirely new crash log format. */
#define PLCRASH_LOG_FILE_VERSION 1
/**
* @ingroup types
* Plausible Crash Log Header.
*/
struct PLCrashLogFileHeader {
/** File magic, not NULL terminated */
const char magic[7];
/** File version */
const uint8_t version;
/** File data */
const uint8_t data[];
} __attribute__((packed));
/**
* @internal
* Private decoder instance variables (used to hide the underlying protobuf parser).
*/
typedef struct _PLCrashLogDecoder _PLCrashLogDecoder;
@interface PLCrashLog : NSObject {
@private
/** Private implementation variables (used to hide the underlying protobuf parser) */
_PLCrashLogDecoder *_decoder;
}
- (id) initWithData: (NSData *) encodedData error: (NSError **) outError;
@end

Просмотреть файл

@ -6,6 +6,23 @@
*/
#import "PLCrashLog.h"
#import "CrashReporter.h"
#import "crash_report.pb-c.h"
struct _PLCrashLogDecoder {
Plcrash__CrashReport *crashReport;
};
@interface PLCrashLog (PrivateMethods)
- (void) populateError: (NSError **) error
errorCode: (PLCrashReporterError) code
description: (NSString *) description;
- (Plcrash__CrashReport *) decodeCrashData: (NSData *) data error: (NSError **) outError;
@end
/**
* Provides decoding of crash logs generated by the PLCrashReporter framework.
@ -29,13 +46,131 @@
if ((self = [super init]) == nil)
return nil;
/* Allocate the struct and attempt to parse */
_decoder = malloc(sizeof(_PLCrashLogDecoder));
_decoder->crashReport = [self decodeCrashData: encodedData error: outError];
if (_decoder->crashReport == NULL) {
[self release];
return nil;
}
return self;
}
- (void) dealloc {
/* Free the decoder state */
if (_decoder != NULL) {
if (_decoder->crashReport != NULL) {
protobuf_c_message_free_unpacked((ProtobufCMessage *) _decoder->crashReport, &protobuf_c_system_allocator);
}
free(_decoder);
}
[super dealloc];
}
#if 0
- (void) unpack {
/* Check the file magic. The file must be large enough for the value + version + data */
STAssertTrue(statbuf.st_size > strlen(PLCRASH_LOG_FILE_MAGIC) + sizeof(uint8_t), @"File is too small for magic + version + data");
STAssertTrue(memcmp(buf, PLCRASH_LOG_FILE_MAGIC, strlen(PLCRASH_LOG_FILE_MAGIC)) == 0, @"File does not start with magic string");
STAssertEquals(((uint8_t *) buf)[strlen(PLCRASH_LOG_FILE_MAGIC)], (uint8_t)PLCRASH_LOG_FILE_VERSION, @"File version is not equal to 0");
/* Try to read the crash report */
Plcrash__CrashReport *crashReport;
crashReport = plcrash__crash_report__unpack(&protobuf_c_system_allocator, statbuf.st_size, buf + strlen(PLCRASH_LOG_FILE_MAGIC) + sizeof(uint8_t));
/* If reading the report didn't fail, test the contents */
STAssertNotNULL(crashReport, @"Could not decode crash report");
if (crashReport != NULL) {
/* Test the report */
[self checkSystemInfo: crashReport];
[self checkThreads: crashReport];
[self checkException: crashReport];
/* Free it */
protobuf_c_message_free_unpacked((ProtobufCMessage *) crashReport, &protobuf_c_system_allocator);
}
}
#endif
@end
/**
* @internal
* Private Methods
*/
@implementation PLCrashLog (PrivateMethods)
/**
* Decode the crash log message.
*
* @warning MEMORY WARNING. The caller is responsible for deallocating th ePlcrash__CrashReport instance
* returned by this method via protobuf_c_message_free_unpacked().
*/
- (Plcrash__CrashReport *) decodeCrashData: (NSData *) data error: (NSError **) outError {
const struct PLCrashLogFileHeader *header;
const void *bytes;
bytes = [data bytes];
header = bytes;
/* Verify that the crash log is sufficently large */
if (sizeof(struct PLCrashLogFileHeader) >= [data length]) {
[self populateError: outError errorCode: PLCrashReporterErrorCrashReportInvalid description: NSLocalizedString(@"Could not decode truncated crash log", @"Crash log decoding error message")];
return NULL;
}
/* Check the file magic */
if (memcmp(header->magic, PLCRASH_LOG_FILE_MAGIC, strlen(PLCRASH_LOG_FILE_MAGIC)) != 0) {
[self populateError: outError errorCode: PLCrashReporterErrorCrashReportInvalid description: NSLocalizedString(@"Could not decode invalid crash log header", @"Crash log decoding error message")];
return NULL;
}
/* Check the version */
if(header->version != PLCRASH_LOG_FILE_VERSION) {
[self populateError: outError errorCode: PLCrashReporterErrorCrashReportInvalid description: [NSString stringWithFormat: NSLocalizedString(@"Could not decode unsupported crash report version: %d", @"Crash log decoding message"), header->version]];
return NULL;
}
Plcrash__CrashReport *crashReport = plcrash__crash_report__unpack(&protobuf_c_system_allocator, [data length] - sizeof(struct PLCrashLogFileHeader), header->data);
if (crashReport == NULL) {
[self populateError: outError errorCode: PLCrashReporterErrorCrashReportInvalid description: NSLocalizedString(@"An unknown error occured decoding the crash report", @"Crash log decoding error message")];
return NULL;
}
return crashReport;
}
/**
* Populate an NSError instance with the provided information.
*
* @param error Error instance to populate. If NULL, this method returns
* and nothing is modified.
* @param code The error code corresponding to this error.
* @param description A localized error description.
* @param cause The underlying cause, if any. May be nil.
*/
- (void) populateError: (NSError **) error
errorCode: (PLCrashReporterError) code
description: (NSString *) description
{
NSMutableDictionary *userInfo;
if (error == NULL)
return;
/* Create the userInfo dictionary */
userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
description, NSLocalizedDescriptionKey,
nil
];
*error = [NSError errorWithDomain: PLCrashReporterErrorDomain code: code userInfo: userInfo];
}
@end

Просмотреть файл

@ -6,14 +6,90 @@
*/
#import "GTMSenTestCase.h"
#import "PLCrashLog.h"
#import "PLCrashReporter.h"
#import "PLCrashFrameWalker.h"
#import "PLCrashLogWriter.h"
#import <fcntl.h>
@interface PLCrashLogTests : SenTestCase {
@private
/* Path to crash log */
NSString *_logPath;
/* Test thread */
plframe_test_thead_t _thr_args;
}
@interface PLCrashLogTests : SenTestCase
@end
@implementation PLCrashLogTests
- (void) testSomething {
- (void) setUp {
/* Create a temporary log path */
_logPath = [[NSTemporaryDirectory() stringByAppendingString: [[NSProcessInfo processInfo] globallyUniqueString]] retain];
/* Create the test thread */
plframe_test_thread_spawn(&_thr_args);
}
- (void) tearDown {
NSError *error;
/* Delete the file */
STAssertTrue([[NSFileManager defaultManager] removeItemAtPath: _logPath error: &error], @"Could not remove log file");
[_logPath release];
/* Stop the test thread */
plframe_test_thread_stop(&_thr_args);
}
- (void) testWriteReport {
siginfo_t info;
plframe_cursor_t cursor;
plcrash_log_writer_t writer;
plcrash_async_file_t file;
NSError *error;
/* Initialze faux crash data */
{
info.si_addr = 0x0;
info.si_errno = 0;
info.si_pid = getpid();
info.si_uid = getuid();
info.si_code = SEGV_MAPERR;
info.si_signo = SIGSEGV;
info.si_status = 0;
/* Steal the test thread's stack for iteration */
plframe_cursor_thread_init(&cursor, pthread_mach_thread_np(_thr_args.thread));
}
/* Open the output file */
int fd = open([_logPath UTF8String], O_RDWR|O_CREAT|O_EXCL, 0644);
plcrash_async_file_init(&file, fd, 0);
/* Initialize a writer */
STAssertEquals(PLCRASH_ESUCCESS, plcrash_log_writer_init(&writer, @"test.id", @"1.0"), @"Initialization failed");
/* Set an exception */
plcrash_log_writer_set_exception(&writer, [NSException exceptionWithName: @"TestException" reason: @"TestReason" userInfo: nil]);
/* Write the crash report */
STAssertEquals(PLCRASH_ESUCCESS, plcrash_log_writer_write(&writer, &file, &info, cursor.uap), @"Crash log failed");
/* Close it */
plcrash_log_writer_close(&writer);
plcrash_log_writer_free(&writer);
plcrash_async_file_flush(&file);
plcrash_async_file_close(&file);
/* Try to parse it */
PLCrashLog *crashLog = [[[PLCrashLog alloc] initWithData: [NSData dataWithContentsOfMappedFile: _logPath] error: &error] autorelease];
STAssertNotNil(crashLog, @"Could not decode crash log: %@", error);
}
@end

Просмотреть файл

@ -21,13 +21,6 @@
* @{
*/
/** Crash file magic identifier */
#define PLCRASH_LOG_FILE_MAGIC "plcrash"
/** Crash format version byte identifier. Will not change outside of the introduction of
* an entirely new crash log format. */
#define PLCRASH_LOG_FILE_VERSION 1
/** CrashReport machine type enums */
enum {
PLCRASH_MACHINE_TYPE_X86_32 = 0,

Просмотреть файл

@ -16,6 +16,7 @@
#import <mach-o/dyld.h>
#import "PLCrashLog.h"
#import "PLCrashLogWriter.h"
#import "PLCrashLogWriterEncoding.h"
#import "PLCrashAsync.h"

Просмотреть файл

@ -7,6 +7,7 @@
#import "GTMSenTestCase.h"
#import "PLCrashLog.h"
#import "PLCrashLogWriter.h"
#import "PLCrashFrameWalker.h"