Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Symbolicate exception call stacks

Example exception output with symbolication:

EXCEPTION CDRSpec some failing test
*** -[__NSArrayI objectAtIndex:]: index 4 beyond bounds for empty array
Call stack:
  ...
  *InvalidArrayAccess.m:13
  *CDRSpecSpec.mm:51
  ...
  *main.m:4
  ...
  • Loading branch information...
commit dfa92be6a1b874b6a086107341304555b8da688e 1 parent bc1c263
@cppforlife cppforlife authored
View
2  Cedar.xcodeproj/project.pbxproj
@@ -2164,6 +2164,7 @@
"\"$(SRCROOT)\"",
);
GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "";
INSTALL_PATH = /usr/local/bin;
@@ -2270,6 +2271,7 @@
"$(inherited)",
"\"$(SRCROOT)\"",
);
+ GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/UIKit.framework/Headers/UIKit.h";
HEADER_SEARCH_PATHS = "";
View
7 Source/CDRDefaultReporter.m
@@ -92,7 +92,8 @@ - (NSString *)failureToken {
}
- (NSString *)failureMessageForExample:(CDRExample *)example {
- return [NSString stringWithFormat:@"FAILURE %@\n%@\n",[example fullText], example.failure];
+ return [NSString stringWithFormat:@"FAILURE %@\n%@\n",
+ example.fullText, example.failure];
}
- (NSString *)errorToken {
@@ -100,10 +101,12 @@ - (NSString *)errorToken {
}
- (NSString *)errorMessageForExample:(CDRExample *)example {
- return [NSString stringWithFormat:@"EXCEPTION %@\n%@\n", [example fullText], example.failure];
+ return [NSString stringWithFormat:@"EXCEPTION %@\n%@\n%@",
+ example.fullText, example.failure, example.failure.callStackSymbolicatedSymbols];
}
#pragma mark Private interface
+
- (void)printMessages:(NSArray *)messages {
printf("\n");
View
42 Source/CDRSpecFailure.m
@@ -1,4 +1,5 @@
#import "CDRSpecFailure.h"
+#import "CDRSymbolicator.h"
#import <regex.h>
@interface CDRSpecFailure ()
@@ -9,7 +10,10 @@ + (BOOL)extractReason:(NSString **)reason fileName:(NSString **)fileName lineNum
@implementation CDRSpecFailure
-@synthesize fileName = fileName_, lineNumber = lineNumber_;
+@synthesize
+ fileName = fileName_,
+ lineNumber = lineNumber_,
+ callStackReturnAddresses = callStackReturnAddresses_;
+ (id)specFailureWithReason:(NSString *)reason {
return [[[self alloc] initWithReason:reason] autorelease];
@@ -44,12 +48,19 @@ - (id)initWithRaisedObject:(NSObject *)object {
if ((self = [super initWithName:@"Spec Failure" reason:reason userInfo:nil])) {
fileName_ = [fileName retain];
lineNumber_ = lineNumber;
+
+ // NSException subclasses tend to have
+ // -callStackReturnAddresses and -callStackSymbols.
+ if ([object respondsToSelector:@selector(callStackReturnAddresses)]) {
+ callStackReturnAddresses_ = [[(id)object callStackReturnAddresses] retain];
+ }
}
return self;
}
- (void)dealloc {
[fileName_ release];
+ [callStackReturnAddresses_ release];
[super dealloc];
}
@@ -60,7 +71,34 @@ - (NSString *)description {
return self.reason;
}
-#pragma mark Private Interface
+- (NSString *)callStackSymbolicatedSymbols {
+ if (!self.callStackReturnAddresses) return nil;
+
+ CDRSymbolicator *symbolicator = [[CDRSymbolicator alloc] init];
+ [symbolicator symbolicateAddresses:self.callStackReturnAddresses];
+
+ NSMutableString *result =
+ [NSMutableString stringWithString:@"Call stack:\n"];
+ BOOL previousAddressWasUnknown = NO;
+
+ for (int i=0; i < self.callStackReturnAddresses.count; i++) {
+ NSUInteger address = [[self.callStackReturnAddresses objectAtIndex:i] unsignedIntegerValue];
+ NSString *fileName = [symbolicator fileNameForStackAddress:address];
+ unsigned long lineNumber = [symbolicator lineNumberForStackAddress:address];
+
+ if (fileName) {
+ previousAddressWasUnknown = NO;
+ [result appendFormat:@" *%@:%lu\n", fileName, lineNumber];
+ } else if (!previousAddressWasUnknown) {
+ previousAddressWasUnknown = YES;
+ [result appendString:@" ...\n"];
+ }
+ }
+ return result;
+}
+
+#pragma mark - Private Interface
+
+ (void)extractReason:(NSString **)reason fileName:(NSString **)fileName lineNumber:(int *)lineNumber fromObject:(NSObject *)object {
if ([object isKindOfClass:[NSException class]]) {
NSDictionary *userInfo = [(NSException *)object userInfo];
View
4 Source/Headers/CDRSpecFailure.h
@@ -3,10 +3,12 @@
@interface CDRSpecFailure : NSException {
NSString *fileName_;
int lineNumber_;
+ NSArray *callStackReturnAddresses_;
}
@property (nonatomic, retain, readonly) NSString *fileName;
@property (nonatomic, assign, readonly) int lineNumber;
+@property (nonatomic, retain, readonly) NSArray *callStackReturnAddresses;
+ (id)specFailureWithReason:(NSString *)reason;
+ (id)specFailureWithReason:(NSString *)reason fileName:(NSString *)fileName lineNumber:(int)lineNumber;
@@ -16,4 +18,6 @@
- (id)initWithReason:(NSString *)reason fileName:(NSString *)fileName lineNumber:(int)lineNumber;
- (id)initWithRaisedObject:(NSObject *)object;
+- (NSString *)callStackSymbolicatedSymbols;
+
@end
View
55 Spec/CDRSpecFailureSpec.mm
@@ -145,6 +145,61 @@
});
});
});
+
+ describe(@"-callStackSymbolicatedSymbols", ^{
+ __block NSString *symbols;
+ __block id raisedObject;
+
+ subjectAction(^{
+ CDRSpecFailure *failure =
+ [CDRSpecFailure specFailureWithRaisedObject:raisedObject];
+ symbols = failure.callStackSymbolicatedSymbols;
+ });
+
+ context(@"when raised object provides call stack return addresses", ^{
+ void (^objectRaiser)(void) = ^{ [[NSException exceptionWithName:@"name" reason:@"reason" userInfo:nil] raise]; };
+
+ beforeEach(^{
+ // Raise and then catch actual exception
+ // to populate its callStackReturnAddresses.
+ @try {
+ objectRaiser();
+ } @catch (NSException *e) {
+ raisedObject = e;
+ }
+ });
+
+ it(@"returns string with symbolicated call stack "
+ "showing originating error location closest to the top", ^{
+ symbols should contain(
+ @" *CDRSpecFailureSpec.mm:160\n"
+ " *CDRSpecFailureSpec.mm:166\n"
+ );
+ });
+
+ it(@"indicates unsymbolicated portions of call stack with '...'", ^{
+ symbols should contain(
+ @"Call stack:\n"
+ " ...\n" // <-+ objc_exception_throw,
+ " *CDRSpecFailureSpec.mm" // | [NSException raise:...], etc.
+ );
+ });
+
+ it(@"includes asterisk before every path "
+ "because paths are not absolute and "
+ "Xcode plugins need to be able to recognize them", ^{
+ symbols should contain(@"*CDRSpecFailureSpec.mm");
+ });
+ });
+
+ context(@"when raised object does not provide call stack return addresses", ^{
+ beforeEach(^{ raisedObject = @"failure"; });
+
+ it(@"returns nil", ^{
+ symbols should be_nil;
+ });
+ });
+ });
});
SPEC_END
View
13 Spec/Support/FibonacciCalculator.m
@@ -3,18 +3,13 @@
@implementation FibonacciCalculator
- (int)computeFibonnaciNumberVeryVerySlowly:(int)n {
- if (n == 0) {
- return 0;
- }
- if (n == 1) {
- return 1;
- }
- return [self computeFibonnaciNumberVeryVerySlowly:n - 1] + [self computeFibonnaciNumberVeryVerySlowly:n - 2];
+ if (n == 0) return 0;
+ if (n == 1) return 1;
+ return [self computeFibonnaciNumberVeryVerySlowly:n - 1] +
+ [self computeFibonnaciNumberVeryVerySlowly:n - 2];
}
- (int)computeFibonnaciNumberVeryVeryQuickly:(int)n {
return (pow((1 + sqrt(5)) / 2.0, n) - pow((1 - sqrt(5)) / 2.0, n) ) / sqrt(5);
}
-
-
@end
Please sign in to comment.
Something went wrong with that request. Please try again.