|
16 | 16 |
|
17 | 17 | #import "GREYFailureHandlerHelpers.h" |
18 | 18 |
|
| 19 | +#include <dlfcn.h> |
| 20 | + |
19 | 21 | #import "GREYErrorConstants.h" |
20 | 22 | #import "GREYFrameworkException.h" |
21 | 23 | #import "GREYElementHierarchy.h" |
22 | 24 |
|
| 25 | +static NSString *DemangleSymbol(NSString *symbol) { |
| 26 | + static char *(*swiftDemangle)(const char *mangledName, size_t mangledNameLength, |
| 27 | + char *outputBuffer, size_t *outputBufferSize, uint32_t flags); |
| 28 | + // Exports a function from the Swift stdlib that isn't exposed by default, for demangling Swift |
| 29 | + // symbol names. |
| 30 | + // |
| 31 | + // https://github.com/swiftlang/swift/blob/80050bb455b22cf2eee6fd77816cd41411b975c9/stdlib/public/runtime/Demangle.cpp#L930 |
| 32 | + static dispatch_once_t onceToken; |
| 33 | + dispatch_once(&onceToken, ^{ |
| 34 | + swiftDemangle = dlsym(RTLD_DEFAULT, "swift_demangle"); |
| 35 | + }); |
| 36 | + if (!swiftDemangle) { |
| 37 | + return symbol; |
| 38 | + } |
| 39 | + |
| 40 | + char *demangledSymbolCString = swiftDemangle(symbol.UTF8String, symbol.length, nil, nil, 0); |
| 41 | + if (!demangledSymbolCString) { |
| 42 | + return symbol; |
| 43 | + } |
| 44 | + |
| 45 | + NSString *demangledSymbol = [NSString stringWithUTF8String:demangledSymbolCString]; |
| 46 | + free(demangledSymbolCString); |
| 47 | + return demangledSymbol; |
| 48 | +} |
| 49 | + |
| 50 | +// Trims the stack symbol to make it more readable. |
| 51 | +static NSString *TrimStackSymbol(NSString *symbol) { |
| 52 | + // The stack symbol is of the form: |
| 53 | + // <index> <module> <address> <function> + <offset> |
| 54 | + // |
| 55 | + // For example: |
| 56 | + // 4 EarlGrey 0x0000000100000000 -[EarlGreyImpl handleException:details:] + 123 |
| 57 | + // |
| 58 | + // Note: The implementation relies on how the stack trace is formatted. But since this is strictly |
| 59 | + // used to improve the debugging experience by making the stack trace more readable, it is fine if |
| 60 | + // any of the steps fails, as long as it fails gracefully (i.e. returns the original symbol |
| 61 | + // without crashing). |
| 62 | + NSString *functionName = |
| 63 | + [symbol stringByReplacingOccurrencesOfString:@".+0x[0-9a-fA-F]+\\s(.+)\\s\\+\\s\\d+$" |
| 64 | + withString:@"$1" |
| 65 | + options:NSRegularExpressionSearch |
| 66 | + range:NSMakeRange(0, symbol.length)]; |
| 67 | + NSString *demangledFunctionName = DemangleSymbol(functionName); |
| 68 | + // Trim the top module name. |
| 69 | + NSString *trimmedFunctionName = [demangledFunctionName |
| 70 | + stringByReplacingOccurrencesOfString:@"\\w+\\.(\\w+)" |
| 71 | + withString:@"$1" |
| 72 | + options:NSRegularExpressionSearch |
| 73 | + range:NSMakeRange(0, demangledFunctionName.length)]; |
| 74 | + return [symbol stringByReplacingOccurrencesOfString:functionName withString:trimmedFunctionName]; |
| 75 | +} |
| 76 | + |
23 | 77 | NSString *GREYAppUIHierarchyFromException(GREYFrameworkException *exception, NSString *details) { |
24 | 78 | NSString *appUIHierarchy = [exception.userInfo valueForKey:kErrorDetailAppUIHierarchyKey]; |
25 | 79 | // For calls from GREYAsserts in the test side, the hierarchy must be populated here. In case an |
|
47 | 101 | if ([stackSymbol containsString:@"__invoking___"]) { |
48 | 102 | break; |
49 | 103 | } else if (![stackSymbol containsString:@"-[GREY"] && ![stackSymbol containsString:@" GREY"]) { |
50 | | - [trimmedCallStack addObject:stackSymbol]; |
| 104 | + [trimmedCallStack addObject:TrimStackSymbol(stackSymbol)]; |
51 | 105 | } |
52 | 106 | } |
53 | 107 | // The trimmed stack trace should at least contain the test name and exception-raising method. |
|
0 commit comments