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:
Родитель
860bb8493d
Коммит
3d99ecb9cc
|
@ -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"
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче