mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-25 09:32:46 +00:00
Add new crash report formatter
Enables automatic and async-safe symbolication of system framework symbols right on the device
This commit is contained in:
parent
d0df4c2825
commit
6e6b3ffdfd
@ -32,6 +32,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "BWGlobal.h"
|
||||
#import "BWQuincyManager.h"
|
||||
#import "CNSCrashReportTextFormatter.h"
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
#include <inttypes.h> //needed for PRIx64 macro
|
||||
@ -511,7 +512,7 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
|
||||
}
|
||||
|
||||
NSString *crashUUID = report.reportInfo.reportGUID ?: @"";
|
||||
NSString *crashLogString = [PLCrashReportTextFormatter stringValueForCrashReport:report withTextFormat:PLCrashReportTextFormatiOS];
|
||||
NSString *crashLogString = [CNSCrashReportTextFormatter stringValueForCrashReport:report withTextFormat:PLCrashReportTextFormatiOS];
|
||||
|
||||
if ([report.applicationInfo.applicationVersion compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) {
|
||||
_crashIdenticalCurrentVersion = YES;
|
||||
|
||||
71
Classes/CNSCrashReportTextFormatter.h
Normal file
71
Classes/CNSCrashReportTextFormatter.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Authors:
|
||||
* Landon Fuller <landonf@plausiblelabs.com>
|
||||
* Damian Morris <damian@moso.com.au>
|
||||
* Andreas Linde <mail@andreaslinde.de>
|
||||
*
|
||||
* Copyright (c) 2008-2012 Plausible Labs Cooperative, Inc.
|
||||
* Copyright (c) 2010 MOSO Corporation, Pty Ltd.
|
||||
* Copyright (c) 2012 Codenauts UG (haftungsbeschränkt)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "CrashReporter/PLCrashReportFormatter.h"
|
||||
|
||||
/**
|
||||
* Supported text output formats.
|
||||
*
|
||||
* @ingroup enums
|
||||
*/
|
||||
typedef enum {
|
||||
/** An iOS-compatible crash log text format. Compatible with the crash logs generated by the device and available
|
||||
* through iTunes Connect. */
|
||||
CNSCrashReportTextFormatiOS = 0
|
||||
} CNSCrashReportTextFormat;
|
||||
|
||||
|
||||
// Dictionary keys for array elements returned by arrayOfAppUUIDsForCrashReport:
|
||||
#define kCNSBinaryImageKeyUUID @"uuid"
|
||||
#define kCNSBinaryImageKeyArch @"arch"
|
||||
#define kCNSBinaryImageKeyType @"type"
|
||||
|
||||
|
||||
@interface CNSCrashReportTextFormatter : NSObject <PLCrashReportFormatter> {
|
||||
@private
|
||||
/** Text output format. */
|
||||
CNSCrashReportTextFormat _textFormat;
|
||||
|
||||
/** Encoding to use for string output. */
|
||||
NSStringEncoding _stringEncoding;
|
||||
}
|
||||
|
||||
+ (NSString *) stringValueForCrashReport: (PLCrashReport *) report withTextFormat: (CNSCrashReportTextFormat) textFormat;
|
||||
+ (NSArray *) arrayOfAppUUIDsForCrashReport: (PLCrashReport *) report;
|
||||
|
||||
- (id) initWithTextFormat: (CNSCrashReportTextFormat) textFormat stringEncoding: (NSStringEncoding) stringEncoding;
|
||||
|
||||
@end
|
||||
627
Classes/CNSCrashReportTextFormatter.m
Normal file
627
Classes/CNSCrashReportTextFormatter.m
Normal file
@ -0,0 +1,627 @@
|
||||
/*
|
||||
* Authors:
|
||||
* Landon Fuller <landonf@plausiblelabs.com>
|
||||
* Damian Morris <damian@moso.com.au>
|
||||
* Andreas Linde <mail@andreaslinde.de>
|
||||
*
|
||||
* Copyright (c) 2008-2012 Plausible Labs Cooperative, Inc.
|
||||
* Copyright (c) 2010 MOSO Corporation, Pty Ltd.
|
||||
* Copyright (c) 2012 Codenauts UG (haftungsbeschränkt)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import "CrashReporter/CrashReporter.h"
|
||||
|
||||
#import "CNSCrashReportTextFormatter.h"
|
||||
|
||||
@interface CNSCrashReportTextFormatter (PrivateAPI)
|
||||
NSInteger binaryImageSort(id binary1, id binary2, void *context);
|
||||
+ (NSString *) formatStackFrame: (PLCrashReportStackFrameInfo *) frameInfo
|
||||
frameIndex: (NSUInteger) frameIndex
|
||||
report: (PLCrashReport *) report;
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
* Formats PLCrashReport data as human-readable text.
|
||||
*/
|
||||
@implementation CNSCrashReportTextFormatter
|
||||
|
||||
|
||||
/**
|
||||
* Formats the provided @a report as human-readable text in the given @a textFormat, and return
|
||||
* the formatted result as a string.
|
||||
*
|
||||
* @param report The report to format.
|
||||
* @param textFormat The text format to use.
|
||||
*
|
||||
* @return Returns the formatted result on success, or nil if an error occurs.
|
||||
*/
|
||||
+ (NSString *) stringValueForCrashReport: (PLCrashReport *) report withTextFormat: (CNSCrashReportTextFormat) textFormat {
|
||||
NSMutableString* text = [NSMutableString string];
|
||||
boolean_t lp64 = true; // quiesce GCC uninitialized value warning
|
||||
|
||||
/* Header */
|
||||
|
||||
/* Map to apple style OS nane */
|
||||
NSString *osName;
|
||||
switch (report.systemInfo.operatingSystem) {
|
||||
case PLCrashReportOperatingSystemMacOSX:
|
||||
osName = @"Mac OS X";
|
||||
break;
|
||||
case PLCrashReportOperatingSystemiPhoneOS:
|
||||
osName = @"iPhone OS";
|
||||
break;
|
||||
case PLCrashReportOperatingSystemiPhoneSimulator:
|
||||
osName = @"Mac OS X";
|
||||
break;
|
||||
default:
|
||||
osName = [NSString stringWithFormat: @"Unknown (%d)", report.systemInfo.operatingSystem];
|
||||
break;
|
||||
}
|
||||
|
||||
/* Map to Apple-style code type, and mark whether architecture is LP64 (64-bit) */
|
||||
NSString *codeType = nil;
|
||||
{
|
||||
/* Attempt to derive the code type from the binary images */
|
||||
for (PLCrashReportBinaryImageInfo *image in report.images) {
|
||||
/* Skip images with no specified type */
|
||||
if (image.codeType == nil)
|
||||
continue;
|
||||
|
||||
/* Skip unknown encodings */
|
||||
if (image.codeType.typeEncoding != PLCrashReportProcessorTypeEncodingMach)
|
||||
continue;
|
||||
|
||||
switch (image.codeType.type) {
|
||||
case CPU_TYPE_ARM:
|
||||
codeType = @"ARM";
|
||||
lp64 = false;
|
||||
break;
|
||||
|
||||
case CPU_TYPE_X86:
|
||||
codeType = @"X86";
|
||||
lp64 = false;
|
||||
break;
|
||||
|
||||
case CPU_TYPE_X86_64:
|
||||
codeType = @"X86-64";
|
||||
lp64 = true;
|
||||
break;
|
||||
|
||||
case CPU_TYPE_POWERPC:
|
||||
codeType = @"PPC";
|
||||
lp64 = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Do nothing, handled below.
|
||||
break;
|
||||
}
|
||||
|
||||
/* Stop immediately if code type was discovered */
|
||||
if (codeType != nil)
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we were unable to determine the code type, fall back on the legacy architecture value. */
|
||||
if (codeType == nil) {
|
||||
switch (report.systemInfo.architecture) {
|
||||
case PLCrashReportArchitectureARMv6:
|
||||
case PLCrashReportArchitectureARMv7:
|
||||
codeType = @"ARM";
|
||||
lp64 = false;
|
||||
break;
|
||||
case PLCrashReportArchitectureX86_32:
|
||||
codeType = @"X86";
|
||||
lp64 = false;
|
||||
break;
|
||||
case PLCrashReportArchitectureX86_64:
|
||||
codeType = @"X86-64";
|
||||
lp64 = true;
|
||||
break;
|
||||
case PLCrashReportArchitecturePPC:
|
||||
codeType = @"PPC";
|
||||
lp64 = false;
|
||||
break;
|
||||
default:
|
||||
codeType = [NSString stringWithFormat: @"Unknown (%d)", report.systemInfo.architecture];
|
||||
lp64 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
NSString *reportGUID = @"[TODO]";
|
||||
if (report.hasReportInfo && report.reportInfo.reportGUID != nil)
|
||||
reportGUID = report.reportInfo.reportGUID;
|
||||
|
||||
NSString *hardwareModel = @"???";
|
||||
if (report.hasMachineInfo && report.machineInfo.modelName != nil)
|
||||
hardwareModel = report.machineInfo.modelName;
|
||||
|
||||
[text appendFormat: @"Incident Identifier: %@\n", reportGUID];
|
||||
[text appendFormat: @"CrashReporter Key: [TODO]\n"];
|
||||
[text appendFormat: @"Hardware Model: %@\n", hardwareModel];
|
||||
}
|
||||
|
||||
/* Application and process info */
|
||||
{
|
||||
NSString *unknownString = @"???";
|
||||
|
||||
NSString *processName = unknownString;
|
||||
NSString *processId = unknownString;
|
||||
NSString *processPath = unknownString;
|
||||
NSString *parentProcessName = unknownString;
|
||||
NSString *parentProcessId = unknownString;
|
||||
|
||||
/* Process information was not available in earlier crash report versions */
|
||||
if (report.hasProcessInfo) {
|
||||
/* Process Name */
|
||||
if (report.processInfo.processName != nil)
|
||||
processName = report.processInfo.processName;
|
||||
|
||||
/* PID */
|
||||
processId = [[NSNumber numberWithUnsignedInteger: report.processInfo.processID] stringValue];
|
||||
|
||||
/* Process Path */
|
||||
if (report.processInfo.processPath != nil) {
|
||||
processPath = report.processInfo.processPath;
|
||||
|
||||
/* Remove username from the path */
|
||||
processPath = [processPath stringByAbbreviatingWithTildeInPath];
|
||||
if ([[processPath substringToIndex:1] isEqualToString:@"~"])
|
||||
processPath = [NSString stringWithFormat:@"/Users/USER%@", [processPath substringFromIndex:1]];
|
||||
}
|
||||
|
||||
/* Parent Process Name */
|
||||
if (report.processInfo.parentProcessName != nil)
|
||||
parentProcessName = report.processInfo.parentProcessName;
|
||||
|
||||
/* Parent Process ID */
|
||||
parentProcessId = [[NSNumber numberWithUnsignedInteger: report.processInfo.parentProcessID] stringValue];
|
||||
}
|
||||
|
||||
[text appendFormat: @"Process: %@ [%@]\n", processName, processId];
|
||||
[text appendFormat: @"Path: %@\n", processPath];
|
||||
[text appendFormat: @"Identifier: %@\n", report.applicationInfo.applicationIdentifier];
|
||||
[text appendFormat: @"Version: %@\n", report.applicationInfo.applicationVersion];
|
||||
[text appendFormat: @"Code Type: %@\n", codeType];
|
||||
[text appendFormat: @"Parent Process: %@ [%@]\n", parentProcessName, parentProcessId];
|
||||
}
|
||||
|
||||
[text appendString: @"\n"];
|
||||
|
||||
/* System info */
|
||||
{
|
||||
NSString *osBuild = @"???";
|
||||
if (report.systemInfo.operatingSystemBuild != nil)
|
||||
osBuild = report.systemInfo.operatingSystemBuild;
|
||||
|
||||
[text appendFormat: @"Date/Time: %@\n", report.systemInfo.timestamp];
|
||||
[text appendFormat: @"OS Version: %@ %@ (%@)\n", osName, report.systemInfo.operatingSystemVersion, osBuild];
|
||||
[text appendFormat: @"Report Version: 104\n"];
|
||||
}
|
||||
|
||||
[text appendString: @"\n"];
|
||||
|
||||
/* Exception code */
|
||||
[text appendFormat: @"Exception Type: %@\n", report.signalInfo.name];
|
||||
[text appendFormat: @"Exception Codes: %@ at 0x%" PRIx64 "\n", report.signalInfo.code, report.signalInfo.address];
|
||||
|
||||
for (PLCrashReportThreadInfo *thread in report.threads) {
|
||||
if (thread.crashed) {
|
||||
[text appendFormat: @"Crashed Thread: %ld\n", (long) thread.threadNumber];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[text appendString: @"\n"];
|
||||
|
||||
/* Uncaught Exception */
|
||||
if (report.hasExceptionInfo) {
|
||||
[text appendFormat: @"Application Specific Information:\n"];
|
||||
[text appendFormat: @"*** Terminating app due to uncaught exception '%@', reason: '%@'\n",
|
||||
report.exceptionInfo.exceptionName, report.exceptionInfo.exceptionReason];
|
||||
|
||||
[text appendString: @"\n"];
|
||||
}
|
||||
|
||||
/* If an exception stack trace is available, output a pseudo-thread to provide the frame info */
|
||||
if (report.exceptionInfo != nil && report.exceptionInfo.stackFrames != nil && [report.exceptionInfo.stackFrames count] > 0) {
|
||||
PLCrashReportExceptionInfo *exception = report.exceptionInfo;
|
||||
|
||||
/* Write out the frames */
|
||||
NSUInteger numberBlankStackFrames = 0;
|
||||
|
||||
for (NSUInteger frame_idx = 0; frame_idx < [exception.stackFrames count]; frame_idx++) {
|
||||
PLCrashReportStackFrameInfo *frameInfo = [exception.stackFrames objectAtIndex: frame_idx];
|
||||
NSString *formattedStackFrame = [self formatStackFrame: frameInfo frameIndex: frame_idx - numberBlankStackFrames report: report];
|
||||
if (formattedStackFrame) {
|
||||
if (frame_idx - numberBlankStackFrames == 0) {
|
||||
/* Create the pseudo-thread header. We use the named thread format to mark this thread */
|
||||
[text appendString: @"Last Exception Backtrace:\n"];
|
||||
}
|
||||
|
||||
[text appendString: formattedStackFrame];
|
||||
} else {
|
||||
numberBlankStackFrames++;
|
||||
}
|
||||
}
|
||||
[text appendString: @"\n\n"];
|
||||
}
|
||||
|
||||
/* Threads */
|
||||
PLCrashReportThreadInfo *crashed_thread = nil;
|
||||
NSInteger maxThreadNum = 0;
|
||||
for (PLCrashReportThreadInfo *thread in report.threads) {
|
||||
|
||||
/* Write out the frames */
|
||||
NSUInteger numberBlankStackFrames = 0;
|
||||
|
||||
for (NSUInteger frame_idx = 0; frame_idx < [thread.stackFrames count]; frame_idx++) {
|
||||
PLCrashReportStackFrameInfo *frameInfo = [thread.stackFrames objectAtIndex: frame_idx];
|
||||
NSString *formattedStackFrame = [self formatStackFrame: frameInfo frameIndex: frame_idx report: report];
|
||||
if (formattedStackFrame) {
|
||||
if (frame_idx - numberBlankStackFrames == 0) {
|
||||
/* Create the thread header. */
|
||||
if (thread.crashed) {
|
||||
[text appendFormat: @"Thread %ld Crashed:\n", (long) thread.threadNumber];
|
||||
crashed_thread = thread;
|
||||
} else {
|
||||
[text appendFormat: @"Thread %ld:\n", (long) thread.threadNumber];
|
||||
}
|
||||
}
|
||||
|
||||
[text appendString: formattedStackFrame];
|
||||
} else {
|
||||
numberBlankStackFrames++;
|
||||
}
|
||||
}
|
||||
[text appendString: @"\n"];
|
||||
|
||||
/* Track the highest thread number */
|
||||
maxThreadNum = MAX(maxThreadNum, thread.threadNumber);
|
||||
}
|
||||
|
||||
/* Registers */
|
||||
if (crashed_thread != nil) {
|
||||
[text appendFormat: @"Thread %ld crashed with %@ Thread State:\n", (long) crashed_thread.threadNumber, codeType];
|
||||
|
||||
int regColumn = 0;
|
||||
for (PLCrashReportRegisterInfo *reg in crashed_thread.registers) {
|
||||
NSString *reg_fmt;
|
||||
|
||||
/* Use 32-bit or 64-bit fixed width format for the register values */
|
||||
if (lp64)
|
||||
reg_fmt = @"%6s: 0x%016" PRIx64 " ";
|
||||
else
|
||||
reg_fmt = @"%6s: 0x%08" PRIx64 " ";
|
||||
|
||||
/* Remap register names to match Apple's crash reports */
|
||||
NSString *regName = reg.registerName;
|
||||
if (report.machineInfo != nil && report.machineInfo.processorInfo.typeEncoding == PLCrashReportProcessorTypeEncodingMach) {
|
||||
PLCrashReportProcessorInfo *pinfo = report.machineInfo.processorInfo;
|
||||
cpu_type_t arch_type = pinfo.type & ~CPU_ARCH_MASK;
|
||||
|
||||
/* Apple uses 'ip' rather than 'r12' on ARM */
|
||||
if (arch_type == CPU_TYPE_ARM && [regName isEqual: @"r12"]) {
|
||||
regName = @"ip";
|
||||
}
|
||||
}
|
||||
[text appendFormat: reg_fmt, [regName UTF8String], reg.registerValue];
|
||||
|
||||
regColumn++;
|
||||
if (regColumn == 4) {
|
||||
[text appendString: @"\n"];
|
||||
regColumn = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (regColumn != 0)
|
||||
[text appendString: @"\n"];
|
||||
|
||||
[text appendString: @"\n"];
|
||||
}
|
||||
|
||||
/* Images. The iPhone crash report format sorts these in ascending order, by the base address */
|
||||
[text appendString: @"Binary Images:\n"];
|
||||
for (PLCrashReportBinaryImageInfo *imageInfo in [report.images sortedArrayUsingFunction: binaryImageSort context: nil]) {
|
||||
NSString *uuid;
|
||||
/* Fetch the UUID if it exists */
|
||||
if (imageInfo.hasImageUUID)
|
||||
uuid = imageInfo.imageUUID;
|
||||
else
|
||||
uuid = @"???";
|
||||
|
||||
/* Determine the architecture string */
|
||||
NSString *archName = @"???";
|
||||
if (imageInfo.codeType != nil && imageInfo.codeType.typeEncoding == PLCrashReportProcessorTypeEncodingMach) {
|
||||
switch (imageInfo.codeType.type) {
|
||||
case CPU_TYPE_ARM:
|
||||
/* Apple includes subtype for ARM binaries. */
|
||||
switch (imageInfo.codeType.subtype) {
|
||||
case CPU_SUBTYPE_ARM_V6:
|
||||
archName = @"armv6";
|
||||
break;
|
||||
|
||||
case CPU_SUBTYPE_ARM_V7:
|
||||
archName = @"armv7";
|
||||
break;
|
||||
|
||||
default:
|
||||
archName = @"arm-unknown";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case CPU_TYPE_X86:
|
||||
archName = @"i386";
|
||||
break;
|
||||
|
||||
case CPU_TYPE_X86_64:
|
||||
archName = @"x86_64";
|
||||
break;
|
||||
|
||||
case CPU_TYPE_POWERPC:
|
||||
archName = @"powerpc";
|
||||
break;
|
||||
|
||||
default:
|
||||
// Use the default archName value (initialized above).
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if this is the main executable */
|
||||
NSString *binaryDesignator = @" ";
|
||||
if ([imageInfo.imageName isEqual: report.processInfo.processPath])
|
||||
binaryDesignator = @"+";
|
||||
|
||||
/* base_address - terminating_address [designator]file_name arch <uuid> file_path */
|
||||
NSString *fmt = nil;
|
||||
if (lp64) {
|
||||
fmt = @"%18#" PRIx64 " - %18#" PRIx64 " %@%@ %@ <%@> %@\n";
|
||||
} else {
|
||||
fmt = @"%10#" PRIx64 " - %10#" PRIx64 " %@%@ %@ <%@> %@\n";
|
||||
}
|
||||
|
||||
/* Remove username from the image path */
|
||||
NSString *imageName = [imageInfo.imageName stringByAbbreviatingWithTildeInPath];
|
||||
if ([[imageName substringToIndex:1] isEqualToString:@"~"])
|
||||
imageName = [NSString stringWithFormat:@"/Users/USER%@", [imageName substringFromIndex:1]];
|
||||
|
||||
[text appendFormat: fmt,
|
||||
imageInfo.imageBaseAddress,
|
||||
imageInfo.imageBaseAddress + (MAX(1, imageInfo.imageSize) - 1), // The Apple format uses an inclusive range
|
||||
binaryDesignator,
|
||||
[imageInfo.imageName lastPathComponent],
|
||||
archName,
|
||||
uuid,
|
||||
imageName];
|
||||
}
|
||||
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of app UUIDs and their architecture
|
||||
* As a dictionary for each element
|
||||
*
|
||||
* @param report The report to format.
|
||||
*
|
||||
* @return Returns the formatted result on success, or nil if an error occurs.
|
||||
*/
|
||||
+ (NSArray *) arrayOfAppUUIDsForCrashReport: (PLCrashReport *) report {
|
||||
NSMutableArray* appUUIDs = [NSMutableArray array];
|
||||
|
||||
/* Images. The iPhone crash report format sorts these in ascending order, by the base address */
|
||||
for (PLCrashReportBinaryImageInfo *imageInfo in [report.images sortedArrayUsingFunction: binaryImageSort context: nil]) {
|
||||
NSString *uuid;
|
||||
/* Fetch the UUID if it exists */
|
||||
if (imageInfo.hasImageUUID)
|
||||
uuid = imageInfo.imageUUID;
|
||||
else
|
||||
uuid = @"???";
|
||||
|
||||
/* Determine the architecture string */
|
||||
NSString *archName = @"???";
|
||||
if (imageInfo.codeType != nil && imageInfo.codeType.typeEncoding == PLCrashReportProcessorTypeEncodingMach) {
|
||||
switch (imageInfo.codeType.type) {
|
||||
case CPU_TYPE_ARM:
|
||||
/* Apple includes subtype for ARM binaries. */
|
||||
switch (imageInfo.codeType.subtype) {
|
||||
case CPU_SUBTYPE_ARM_V6:
|
||||
archName = @"armv6";
|
||||
break;
|
||||
|
||||
case CPU_SUBTYPE_ARM_V7:
|
||||
archName = @"armv7";
|
||||
break;
|
||||
|
||||
default:
|
||||
archName = @"arm-unknown";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case CPU_TYPE_X86:
|
||||
archName = @"i386";
|
||||
break;
|
||||
|
||||
case CPU_TYPE_X86_64:
|
||||
archName = @"x86_64";
|
||||
break;
|
||||
|
||||
case CPU_TYPE_POWERPC:
|
||||
archName = @"powerpc";
|
||||
break;
|
||||
|
||||
default:
|
||||
// Use the default archName value (initialized above).
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if this is the app executable or app specific framework */
|
||||
NSString *imagePath = [imageInfo.imageName stringByStandardizingPath];
|
||||
NSString *appBundleContentsPath = [[report.processInfo.processPath stringByDeletingLastPathComponent] stringByDeletingLastPathComponent];
|
||||
NSString *imageType = @"";
|
||||
|
||||
if ([imageInfo.imageName isEqual: report.processInfo.processPath]) {
|
||||
imageType = @"app";
|
||||
} else {
|
||||
imageType = @"framework";
|
||||
}
|
||||
|
||||
if ([imagePath isEqual: report.processInfo.processPath] || [imagePath hasPrefix:appBundleContentsPath]) {
|
||||
[appUUIDs addObject:[NSDictionary dictionaryWithObjectsAndKeys:uuid, kCNSBinaryImageKeyUUID, archName, kCNSBinaryImageKeyArch, imageType, kCNSBinaryImageKeyType, nil]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return appUUIDs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize with the request string encoding and output format.
|
||||
*
|
||||
* @param textFormat Format to use for the generated text crash report.
|
||||
* @param stringEncoding Encoding to use when writing to the output stream.
|
||||
*/
|
||||
- (id) initWithTextFormat: (CNSCrashReportTextFormat) textFormat stringEncoding: (NSStringEncoding) stringEncoding {
|
||||
if ((self = [super init]) == nil)
|
||||
return nil;
|
||||
|
||||
_textFormat = textFormat;
|
||||
_stringEncoding = stringEncoding;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// from PLCrashReportFormatter protocol
|
||||
- (NSData *) formatReport: (PLCrashReport *) report error: (NSError **) outError {
|
||||
NSString *text = [PLCrashReportTextFormatter stringValueForCrashReport: report withTextFormat: _textFormat];
|
||||
return [text dataUsingEncoding: _stringEncoding allowLossyConversion: YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CNSCrashReportTextFormatter (PrivateAPI)
|
||||
|
||||
|
||||
/**
|
||||
* Format a stack frame for display in a thread backtrace.
|
||||
*
|
||||
* @param frameInfo The stack frame to format
|
||||
* @param frameIndex The frame's index
|
||||
* @param report The report from which this frame was acquired.
|
||||
*
|
||||
* @return Returns a formatted frame line.
|
||||
*/
|
||||
+ (NSString *) formatStackFrame: (PLCrashReportStackFrameInfo *) frameInfo
|
||||
frameIndex: (NSUInteger) frameIndex
|
||||
report: (PLCrashReport *) report
|
||||
{
|
||||
/* Base image address containing instrumention pointer, offset of the IP from that base
|
||||
* address, and the associated image name */
|
||||
uint64_t baseAddress = 0x0;
|
||||
uint64_t pcOffset = 0x0;
|
||||
NSString *imageName = @"\?\?\?";
|
||||
NSString *symbol = nil;
|
||||
|
||||
PLCrashReportBinaryImageInfo *imageInfo = [report imageForAddress: frameInfo.instructionPointer];
|
||||
if (imageInfo != nil) {
|
||||
imageName = [imageInfo.imageName lastPathComponent];
|
||||
baseAddress = imageInfo.imageBaseAddress;
|
||||
pcOffset = frameInfo.instructionPointer - imageInfo.imageBaseAddress;
|
||||
NSString *imagePath = [imageInfo.imageName stringByStandardizingPath];
|
||||
NSString *appBundleContentsPath = [[report.processInfo.processPath stringByDeletingLastPathComponent] stringByDeletingLastPathComponent];
|
||||
|
||||
if (![imagePath isEqual: report.processInfo.processPath] && ![imagePath hasPrefix:appBundleContentsPath]) {
|
||||
symbol = frameInfo.symbolName;
|
||||
pcOffset = frameInfo.instructionPointer - frameInfo.symbolStart;
|
||||
}
|
||||
}
|
||||
|
||||
/* The frame has nothing useful, so return nil so it can be filtered out */
|
||||
if (frameInfo.instructionPointer == 0 && baseAddress == 0 && pcOffset == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* Make sure UTF8/16 characters are handled correctly */
|
||||
NSInteger offset = 0;
|
||||
NSInteger index = 0;
|
||||
for (index = 0; index < [imageName length]; index++) {
|
||||
NSRange range = [imageName rangeOfComposedCharacterSequenceAtIndex:index];
|
||||
if (range.length > 1) {
|
||||
offset += range.length - 1;
|
||||
index += range.length - 1;
|
||||
}
|
||||
if (index > 32) {
|
||||
imageName = [NSString stringWithFormat:@"%@...", [imageName substringToIndex:index - 1]];
|
||||
index += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index-offset < 36) {
|
||||
imageName = [imageName stringByPaddingToLength:36+offset withString:@" " startingAtIndex:0];
|
||||
}
|
||||
|
||||
if (symbol) {
|
||||
return [NSString stringWithFormat: @"%-4ld%@0x%08" PRIx64 " %@ + %" PRId64 "\n",
|
||||
(long) frameIndex,
|
||||
imageName,
|
||||
frameInfo.instructionPointer,
|
||||
symbol,
|
||||
pcOffset];
|
||||
} else {
|
||||
return [NSString stringWithFormat: @"%-4ld%@0x%08" PRIx64 " 0x%" PRIx64 " + %" PRId64 "\n",
|
||||
(long) frameIndex,
|
||||
imageName,
|
||||
frameInfo.instructionPointer,
|
||||
baseAddress,
|
||||
pcOffset];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort PLCrashReportBinaryImageInfo instances by their starting address.
|
||||
*/
|
||||
NSInteger binaryImageSort(id binary1, id binary2, void *context) {
|
||||
uint64_t addr1 = [binary1 imageBaseAddress];
|
||||
uint64_t addr2 = [binary2 imageBaseAddress];
|
||||
|
||||
if (addr1 < addr2)
|
||||
return NSOrderedAscending;
|
||||
else if (addr1 > addr2)
|
||||
return NSOrderedDescending;
|
||||
else
|
||||
return NSOrderedSame;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -9,6 +9,8 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
1E322DAD148FCE2100077977 /* CNSFixCategoryBug.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E322DAC148FCE2100077977 /* CNSFixCategoryBug.h */; };
|
||||
1E40BCB515A3487500BD64D9 /* BWQuincyManagerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E40BCB415A3487500BD64D9 /* BWQuincyManagerDelegate.h */; };
|
||||
1E40BCB915A3494400BD64D9 /* CNSCrashReportTextFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E40BCB715A3494400BD64D9 /* CNSCrashReportTextFormatter.h */; };
|
||||
1E40BCBA15A3494400BD64D9 /* CNSCrashReportTextFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E40BCB815A3494400BD64D9 /* CNSCrashReportTextFormatter.m */; };
|
||||
E400561E148D79B500EB22B9 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E400561D148D79B500EB22B9 /* Foundation.framework */; };
|
||||
E41EB471148D7BF50015DEDC /* BWApp.h in Headers */ = {isa = PBXBuildFile; fileRef = E41EB459148D7BF50015DEDC /* BWApp.h */; };
|
||||
E41EB472148D7BF50015DEDC /* BWApp.m in Sources */ = {isa = PBXBuildFile; fileRef = E41EB45A148D7BF50015DEDC /* BWApp.m */; };
|
||||
@ -40,6 +42,8 @@
|
||||
/* Begin PBXFileReference section */
|
||||
1E322DAC148FCE2100077977 /* CNSFixCategoryBug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CNSFixCategoryBug.h; sourceTree = "<group>"; };
|
||||
1E40BCB415A3487500BD64D9 /* BWQuincyManagerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BWQuincyManagerDelegate.h; sourceTree = "<group>"; };
|
||||
1E40BCB715A3494400BD64D9 /* CNSCrashReportTextFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CNSCrashReportTextFormatter.h; sourceTree = "<group>"; };
|
||||
1E40BCB815A3494400BD64D9 /* CNSCrashReportTextFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CNSCrashReportTextFormatter.m; sourceTree = "<group>"; };
|
||||
E400561A148D79B500EB22B9 /* libHockeySDK.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libHockeySDK.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E400561D148D79B500EB22B9 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
E400562B148D79B500EB22B9 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||
@ -146,6 +150,8 @@
|
||||
E41EB463148D7BF50015DEDC /* BWQuincyManager.h */,
|
||||
E41EB464148D7BF50015DEDC /* BWQuincyManager.m */,
|
||||
1E40BCB415A3487500BD64D9 /* BWQuincyManagerDelegate.h */,
|
||||
1E40BCB715A3494400BD64D9 /* CNSCrashReportTextFormatter.h */,
|
||||
1E40BCB815A3494400BD64D9 /* CNSCrashReportTextFormatter.m */,
|
||||
E41EB465148D7BF50015DEDC /* CNSHockeyManager.h */,
|
||||
E41EB466148D7BF50015DEDC /* CNSHockeyManager.m */,
|
||||
);
|
||||
@ -200,6 +206,7 @@
|
||||
E41EB487148D7BF50015DEDC /* UIImage+HockeyAdditions.h in Headers */,
|
||||
1E322DAD148FCE2100077977 /* CNSFixCategoryBug.h in Headers */,
|
||||
1E40BCB515A3487500BD64D9 /* BWQuincyManagerDelegate.h in Headers */,
|
||||
1E40BCB915A3494400BD64D9 /* CNSCrashReportTextFormatter.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -265,6 +272,7 @@
|
||||
E41EB484148D7BF50015DEDC /* PSStoreButton.m in Sources */,
|
||||
E41EB486148D7BF50015DEDC /* PSWebTableViewCell.m in Sources */,
|
||||
E41EB488148D7BF50015DEDC /* UIImage+HockeyAdditions.m in Sources */,
|
||||
1E40BCBA15A3494400BD64D9 /* CNSCrashReportTextFormatter.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user