[
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Pull Request Build\n\non:\n  pull_request:\n    branches:\n      - main\n\njobs:\n  build:\n    runs-on: macos-15\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v2\n      - name: Xcode select\n        run: sudo xcode-select -s '/Applications/Xcode_16.0.app/Contents/Developer'\n      - name: Show destinations\n        run: xcodebuild -scheme ETTrace -showdestinations\n      - name: Build ETTrace for iOS Simulator\n        run: xcodebuild build -scheme ETTrace -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' CLANG_CXX_LANGUAGE_STANDARD=c++17\n      - name: Build ETTrace for iOS\n        run: xcodebuild build -scheme ETTrace -sdk iphoneos -destination 'generic/platform=iOS' CLANG_CXX_LANGUAGE_STANDARD=c++17\n      - name: Build ETTrace for macOS\n        run: xcodebuild build -scheme ETTrace -sdk macosx -destination 'generic/platform=macOS' ONLY_ACTIVE_ARCH=NO\n      - name: Build ETTraceRunner for macOS\n        run: xcodebuild build -scheme ETTraceRunner -sdk macosx -destination 'generic/platform=macOS' ONLY_ACTIVE_ARCH=NO\n      - name: Build ETTrace for tvOS\n        run: xcodebuild build -scheme ETTrace -sdk appletvos -destination 'generic/platform=tvOS' ONLY_ACTIVE_ARCH=NO\n      - name: Build ETTrace for tvOS Simulator\n        run: xcodebuild build -scheme ETTrace -sdk appletvsimulator -destination 'generic/platform=tvOS Simulator' ONLY_ACTIVE_ARCH=NO\n      - name: Build ETTrace for visionOS\n        run: xcodebuild build -scheme ETTrace -sdk xros -destination 'generic/platform=visionOS' ONLY_ACTIVE_ARCH=NO\n      - name: Build ETTrace for visionOS Simulator\n        run: xcodebuild build -scheme ETTrace -sdk xrsimulator -destination 'generic/platform=visionOS Simulator' ONLY_ACTIVE_ARCH=NO\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release Workflow\n\non:\n  push:\n    tags:\n      - 'v*'\n\njobs:\n  release:\n    runs-on: macos-15\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v2\n      - name: Xcode select\n        run: sudo xcode-select -s '/Applications/Xcode_16.0.app/Contents/Developer'\n      - name: Setup Signing Certificate\n        uses: apple-actions/import-codesign-certs@v3\n        with: \n          p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}\n          p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}\n      - name: Build ETTrace xcframework\n        run: sh build.sh\n      - name: Zip xcframework\n        run: zip -r ETTrace.xcframework.zip ETTrace.xcframework\n      - name: Build ETTraceRunner\n        run: sh build_runner.sh\n        env:\n          SIGNING_IDENTITY: ${{ secrets.SIGNING_IDENTITY }}\n      - name: Upload Artifact\n        uses: softprops/action-gh-release@v1\n        if: startsWith(github.ref, 'refs/tags/')\n        with:\n          files: |\n            ETTrace.xcframework.zip\n            ETTraceRunner\n          body:\n            Release ${{ github.ref }}\n            Automated release created by GitHub Actions.\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n**/*.xcodeproj/xcuserdata/\n**/*.xcworkspace/xcuserdata/\n**/*.xcodeproj/xcshareddata/\nETTrace-iphonesimulator.xcarchive/\nETTrace-iphoneos.xcarchive/\n./ETTrace.xcframework/\noutput.json\noutput.folded\n.swiftpm\n.build\noutput_*.json\n"
  },
  {
    "path": ".spi.yml",
    "content": "version: 1\nbuilder:\n  configs:\n  - platform: ios\n    scheme: ETTrace\n  - platform: macos\n    scheme: ETTraceRunner\n"
  },
  {
    "path": "ETTrace/.gitignore",
    "content": "Pods/"
  },
  {
    "path": "ETTrace/CommunicationFrame/EMGDummyEmptyClass.h",
    "content": "//\n//  DummyEmptyClass.h\n//  \n//\n//  Created by Itay Brenner on 2/6/23.\n//\n\n#import <Foundation/Foundation.h>\n\n// This class is required becasue SPM doesn't support header only targets\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface EMGDummyEmptyClass : NSObject\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "ETTrace/CommunicationFrame/EMGDummyEmptyClass.m",
    "content": "//\n//  DummyEmptyClass.m\n//  \n//\n//  Created by Itay Brenner on 2/6/23.\n//\n\n#import \"EMGDummyEmptyClass.h\"\n\n// This class is required becasue SPM doesn't support header only targets\n\n@implementation EMGDummyEmptyClass\n\n@end\n"
  },
  {
    "path": "ETTrace/CommunicationFrame/Public/CommunicationFrame.h",
    "content": "//\n//  CommunicationFrame.h\n//  CommunicationFrame\n//\n//  Created by Itay Brenner on 6/3/23.\n//\n\n#import <Foundation/Foundation.h>\n#include <stdint.h>\n\n//! Project version number for CommunicationFrame.\nFOUNDATION_EXPORT double CommunicationFrameVersionNumber;\n\n//! Project version string for CommunicationFrame.\nFOUNDATION_EXPORT const unsigned char CommunicationFrameVersionString[];\n\n// In this header, you should import all the public headers of your framework using statements like #import <CommunicationFrame/PublicHeader.h>\n\n\nstatic const int PTPortNumber = 3116;\n\nstatic const int PTNoFrameTag = 0;\n\n// Use 1MB as max size to transfer\nstatic const int PTMaxChunkSize = 1024 * 1024;\n\nenum {\n    PTFrameTypeStart = 101,\n    PTFrameTypeStop = 102,\n    PTFrameTypeReportCreated = 103,\n    PTFrameTypeRequestResults = 104,\n    PTFrameTypeResultsMetadata = 105,\n    PTFrameTypeResultsData = 106,\n    PTFrameTypeResultsTransferComplete = 107,\n    PTFrameTypeStartMultiThread = 108,\n};\n\ntypedef struct _PTStartFrame {\n    bool runAtStartup;\n    // Added in v1.6\n    uint32_t sampleRate;\n} PTStartFrame;\n\ntypedef struct _PTMetadataFrame {\n    uint64_t fileSize;\n} PTMetadataFrame;\n"
  },
  {
    "path": "ETTrace/ETModels/FlameNode.swift",
    "content": "//\n//  FlameNode.swift\n//  PerfAnalysisRunner\n//\n//  Created by Itay Brenner on 7/3/23.\n//\n\nimport Foundation\n\n@objc\npublic class FlameNode: NSObject {\n    @objc\n    public let name: String\n    \n    @objc\n    public let start: Double\n    \n    @objc\n    public var duration: Double\n    \n    @objc\n    public var children: [FlameNode]\n    \n    @objc\n    public let library: String?\n    \n    @objc\n    public let address: NSNumber?\n    \n    public init(name: String, start: Double, duration: Double, library: String?, address: NSNumber?) {\n        self.name = name\n        self.start = start\n        self.duration = duration\n        self.library = library\n        self.children = []\n        self.address = address\n    }\n    \n    private func stop() -> Double {\n        return start + duration\n    }\n    \n    public func add(stack: [(String?, String, UInt64?)], duration: Double) {\n        // Add a nil element at the end, or else siblings with the same name, separated by a gap, will be merged into each other\n      var newStack: [(String?, String, UInt64?)?] = stack + [nil]\n      var currentNode = self\n      while !newStack.isEmpty {\n        currentNode.duration += duration\n        let s = newStack[0]\n        var lib: String? = nil\n        let name: String\n        var address: NSNumber? = nil\n        if let tuple = s {\n            lib = tuple.0\n            name = tuple.1\n            address = tuple.2 != nil ? NSNumber(value: tuple.2!) : nil\n        } else {\n            name = \"\"\n        }\n        if currentNode.children.count == 0 || (currentNode.children.last!.name != name || currentNode.children.last!.library != lib) {\n            let child = FlameNode(name: name,\n                                  start: currentNode.children.last?.stop() ?? currentNode.start,\n                                  duration: 0,\n                                  library: lib,\n                                  address: address)\n            currentNode.children.append(child)\n        }\n        newStack = Array(newStack.dropFirst())\n        currentNode = currentNode.children.last!\n      }\n    }\n    \n    public static func fromSamples(_ samples: [Sample]) -> FlameNode {\n        let root = FlameNode(name: \"<root>\", start: 0, duration: 0, library: nil, address: nil)\n        for sample in samples {\n            let sampleDuration = sample.time\n            root.add(stack: sample.stack, duration: sampleDuration)\n        }\n        return root\n    }\n}\n\n"
  },
  {
    "path": "ETTrace/ETModels/Flamegraph.swift",
    "content": "//\n//  Flamegraph.swift\n//  \n//\n//  Created by Itay Brenner on 27/6/23.\n//\n\nimport Foundation\n\n@objc\npublic class Flamegraph: NSObject {\n    @objc\n    public let osBuild: String\n    \n    @objc\n    public let device: String\n    \n    @objc\n    public let isSimulator: Bool\n    \n    @objc\n    public var events: [FlamegraphEvent]\n    \n    @objc\n    public var libraries: [String:UInt64]\n    \n    @objc\n    public var threadNodes: [ThreadNode]\n    \n    public init(osBuild: String,\n                device: String,\n                isSimulator: Bool,\n                libraries: [String:UInt64],\n                events: [FlamegraphEvent],\n                threadNodes: [ThreadNode]) {\n        self.osBuild = osBuild\n        self.device = device\n        self.isSimulator = isSimulator\n        self.events = events\n        self.libraries = libraries\n        self.threadNodes = threadNodes\n    }\n}\n"
  },
  {
    "path": "ETTrace/ETModels/FlamegraphEvent.swift",
    "content": "//\n//  File.swift\n//  \n//\n//  Created by Itay Brenner on 28/6/23.\n//\n\nimport Foundation\n\n@objc\npublic class FlamegraphEvent: NSObject {\n    @objc\n    public let name: String\n    \n    @objc\n    public let type: String\n    \n    @objc\n    public let time: Double\n    \n    public init(name: String, type: String, time: Double) {\n        self.name = name\n        self.type = type\n        self.time = time\n    }\n}\n"
  },
  {
    "path": "ETTrace/ETModels/Sample.swift",
    "content": "//\n//  Sample.swift\n//  PerfAnalysisRunner\n//\n//  Created by Itay Brenner on 7/3/23.\n//\n\nimport Foundation\n\npublic class Sample {\n    public let stack: [(String?, String, UInt64?)]\n    public var time: Double\n    \n    public init(time: Double, stack: [(String?, String, UInt64?)]) {\n        self.time = time\n        self.stack = stack\n    }\n    \n    public var description: String {\n        let timeStr = String(format: \"%.15f\", self.time).replacingOccurrences(of: \"0*$\", with: \"\", options: .regularExpression)\n        let stackStr = stack.map { s in\n            return \"\\(s.1)\"\n        }.joined(separator: \";\")\n        return \"\\(stackStr) \\(timeStr)\"\n    }\n}\n"
  },
  {
    "path": "ETTrace/ETModels/ThreadNode.swift",
    "content": "//\n//  ThreadNode.swift\n//  \n//\n//  Created by Itay Brenner on 18/8/23.\n//\n\nimport Foundation\n\n@objc\npublic class ThreadNode: NSObject {\n    @objc\n    public let threadName: String?\n    \n    @objc\n    public var nodes: FlameNode\n    \n    public init(nodes: FlameNode,\n                threadName: String? = nil) {\n        self.nodes = nodes\n        self.threadName = threadName\n    }\n}\n"
  },
  {
    "path": "ETTrace/ETTrace/EMGChannelListener.h",
    "content": "//\n//  EMGChannelListener.h\n//  PerfAnalysis\n//\n//  Created by Itay Brenner on 6/3/23.\n//\n\n#import <Foundation/Foundation.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface EMGChannelListener : NSObject\n- (instancetype) init;\n- (void) sendReportCreatedMessage;\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "ETTrace/ETTrace/EMGChannelListener.m",
    "content": "//\n//  EMGChannelListener.m\n//  PerfAnalysis\n//\n//  Created by Itay Brenner on 6/3/23.\n//\n\n#import \"EMGChannelListener.h\"\n#import \"EMGPerfAnalysis_Private.h\"\n#import <Peertalk.h>\n#import <CommunicationFrame.h>\n\n@interface EMGChannelListener () <PTChannelDelegate>\n@property (nonatomic, weak) PTChannel *serverChannel;\n@property (nonatomic, weak) PTChannel *peerChannel;\n@end\n\n@implementation EMGChannelListener\n- (instancetype) init {\n    self = [super init];\n    if (self)\n    {\n        [self setupChannel];\n    }\n    return self;\n}\n\n- (void) setupChannel {\n    dispatch_queue_t peertalk_queue = dispatch_queue_create(\"emg_queue\", DISPATCH_QUEUE_SERIAL);\n    PTProtocol *protocol = [[PTProtocol alloc] initWithDispatchQueue:peertalk_queue];\n    PTChannel *channel = [[PTChannel alloc] initWithProtocol:protocol delegate:self];\n    \n    [channel listenOnPort:PTPortNumber IPv4Address:INADDR_LOOPBACK callback:^(NSError *error) {\n    if (error) {\n        NSLog(@\"Failed to listen on 127.0.0.1:%d: %@\", PTPortNumber, error);\n    } else {\n        NSLog(@\"Listening on 127.0.0.1:%d\", PTPortNumber);\n        self.serverChannel = channel;\n    }\n    }];\n}\n\n#pragma mark - PTChannelDelegate\n\n- (BOOL)ioFrameChannel:(PTChannel*)channel shouldAcceptFrameOfType:(uint32_t)type tag:(uint32_t)tag payloadSize:(uint32_t)payloadSize {\n    if (channel != self.peerChannel) {\n        // A previous channel that has been canceled but not yet ended. Ignore.\n        return NO;\n    } else if (type == PTFrameTypeStart || type == PTFrameTypeStop ||\n               type == PTFrameTypeRequestResults || type == PTFrameTypeStartMultiThread){\n        return YES;\n    } else {\n        NSLog(@\"Unexpected frame of type %u\", type);\n        [channel close];\n        return NO;\n    }\n}\n\n- (void)ioFrameChannel:(PTChannel*)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(NSData *)payload {\n    if (type == PTFrameTypeStart || type == PTFrameTypeStartMultiThread) {\n        PTStartFrame *startFrame = (PTStartFrame *)payload.bytes;\n        NSLog(@\"Start received, with: %i\", startFrame->runAtStartup);\n        BOOL runAtStartup = startFrame->runAtStartup;\n        BOOL recordAllThreads = type == PTFrameTypeStartMultiThread;\n        NSInteger sampleRate = 0;\n        // If the size is smaller it is a message from an older version of the CLI\n        // before sampleRate was added\n        if (payload.length >= sizeof(PTStartFrame)) {\n          sampleRate = startFrame->sampleRate;\n        }\n        if (runAtStartup) {\n            [EMGPerfAnalysis setupRunAtStartup:recordAllThreads rate:sampleRate];\n        } else {\n            [EMGPerfAnalysis startRecording:recordAllThreads rate:sampleRate];\n        }\n    } else if (type == PTFrameTypeStop) {\n        [EMGPerfAnalysis stopRecording];\n    } else if (type == PTFrameTypeRequestResults) {\n        [self sendReportData];\n    }\n}\n\n- (void)ioFrameChannel:(PTChannel*)channel didEndWithError:(NSError*)error {\n    if (error) {\n        NSLog(@\"%@ ended with error: %@\", channel, error);\n    } else {\n        NSLog(@\"Disconnected from %@\", channel.userInfo);\n    }\n}\n\n- (void)ioFrameChannel:(PTChannel*)channel didAcceptConnection:(PTChannel*)otherChannel fromAddress:(PTAddress*)address {\n    if (self.peerChannel) {\n        [self.peerChannel cancel];\n    }\n  \n    self.peerChannel = otherChannel;\n    self.peerChannel.userInfo = address;\n    NSLog(@\"Connected to %@\", address);\n}\n\n- (void) sendReportCreatedMessage {\n    NSData *emptyData = [[NSData alloc] init];\n    [self.peerChannel sendFrameOfType:PTFrameTypeReportCreated tag:PTFrameNoTag withPayload:emptyData callback:^(NSError * _Nullable error) {\n        if (error) {\n            NSLog(@\"Could not send message\");\n        } else {\n            NSLog(@\"Message sent\");\n        }\n    }];\n}\n\n- (void) sendReportData {\n    NSURL *outURL = [EMGPerfAnalysis outputPath];\n    \n    NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:outURL.path];\n    if (!fileHandle) {\n        NSLog(@\"Error opening file\");\n        return;\n    }\n    \n    // Sending metadata\n    PTMetadataFrame *frame = CFAllocatorAllocate(nil, sizeof(PTMetadataFrame), 0);\n    frame->fileSize = fileHandle.availableData.length;\n    dispatch_data_t dataFrame = dispatch_data_create((const void*)frame, sizeof(PTMetadataFrame), nil, ^{\n        CFAllocatorDeallocate(nil, frame);\n    });\n    [self.peerChannel sendFrameOfType:PTFrameTypeResultsMetadata tag:PTFrameNoTag withPayload:dataFrame callback:nil];\n    \n    [fileHandle seekToFileOffset:0];\n    while (YES) {\n        NSData *chunk = [fileHandle readDataOfLength:PTMaxChunkSize];\n        if (chunk.length == 0) {\n            break;\n        }\n        [self.peerChannel sendFrameOfType:PTFrameTypeResultsData tag:PTFrameNoTag withPayload:chunk callback:nil];\n    }\n    \n    // Confirm file completed\n    [self.peerChannel sendFrameOfType:PTFrameTypeResultsTransferComplete tag:PTFrameNoTag withPayload:nil callback:nil];\n}\n\n@end\n"
  },
  {
    "path": "ETTrace/ETTrace/EMGPerfAnalysis.mm",
    "content": "//\n//  Constructor.m\n//  PerfAnalysis\n//\n//  Created by Noah Martin on 11/23/22.\n//\n\n#import <Foundation/Foundation.h>\n#import <QuartzCore/QuartzCore.h>\n#import <Tracer.h>\n#import <vector>\n#import <mutex>\n#import <mach/mach.h>\n#import <sys/sysctl.h>\n#import <mach-o/arch.h>\n#import <sys/utsname.h>\n#import \"EMGChannelListener.h\"\n#import <QuartzCore/QuartzCore.h>\n#import \"PerfAnalysis.h\"\n#include <map>\n\nNSString *const kEMGSpanStarted = @\"EmergeMetricStarted\";\nNSString *const kEMGSpanEnded = @\"EmergeMetricEnded\";\n\n@implementation EMGPerfAnalysis\n\nstatic dispatch_queue_t fileEventsQueue;\n\nstatic EMGChannelListener *channelListener;\nstatic NSMutableArray <NSDictionary *> *sSpanTimes;\n\n+ (void)startRecording:(BOOL)recordAllThreads rate:(NSInteger)sampleRate {\n  sSpanTimes = [NSMutableArray array];\n  [EMGTracer setupStackRecording:recordAllThreads rate:(useconds_t) sampleRate];\n}\n\n+ (void)setupRunAtStartup:(BOOL) recordAllThreads rate:(NSInteger)sampleRate {\n    [[NSUserDefaults standardUserDefaults] setBool:true forKey:@\"runAtStartup\"];\n    [[NSUserDefaults standardUserDefaults] setBool:recordAllThreads forKey:@\"recordAllThreads\"];\n    [[NSUserDefaults standardUserDefaults] setInteger:sampleRate forKey:@\"ETTraceSampleRate\"];\n    exit(0);\n}\n\n+ (void)startObserving {\n    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{\n        NSURL *documentsURL = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0];\n        NSURL *emergeDirectoryURL = [documentsURL URLByAppendingPathComponent:@\"emerge-perf-analysis\"];\n        if (![[NSFileManager defaultManager] fileExistsAtPath:emergeDirectoryURL.path isDirectory:NULL]) {\n            [[NSFileManager defaultManager] createDirectoryAtURL:emergeDirectoryURL withIntermediateDirectories:YES attributes:nil error:nil];\n        }\n        \n        channelListener = [[EMGChannelListener alloc] init];\n    });\n    \n    [[NSNotificationCenter defaultCenter] addObserverForName:kEMGSpanStarted\n                                                      object:nil\n                                                       queue:nil\n                                                  usingBlock:^(NSNotification * _Nonnull notification) {\n        if (![EMGTracer isRecording]) {\n            return;\n        }\n        \n        NSString *span = notification.userInfo[@\"metric\"];\n        [sSpanTimes addObject:@{\n            @\"span\": span,\n            @\"type\": @\"start\",\n            @\"time\": @(CACurrentMediaTime())\n        }];\n    }];\n\n    [[NSNotificationCenter defaultCenter] addObserverForName:kEMGSpanEnded\n                                                      object:nil\n                                                       queue:nil\n                                                  usingBlock:^(NSNotification * _Nonnull notification) {\n        if (![EMGTracer isRecording]) {\n            return;\n        }\n        \n        NSString *span = notification.userInfo[@\"metric\"];\n        [sSpanTimes addObject:@{\n            @\"span\": span,\n            @\"type\": @\"stop\",\n            @\"time\": @(CACurrentMediaTime())\n        }];\n    }];\n}\n\n+ (void)stopRecording {\n  [EMGTracer stopRecording:^(NSDictionary *results) {\n    NSMutableDictionary *info = [results mutableCopy];\n    info[@\"events\"] = sSpanTimes;\n\n    NSError *error = nil;\n    NSData *data = [NSJSONSerialization dataWithJSONObject:info options:0 error:&error];\n    if (error) {\n        @throw error;\n    }\n    NSURL *documentsURL = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0];\n    NSURL *emergeDirectoryURL = [documentsURL URLByAppendingPathComponent:@\"emerge-output\"];\n    if (![[NSFileManager defaultManager] fileExistsAtPath:emergeDirectoryURL.path isDirectory:NULL]) {\n        [[NSFileManager defaultManager] createDirectoryAtURL:emergeDirectoryURL withIntermediateDirectories:YES attributes:nil error:nil];\n    }\n    NSURL *outputURL = [emergeDirectoryURL URLByAppendingPathComponent:@\"output.json\"];\n    BOOL result = [data writeToURL:outputURL options:NSDataWritingAtomic error:&error];\n    if (!result || error) {\n        NSLog(@\"Error writing ETTrace state %@\", error);\n    } else {\n        NSLog(@\"ETTrace result written\");\n    }\n    [channelListener sendReportCreatedMessage];\n  }];\n}\n\n+ (void)load {\n    NSLog(@\"Starting ETTrace\");\n    [EMGTracer setup];\n    fileEventsQueue = dispatch_queue_create(\"com.emerge.file_queue\", DISPATCH_QUEUE_SERIAL);\n    BOOL infoPlistRunAtStartup = ((NSNumber *) NSBundle.mainBundle.infoDictionary[@\"ETTraceRunAtStartup\"]).boolValue;\n    if ([[NSUserDefaults standardUserDefaults] boolForKey:@\"runAtStartup\"] || infoPlistRunAtStartup) {\n        NSInteger sampleRate = [[NSUserDefaults standardUserDefaults] integerForKey:@\"ETTraceSampleRate\"];\n        BOOL recordAllThreads = [[NSUserDefaults standardUserDefaults] boolForKey:@\"recordAllThreads\"];\n        [EMGPerfAnalysis startRecording:recordAllThreads rate:sampleRate];\n        [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@\"runAtStartup\"];\n        [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@\"recordAllThreads\"];\n    }\n    [EMGPerfAnalysis startObserving];\n}\n\n+ (NSURL *) outputPath {\n    NSURL *documentsURL = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0];\n    NSURL *emergeDirectoryURL = [documentsURL URLByAppendingPathComponent:@\"emerge-output\"];\n    return [emergeDirectoryURL URLByAppendingPathComponent:@\"output.json\"];\n}\n\n@end\n"
  },
  {
    "path": "ETTrace/ETTrace/EMGPerfAnalysis_Private.h",
    "content": "//\n//  EMGPerfAnalysis_Private.h\n//  PerfAnalysis\n//\n//  Created by Itay Brenner on 6/3/23.\n//\n\n#ifndef EMGPerfAnalysis_Private_h\n#define EMGPerfAnalysis_Private_h\n#import \"PerfAnalysis.h\"\n\n@interface EMGPerfAnalysis (Private)\n+ (void)startRecording:(BOOL) recordAllThreads rate:(NSInteger)sampleRate;\n+(void)stopRecording;\n+ (void)setupRunAtStartup:(BOOL) recordAllThreads rate:(NSInteger)sampleRate;\n+ (NSURL *)outputPath;\n@end\n\n\n#endif /* EMGPerfAnalysis_Private_h */\n"
  },
  {
    "path": "ETTrace/ETTrace/Public/PerfAnalysis.h",
    "content": "//\n//  PerfAnalysis.h\n//  PerfAnalysis\n//\n//  Created by Noah Martin on 12/9/22.\n//\n\n#import <Foundation/Foundation.h>\n\n//! Project version number for PerfAnalysis.\nFOUNDATION_EXPORT double PerfAnalysisVersionNumber;\n\n//! Project version string for PerfAnalysis.\nFOUNDATION_EXPORT const unsigned char PerfAnalysisVersionString[];\n\n// In this header, you should import all the public headers of your framework using statements like #import <PerfAnalysis/PublicHeader.h>\n\nextern NSString *const kEMGSpanStarted;\nextern NSString *const kEMGSpanEnded;\n\n@interface EMGPerfAnalysis : NSObject\n\n@end\n"
  },
  {
    "path": "ETTrace/ETTraceRunner/ConnectivityHelper.swift",
    "content": "//\n//  ConnectivityHelper.swift\n//  ETTrace\n//\n//  Created by Noah Martin on 2/27/25.\n//\n\nimport Foundation\n\nfunc isPortInUse(port: Int) -> Bool {\n    let sock = socket(AF_INET, SOCK_STREAM, 0)\n    if sock == -1 {\n        print(\"Failed to create socket\")\n        return false\n    }\n\n    var addr = sockaddr_in()\n    addr.sin_len = UInt8(MemoryLayout<sockaddr_in>.size);\n    addr.sin_family = sa_family_t(AF_INET)\n    addr.sin_port = in_port_t(port).bigEndian\n    addr.sin_addr.s_addr = INADDR_LOOPBACK.bigEndian\n\n    let result = withUnsafePointer(to: &addr) {\n        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {\n            connect(sock, $0, socklen_t(MemoryLayout<sockaddr_in>.size))\n        }\n    }\n\n    close(sock)\n\n    return result == 0\n}\n"
  },
  {
    "path": "ETTrace/ETTraceRunner/Devices/CommunicationChannel.swift",
    "content": "//\n//  CommunicationChannel.swift\n//  ETTraceRunner\n//\n//  Created by Noah Martin on 4/12/23.\n//\n\nimport Foundation\nimport CommunicationFrame\nimport Peertalk\n\nclass CommunicationChannel: NSObject {\n  \n  lazy var channel = PTChannel(protocol: nil, delegate: self)\n\n  private var expectedDataLength: UInt64 = 0\n  private var receivedData = Data()\n\n  private var resultsReceived: Bool = false\n  private var resultsReceivedContinution: CheckedContinuation<Data, Never>?\n\n  private var reportGenerated: Bool = false\n  private var reportedGeneratedContinuation: CheckedContinuation<Void, Never>?\n\n  private let verbose: Bool\n  private var relaunch: Bool\n  \n  init(verbose: Bool, relaunch: Bool) {\n    self.verbose = verbose\n    self.relaunch = relaunch\n  }\n  \n  func waitForReportGenerated() async {\n    return await withCheckedContinuation { continuation in\n      DispatchQueue.main.async { [weak self] in\n        if self?.reportGenerated == true {\n          continuation.resume()\n        } else {\n          self?.reportedGeneratedContinuation = continuation\n        }\n      }\n    }\n  }\n  \n  func waitForResultsReceived() async -> Data {\n    return await withCheckedContinuation{ continuation in\n      DispatchQueue.main.async { [weak self] in\n        guard let self = self else { return }\n\n        if self.resultsReceived == true {\n          continuation.resume(returning: self.receivedData)\n        } else {\n          self.resultsReceivedContinution = continuation\n        }\n      }\n    }\n  }\n  \n}\n\nextension CommunicationChannel: PTChannelDelegate {\n  @objc\n  func channel(_ channel: PTChannel, didRecieveFrame type: UInt32, tag: UInt32, payload: Data?) {\n    dispatchPrecondition(condition: .onQueue(.main))\n\n      if type == PTFrameTypeReportCreated {\n          reportGenerated = true\n          reportedGeneratedContinuation?.resume()\n          reportedGeneratedContinuation = nil\n      } else if type == PTFrameTypeResultsMetadata,\n                let payload = payload {\n          let metadata = payload.withUnsafeBytes { buffer in\n              buffer.load(as: PTMetadataFrame.self)\n          }\n          expectedDataLength = UInt64(metadata.fileSize)\n          \n      } else if type == PTFrameTypeResultsData,\n                let payload = payload {\n          receivedData.append(payload)\n      } else if type == PTFrameTypeResultsTransferComplete {\n          guard receivedData.count == expectedDataLength else {\n              fatalError(\"Received \\(receivedData.count) bytes, expected \\(expectedDataLength)\")\n          }\n          resultsReceived = true\n          resultsReceivedContinution?.resume(returning: receivedData)\n          resultsReceivedContinution = nil\n      }\n  }\n\n  @objc\n  func channelDidEnd(_ channel: PTChannel, error: Error?) {\n    dispatchPrecondition(condition: .onQueue(.main))\n\n    guard !relaunch else {\n      relaunch = false\n      return\n    }\n\n    if !resultsReceived {\n        print(\"Disconnected before results received, exiting early\")\n        exit(1)\n    } else if verbose {\n        print(\"Disconnected\")\n    }\n  }\n}\n"
  },
  {
    "path": "ETTrace/ETTraceRunner/Devices/DeviceManager.swift",
    "content": "//\n//  DeviceManager.swift\n//  PerfAnalysisRunner\n//\n//  Created by Itay Brenner on 6/3/23.\n//\n\nimport Foundation\nimport CommunicationFrame\nimport Peertalk\n\nprotocol DeviceManager {\n    var communicationChannel: CommunicationChannel { get }\n    var verbose: Bool { get }\n\n    func connect() async throws -> Void\n}\n\nextension DeviceManager {\n    func sendStartRecording(_ runAtStartup: Bool, _ multiThread: Bool, _ sampleRate: UInt32) async throws -> Void {\n        return try await withCheckedThrowingContinuation { continuation in\n            var startFrame = _PTStartFrame(runAtStartup: runAtStartup, sampleRate: sampleRate)\n            let data = Data(bytes: &startFrame, count: MemoryLayout<_PTStartFrame>.size)\n\n            let type = multiThread ? PTFrameTypeStartMultiThread : PTFrameTypeStart\n            communicationChannel.channel.sendFrame(type: UInt32(type), tag: UInt32(PTNoFrameTag), payload: data) { error in\n                if let error = error {\n                    continuation.resume(throwing: error)\n                } else {\n                    continuation.resume()\n                }\n            }\n        }\n    }\n    \n    private func sendStopRecording() async throws -> Void {\n        return try await withCheckedThrowingContinuation { continuation in\n          communicationChannel.channel.sendFrame(type: UInt32(PTFrameTypeStop), tag: UInt32(PTNoFrameTag), payload: Data()) { error in\n                if let error = error {\n                    continuation.resume(throwing: error)\n                } else {\n                    continuation.resume()\n                }\n            }\n        }\n    }\n  \n    private func sendRequestResults() async throws {\n        return try await withCheckedThrowingContinuation { continuation in\n            communicationChannel.channel.sendFrame(type: UInt32(PTFrameTypeRequestResults), tag: UInt32(PTNoFrameTag), payload: Data()) { error in\n                if let error = error {\n                    continuation.resume(throwing: error)\n                } else {\n                    if verbose {\n                        print(\"Extracting results from device...\")\n                    }\n                    continuation.resume()\n                }\n            }\n        }\n    }\n    \n    func getResults() async throws -> Data {\n        try await sendStopRecording()\n      \n        await communicationChannel.waitForReportGenerated()\n      \n        try await sendRequestResults()\n\n        return await communicationChannel.waitForResultsReceived()\n    }\n}\n"
  },
  {
    "path": "ETTrace/ETTraceRunner/Devices/PhysicalDeviceManager.swift",
    "content": "//\n//  PhysicalDeviceManager.swift\n//  PerfAnalysisRunner\n//\n//  Created by Itay Brenner on 6/3/23.\n//\n\nimport Foundation\nimport Peertalk\nimport CommunicationFrame\n\nenum ConnectionError: Error {\n    case noUsbHub\n    case connectionFailed\n}\n\nclass PhysicalDevicemanager: DeviceManager {\n  \n    var communicationChannel: CommunicationChannel\n\n    init(verbose: Bool, relaunch: Bool) {\n      communicationChannel = CommunicationChannel(verbose: verbose, relaunch: relaunch)\n      self.verbose = verbose\n    }\n\n    let verbose: Bool\n    private var observer: NSObjectProtocol? = nil\n    private var deviceID: NSNumber? = nil\n  \n  private func connect(withId deviceID: NSNumber, usbHub: PTUSBHub, continuation: CheckedContinuation<Void, Error>) {\n      communicationChannel.channel.connect(to: PTPortNumber, over: usbHub, deviceID: deviceID) { error in\n          if error != nil {\n              print(\"Connection failed, make sure the app is open on your device\")\n              continuation.resume(throwing: ConnectionError.connectionFailed)\n          } else {\n              print(\"Connected\")\n              continuation.resume()\n          }\n      }\n    }\n\n    func connect() async throws -> Void {\n        return try await withCheckedThrowingContinuation { continuation in\n            if let usbHub = PTUSBHub.shared() {\n              if let deviceID = self.deviceID {\n                connect(withId: deviceID, usbHub: usbHub, continuation: continuation)\n              } else {\n                observer = NotificationCenter.default.addObserver(forName:.deviceDidAttach, object: usbHub, queue: nil) {[weak self] notification in\n                    if self?.verbose == true {\n                      print(\"Device did attach notification\")\n                    }\n                    guard let deviceID = notification.userInfo?[PTUSBHubNotificationKey.deviceID] as? NSNumber else {\n                        return\n                    }\n                    self?.deviceID = deviceID\n                    \n                    NotificationCenter.default.removeObserver(self?.observer as Any, name: .deviceDidAttach, object: usbHub)\n                  \n                    self?.connect(withId: deviceID, usbHub: usbHub, continuation: continuation)\n                }\n              }\n            } else {\n                continuation.resume(throwing: ConnectionError.noUsbHub)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ETTrace/ETTraceRunner/Devices/SimulatorDeviceManager.swift",
    "content": "//\n//  SimulatorDeviceManager.swift\n//  PerfAnalysisRunner\n//\n//  Created by Itay Brenner on 6/3/23.\n//\n\nimport Foundation\nimport Peertalk\nimport CommunicationFrame\n\nstruct SimulatorDeviceManager: DeviceManager {\n\n    var communicationChannel: CommunicationChannel\n\n    let verbose: Bool\n\n    init(verbose: Bool, relaunch: Bool) {\n      communicationChannel = CommunicationChannel(verbose: verbose, relaunch: relaunch)\n      self.verbose = verbose\n    }\n    \n    func connect() async throws -> Void {\n        return try await withCheckedThrowingContinuation { continuation in\n            communicationChannel.channel.connect(to: UInt16(PTPortNumber), IPv4Address: INADDR_LOOPBACK) { error, address in\n                if let error = error {\n                    continuation.resume(throwing: error)\n                } else {\n                    print(\"Connected\")\n                    continuation.resume()\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ETTrace/ETTraceRunner/ETTrace.swift",
    "content": "//\n//  ETTrace.swift\n//  PerfAnalysisRunner\n//\n//  Created by Itay Brenner on 6/3/23.\n//\n\nimport Foundation\nimport ArgumentParser\n\nstruct ETTrace: ParsableCommand {\n    @Option(name: .shortAndLong, help: \"Directory with dSYMs\")\n    var dsyms: String? = nil\n    \n    @Flag(name: .shortAndLong, help: \"Relaunch app with profiling from startup.\")\n    var launch = false\n\n    @Flag(name: .shortAndLong, help: \"Use simulator\")\n    var simulator: Bool = false\n  \n    @Flag(name: .shortAndLong, help: \"Verbose logging\")\n    var verbose: Bool = false\n  \n    @Flag(name: .long, help: \"Save intermediate files directly from the phone before processing them.\")\n    var saveIntermediate: Bool = false\n  \n    @Option(name: .shortAndLong, help: \"Directory for output files\")\n    var output: String? = nil\n    \n    @Flag(name: .shortAndLong, help: \"Record all threads\")\n    var multiThread: Bool = false\n\n    @Option(name: .long, help: \"Sample rate\")\n    var sampleRate: UInt32 = 0\n\n    mutating func run() throws {\n      if let dsym = dsyms, dsym.hasSuffix(\".dSYM\") {\n        ETTrace.exit(withError: ValidationError(\"The dsym argument should be set to a folder containing your dSYM files, not the dSYM itself\"))\n      }\n        let helper = RunnerHelper(dsyms, launch, simulator, verbose, saveIntermediate, output, multiThread, sampleRate)\n        Task {\n            do {\n                try await helper.start()\n            } catch let error {\n                print(\"ETTrace error: \\(error)\")\n            }\n          ETTrace.exit()\n        }\n        \n        RunLoop.main.run()\n    }\n}\n"
  },
  {
    "path": "ETTrace/ETTraceRunner/ETTraceRunner.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.temporary-exception.sbpl</key>\n\t<array>\n\t\t<string>(allow network-outbound (literal \"/private/var/run/usbmuxd\"))</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "ETTrace/ETTraceRunner/ProcessSelector.swift",
    "content": "//\n//  ProcessSelector.swift\n//  ETTrace\n//\n//  Created by Noah Martin on 2/27/25.\n//\n\nimport Foundation\n\nstruct RunningProcess {\n  let path: String\n  let pid: Int\n  let bundleID: String?\n}\n\nfunc trimPath(_ path: String) -> String {\n    let pattern = \"/([^/]+\\\\.app)/\"\n    if let range = path.range(of: pattern, options: .regularExpression) {\n        return String(path[range.lowerBound...])\n    }\n    return path\n}\n\nfunc listRunningProcesses() -> [RunningProcess] {\n  var results: [RunningProcess] = []\n  let numberOfProcesses = proc_listpids(UInt32(PROC_ALL_PIDS), 0, nil, 0) / Int32(MemoryLayout<pid_t>.size)\n  guard numberOfProcesses > 0 else { return [] }\n    \n  var pids = [pid_t](repeating: 0, count: Int(numberOfProcesses))\n    \n  let result = proc_listpids(UInt32(PROC_ALL_PIDS), 0, &pids, Int32(pids.count * MemoryLayout<pid_t>.size))\n  guard result > 0 else { return [] }\n    \n  for pid in pids {\n    if pid == 0 { continue }\n      \n    var pathBuffer = [CChar](repeating: 0, count: 4 * 1024)\n      \n    let pathResult = proc_pidpath(pid, &pathBuffer, UInt32(pathBuffer.count))\n    if pathResult > 0 {\n      let path = String(cString: pathBuffer)\n      if path.contains(\"CoreSimulator/Devices/\") {\n        let url = URL(fileURLWithPath: path)\n        var bundleID: String? = nil\n        var bundleURL = url\n        while bundleURL.pathComponents.count > 1 {\n          if bundleURL.pathExtension == \"app\" {\n            break\n          }\n          bundleURL.deleteLastPathComponent()\n        }\n\n        if bundleURL.pathExtension == \"app\",\n        let bundle = Bundle(url: bundleURL) {\n          bundleID = bundle.bundleIdentifier\n        }\n        results.append(.init(path: trimPath(path), pid: Int(pid), bundleID: bundleID))\n      }\n    }\n  }\n  return results\n}\n"
  },
  {
    "path": "ETTrace/ETTraceRunner/ResponseModels/ResponseModel.swift",
    "content": "//\n//  ResponseModel.swift\n//  PerfAnalysisRunner\n//\n//  Created by Itay Brenner on 7/3/23.\n//\n\nimport Foundation\nimport Symbolicator\n\nstruct ResponseModel: Decodable {\n    let osBuild: String\n    let osVersion: String?\n    let isSimulator: Bool\n    let libraryInfo: LibraryInfo\n    let cpuType: String\n    let device: String\n    let events: [Event]\n    let threads: [String: Thread]\n    let sampleRate: UInt32?\n}\n\nstruct LibraryInfo: Decodable {\n    let relativeTime: Double\n    let mainThreadId: Int\n    let loadedLibraries: [LoadedLibrary]\n}\n\nstruct Thread: Decodable {\n    let name: String\n    let stacks: [Stack]\n}\n"
  },
  {
    "path": "ETTrace/ETTraceRunner/RunnerHelper.swift",
    "content": "//\n//  RunnerHelper.swift\n//  PerfAnalysisRunner\n//\n//  Created by Itay Brenner on 8/3/23.\n//\n\nimport AppKit\nimport Foundation\nimport Peertalk\nimport CommunicationFrame\nimport Swifter\nimport JSONWrapper\nimport ETModels\nimport Symbolicator\n\nclass RunnerHelper {\n    let dsyms: String?\n    let launch: Bool\n    let useSimulator: Bool\n    let verbose: Bool\n    let saveIntermediate: Bool\n    let outputDirectory: String?\n    let multiThread: Bool\n    let sampleRate: UInt32\n\n    var server: HttpServer? = nil\n\n    init(_ dsyms: String?, _ launch: Bool, _ simulator: Bool, _ verbose: Bool, _ saveIntermediate: Bool, _ outputDirectory: String?, _ multiThread: Bool, _ sampleRate: UInt32) {\n        self.dsyms = dsyms\n        self.launch = launch\n        self.useSimulator = simulator\n        self.verbose = verbose\n        self.saveIntermediate = saveIntermediate\n        self.outputDirectory = outputDirectory\n        self.multiThread = multiThread\n        self.sampleRate = sampleRate\n    }\n\n    private func printMessageAndWait() {\n      print(\"Please open the app on the \\(useSimulator ? \"simulator\" : \"device\")\")\n      if !useSimulator {\n          print(\"Re-run with `--simulator` to connect to the simulator.\")\n      }\n      print(\"Press return when ready...\")\n      _ = readLine()\n    }\n    \n    func start() async throws {\n        while useSimulator && !isPortInUse(port: Int(PTPortNumber)) {\n          let running = listRunningProcesses()\n          if !running.isEmpty {\n            print(running.count == 1 ? \"1 app was found but it is not running\" : \"\\(running.count) apps were found but they are not running\")\n            for p in running {\n              if let bundleId = p.bundleID {\n                print(\"\\tBundle Id: \\(bundleId) path: \\(p.path)\")\n              } else {\n                print(\"\\tPath: \\(p.path)\")\n              }\n            }\n          } else {\n            print(\"No apps found running on the simulator\")\n          }\n\n          printMessageAndWait()\n        }\n\n        if !useSimulator {\n          printMessageAndWait()\n        }\n\n        if verbose {\n          print(\"Connecting to device.\")\n        }\n\n        let deviceManager: DeviceManager = useSimulator ? SimulatorDeviceManager(verbose: verbose, relaunch: launch) : PhysicalDevicemanager(verbose: verbose, relaunch: launch)\n\n        try await deviceManager.connect()\n\n        try await deviceManager.sendStartRecording(launch, multiThread, sampleRate)\n\n        if launch {\n            print(\"Re-launch the app to start recording, then press return to exit\")\n        } else {\n            print(\"Started recording, press return to exit\")\n        }\n\n        _ = readLine()\n      \n        if launch {\n            try await deviceManager.connect()\n        }\n\n        if verbose {\n          print(\"Waiting for report to be generated...\");\n        }\n\n        let receivedData = try await deviceManager.getResults()\n\n        if saveIntermediate {\n          let outFolder = \"\\(NSTemporaryDirectory())/emerge-output\"\n          try FileManager.default.createDirectory(atPath: outFolder, withIntermediateDirectories: true)\n          let outputPath = \"\\(outFolder)/output.json\"\n          if FileManager.default.fileExists(atPath: outputPath) {\n            try FileManager.default.removeItem(atPath: outputPath)\n          }\n          FileManager.default.createFile(atPath: outputPath, contents: receivedData)\n          print(\"Intermediate file saved to \\(outputPath)\")\n        }\n        \n        if verbose {\n          print(\"Stopped recording, symbolicating...\")\n        }\n\n        let responseData = try JSONDecoder().decode(ResponseModel.self, from: receivedData)\n\n        let isSimulator = responseData.isSimulator\n        var arch = responseData.cpuType.lowercased()\n        if arch == \"arm64e\" {\n            arch = \" arm64e\"\n        } else {\n            arch = \"\"\n        }\n        var osBuild = responseData.osBuild\n        osBuild.removeAll(where: { !$0.isLetter && !$0.isNumber })\n\n        let threadIds = responseData.threads.keys\n        let threads = threadIds.map { responseData.threads[$0]!.stacks }\n        let symbolicator = StackSymbolicator(isSimulator: isSimulator, dSymsDir: dsyms, osBuild: osBuild, osVersion: responseData.osVersion, arch: arch, verbose: verbose)\n        let flamegraphs = FlamegraphGenerator.generate(\n          events: responseData.events,\n          threads: threads,\n          sampleRate: responseData.sampleRate,\n          loadedLibraries: responseData.libraryInfo.loadedLibraries,\n          symbolicator: symbolicator)\n        let outputUrl = URL(fileURLWithPath: outputDirectory ?? FileManager.default.currentDirectoryPath)\n\n        var mainThreadData: Data?\n        for (threadId, symbolicationResult) in zip(threadIds, flamegraphs) {\n            let thread = responseData.threads[threadId]!\n            let flamegraph = createFlamegraphForThread(symbolicationResult.0, symbolicationResult.1, thread, responseData)\n            \n            let outJsonData = JSONWrapper.toData(flamegraph)!\n            \n            if thread.name == \"Main Thread\" {\n                if verbose {\n                    try symbolicationResult.2.write(toFile: \"output.folded\", atomically: true, encoding: .utf8)\n                }\n                mainThreadData = outJsonData\n            }\n            try saveFlamegraph(outJsonData, outputUrl, threadId)\n        }\n        \n        guard let mainThreadData else {\n            fatalError(\"No main thread flamegraphs generated\")\n        }\n        \n        // Serve Main Thread\n        try startLocalServer(mainThreadData)\n        \n        let url = URL(string: \"https://emergetools.com/ettrace\")!\n        NSWorkspace.shared.open(url)\n\n        // Wait 4 seconds for results to be accessed from server, then exit\n        sleep(4)\n        print(\"Results saved to \\(outputUrl)\")\n    }\n    \n  private func createFlamegraphForThread(_ flamegraphNodes: FlameNode, _ eventTimes: [Double], _ thread: Thread, _ responseData: ResponseModel) -> Flamegraph {\n        let threadNode = ThreadNode(nodes: flamegraphNodes, threadName: thread.name)\n        \n        let events = zip(responseData.events, eventTimes).map { (event, t) in\n            return FlamegraphEvent(name: event.span,\n                                   type: event.type.rawValue,\n                                   time: t)\n        }\n\n        let libraries = responseData.libraryInfo.loadedLibraries.reduce(into: [String:UInt64]()) { partialResult, library in\n            partialResult[library.path] = library.loadAddress\n        }\n        \n        return Flamegraph(osBuild: responseData.osBuild,\n                          device: responseData.device,\n                          isSimulator: responseData.isSimulator,\n                          libraries: libraries,\n                          events: events,\n                          threadNodes: [threadNode])\n    }\n    \n    func startLocalServer(_ data: Data) throws {\n        server = HttpServer()\n        \n        let headers = [\n            \"Content-Type\": \"application/json\",\n            \"Access-Control-Allow-Origin\": \"*\",\n            \"Content-Length\": \"\\(data.count)\",\n            \"Access-Control-Allow-Headers\": \"baggage,sentry-trace\"\n        ]\n        \n        server?[\"/output.json\"] = { a in\n            if a.method == \"OPTIONS\" {\n                return .raw(204, \"No Content\", [\n                    \"Access-Control-Allow-Methods\": \"GET\",\n                    \"Access-Control-Allow-Origin\": \"*\",\n                    \"Access-Control-Allow-Headers\": \"baggage,sentry-trace\"\n                ], nil)\n            }\n            \n            return .raw(200, \"OK\", headers, { writter in\n                try? writter.write(data)\n                exit(0)\n            })\n        }\n        try server?.start(37577)\n    }\n\n    private func saveFlamegraph(_ outJsonData: Data, _ outputUrl: URL, _ threadId: String? = nil) throws {\n        var saveUrl = outputUrl.appendingPathComponent(\"output.json\")\n        if let threadId = threadId {\n            saveUrl = outputUrl.appendingPathComponent(\"output_\\(threadId).json\")\n        }\n        \n        let jsonString = String(data: outJsonData, encoding: .utf8)!\n        try jsonString.write(to: saveUrl, atomically: true, encoding: .utf8)\n    }\n}\n"
  },
  {
    "path": "ETTrace/ETTraceRunner/main.swift",
    "content": "//\n//  main.swift\n//  \n//\n//  Created by Itay Brenner on 6/6/23.\n//\n\nETTrace.main()\n"
  },
  {
    "path": "ETTrace/JSONWrapper/JSONWrapper.m",
    "content": "//\n//  TestClass.m\n//  ETTraceRunner\n//\n//  Created by Noah Martin on 4/13/23.\n//\n\n#import <Foundation/Foundation.h>\n#import \"JSONWrapper.h\"\n@import ETModels;\n\n@implementation JSONWrapper\n\n+ (NSDictionary *)flameNodeToDictionary:(FlameNode *)node {\n    NSObject *children;\n    if (node.children.count == 1) {\n        children = [JSONWrapper flameNodeToDictionary:node.children[0]];\n    } else {\n        children = [[NSMutableArray alloc] init];\n        for (FlameNode * c in node.children) {\n            [(NSMutableArray *) children addObject:[JSONWrapper flameNodeToDictionary:c]];\n        }\n    }\n    \n    NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithDictionary:@{\n        @\"name\": node.name,\n        @\"start\": @(node.start),\n        @\"duration\": @(node.duration),\n        @\"library\": node.library ? node.library : @\"\",\n        @\"children\": children,\n    }];\n    \n    if (node.address != nil) {\n        [result setObject:node.address forKey:@\"address\"];\n    }\n\n    return result;\n}\n\n+ (NSDictionary *)flamegraphToDictionary:(Flamegraph *)flamegraph {\n    NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithDictionary:@{\n        @\"osBuild\": flamegraph.osBuild,\n        @\"isSimulator\": @(flamegraph.isSimulator),\n        @\"libraries\": flamegraph.libraries,\n        @\"events\": [self eventsToArray:flamegraph.events],\n        @\"device\": flamegraph.device,\n    }];\n\n    ThreadNode *thread = flamegraph.threadNodes.firstObject;\n    [result setObject:[self flameNodeToDictionary:thread.nodes] forKey:@\"nodes\"];\n\n    return result;\n}\n\n+ (NSArray *)eventsToArray:(NSArray<FlamegraphEvent *> *)events {\n    NSMutableArray *result = [NSMutableArray array];\n    \n    for (FlamegraphEvent *event in events) {\n        [result addObject:@{\n            @\"name\": event.name,\n            @\"type\": [event.type uppercaseString],\n            @\"time\": @(event.time),\n        }];\n    }\n    \n    return result;\n}\n\n+ (NSData *)toData:(NSObject *)anyInput {\n  Flamegraph *input = (Flamegraph *)anyInput;\n    \n  return [NSJSONSerialization dataWithJSONObject:[JSONWrapper flamegraphToDictionary:input]\n                                         options:NSJSONWritingWithoutEscapingSlashes\n                                           error:nil];\n}\n\n@end\n"
  },
  {
    "path": "ETTrace/JSONWrapper/Public/JSONWrapper.h",
    "content": "//\n//  TestClass.h\n//  ETTrace\n//\n//  Created by Noah Martin on 4/13/23.\n//\n\n#ifndef TestClass_h\n#define TestClass_h\n\n@import Foundation;\n\n@interface JSONWrapper : NSObject\n\n// Use NSObject here because we cannot import Swift packages from the public header to avoid circular dependencies\n+ (NSData *)toData:(NSObject *)input;\n\n@end\n\n\n#endif /* TestClass_h */\n"
  },
  {
    "path": "ETTrace/Symbolicator/FlamegraphGenerator.swift",
    "content": "//\n//  FlamegraphGenerator.swift\n//  PerfAnalysisRunner\n//\n//  Created by Itay Brenner on 7/3/23.\n//\n\nimport Foundation\nimport ETModels\n\npublic enum FlamegraphGenerator {\n\n  public static func generate(events: [Event], threads: [[Stack]], sampleRate: UInt32?, loadedLibraries: [LoadedLibrary], symbolicator: StackSymbolicator) -> [(FlameNode, [Double], String)] {\n    let syms = symbolicator.symbolicate(threads.flatMap { $0 }, loadedLibraries)\n    return threads.map { generateFlamegraphs(events: events, stacks: $0, sampleRate: sampleRate, syms: syms) }\n  }\n\n    private static func generateFlamegraphs(\n      events: [Event],\n      stacks: [Stack],\n      sampleRate: UInt32?,\n      syms: SymbolicationResult) -> (FlameNode, [Double], String)\n  {\n        var eventTimes = [Double](repeating: 0, count: events.count)\n        let times = stacks.map { $0.time }\n        var timeDiffs: [Double] = []\n        let rate = sampleRate == 0 ? 4500 : (sampleRate ?? 4500)\n        // The default sample rate is 4500 microseconds, add 500 because\n        // the samples have slightly more delay than the rate passed to usleep.\n        let sampleInterval = Double(rate + 500) / 1000000.0\n        var unattributedTime = 0.0\n        let partitions = partitions(times, size: 2, step: 1)\n        var eventTime: Double = 0\n        var eventIndex = 0\n        for (t1, t2) in partitions {\n            let timeDiff: Double\n            if t2 - t1 > sampleInterval * 2 {\n                unattributedTime += t2 - t1 - sampleInterval * 2\n                timeDiff = sampleInterval * 2\n                timeDiffs.append(timeDiff)\n            } else {\n                timeDiff = t2 - t1\n                timeDiffs.append(timeDiff)\n            }\n            let previousIndex = eventIndex\n            while eventIndex < events.count && events[eventIndex].time < t1 {\n                eventIndex += 1\n            }\n            for i in previousIndex..<eventIndex {\n                eventTimes[i] = eventTime\n            }\n            eventTime += timeDiff\n        }\n        timeDiffs.append(sampleInterval) // Assume last stack was the usual amount of time\n        var samples = zip(stacks, timeDiffs).map { (stack, timeDiff) -> Sample in\n            let stackSyms: [(String?, String, UInt64?)] = stack.stack.map { address in\n              guard let sym = syms[address] else {\n                return (\"<unknown>\", \"<unknown>\", nil)\n              }\n              if sym.2 {\n                return (sym.0, sym.1, address)\n              }\n              return (sym.0, sym.1, nil)\n            }\n            return Sample(time: timeDiff, stack: stackSyms)\n        }\n        if unattributedTime > 0 {\n            let stack = (nil as String?, \"<unattributed>\", nil as UInt64?)\n            samples.append(Sample(time: unattributedTime, stack: [stack]))\n        }\n        let folded = samples.map { $0.description }.joined(separator: \"\\n\")\n        let node = FlameNode.fromSamples(samples)\n        return (node, eventTimes, folded)\n    }\n    \n    private static func partitions(_ array: [Double], size: Int, step: Int? = nil) -> [(Double, Double)] {\n        let step = step ?? size\n        var startIdx = 0\n        var endIdx = size - 1\n        var partitions: [(Double, Double)] = []\n        while Int(endIdx) < array.count {\n            partitions.append( (array[startIdx], array[endIdx]) )\n            startIdx += step\n            endIdx += step\n        }\n        return partitions\n    }\n}\n"
  },
  {
    "path": "ETTrace/Symbolicator/Models.swift",
    "content": "//\n//  Models.swift\n//\n//\n//  Created by Noah Martin on 11/7/23.\n//\n\nimport Foundation\n\npublic struct Event: Decodable {\n    public let span: String\n    public let type: EventType\n    public let time: Double\n}\n\npublic enum EventType: String, Decodable {\n    case start\n    case stop\n}\n\npublic struct LoadedLibrary: Decodable, Equatable, Hashable {\n    public let path: String\n    public let loadAddress: UInt64\n    public let uuid: String\n}\n\npublic struct Stack: Decodable {\n    let stack: [UInt64]\n    let time: Double\n}\n"
  },
  {
    "path": "ETTrace/Symbolicator/Symbolicator.swift",
    "content": "//\n//  Symbolicator.swift\n//  PerfAnalysisRunner\n//\n//  Created by Itay Brenner on 7/3/23.\n//\n\nimport Foundation\nimport ETModels\n\nstruct Address {\n    let originalAddress: UInt64\n    let offset: UInt64?\n    let lib: LoadedLibrary?\n}\n\ntypealias SymbolicationResult = [UInt64: (String, String, Bool)]\n\npublic class StackSymbolicator {\n    var formatSymbolCache: [String: String] = [:]\n    \n    let isSimulator: Bool\n    let dSymsDir: String?\n    let osBuild: String\n    let osVersion: String?\n    let arch: String\n    let verbose: Bool\n    \n    public init(isSimulator: Bool, dSymsDir: String?, osBuild: String, osVersion: String?, arch: String, verbose: Bool) {\n        self.isSimulator = isSimulator\n        self.dSymsDir = dSymsDir\n        self.osBuild = osBuild\n        self.osVersion = osVersion\n        self.arch = arch\n        self.verbose = verbose\n    }\n\n    // Return value is map of address to (lib, symbol, isMissing)\n    func symbolicate(_ stacks: [Stack], _ loadedLibs: [LoadedLibrary]) -> SymbolicationResult {\n        var libToAddrs: [LoadedLibrary: Set<UInt64>] = [:]\n        let stacks = stacksFromResults(stacks, loadedLibs)\n        stacks.flatMap { $0 }.forEach { addr in\n            if let lib = addr.lib, let offset = addr.offset {\n              libToAddrs[lib, default: []].insert(offset)\n            }\n        }\n        \n        let stateLock = NSLock()\n        var libToCleanedPath = [String: (String, String)]()\n        var libToAddrToSym: [String: [UInt64: String]] = [:]\n        let queue = DispatchQueue(label: \"com.emerge.symbolication\", qos: .userInitiated, attributes: .concurrent)\n        let group = DispatchGroup()\n        for (lib, addrs) in libToAddrs {\n            let cleanedPath = cleanedUpPath(lib.path)\n            libToCleanedPath[lib.path] = (cleanedPath, URL(string: cleanedPath)?.lastPathComponent ?? \"\")\n            group.enter()\n            queue.async {\n                if let dSym = self.dsymForLib(lib) {\n                    let addrToSym = Self.addrToSymForBinary(dSym, self.archForBinary(dSym), addrs)\n                    stateLock.lock()\n                    libToAddrToSym[lib.path] = addrToSym\n                    stateLock.unlock()\n                }\n                group.leave()\n            }\n        }\n        group.wait()\n        \n        var noLibCount = 0\n        var noSymMap: [String: UInt64] = [:]\n        var result: SymbolicationResult = [:]\n        stacks.forEach { stack in\n            stack.forEach { addr in\n                if let lib = addr.lib, let offset = addr.offset {\n                    let (libPath, lastPathComponent) = libToCleanedPath[lib.path]!\n                    guard let addrToSym = libToAddrToSym[lib.path],\n                          let sym = addrToSym[offset] else {\n                        noSymMap[libPath, default: 0] += 1\n                      result[addr.originalAddress] = (libPath, lastPathComponent, true)\n                      return\n                    }\n                    result[addr.originalAddress] = (libPath, formatSymbol(sym), false)\n                } else {\n                    noLibCount += 1\n                }\n            }\n        }\n        let totalCount = stacks.flatMap { $0 }.count\n        let noLibPercentage = (Double(noLibCount) / Double(totalCount) * 100.0)\n        if verbose {\n            print(\"\\(noLibPercentage)% have no library\")\n            for (key, value) in noSymMap {\n                let percentage = (Double(value) / Double(totalCount) * 100.0)\n                print(\"\\(percentage)% from \\(key) have library but no symbol\")\n            }\n        }\n        return result\n    }\n    \n    private func cleanedUpPath(_ path: String) -> String {\n        if path.contains(\".app/\") && !path.contains(\"/Xcode.app/\") {\n            return path.split(separator: \"/\").drop(while: { $0.hasSuffix(\".app\") }).joined(separator: \"/\")\n        } else if path.contains(\"/RuntimeRoot/\") {\n            if let index = path.range(of: \"/RuntimeRoot/\")?.upperBound {\n                return String(path[index...])\n            }\n        }\n        return path\n    }\n    \n    private func stacksFromResults(_ stacks: [Stack], _ loadedLibs: [LoadedLibrary]) -> [[Address]] {\n        let sortedLibs = loadedLibs.sorted(by: { $0.loadAddress > $1.loadAddress } )\n        let firstTextSize: UInt64 = 50 * 1024 * 1024\n        var addrToAddress: [UInt64: Address] = [:]\n\n        let addrs: [[Address]] = stacks.map { stack in\n            return stack.stack.map { addr in\n                let cachedAddress = addrToAddress[addr]\n                if let cachedAddress = cachedAddress {\n                    return cachedAddress\n                }\n\n                var lib: LoadedLibrary? = sortedLibs.first(where: { $0.loadAddress <= addr })\n\n                if lib == sortedLibs.first {\n                    if !(addr < sortedLibs.first!.loadAddress + firstTextSize) {\n                        // TODO: sometimes there are a few really large addresses that neither us nor instruments can symbolicate. Investigate why\n                        lib = nil\n                    }\n                }\n\n                if lib == nil {\n                    if verbose {\n                        print(\"\\(addr) not contained within any frameworks\")\n                    }\n                    return Address(originalAddress: addr, offset: nil, lib: nil)\n                }\n\n                let address = Address(originalAddress: addr, offset: addr - lib!.loadAddress, lib: lib)\n                addrToAddress[addr] = address\n                return address\n            }\n        }\n\n        return addrs\n    }\n\n    private func archForBinary(_ binary: String) -> String {\n      let archsStr = try? processWithOutput(\"/usr/bin/lipo\", args: [\"-archs\", binary])\n      let archs = archsStr?.split(separator: \" \").map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } ?? []\n      let trimmedArch = self.arch.trimmingCharacters(in: .whitespacesAndNewlines)\n      if archs.contains(trimmedArch) {\n        return trimmedArch\n      }\n      if trimmedArch == \"arm64e\" && archs.contains(\"arm64\") {\n        return \"arm64\"\n      }\n      return archs.first ?? trimmedArch\n    }\n\n    private static func addrToSymForBinary(_ binary: String, _ arch: String, _ addrs: Set<UInt64>) -> [UInt64: String] {\n        let addrsArray = Array(addrs)\n        let addrsFile = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString)!.path\n\n        let addition: UInt64 = 0x1000000 // atos can fail when the load address is 0, so add extra\n        let strs = addrsArray.map { String($0 + addition, radix: 16) }\n        try! strs.joined(separator: \"\\n\").write(toFile: addrsFile, atomically: true, encoding: .utf8)\n\n        let symsStr = try? processWithOutput(\"/usr/bin/atos\", args: [\"-l\", String(addition, radix: 16), \"-o\", binary, \"-f\", addrsFile, \"-arch\", arch])\n\n        let syms = symsStr!.split(separator: \"\\n\").enumerated().map { (idx, sym) -> (UInt64, String?) in\n            let trimmed = sym.trimmingCharacters(in: .whitespacesAndNewlines)\n            if trimmed.count == 0 || trimmed.starts(with: \"0x\") || trimmed == strs[idx] {\n                return (addrsArray[idx], nil)\n            } else {\n                return (addrsArray[idx], trimmed)\n            }\n        }.filter({ (_, sym) in\n            return sym != nil\n        })\n\n        var result: [UInt64: String] = [:]\n        for (addr, sym) in syms {\n            result[addr] = sym\n        }\n\n        return result\n    }\n\n    private func formatSymbol(_ sym: String) -> String {\n        if let cachedResult = formatSymbolCache[sym] {\n            return cachedResult\n        }\n        let result = sym.replacingOccurrences(of: \":\\\\d+\\\\)\", with: \")\", options: .regularExpression) // static AppDelegate.$main() (in emergeTest) (AppDelegate.swift:10)\n            .replacingOccurrences(of: \" \\\\+ \\\\d+$\", with: \"\", options: .regularExpression) // _dyld_start (in dyld) + 0\n            .replacingOccurrences(of: \" (<compiler-generated>)$\", with: \"\", options: .regularExpression) // static UIApplicationDelegate.main() (in emergeTest) (<compiler-generated>)\n            .replacingOccurrences(of: \" \\\\(\\\\S+.\\\\S+\\\\)$\", with: \"\", options: .regularExpression) // static AppDelegate.$main() (in emergeTest) (AppDelegate.swift)\n            .replacingOccurrences(of: \" \\\\(in (\\\\S| )+\\\\)\", with: \"\", options: .regularExpression) // static AppDelegate.$main() (in emergeTest)\n            .replacingOccurrences(of: \"^__\\\\d+\\\\+\", with: \"\", options: .regularExpression)\n            .replacingOccurrences(of: \"^__\\\\d+\\\\-\", with: \"\", options: .regularExpression)\n            .trimmingCharacters(in: .whitespacesAndNewlines)\n        formatSymbolCache[sym] = result\n        return result\n    }\n\n  private static func fileIfExists(_ path: String) -> String? {\n    FileManager.default.fileExists(atPath: path) ? path : nil\n  }\n\n    private func dsymForLib(_ lib: LoadedLibrary) -> String? {\n        let libPath = lib.path\n        \n        if libPath.contains(\".app/\") {\n            // Look for matching dsyms\n            if let dsymsDir = dSymsDir {\n                let libName = URL(fileURLWithPath: libPath).lastPathComponent\n                let folderExtension = libPath.contains(\".framework\") ? \"framework\" : \"app\"\n                let dsyms = try? FileManager.default.contentsOfDirectory(atPath: \"\\(dsymsDir)/\\(libName).\\(folderExtension).dSYM/Contents/Resources/DWARF/\")\n                if let dsym = dsyms?.first {\n                    return \"\\(dsymsDir)/\\(libName).\\(folderExtension).dSYM/Contents/Resources/DWARF/\\(dsym)\"\n                }\n            }\n\n            // Use spotlight to find dsyms\n            let foundDsyms = try? safeShellWithOutput(\"/usr/bin/mdfind \\\"com_apple_xcode_dsym_uuids == \\(lib.uuid)\\\"\").components(separatedBy: .newlines)\n            if let foundDsym = foundDsyms?.first {\n                let dwarfFiles = try? FileManager.default.contentsOfDirectory(atPath: \"\\(foundDsym)/Contents/Resources/DWARF/\")\n                if let dwarfFile = dwarfFiles?.first {\n                    return \"\\(foundDsym)/Contents/Resources/DWARF/\\(dwarfFile)\"\n                }\n            }\n            // Try using the binary in the simulator to symbolicate\n            if isSimulator {\n              return libPath\n            }\n            return nil\n        } else {\n          if !isSimulator {\n            // Get symbols from device support dir\n            let searchFolder = \"\\(FileManager.default.homeDirectoryForCurrentUser.path)/Library/Developer/Xcode/iOS DeviceSupport\"\n            let directories = (try? FileManager.default.contentsOfDirectory(atPath: searchFolder)) ?? []\n\n            // First look for matching os and arch, then just matching os\n            for folder in directories where folder.contains(osBuild) && folder.hasSuffix(arch) {\n                return Self.fileIfExists(\"\\(searchFolder)/\\(folder)/Symbols\\(libPath)\")\n            }\n            for folder in directories where folder.contains(osBuild) {\n              return Self.fileIfExists(\"\\(searchFolder)/\\(folder)/Symbols\\(libPath)\")\n            }\n            return nil\n          } else {\n            return libPath\n          }\n        }\n    }\n}\n"
  },
  {
    "path": "ETTrace/Symbolicator/Utils.swift",
    "content": "//\n//  Utils.swift\n//  PerfAnalysisRunner\n//\n//  Created by Itay Brenner on 6/3/23.\n//\n\nimport Foundation\nimport AppKit\n\nfunc safeShell(_ command: String) throws {\n    let task = Process()\n    \n    task.arguments = [\"--login\", \"-c\", command]\n    task.executableURL = URL(fileURLWithPath: \"/bin/zsh\")\n    task.standardInput = nil\n\n    try task.run()\n    \n    task.waitUntilExit()\n}\n\nfunc processWithOutput(_ executable: String, args: [String]) throws -> String {\n  let task = Process()\n\n  task.arguments = args\n  task.executableURL = URL(fileURLWithPath: executable)\n  task.standardInput = nil\n\n  return try runTask(task)\n}\n\nfunc safeShellWithOutput(_ command: String) throws -> String {\n    let task = Process()\n\n    task.arguments = [\"--login\", \"-c\", command]\n    task.executableURL = URL(fileURLWithPath: \"/bin/zsh\")\n    task.standardInput = nil\n\n    return try runTask(task)\n}\n\nprivate func runTask(_ task: Process) throws -> String {\n  let pipe = Pipe()\n  task.standardOutput = pipe\n  let group = DispatchGroup()\n  group.enter()\n  var result = String()\n  pipe.fileHandleForReading.readabilityHandler = { fh in\n      let data = fh.availableData\n      if data.isEmpty { // EOF on the pipe\n          pipe.fileHandleForReading.readabilityHandler = nil\n          group.leave()\n      } else {\n        if let newString = String(data: data, encoding: .utf8) {\n          result.append(newString)\n        }\n      }\n  }\n\n  try task.run()\n  task.waitUntilExit()\n  group.wait()\n\n  return result\n}\n"
  },
  {
    "path": "ETTrace/Tracer/EMGStackTraceRecorder.cpp",
    "content": "#include \"EMGStackTraceRecorder.h\"\n\n#import <QuartzCore/QuartzCore.h>\n#import <mach-o/arch.h>\n#import <mach/mach.h>\n#import <pthread.h>\n#import <deque>\n#import <iostream>\n#import <mutex>\n#import <unordered_map>\n\nextern \"C\" {\nvoid FIRCLSWriteThreadStack(thread_t thread, uintptr_t *frames, uint64_t framesCapacity, uint64_t *framesWritten);\n}\n\nstatic const int kMaxFramesPerStack = 1024;\n\nkern_return_t checkMachCall(kern_return_t result) {\n    if (result != KERN_SUCCESS) {\n        std::cerr << \"Mach call failed with \" << result << std::endl;\n    }\n    return result;\n}\n\nThread::Thread(thread_t threadId, thread_t mainThreadId) {\n    name = \"Failed to get name\"; // Error case\n\n    if(threadId == mainThreadId) {\n        name = \"Main Thread\";\n    } else {\n        // Get thread Name\n        char cName[1024];\n        pthread_t pt = pthread_from_mach_thread_np(threadId);\n        if (pt) {\n            int rc = pthread_getname_np(pt, cName, sizeof(cName));\n            if (rc == 0) {\n                name = cName;\n            }\n        }\n    }\n}\n\nstd::vector<ThreadSummary> EMGStackTraceRecorder::collectThreadSummaries() {\n    std::lock_guard<std::mutex> lockGuard(threadsLock);\n    \n    std::vector<ThreadSummary> summaries;\n    for (const auto &[threadId, thread] : threadsMap) {\n        std::vector<StackSummary> stackSummaries;\n        for (const auto &stack : thread.stacks) {\n            std::vector<uintptr_t> addresses;\n            for (auto i = stack.storageStartIndex; i < stack.storageEndIndex; i++) {\n                addresses.emplace_back(addressStorage[i]);\n            }\n            // Reverse the stack addresses to get the correct order\n            std::reverse(addresses.begin(), addresses.end());\n            stackSummaries.emplace_back(stack.time, addresses);\n        }\n        summaries.emplace_back(threadId, thread.name, stackSummaries);\n    }\n    return summaries;\n}\n\nvoid EMGStackTraceRecorder::recordStackForAllThreads(bool recordAllThreads, thread_t mainMachThread, thread_t etTraceThread) {\n    std::lock_guard<std::mutex> lockGuard(threadsLock);\n    thread_act_array_t threads = nullptr;\n    mach_msg_type_number_t threadCount = 0;\n    if (recordAllThreads) {\n        int result = checkMachCall(task_threads(mach_task_self(), &threads, &threadCount));\n        if (result != KERN_SUCCESS) {\n            threadCount = 0;\n        }\n    } else {\n        threads = &mainMachThread;\n        threadCount = 1;\n    }\n    \n    // This time gets less accurate for later threads, but still good\n    CFTimeInterval time = CACurrentMediaTime();\n    for (mach_msg_type_number_t i = 0; i < threadCount; i++) {\n        if (threads[i] == etTraceThread) {\n            continue;\n        }\n\n        uintptr_t frames[kMaxFramesPerStack];\n        uint64_t frameCount = 0;\n\n        if (thread_suspend(threads[i]) != KERN_SUCCESS) {\n            // In theory, the thread may have been destroyed by now, so we exit early if this fails\n            continue;\n        }\n        // BEGIN REENTRANT SECTION\n        FIRCLSWriteThreadStack(threads[i], frames, kMaxFramesPerStack, &frameCount);\n        // END REENTRANT SECTION\n        checkMachCall(thread_resume(threads[i]));\n\n        auto emplaceResult = threadsMap.try_emplace(threads[i], threads[i], mainMachThread);\n        size_t startIndex = addressStorage.size();\n        for (int frame_idx = 0; frame_idx < frameCount; frame_idx++) {\n            addressStorage.emplace_back(frames[frame_idx]);\n        }\n        size_t endIndex = addressStorage.size();\n        emplaceResult.first->second.stacks.emplace_back(time, startIndex, endIndex);\n    }\n    if (recordAllThreads) {\n      vm_deallocate(mach_task_self(), (vm_address_t) threads, sizeof(thread_t) * threadCount);\n    }\n}\n"
  },
  {
    "path": "ETTrace/Tracer/EMGStackTraceRecorder.h",
    "content": "#import <deque>\n#import <vector>\n#import <unordered_map>\n#import <mach/mach.h>\n#import <QuartzCore/QuartzCore.h>\n#import <iostream>\n\nstruct StackSummary {\n    CFTimeInterval time;\n    std::vector<uintptr_t> stack;\n    \n    StackSummary(CFTimeInterval time, std::vector<uintptr_t> &stack) : time(time), stack(stack) {\n    }\n};\n\nstruct ThreadSummary {\n    thread_t threadId;\n    std::string name;\n    std::vector<StackSummary> stacks;\n    \n    ThreadSummary(thread_t threadId, const std::string &name, std::vector<StackSummary> &stacks) : threadId(threadId), name(name), stacks(stacks) {\n    }\n};\n\nstruct Stack {\n    CFTimeInterval time;\n    size_t storageStartIndex; // Inclusive\n    size_t storageEndIndex; // Exclusive\n    \n    Stack(CFTimeInterval time, size_t storageStartIndex, size_t storageEndIndex) : time(time), storageStartIndex(storageStartIndex), storageEndIndex(storageEndIndex) {\n    }\n};\n\nstruct Thread {\n    std::deque<Stack> stacks;\n    std::string name;\n    \n    Thread(thread_t threadId, thread_t mainThreadId);\n};\n\nclass EMGStackTraceRecorder {\n    std::unordered_map<unsigned int, Thread> threadsMap;\n    std::mutex threadsLock;\n    std::deque<uintptr_t> addressStorage;\n    \npublic:\n    void recordStackForAllThreads(bool recordAllThreads, thread_t mainMachThread, thread_t etTraceThread);\n\n    std::vector<ThreadSummary> collectThreadSummaries();\n};\n"
  },
  {
    "path": "ETTrace/Tracer/EMGTracer+PrintThreads.m",
    "content": "//\n//  EMGTracer+PrintThreads.m\n//  \n//\n//  Created by Itay Brenner on 15/8/24.\n//\n\n#import <Foundation/Foundation.h>\n#import <Tracer.h>\n@import TracerSwift;\n\n@implementation EMGTracer (PrintThread)\n\n+ (void)printThreads {\n  [ThreadHelper printThreads];\n}\n\n@end\n"
  },
  {
    "path": "ETTrace/Tracer/EMGTracer.mm",
    "content": "//\n//  Tracer.m\n//  \n//\n//  Created by Noah Martin on 10/27/23.\n//\n\n#import \"Tracer.h\"\n#import <Foundation/Foundation.h>\n#import <vector>\n#import <mutex>\n#import <map>\n#import <mach/mach.h>\n#import <sys/sysctl.h>\n#import <mach-o/arch.h>\n#import <sys/utsname.h>\n#import <QuartzCore/QuartzCore.h>\n#import \"EMGStackTraceRecorder.h\"\n\nstatic NSThread *sStackRecordingThread = nil;\n\nstatic thread_t sMainMachThread = {0};\n\nstatic useconds_t sSampleRate = 0;\n\n// To avoid static initialization order fiasco, we access it from a function\nEMGStackTraceRecorder &getRecorder() {\n    static EMGStackTraceRecorder recorder;\n    return recorder;\n}\n\n@implementation EMGTracer\n\n+ (BOOL)isRecording {\n  return sStackRecordingThread != nil;\n}\n\n+ (void)stopRecording:(void (^)(NSDictionary *))stopped {\n    [sStackRecordingThread cancel];\n    sStackRecordingThread = nil;\n    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{\n        stopped([EMGTracer getResults]);\n    });\n}\n\n+ (NSDictionary *)getResults {\n    NSMutableDictionary <NSString *, NSDictionary<NSString *, id> *> *threads = [NSMutableDictionary dictionary];\n    \n    auto threadSummaries = getRecorder().collectThreadSummaries();\n    for (const auto &thread : threadSummaries) {\n        NSString *threadId = [@(thread.threadId) stringValue];\n        threads[threadId] = @{\n            @\"name\": @(thread.name.c_str()),\n            @\"stacks\": [self arrayFromStacks:thread.stacks]\n        };\n    }\n\n    const NXArchInfo *archInfo = NXGetLocalArchInfo();\n    NSString *cpuType = [NSString stringWithUTF8String:archInfo->description];\n    NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion;\n    return @{\n        @\"libraryInfo\": EMGLibrariesData(),\n        @\"isSimulator\": @([self isRunningOnSimulator]),\n        @\"osBuild\": [self osBuild],\n        @\"osVersion\": [NSString stringWithFormat:@\"%ld.%ld.%ld\", (long)version.majorVersion, (long)version.minorVersion, (long)version.patchVersion],\n        @\"cpuType\": cpuType,\n        @\"device\": [self deviceName],\n        @\"threads\": threads,\n        @\"sampleRate\": @(sSampleRate),\n    };\n}\n\n+ (NSArray <NSDictionary <NSString *, id> *> *) arrayFromStacks: (const std::vector<StackSummary> &)stacks {\n    NSMutableArray <NSDictionary <NSString *, id> *> *threadStacks = [NSMutableArray array];\n    for (const auto &cStack : stacks) {\n        NSMutableArray <NSNumber *> *stack = [NSMutableArray array];\n        for (const auto &address : cStack.stack) {\n            [stack addObject:@((NSUInteger)address)];\n        }\n        NSDictionary *stackDictionary = @{\n            @\"stack\": [stack copy],\n            @\"time\": @(cStack.time)\n        };\n        [threadStacks addObject:stackDictionary];\n    }\n    return threadStacks;\n}\n\n+ (BOOL)isRunningOnSimulator\n{\n#if TARGET_OS_SIMULATOR\n    return YES;\n#else\n    return NO;\n#endif\n}\n\n+ (NSString *)osBuild {\n    int mib[2] = {CTL_KERN, KERN_OSVERSION};\n    u_int namelen = sizeof(mib) / sizeof(mib[0]);\n    size_t bufferSize = 0;\n\n    NSString *osBuildVersion = nil;\n\n    // Get the size for the buffer\n    sysctl(mib, namelen, NULL, &bufferSize, NULL, 0);\n\n    u_char buildBuffer[bufferSize];\n    int result = sysctl(mib, namelen, buildBuffer, &bufferSize, NULL, 0);\n\n    if (result >= 0) {\n        osBuildVersion = [[NSString alloc] initWithBytes:buildBuffer length:bufferSize encoding:NSUTF8StringEncoding];\n    }\n\n    NSCharacterSet *nonAlphanumericStrings = [[NSCharacterSet alphanumericCharacterSet] invertedSet];\n\n    // Remove final NULL character\n    return [osBuildVersion stringByTrimmingCharactersInSet:nonAlphanumericStrings];\n}\n\n+ (NSString *)deviceName {\n    struct utsname systemInfo;\n    uname(&systemInfo);\n\n    return [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];\n}\n\n+ (void)setup {\n  sMainMachThread = mach_thread_self();\n  EMGBeginCollectingLibraries();\n}\n\n+ (void)setupStackRecording:(BOOL)recordAllThreads rate:(useconds_t)sampleRate\n{\n    if (sStackRecordingThread != nil) {\n        return;\n    }\n    sSampleRate = sampleRate;\n\n    // Make sure that +recordStack is always called on the same (non-main) thread.\n    // This is because a Process keeps its own \"current thread\" variable which we need\n    // to keep separate\n    // from the main thread. This is because unwinding itself from the main thread\n    // requires Crashlyics to use a hack, and because the stack recording would show up\n    // in the trace. The current strategy is to sleep for 4.5 ms because\n    // usleep is guaranteed to sleep more than that, in practice ~5ms. We could use a\n    // dispatch_timer, which at least tries to compensate for drift etc., but the\n    // timer's queue could theoretically end up run on the main thread\n    sStackRecordingThread = [[NSThread alloc] initWithBlock:^{\n        thread_t etTraceThread = mach_thread_self();\n\n        NSThread *thread = [NSThread currentThread];\n        while (!thread.cancelled) {\n            getRecorder().recordStackForAllThreads(recordAllThreads, sMainMachThread, etTraceThread);\n            usleep(sampleRate > 0 ? sampleRate : 4500);\n        }\n    }];\n    sStackRecordingThread.qualityOfService = NSQualityOfServiceUserInteractive;\n    [sStackRecordingThread start];\n}\n\n@end\n"
  },
  {
    "path": "ETTrace/Tracer/EMGWriteLibraries.m",
    "content": "//\n//  EMGWriteLibraries.m\n//  PerfAnalysis\n//\n//  Created by Noah Martin on 12/9/22.\n//\n\n#import <Foundation/Foundation.h>\n#import <dlfcn.h>\n#import <mach/mach_init.h>\n#import <mach/task.h>\n#import <mach-o/dyld_images.h>\n#import <mach-o/dyld.h>\n#import <pthread.h>\n#import <QuartzCore/QuartzCore.h>\n\n#import \"Tracer.h\"\n\nstatic NSRecursiveLock *sLock;\nstatic NSMutableArray *sLoadedLibraries;\nstatic uint64_t sMainThreadID;\n\nstatic void addLibrary(const char *path, const void *loadAddress, NSUUID *binaryUUID) {\n    // Note that the slide given is very odd (seems incorrect, and many binaries share the same slide value)\n    // So, just print out the header address\n    [sLoadedLibraries addObject:@{\n        @\"path\": @(path),\n        // Although it's undefined if JSON can handle 64-bit integers, Apple's NSJSONSerialization seems to write them\n        // out correctly\n        @\"loadAddress\": @((uint64_t)loadAddress),\n        @\"uuid\": binaryUUID.UUIDString\n    }];\n}\n\nstatic NSUUID* uuid(const struct mach_header *header) {\n    BOOL is64bit = header->magic == MH_MAGIC_64 || header->magic == MH_CIGAM_64;\n    uintptr_t cursor = (uintptr_t)header + (is64bit ? sizeof(struct mach_header_64) : sizeof(struct mach_header));\n    const struct segment_command *segmentCommand = NULL;\n    for (uint32_t i = 0; i < header->ncmds; i++, cursor += segmentCommand->cmdsize) {\n        segmentCommand = (struct segment_command *)cursor;\n        if (segmentCommand->cmd == LC_UUID) {\n            const struct uuid_command *uuidCommand = (const struct uuid_command *)segmentCommand;\n            return [[NSUUID alloc] initWithUUIDBytes:uuidCommand->uuid];\n        }\n    }\n    return NULL;\n}\n\nstatic void printLibrary(const struct mach_header *header, intptr_t slide) {\n    // Lock just in case this function isn't called in a thread-safe manner\n    [sLock lock];\n    Dl_info info = {0};\n    dladdr(header, &info);\n    addLibrary(info.dli_fname, header, uuid(header));\n    [sLock unlock];\n}\n\nvoid EMGBeginCollectingLibraries() {\n    sLoadedLibraries = [NSMutableArray array];\n    sLock = [NSRecursiveLock new];\n\n    struct task_dyld_info dyld_info;\n    mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;\n    task_info(mach_task_self_, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count);\n    struct dyld_all_image_infos *infos = (struct dyld_all_image_infos *)dyld_info.all_image_info_addr;\n    void *header = (void *)infos->dyldImageLoadAddress;\n    addLibrary(\"/usr/lib/dyld\", header, uuid(header));\n\n    pthread_threadid_np(NULL, &sMainThreadID);\n\n    _dyld_register_func_for_add_image(printLibrary);\n}\n\nNSDictionary *EMGLibrariesData() {\n    [sLock lock];\n    NSString *runId = [NSProcessInfo processInfo].environment[@\"EMERGE_RUN_ID\"];\n    NSDictionary *result = @{\n        @\"runId\": runId ?: [NSNull null],\n        @\"relativeTime\": @(CACurrentMediaTime()),\n        @\"mainThreadId\": @(sMainThreadID),\n        @\"loadedLibraries\": [sLoadedLibraries copy]\n    };\n    [sLock unlock];\n    return result;\n}\n"
  },
  {
    "path": "ETTrace/Tracer/Public/Tracer.h",
    "content": "//\n//  EMGWriteLibraries.h\n//  PerfAnalysis\n//\n//  Created by Noah Martin on 12/9/22.\n//\n\n#ifndef EMGWriteLibraries_h\n#define EMGWriteLibraries_h\n\n#import <Foundation/Foundation.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nNSDictionary *EMGLibrariesData(void);\nvoid EMGBeginCollectingLibraries(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n@interface EMGTracer : NSObject\n\n+ (void)setupStackRecording:(BOOL)recordAllThreads rate:(useconds_t)sampleRate;\n+ (void)stopRecording:(void (^)(NSDictionary *))stopped;\n// Must be called on the main thread, before setupStackRecording is called\n+ (void)setup;\n+ (NSDictionary *)getResults;\n+ (BOOL)isRecording;\n\n@end\n\n@interface EMGTracer (PrintThread)\n+ (void)printThreads;\n@end\n\n\n#endif /* EMGWriteLibraries_h */\n"
  },
  {
    "path": "ETTrace/TracerSwift/ThreadHelper.swift",
    "content": "//\n//  ThreadHelper.swift\n//  Tracer\n//\n//  Created by Itay Brenner on 23/7/24.\n//\n\nimport Foundation\nimport Darwin\nimport MachO\n\npublic struct StackFrame {\n  let symbol: String\n  let file: String\n  let address: UInt\n\n  var demangledSymbol: String {\n    return _stdlib_demangleName(symbol)\n  }\n}\n\npublic struct ThreadInfo: Hashable {\n  let name: String\n  let number: Int\n}\n\n@objc\npublic class ThreadHelper: NSObject {\n  public static let main_thread_t = mach_thread_self()\n  static var symbolsLoaded = false\n  static var symbolAddressTuples = [(UInt, String)]()\n  static let kMaxFramesPerStack = 512\n\n  @objc\n  public static func printThreads() {\n    NSLog(\"Stack trace:\")\n    let backtrace = callStackForAllThreads()\n    \n    for (thread, stackframe) in backtrace {\n      NSLog(\"Thread \\(thread.number): \\(thread.name)\")\n      \n      for (index, frame) in stackframe.enumerated() {\n        NSLog(\"  \\(index) - \\(frame.demangledSymbol) [0x\\(String(frame.address, radix: 16))] (\\(frame.file)\")\n      }\n    }\n  }\n  \n  public static func callStackForAllThreads() -> [ThreadInfo: [StackFrame]] {\n    var result: [ThreadInfo: [StackFrame]] = [:]\n    \n    var count: mach_msg_type_number_t = 0\n    var threads: thread_act_array_t!\n\n    guard task_threads(mach_task_self_, &(threads), &count) == KERN_SUCCESS else {\n      return result\n    }\n\n    defer {\n      let size = MemoryLayout<thread_t>.size * Int(count)\n      vm_deallocate(mach_task_self_, vm_address_t(bitPattern: threads), vm_size_t(size))\n    }\n\n    var frameCount: UInt64 = 0\n    var frames = [UInt64](repeating: 0, count: kMaxFramesPerStack)\n    for i in 0..<count {\n      let index = Int(i)\n      if let p_thread = pthread_from_mach_thread_np((threads[index])) {\n        let thread: thread_t = threads[index]\n        let pthread = pthread_from_mach_thread_np(thread)!\n        if pthread == pthread_self() {\n          // Skip our thread\n          continue\n        }\n\n        let threadName = getThreadName(p_thread) ?? \"\"\n\n        withUnsafeMutablePointer(to: &frameCount) { frameCountPtr in\n          frames.withUnsafeMutableBufferPointer { framesPtr in\n            let firstPtr: UnsafeMutablePointer<UInt64> = framesPtr.baseAddress!\n            thread_suspend(thread)\n            // Ensure any code here does not take locks using @_noLocks\n            getStacktrace(forThread: thread, frames: firstPtr, maxFrames: UInt64(kMaxFramesPerStack), frameCount: frameCountPtr)\n            thread_resume(thread)\n          }\n        }\n        let stacktrace = Array(frames.prefix(Int(frameCount)))\n        let stacks = getCallStack(stacktrace) ?? []\n\n        let threadInfo: ThreadInfo = ThreadInfo(name: threadName,number: index)\n\n        result[threadInfo] = stacks\n      }\n    }\n        \n    return result\n  }\n  \n  static func getCallStack(_ array: [UInt64]) -> [StackFrame]? {\n    var symbols = [StackFrame]()\n    for address in array {\n      var info = Dl_info()\n      if dladdr(UnsafeRawPointer(bitPattern: UInt(address)), &info) != 0 {\n        let functionName = info.dli_sname.map { String(cString: $0) } ?? alternativeSymbolName(UInt(address))\n        let fileName = info.dli_fname.map { String(cString: $0) } ?? \"<unknown>\"\n          \n        symbols.append(StackFrame(symbol: functionName, file: fileName, address: UInt(address)))\n      }\n    }\n    return symbols\n  }\n  \n  static func alternativeSymbolName(_ address: UInt) -> String {\n    NSLog(\"Using alternate name\")\n    if (!symbolsLoaded) {\n      parseImages()\n    }\n    \n    var previous: (UInt, String)? = nil\n    for (addr, str) in symbolAddressTuples {\n      if addr > address {\n        return previous?.1 ?? \"Invalid\"\n      }\n      previous = (addr, str)\n    }\n    return \"<unknown>\"\n  }\n\n  private static func getThreadName(_ thread: pthread_t) -> String? {\n    var name = [Int8](repeating: 0, count: 256)\n\n    let result = pthread_getname_np(thread, &name, name.count)\n    if result != 0 {\n      print(\"Failed to get thread name: \\(result)\")\n      return nil\n    }\n\n    return String(cString: name)\n  }\n  \n  private static func parseImages() {\n    for i in 0..<_dyld_image_count() {\n      guard let header = _dyld_get_image_header(i) else { continue }\n      let slide = _dyld_get_image_vmaddr_slide(i)\n      \n      let bytes: UnsafeRawPointer = UnsafeRawPointer(OpaquePointer(header))\n      var symtabCommand: symtab_command?\n      var linkeditCmd: segment_command_64?\n      bytes.processLoadComands { command, commandPointer in\n        switch command.cmd {\n        case UInt32(LC_SYMTAB):\n          let commandType = commandPointer.load(as: symtab_command.self)\n          symtabCommand = commandType\n        case UInt32(LC_SEGMENT_64):\n          let cmd = commandPointer.load(as: segment_command_64.self)\n          var segname = cmd.segname\n          if strcmp(&segname, SEG_LINKEDIT) == 0 {\n            linkeditCmd = commandPointer.load(as: segment_command_64.self)\n          }\n        default:\n          break\n        }\n        return true\n      }\n      \n      guard let command = symtabCommand, let linkeditCmd = linkeditCmd else { continue }\n      \n      let linkeditBase = slide + Int(linkeditCmd.vmaddr) - Int(linkeditCmd.fileoff)\n      parseTable(command: command, linkeditBase, slide)\n    }\n    \n    symbolAddressTuples.sort { addr1, addr2 in\n      return addr1.0 < addr2.0\n    }\n    symbolsLoaded = true\n  }\n\n  private static func parseTable(command: symtab_command, _ linkeditBase: Int, _ slide: Int) {\n    let imageBase = UnsafeRawPointer(bitPattern: linkeditBase)!\n    let nsyms = command.nsyms\n    let symStart = imageBase.advanced(by: Int(command.symoff))\n    let strStart = imageBase.advanced(by: Int(command.stroff))\n    for i in 0..<nsyms {\n      let symbolStart = symStart.advanced(by: Int(i) * MemoryLayout<nlist_64>.size)\n      let nlist = symbolStart.load(as: nlist_64.self)\n      guard (nlist.n_type & UInt8(N_STAB) == 0) && nlist.n_value != 0 else { continue }\n\n      let stringStart = strStart.advanced(by: Int(nlist.n_un.n_strx))\n      let string = String(cString: stringStart.assumingMemoryBound(to: UInt8.self))\n      \n      // Add slide since frame addresses will have it\n      symbolAddressTuples.append((UInt(nlist.n_value) + UInt(slide), string))\n    }\n  }\n\n#if swift(>=5.10)\n  @_noLocks static func getStacktrace(\n    forThread thread: thread_t,\n    frames: UnsafeMutablePointer<UInt64>,\n    maxFrames: UInt64,\n    frameCount: UnsafeMutablePointer<UInt64>) {\n    FIRCLSWriteThreadStack(thread, frames, maxFrames, frameCount)\n  }\n#else\n  static func getStacktrace(\n    forThread thread: thread_t,\n    frames: UnsafeMutablePointer<UInt64>,\n    maxFrames: UInt64,\n    frameCount: UnsafeMutablePointer<UInt64>) {\n    FIRCLSWriteThreadStack(thread, frames, maxFrames, frameCount)\n  }\n#endif\n}\n\n@_silgen_name(\"swift_demangle\")\npublic\nfunc _stdlib_demangleImpl(\n  mangledName: UnsafePointer<CChar>?,\n  mangledNameLength: UInt,\n  outputBuffer: UnsafeMutablePointer<CChar>?,\n  outputBufferSize: UnsafeMutablePointer<UInt>?,\n  flags: UInt32\n  ) -> UnsafeMutablePointer<CChar>?\n\npublic func _stdlib_demangleName(_ mangledName: String) -> String {\n  return mangledName.utf8CString.withUnsafeBufferPointer { (mangledNameUTF8CStr) in\n    let demangledNamePtr = _stdlib_demangleImpl(\n      mangledName: mangledNameUTF8CStr.baseAddress,\n      mangledNameLength: UInt(mangledNameUTF8CStr.count - 1),\n      outputBuffer: nil,\n      outputBufferSize: nil,\n      flags: 0)\n\n    if let demangledNamePtr = demangledNamePtr {\n      let demangledName = String(cString: demangledNamePtr)\n      free(demangledNamePtr)\n      return demangledName\n    }\n    return mangledName\n  }\n}\n\n#if swift(>=5.10)\n@_silgen_name(\"FIRCLSWriteThreadStack\")\n@_noLocks func FIRCLSWriteThreadStack(_ thread: thread_t, _ frames: UnsafeMutablePointer<UInt64>, _ framesCapacity: UInt64, _ framesWritten: UnsafeMutablePointer<UInt64>)\n#else\n@_silgen_name(\"FIRCLSWriteThreadStack\")\nfunc FIRCLSWriteThreadStack(_ thread: thread_t, _ frames: UnsafeMutablePointer<UInt64>, _ framesCapacity: UInt64, _ framesWritten: UnsafeMutablePointer<UInt64>)\n#endif\n"
  },
  {
    "path": "ETTrace/TracerSwift/UnsafeRawPointer+Commands.swift",
    "content": "//\n//  UnsafeRawPointer+Commands.swift\n//  Tracer\n//\n//  Created by Itay Brenner on 15/8/24.\n//\n\nimport Foundation\nimport MachO\n\nextension UnsafeRawPointer {\n  func numberOfCommands() -> (Int, UnsafeRawPointer)? {\n    let headerPointer = load(as: mach_header_64.self)\n    let headerSize: Int\n    if headerPointer.magic == MH_MAGIC_64 {\n      headerSize = MemoryLayout<mach_header_64>.size\n    } else {\n      return nil\n    }\n\n    return (Int(headerPointer.ncmds), advanced(by: headerSize))\n  }\n  \n  func processLoadComands(_ callback: (load_command, UnsafeRawPointer) -> Bool) {\n    var pointer: UnsafeRawPointer\n    guard let (numberOfCommands, headers) = numberOfCommands() else { return }\n\n    if numberOfCommands > 1000 {\n      print(\"Too many load commands\")\n      return\n    }\n\n    pointer = headers\n    for _ in 0..<numberOfCommands {\n      let command = pointer.load(as: load_command.self)\n      if !callback(command, pointer) {\n        break\n      }\n      pointer = pointer.advanced(by: Int(command.cmdsize))\n    }\n  }\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2023 Emerge Tools\n\nPermission 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:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE 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.\n"
  },
  {
    "path": "Package.resolved",
    "content": "{\n  \"pins\" : [\n    {\n      \"identity\" : \"peertalk\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/EmergeTools/peertalk.git\",\n      \"state\" : {\n        \"revision\" : \"02b8bebff91cd3d7a5d89c0ddb4bbd873c62879d\",\n        \"version\" : \"1.0.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-argument-parser\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-argument-parser.git\",\n      \"state\" : {\n        \"revision\" : \"fee6933f37fde9a5e12a1e4aeaa93fe60116ff2a\",\n        \"version\" : \"1.2.2\"\n      }\n    },\n    {\n      \"identity\" : \"swifter\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/httpswift/swifter.git\",\n      \"state\" : {\n        \"revision\" : \"9483a5d459b45c3ffd059f7b55f9638e268632fd\",\n        \"version\" : \"1.5.0\"\n      }\n    }\n  ],\n  \"version\" : 2\n}\n"
  },
  {
    "path": "Package.swift",
    "content": "// swift-tools-version: 5.9\n// The swift-tools-version declares the minimum version of Swift required to build this package.\n\nimport PackageDescription\n\nlet package = Package(\n    name: \"ETTrace\",\n    platforms: [.iOS(.v13), .macOS(.v12), .tvOS(.v13), .visionOS(.v1)],\n    products: [\n        .library(\n            name: \"ETTrace\",\n            type: .dynamic,\n            targets: [\"ETTrace\"]\n        ),\n        .library(name: \"Tracer\", targets: [\"Tracer\"]),\n        .library(name: \"Symbolicator\", targets: [\"Symbolicator\"]),\n        .executable(\n            name: \"ETTraceRunner\",\n            targets: [\"ETTraceRunner\"]\n        )\n    ],\n    dependencies: [\n        .package(url: \"https://github.com/EmergeTools/peertalk.git\", from: \"1.0.0\"),\n        .package(url: \"https://github.com/apple/swift-argument-parser.git\", from: \"1.2.0\"),\n        .package(url: \"https://github.com/httpswift/swifter.git\", from: \"1.5.0\")\n    ],\n    targets: [\n        .target(\n            name: \"ETTrace\",\n            dependencies: [\n                \"Tracer\",\n                \"CommunicationFrame\",\n                .product(name: \"Peertalk\", package: \"peertalk\")\n            ],\n            path: \"ETTrace/ETTrace\",\n            publicHeadersPath: \"Public\"\n        ),\n        .target(\n          name: \"Tracer\",\n          dependencies: [\n              \"Unwinding\",\n              \"TracerSwift\"\n          ],\n          path: \"ETTrace/Tracer\",\n          publicHeadersPath: \"Public\"\n        ),\n        .target(\n          name: \"TracerSwift\",\n          dependencies: [\n              \"Unwinding\",\n          ],\n          path: \"ETTrace/TracerSwift\"\n        ),\n        .target(name: \"Symbolicator\", dependencies: [\"ETModels\"], path: \"ETTrace/Symbolicator\"),\n        .target(\n            name: \"CommunicationFrame\",\n            path: \"ETTrace/CommunicationFrame\",\n            publicHeadersPath: \"Public\"\n        ),\n        .target(\n            name: \"Unwinding\",\n            dependencies: [],\n            path: \"Unwinding/Crashlytics\",\n            exclude: [\n                \"LICENSE\",\n                \"README.md\"\n            ],\n            publicHeadersPath: \"Public\"\n        ),\n        .executableTarget(\n            name: \"ETTraceRunner\",\n            dependencies: [\n                \"CommunicationFrame\",\n                \"JSONWrapper\",\n                \"ETModels\",\n                \"Symbolicator\",\n                .product(name: \"Peertalk\", package: \"peertalk\"),\n                .product(name: \"ArgumentParser\", package: \"swift-argument-parser\"),\n                .product(name: \"Swifter\", package: \"swifter\")\n            ],\n            path: \"ETTrace/ETTraceRunner\",\n            exclude: [\n                \"ETTraceRunner.entitlements\"\n            ]\n        ),\n        .target(\n            name: \"JSONWrapper\",\n            dependencies: [\n                \"ETModels\"\n            ],\n            path: \"ETTrace/JSONWrapper\",\n            publicHeadersPath: \"Public\"\n        ),\n        .target(\n            name: \"ETModels\",\n            dependencies: [],\n            path: \"ETTrace/ETModels\"\n        ),\n    ],\n    cxxLanguageStandard: .cxx17\n)\n"
  },
  {
    "path": "README.md",
    "content": "# ETTrace 👽\n\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FEmergeTools%2FETTrace%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/EmergeTools/ETTrace)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FEmergeTools%2FETTrace%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/EmergeTools/ETTrace)\n[![](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.emergetools.com%2Fapi%2Fv2%2Fpublic_new_build%3FexampleId%3Dettrace.ETTrace%26platform%3Dios%26badgeOption%3Dversion_and_max_install_size%26buildType%3Drelease&query=$.badgeMetadata&label=ETTrace&logo=apple)](https://www.emergetools.com/app/example/ios/ettrace.ETTrace/release?utm_campaign=badge-data)\n\nLocally measure performance of your app, without Xcode or Instruments. Read all about it in the [launch blog post](https://www.emergetools.com/blog/posts/ettrace-reliable-ios-profiling-with-flamecharts)\n\n![Example Flamechart](https://raw.githubusercontent.com/EmergeTools/ETTrace/master/images/example_flamechart.png)\n\n## Building and Installing\n\nETTrace has two components, the \"runner\" which is a command line tool, and a framework that your app links to.\n\nFirst, install the runner with `brew install emergetools/homebrew-tap/ettrace`. Then install the framework using one of the following methods:\n\n### Swift Package Manager\n\nAdd this repo as a swift package (either in Xcode or your Package.swift file) using the URL `https://github.com/EmergeTools/ETTrace`. Select \"ETTrace\" as the package product to add to your app.\n> [!WARNING]\n> Make sure to add the ETTrace package product to your app as shown in the screenshot below.\n\n![SPM Installation](https://raw.githubusercontent.com/EmergeTools/ETTrace/master/images/spm_installation.png)\n\n### Manual\n\nAlternatively, run `./build.sh` to build the xcframework `ETTrace.xcframework`. Link the xcframework to your app.\n\n> [!NOTE]\n> Linking the framework to your app is the only installation step, there are no code changes you need to make.\n\nIf everything is set up correctly \"Starting ETTrace\" will be printed to the console when you launch the app.\n\n## Testimonials\n\nOur users love ETTrace and find it essential in their iOS development process. Here’s what some of them have to say:\n\n- **Keith Smiley, Principal Engineer @ Lyft**: \"With ETTrace I can pinpoint exactly what is taking time in my app, and quickly find opportunities to improve it. It’s my go-to tool for in-depth performance analysis.\"\n\n- **Bruno Rocha, Software Engineer @ Spotify**: \"ETTrace is my favorite tool for diagnosing iOS performance, the flamechart view makes it easy to find bottlenecks and speed up app launch.\"\n\n## Using\n\nLaunch your app and run `ettrace` or `ettrace --simulator`. After profiling, the result will be displayed on https://emergetools.com/flamegraph\n\nNote: Always launch the app by manually tapping the icon on the iOS homescreen, running the app through Xcode can result in inaccurate results.\n\n## dSYMs\n\nYou can point `ettrace` to a folder containing your dsyms with the `--dsyms` flag. If the dsyms are indexed by spotlight they will be automatically found and used.\n\n## Run at Launch\n\nUse the flag `--launch` to start recording on app launch. When you first connect to the app using this flag, the app will force quit. On the next launch it will start profiling automatically and capture all of your `main` function. In some cases you need to record the first launch after an install. You can't use the `--launch` flag for this because that requires re-launching the app. Instead, add a boolean set to `YES` in your Info.plist with the key `ETTraceRunAtStartup`. You can then run `ettrace` regularly, without the `--launch` flag, and still start profiling at the start of app launch.\n\n## Under the hood\n\nETTrace spawns a new thread which captures a stacktrace of the main thread periodically. This is sampling based profiling. The sampling thread starts either when ettrace.framework is loaded (+load method), or when the CLI sends a message to the application. These control messages and the sampled data are communicated using PeerTalk.\n\n## Adding events to the flamechart\n\nETTrace supports displaying events in the flamechart from version v1.0. You can use them to track user flows in your code, you can easily add them by posting a notification in your code:\n```swift\nNotificationCenter.default.post(name: Notification.Name(rawValue: \"EmergeMetricStarted\" | \"EmergeMetricEnded\"), \n                                object: nil,\n                                userInfo: [\n    \"metric\": \"EVENT_NAME\"\n])\n```\nUse `EmergeMetricStarted` to register the start of your flow and `EmergeMetricEnded` to track it's end. \n\nFor example we would post this notification to track the start of an user login flow.\n```swift\nNotificationCenter.default.post(name: Notification.Name(rawValue: \"EmergeMetricStarted\"), \n                                object: nil,\n                                userInfo: [\n    \"metric\": \"USER_LOGIN\"\n])\n```\n\nAnd then this one when the user successfuly logins:\n```swift\nNotificationCenter.default.post(name: Notification.Name(rawValue: \"EmergeMetricEnded\"), \n                                object: nil,\n                                userInfo: [\n    \"metric\": \"USER_LOGIN\"\n])\n```\n\nIn the flamechart we would be able to see this flow as follows:\n![Events Flamechart](images/events_flamechart.png)\n\n## Profiling background threads\n\nUse the flag `--multi-thread` (`-m`) to record all threads. This will provide a output.json file for every thread recorded, which you can drag into ETTrace.\n"
  },
  {
    "path": "Unwinding/.gitignore",
    "content": "FirebaseAuth/Tests/Sample/Sample/Application.plist\nFirebaseAuth/Tests/Sample/Sample/AuthCredentials.h\nFirebaseAuth/Tests/Sample/Sample/GoogleService-Info_multi.plist\nFirebaseAuth/Tests/Sample/Sample/GoogleService-Info.plist\nFirebaseAuth/Tests/Sample/Sample/Sample.entitlements\nFirebaseAuth/Tests/Sample/ApiTests/AuthCredentials.h\nFirebaseAuth/Tests/Sample/SwiftApiTests/Credentials.swift\n\nFirebaseDatabase/Tests/Resources/GoogleService-Info.plist\n\nFirebaseRemoteConfig/Tests/Sample/GoogleService-Info.plist\n\n# FirebaseStorage integration tests GoogleService-Info.plist\nFirebaseStorage/Tests/Integration/Resources/GoogleService-Info.plist\n\n# FirebaseInstallations integration tests GoogleService-Info.plist\nFirebaseInstallations/Source/Tests/Resources/GoogleService-Info.plist\n\n# FirebaseMessaging integration tests GoogleService-Info.plist\nFirebaseMessaging/Tests/IntegrationTests/Resources/GoogleService-Info.plist\n# FirebaseMessaging test app GoogleService-Info.plist\nFirebaseMessaging/Apps/Shared/GoogleService-Info.plist\nFirebaseMessaging/Apps/AdvancedSample/SampleWatchWatchKitExtension/GoogleService-Info.plist\nFirebaseMessaging/Apps/AdvancedSample/AppClips/GoogleService-Info.plist\n\n# Credentials for Firebase Storage Integration Tests\nFirebaseStorage/Tests/Integration/Credentials.h\nFirebaseStorage/Tests/SwiftIntegration/Credentials.swift\nFirebaseStorageSwift/Tests/Integration/Credentials.swift\n\n# FirebaseMLModelDownloader integration tests GoogleService-Info.plist\nFirebaseMLModelDownloader/Tests/Integration/Resources/GoogleService-Info.plist\nFirebaseMLModelDownloader/Apps/Sample/**/GoogleService-Info.plist\n\n# FirebasePerformance dev test App and integration tests GoogleService-Info.plist\nFirebasePerformance/**/GoogleService-Info.plist\n\nSecrets.tar\n\n# OS X\n.DS_Store\n\n# Xcode\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata/\n*.xccheckout\nprofile\n*.moved-aside\nDerivedData\n*.hmap\n*.ipa\n\n# Swift Package Manager\nPackage.resolved\n*/.build\nReleaseTooling/.swiftpm\nReleaseTooling/Packages\nReleaseTooling/*.xcodeproj\nReleaseTooling/Package.resolved\nscripts/code_coverage_report/*/Package.resolved\nscripts/code_coverage_report/*/.build\n\n# Bad sorts get generated if the package .xcscheme is not regenerated.\n# Anything committed to xcshareddata gets propagated to clients. (#8167)\n.swiftpm/xcode/xcshareddata/\n\n# Mint package manager\nMint\n\n# IntelliJ\n.idea\n\n# Vim\n*.swo\n*.swp\n*~\n\n# Bundler\n/.bundle\n/vendor\n\nCarthage\n# Cocoapods recommends against adding the Pods directory to your .gitignore. See\n# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control\n\n# Since Firebase is building libraries, not apps, we should not check in Pods.\n# Pods are only used in the Examples and tests and doing a 'pod install' better\n# matches our customers' environments.\n#\n# Note: if you ignore the Pods directory, make sure to uncomment\n# `pod install` in .travis.yml\n#\nPods/\nPodfile.lock\n*.xcworkspace\n\n# CMake\n.downloads\nDebug\nRelease\nNinja\n\n# CLion\n/cmake-build-debug\n/cmake-build-release\n\n# Python\n*.pyc\n\n# Visual Studio\n/.vs\n\n# Visual Studio Code\n/.vscode\n\n# clangd support file\ncompile_commands.json\n\n# CocoaPods generate\ngen/\n\n# b/111916494\ndefault.profraw\n\n# Firestore emulator\ncloud-firestore-emulator.log\ncloud-firestore-emulator.pid\n\n# Let Gemfiles in CocoaPodsIntegrationTest float to catch issues asap\nCocoaPodsIntegrationTest/**/Gemfile.lock\nCocoaPodsIntegrationTest/Gemfile\nCocoaPodsIntegrationTest/Podfile\n\n# In-app messaging integration tests\nFirebaseInAppMessaging/Tests/Integration/FunctionalTestApp/GoogleService-Info.plist\nFirebaseInAppMessaging/Tests/Integration/FunctionalTestApp/App/InAppMessaging-Example-iOS/AppDelegate.m\n\n# FIRAppCheckTestApp\nFirebaseAppCheck/Apps/FIRAppCheckTestApp/FIRAppCheckTestApp/GoogleService-Info.plist\nFirebaseAppCheck/Apps/AppCheckCustomProvideApp/AppCheckCustomProvideApp/GoogleService-Info.plist\n\n# FirestoreSample\n/Example/FirestoreSample/FirestoreSample/GoogleService-Info.plist\n/Example/FirestoreSample/ui-debug.log\n/Example/FirestoreSample/firestore-debug.log\n"
  },
  {
    "path": "Unwinding/Crashlytics/CHANGELOG.md",
    "content": "# Unreleased\n- [fixed] Fixed an issue where passing nil as a value for a custom key or user ID did not clear the stored value as expected.\n\n# v8.9.0\n- [fixed] Fixed an issue where exceptions with `nil` reasons weren't properly recorded (#8671).\n\n# v8.8.0\n- [added] Internal SDK updates to test potential future MetricKit support.\n\n# v8.4.0\n- [fixed] Bump Promises dependency. (#8365)\n\n# v8.3.0\n- [fixed] Add missing dependency that could cause missing symbol build failures. (#8137)\n\n# v8.2.0\n- [changed] Incorporated code quality changes around integer overflow, potential race conditions, and reinstalling signal handlers.\n- [fixed] Fixed an issue where iOS-only apps running on iPads would report iOS as their OS Name.\n- [fixed] Fixed depcrecation warning for projects with minimum deployment version iOS 13 and up.\n\n# v8.0.0\n- [changed] Added a warning to upload-symbols when it detects a dSYM with hidden symbols.\n\n# v7.10.0\n- [changed] Added a warning to upload-symbols when it detects a dSYM without any symbols.\n\n# v7.9.0\n- [changed] Updated Firebase pod to allow iOS 9 installation via `pod 'Firebase/Crashlytics'`\n\n# v7.8.0\n- [added] Added a new API checkAndUpdateUnsentReportsWithCompletion for updating the crash report from the previous run of the app if, for example, the developer wants to implement a feedback dialog to ask end-users for more information. Unsent Crashlytics Reports have familiar methods like setting custom keys and logs (#7503).\n- [changed] Added a limit to the number of unsent reports on disk to prevent disk filling up when automatic data collection is off. Developers can ensure this limit is never reached by calling send/deleteUnsentReports every run (#7619).\n\n# v7.7.0\n- [added] Added a new API to allow for bulk logging of custom keys and values (#7302).\n\n# v7.6.0\n- [fixed] Fixed an issue where some developers experienced a race condition involving binary image operations (#7459).\n\n# v7.5.0\n- [changed] Improve start-up performance by moving some initialization work to a background thread (#7332).\n- [changed] Updated upload-symbols to a version that is notarized to avoid macOS security alerts (#7323).\n- [changed] Deleting unsent reports with deleteUnsentReports no longer happens on the main thread (#7298).\n\n# v7.4.0\n- [changed] Removed obsolete crash reporting mechanism from the SDK (#7076).\n\n# v7.3.0\n- [added] Added Crashlytics support for x86 apps running on Apple Silicon via Rosetta 2\n- [changed] Decreased Crashlytics CocoaPods minimum deployment target from iOS 10 to iOS 9\n- [changed] Removed obsolete API calls from upload-symbols\n- [changed] Removed obsolete onboarding calls from the SDK.\n\n# v7.1.0\n- [fixed] Fixed an issue where symbol uploads would fail when there are spaces in the project path, particularly in Unity builds (#6789).\n- [changed] Added additional logging when settings requests fail with a 404 status to help customers debug onboarding issues (#6847).\n\n# v4.6.2\n\n- [changed] Improved upload-symbols conversion speed. Customers with large dSYMs should see a significant improvement in the time it takes to upload Crashlytics symbols.\n- [fixed] Fixed Apple Watch crash related to `sigaction` (#6434).\n\n# v4.6.0\n\n- [added] Added stackFrameWithAddress API for recording custom errors that are symbolicated on the backend (#5975).\n- [fixed] Fixed comment typos (#6363).\n- [fixed] Remove device information from binary image data crash info entries (#6382).\n\n# v4.5.0\n\n- [fixed] Fixed a compiler warning and removed unused networking code (#6210).\n- [fixed] Fixed a crash that occurred rarely when trying to restart a URL session task without a valid request (#5984).\n- [added] Introduced watchOS support (#6262).\n\n# v4.3.1\n\n- [fixed] Fixed a segmentation fault that could occur when writing crash contexts to disk (#6048).\n\n# v4.3.0\n\n- [changed] Add dispatch_once for opening sdk log file. (#5904)\n- [changed] Functionally neutral updated import references for dependencies. (#5902)\n\n# v4.2.0\n\n- [changed] Removed an unnecessary linker rule for embedding the Info.plist. (#5804)\n\n# v4.1.1\n\n- [fixed] Fixed a crash that could occur if certain plist fields necessary to create Crashlytics records were missing at runtime. Also added some diagnostic logging to make the issue cause more explicit (#5565).\n\n# v4.1.0\n\n- [fixed] Fixed unchecked `malloc`s in Crashlytics (#5428).\n- [fixed] Fixed an instance of undefined behavior when loading files from disk (#5454).\n\n# v4.0.0\n\n - [changed] The Firebase Crashlytics SDK is now generally available.\n\n# v4.0.0-beta.7\n\n - [changed] Increased network timeout for symbol uploads to improve reliability on limited internet connections. (#5228)\n\n# v4.0.0-beta.6\n\n - [added] Added a new API to record custom exception models and stacktraces to Crashlytics. This is a replacement for the `recordCustomException` API that existed in the Fabric Crashlytics SDK (#5055)\n - [fixed] Fixed an issue with the `sendUnsentReports` API where reports wouldn't be uploaded until the method was called twice in specific instances (#5060)\n - [changed] Changed Crashlytics to use GoogleDataTransport to upload crashes (#4989)\n - [changed] Changed the origin that Crashlytics uses to register Crash events for Crash Free Users. Ensure you have installed Firebase Analytics version 6.3.1 or above (#5030)\n\n# v4.0.0-beta.5\n\n- [changed] Changed two endpoints in the Firebase Crashlytics SDK with no expected end-user impact (#4953, #4988).\n\n# v4.0.0-beta.4\n\n- [fixed] Fixed symbol collisions with the legacy Fabric Crashlytics SDK and added a warning not to include both (#4753, #4755)\n- [fixed] Added crash prevention checks (#4661)\n\n# v4.0.0-beta.3\n\n- [fixed] Fixed an import declaration for installing Crashlytics. Previously, the declaration caused a compile error when you installed using CocoaPods with the `generate_multiple_pods_project` flag set to true (#4786)\n\n# v4.0.0-beta.2\n\n- [fixed] Fixed VeraCode scanner issues for unchecked error conditions (#4669)\n\n# v4.0.0-beta.1\n\nThis Firebase Crashlytics version includes the initial beta release of the Firebase Crashlytics SDK:\n\n - [feature] The SDK is now open-sourced. Take a look in our [GitHub repository](https://github.com/firebase/firebase-ios-sdk/tree/master/Crashlytics).\n - [feature] Added support for Catalyst (note that Crashlytics still supports tvOS and macOS).\n - [feature] Added new APIs that are more consistent with other Firebase SDKs and more intuitive to use. The new APIs also give your users more control over how you collect their data.\n - [removed] Removed the Fabric API Key. Now, Crashlytics uses the GoogleService-Info.plist file to associate your app with your project. If you linked your app from Fabric and want to upgrade to the new SDK, remove the Fabric API key from your `run` and `upload-symbols` scripts. We also recommend removing the Fabric section from your app's Info.plist (when you upgrade, Crashlytics uses the new configuration you set up in Firebase).\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Components/FIRCLSEmerge.c",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"../Helpers/FIRCLSDefines.h\"\n#include \"../Helpers/FIRCLSThreadState.h\"\n#include \"../Unwind/FIRCLSUnwind.h\"\n#include \"../Helpers/FIRCLSUtility.h\"\n\n#include <mach/mach.h>\n#include <stdbool.h>\n#include <dispatch/dispatch.h>\n#include <objc/message.h>\n#include <pthread.h>\n#include <sys/sysctl.h>\n\n#define THREAD_NAME_BUFFER_SIZE (64)\n\nvoid FIRCLSWriteThreadStack(thread_t thread, uintptr_t *frames, uint64_t framesCapacity, uint64_t *framesWritten) {\n  *framesWritten = 0;\n  FIRCLSUnwindContext unwindContext;\n  FIRCLSThreadContext context;\n\n  // try to get the value by querying the thread state\n  mach_msg_type_number_t stateCount = FIRCLSThreadStateCount;\n\n  // For unknown reasons, thread_get_state returns this value on Rosetta,\n  // but still succeeds.\n  const int ROSETTA_SUCCESS = 268435459;\n  kern_return_t status = thread_get_state(thread, FIRCLSThreadState, (thread_state_t)(&(context.__ss)),\n                                   &stateCount);\n  if (status != KERN_SUCCESS && status != ROSETTA_SUCCESS) {\n    FIRCLSSDKLogError(\"Failed to get thread state via thread_get_state for thread: %i\\n\", thread);\n    *framesWritten = 0;\n    return;\n  }\n\n  if (!FIRCLSUnwindInit(&unwindContext, context)) {\n    FIRCLSSDKLog(\"Unable to init unwind context\\n\");\n    return;\n  }\n\n  uint32_t repeatedPCCount = 0;\n  uint64_t repeatedPC = 0;\n  while (FIRCLSUnwindNextFrame(&unwindContext) && (*framesWritten) < framesCapacity) {\n    const uintptr_t pc = FIRCLSUnwindGetPC(&unwindContext);\n    const uint32_t frameCount = FIRCLSUnwindGetFrameRepeatCount(&unwindContext);\n\n    if (repeatedPC == pc && repeatedPC != 0) {\n      // actively counting a recursion\n      repeatedPCCount = frameCount;\n      continue;\n    }\n\n    if (frameCount >= FIRCLSUnwindInfiniteRecursionCountThreshold && repeatedPC == 0) {\n      repeatedPC = pc;\n      continue;\n    }\n\n    frames[*framesWritten] = pc;\n    (*framesWritten)++;\n  }\n  return;\n}\n\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Components/FIRCLSGlobals.h",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"../Helpers/FIRCLSInternalLogging.h\"\n#include <dispatch/dispatch.h>\n\n__BEGIN_DECLS\n\n__END_DECLS\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <TargetConditionals.h>\n\n// macro trickiness\n#define CONCAT_EXPANDED(a, b) a##b\n#define CONCAT(a, b) CONCAT_EXPANDED(a, b)\n\n// These macros generate a function to force a symbol for the containing .o, to work around an issue\n// where strip will not strip debug information without a symbol to strip.\n#define DUMMY_FUNCTION_NAME(x) CONCAT(fircls_strip_this_, x)\n#define INJECT_STRIP_SYMBOL(x)        \\\n  void DUMMY_FUNCTION_NAME(x)(void) { \\\n  }\n\n// These make some target os types available to previous versions of xcode that do not yet have them\n// in their SDKs\n#ifndef TARGET_OS_IOS\n#define TARGET_OS_IOS TARGET_OS_IPHONE\n#endif\n\n#ifndef TARGET_OS_WATCH\n#define TARGET_OS_WATCH 0\n#endif\n\n#ifndef TARGET_OS_TV\n#define TARGET_OS_TV 0\n#endif\n\n// Whether MetricKit should be supported\n#if defined(__IPHONE_15_0)\n#define CLS_METRICKIT_SUPPORTED (__has_include(<MetricKit/MetricKit.h>) && TARGET_OS_IOS)\n#else\n#define CLS_METRICKIT_SUPPORTED 0\n#endif\n\n// These help compile based on availability of technologies/frameworks.\n#define CLS_TARGET_OS_OSX (TARGET_OS_MAC && !TARGET_OS_IPHONE)\n#define CLS_TARGET_OS_HAS_UIKIT (TARGET_OS_IOS || TARGET_OS_TV)\n\n// arch definitions\n#if defined(__arm__) || defined(__arm64__) || defined(__arm64e__)\n#include <arm/arch.h>\n#endif\n\n#if defined(__arm__)\n#define CLS_CPU_ARM 1\n#endif\n#if defined(__arm64__) || defined(__arm64e__)\n#define CLS_CPU_ARM64 1\n#endif\n#if defined(__ARM_ARCH_7S__)\n#define CLS_CPU_ARMV7S 1\n#endif\n#if defined(_ARM_ARCH_7)\n#define CLS_CPU_ARMV7 1\n#endif\n#if defined(_ARM_ARCH_6)\n#define CLS_CPU_ARMV6 1\n#endif\n#if defined(__i386__)\n#define CLS_CPU_I386 1\n#endif\n#if defined(__x86_64__)\n#define CLS_CPU_X86_64 1\n#endif\n#define CLS_CPU_X86 (CLS_CPU_I386 || CLS_CPU_X86_64)\n#define CLS_CPU_64BIT (CLS_CPU_X86_64 || CLS_CPU_ARM64)\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include \"FIRCLSDefines.h\"\n\n#define CLS_MEMORY_PROTECTION_ENABLED 1\n// #define CLS_COMPACT_UNWINDED_ENABLED 1\n#define CLS_DWARF_UNWINDING_ENABLED 1\n\n#define CLS_USE_SIGALTSTACK (!TARGET_OS_WATCH && !TARGET_OS_TV)\n#define CLS_CAN_SUSPEND_THREADS !TARGET_OS_WATCH\n#define CLS_MACH_EXCEPTION_SUPPORTED (!TARGET_OS_WATCH && !TARGET_OS_TV)\n#define CLS_SIGNAL_SUPPORTED !TARGET_OS_WATCH  // As of WatchOS 3, Signal crashes are not supported\n\n#define CLS_COMPACT_UNWINDING_SUPPORTED \\\n  ((CLS_CPU_I386 || CLS_CPU_X86_64 || CLS_CPU_ARM64) && CLS_COMPACT_UNWINDED_ENABLED)\n\n#define CLS_DWARF_UNWINDING_SUPPORTED \\\n  (CLS_COMPACT_UNWINDING_SUPPORTED && CLS_DWARF_UNWINDING_ENABLED)\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Helpers/FIRCLSInternalLogging.c",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <dispatch/dispatch.h>\n\n#include \"FIRCLSInternalLogging.h\"\n#include \"../Components/FIRCLSGlobals.h\"\n#include \"../Helpers/FIRCLSUtility.h\"\n\nvoid FIRCLSSDKFileLog(FIRCLSInternalLogLevel level, const char* format, ...) {\n  va_list args;\n  va_start(args, format);\n  va_end(args);\n}\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Helpers/FIRCLSInternalLogging.h",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdio.h>\n\n#if __OBJC__\n#import \"FIRCLSLogger.h\"\n#define FIRCLSDeveloperLog(label, __FORMAT__, ...) \\\n  FIRCLSDebugLog(@\"[\" label \"] \" __FORMAT__, ##__VA_ARGS__);\n#endif\n\ntypedef enum {\n  FIRCLSInternalLogLevelUnknown = 0,\n  FIRCLSInternalLogLevelDebug = 1,\n  FIRCLSInternalLogLevelInfo = 2,\n  FIRCLSInternalLogLevelWarn = 3,\n  FIRCLSInternalLogLevelError = 4\n} FIRCLSInternalLogLevel;\n\ntypedef struct {\n  int logFd;\n  FIRCLSInternalLogLevel logLevel;\n} FIRCLSInternalLoggingWritableContext;\n\n#define FIRCLSSDKLogDebug(__FORMAT__, ...)                                                 \\\n  FIRCLSSDKFileLog(FIRCLSInternalLogLevelDebug, \"DEBUG [%s:%d] \" __FORMAT__, __FUNCTION__, \\\n                   __LINE__, ##__VA_ARGS__)\n#define FIRCLSSDKLogInfo(__FORMAT__, ...)                                                 \\\n  FIRCLSSDKFileLog(FIRCLSInternalLogLevelInfo, \"INFO  [%s:%d] \" __FORMAT__, __FUNCTION__, \\\n                   __LINE__, ##__VA_ARGS__)\n#define FIRCLSSDKLogWarn(__FORMAT__, ...)                                                 \\\n  FIRCLSSDKFileLog(FIRCLSInternalLogLevelWarn, \"WARN  [%s:%d] \" __FORMAT__, __FUNCTION__, \\\n                   __LINE__, ##__VA_ARGS__)\n#define FIRCLSSDKLogError(__FORMAT__, ...)                                                 \\\n  FIRCLSSDKFileLog(FIRCLSInternalLogLevelError, \"ERROR [%s:%d] \" __FORMAT__, __FUNCTION__, \\\n                   __LINE__, ##__VA_ARGS__)\n\n#define FIRCLSSDKLog FIRCLSSDKLogWarn\n\n__BEGIN_DECLS\n\nvoid FIRCLSSDKFileLog(FIRCLSInternalLogLevel level, const char* format, ...) __printflike(2, 3);\n\n__END_DECLS\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Helpers/FIRCLSLogger.h",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import <Foundation/Foundation.h>\n\n__BEGIN_DECLS\n\nvoid FIRCLSDebugLog(NSString *message, ...);\nvoid FIRCLSInfoLog(NSString *message, ...);\nvoid FIRCLSWarningLog(NSString *message, ...);\nvoid FIRCLSErrorLog(NSString *message, ...);\n\n__END_DECLS\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Helpers/FIRCLSLogger.m",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import \"FIRCLSLogger.h\"\n\nNSString *const CrashlyticsMessageCode = @\"I-CLS000000\";\n\nvoid FIRCLSDebugLog(NSString *message, ...) {\n  va_list args_ptr;\n  va_start(args_ptr, message);\n  va_end(args_ptr);\n}\n\nvoid FIRCLSInfoLog(NSString *message, ...) {\n  va_list args_ptr;\n  va_start(args_ptr, message);\n  va_end(args_ptr);\n}\n\nvoid FIRCLSWarningLog(NSString *message, ...) {\n  va_list args_ptr;\n  va_start(args_ptr, message);\n  va_end(args_ptr);\n}\n\nvoid FIRCLSErrorLog(NSString *message, ...) {\n  va_list args_ptr;\n  va_start(args_ptr, message);\n  va_end(args_ptr);\n}\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Helpers/FIRCLSThreadState.c",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"FIRCLSThreadState.h\"\n#include \"FIRCLSDefines.h\"\n#include \"FIRCLSUtility.h\"\n\n#if defined(__arm__) || defined(__arm64__)\n#include <mach/arm/thread_status.h>\n#include <ptrauth.h>\n#endif\n\n#if CLS_CPU_X86_64\n#define GET_IP_REGISTER(r) (r->__ss.__rip)\n#define GET_FP_REGISTER(r) (r->__ss.__rbp)\n#define GET_SP_REGISTER(r) (r->__ss.__rsp)\n#define GET_LR_REGISTER(r) 0\n#define SET_IP_REGISTER(r, v) (r->__ss.__rip = v)\n#define SET_FP_REGISTER(r, v) (r->__ss.__rbp = v)\n#define SET_SP_REGISTER(r, v) (r->__ss.__rsp = v)\n#define SET_LR_REGISTER(r, v)\n#elif CLS_CPU_I386\n#define GET_IP_REGISTER(r) (r->__ss.__eip)\n#define GET_FP_REGISTER(r) (r->__ss.__ebp)\n#define GET_SP_REGISTER(r) (r->__ss.__esp)\n#define GET_LR_REGISTER(r) 0\n#define SET_IP_REGISTER(r, v) (r->__ss.__eip = v)\n#define SET_FP_REGISTER(r, v) (r->__ss.__ebp = v)\n#define SET_SP_REGISTER(r, v) (r->__ss.__esp = v)\n#define SET_LR_REGISTER(r, v)\n#elif CLS_CPU_ARM64\n// The arm_thread_state64_get_* macros translate down to the AUTIA and AUTIB instructions which\n// authenticate the address, but don't clear the upper bits. From the docs:\n//      \"If the authentication passes, the upper bits of the address are restored to enable\n//      subsequent use of the address. the authentication fails, the upper bits are corrupted and\n//      any subsequent use of the address results in a Translation fault.\"\n// Since we only want the address (with the metadata in the upper bits masked out), we used the\n// ptrauth_strip macro to clear the upper bits.\n//\n// We found later that ptrauth_strip doesn't seem to do anything. In many cases, the upper bits were\n// already stripped, so for most non-system-library code, Crashlytics would still symbolicate. But\n// for system libraries, the upper bits were being left in even when we called ptrauth_strip.\n// Instead, we're bit masking and only allowing the latter 36 bits.\n#define CLS_PTRAUTH_STRIP(pointer) ((uintptr_t)pointer & 0x0000000FFFFFFFFF)\n#define GET_IP_REGISTER(r) (CLS_PTRAUTH_STRIP(arm_thread_state64_get_pc(r->__ss)))\n#define GET_FP_REGISTER(r) (CLS_PTRAUTH_STRIP(arm_thread_state64_get_fp(r->__ss)))\n#define GET_SP_REGISTER(r) (CLS_PTRAUTH_STRIP(arm_thread_state64_get_sp(r->__ss)))\n#define GET_LR_REGISTER(r) (CLS_PTRAUTH_STRIP(arm_thread_state64_get_lr(r->__ss)))\n#define SET_IP_REGISTER(r, v) arm_thread_state64_set_pc_fptr(r->__ss, (void*)v)\n#define SET_FP_REGISTER(r, v) arm_thread_state64_set_fp(r->__ss, v)\n#define SET_SP_REGISTER(r, v) arm_thread_state64_set_sp(r->__ss, v)\n#define SET_LR_REGISTER(r, v) arm_thread_state64_set_lr_fptr(r->__ss, (void*)v)\n#elif CLS_CPU_ARM\n#define GET_IP_REGISTER(r) (r->__ss.__pc)\n#define GET_FP_REGISTER(r) (r->__ss.__r[7])\n#define GET_SP_REGISTER(r) (r->__ss.__sp)\n#define GET_LR_REGISTER(r) (r->__ss.__lr)\n#define SET_IP_REGISTER(r, v) (r->__ss.__pc = v)\n#define SET_FP_REGISTER(r, v) (r->__ss.__r[7] = v)\n#define SET_SP_REGISTER(r, v) (r->__ss.__sp = v)\n#define SET_LR_REGISTER(r, v) (r->__ss.__lr = v)\n#else\n#error \"Architecture Unsupported\"\n#endif\n\nuintptr_t FIRCLSThreadContextGetPC(FIRCLSThreadContext* registers) {\n  if (!registers) {\n    return 0;\n  }\n\n  return GET_IP_REGISTER(registers);\n}\n\nuintptr_t FIRCLSThreadContextGetStackPointer(const FIRCLSThreadContext* registers) {\n  if (!registers) {\n    return 0;\n  }\n\n  return GET_SP_REGISTER(registers);\n}\n\nbool FIRCLSThreadContextSetStackPointer(FIRCLSThreadContext* registers, uintptr_t value) {\n  if (!FIRCLSIsValidPointer(registers)) {\n    return false;\n  }\n\n  SET_SP_REGISTER(registers, value);\n\n  return true;\n}\n\nuintptr_t FIRCLSThreadContextGetLinkRegister(const FIRCLSThreadContext* registers) {\n  if (!FIRCLSIsValidPointer(registers)) {\n    return 0;\n  }\n\n  return GET_LR_REGISTER(registers);\n}\n\nbool FIRCLSThreadContextSetLinkRegister(FIRCLSThreadContext* registers, uintptr_t value) {\n  if (!FIRCLSIsValidPointer(registers)) {\n    return false;\n  }\n\n  SET_LR_REGISTER(registers, value);\n\n  return true;\n}\n\nbool FIRCLSThreadContextSetPC(FIRCLSThreadContext* registers, uintptr_t value) {\n  if (!registers) {\n    return false;\n  }\n\n  SET_IP_REGISTER(registers, value);\n\n  return true;\n}\n\nuintptr_t FIRCLSThreadContextGetFramePointer(const FIRCLSThreadContext* registers) {\n  if (!FIRCLSIsValidPointer(registers)) {\n    return 0;\n  }\n\n  return GET_FP_REGISTER(registers);\n}\n\nbool FIRCLSThreadContextSetFramePointer(FIRCLSThreadContext* registers, uintptr_t value) {\n  if (!FIRCLSIsValidPointer(registers)) {\n    return false;\n  }\n\n  SET_FP_REGISTER(registers, value);\n\n  return true;\n}\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Helpers/FIRCLSThreadState.h",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdbool.h>\n#include <sys/ucontext.h>\n\n#if CLS_CPU_ARM\n#define FIRCLSThreadStateCount ARM_THREAD_STATE_COUNT\n#define FIRCLSThreadState ARM_THREAD_STATE\n#elif CLS_CPU_ARM64\n#define FIRCLSThreadStateCount ARM_THREAD_STATE64_COUNT\n#define FIRCLSThreadState ARM_THREAD_STATE64\n#elif CLS_CPU_I386\n#define FIRCLSThreadStateCount x86_THREAD_STATE32_COUNT\n#define FIRCLSThreadState x86_THREAD_STATE32\n#elif CLS_CPU_X86_64\n#define FIRCLSThreadStateCount x86_THREAD_STATE64_COUNT\n#define FIRCLSThreadState x86_THREAD_STATE64\n#endif\n\n// _STRUCT_MCONTEXT was fixed to point to the right thing on ARM in the iOS 7.1 SDK\ntypedef _STRUCT_MCONTEXT FIRCLSThreadContext;\n\n// I'm not entirely sure what happened when, but this appears to have disappeared from\n// the SDKs...\n#if !defined(_STRUCT_UCONTEXT64)\ntypedef _STRUCT_UCONTEXT _STRUCT_UCONTEXT64;\n#endif\n\n#pragma mark Register Access\n\nuintptr_t FIRCLSThreadContextGetPC(FIRCLSThreadContext* registers);\nuintptr_t FIRCLSThreadContextGetStackPointer(const FIRCLSThreadContext* registers);\nuintptr_t FIRCLSThreadContextGetFramePointer(const FIRCLSThreadContext* registers);\n\nbool FIRCLSThreadContextSetPC(FIRCLSThreadContext* registers, uintptr_t value);\nbool FIRCLSThreadContextSetStackPointer(FIRCLSThreadContext* registers, uintptr_t value);\nbool FIRCLSThreadContextSetFramePointer(FIRCLSThreadContext* registers, uintptr_t value);\n\n// The link register only exists on ARM platforms.\n#if CLS_CPU_ARM || CLS_CPU_ARM64\nuintptr_t FIRCLSThreadContextGetLinkRegister(const FIRCLSThreadContext* registers);\nbool FIRCLSThreadContextSetLinkRegister(FIRCLSThreadContext* registers, uintptr_t value);\n#endif\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <mach/vm_types.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include \"../Components/FIRCLSGlobals.h\"\n\n#define FIRCLSIsValidPointer(x) ((uintptr_t)x >= 4096)\n#define FIRCLSInvalidCharNybble (255)\n\n__BEGIN_DECLS\n\nvoid FIRCLSLookupFunctionPointer(void* ptr, void (^block)(const char* name, const char* lib));\n\nvoid FIRCLSHexFromByte(uint8_t c, char output[]);\nuint8_t FIRCLSNybbleFromChar(char c);\n\nbool FIRCLSReadMemory(vm_address_t src, void* dest, size_t len);\nbool FIRCLSReadString(vm_address_t src, char** dest, size_t maxlen);\n\nbool FIRCLSUnlinkIfExists(const char* path);\nvoid FIRCLSRedactUUID(char* value);\n\n#if __OBJC__\nvoid FIRCLSDispatchAfter(float timeInSeconds, dispatch_queue_t queue, dispatch_block_t block);\n\nNSString* FIRCLSNormalizeUUID(NSString* value);\n\nvoid FIRCLSAddOperationAfter(float timeInSeconds, NSOperationQueue* queue, void (^block)(void));\n#endif\n\n#if DEBUG\nvoid FIRCLSPrintAUUID(const uint8_t* value);\n#endif\n\n__END_DECLS\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Helpers/FIRCLSUtility.m",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"FIRCLSUtility.h\"\n\n#include <mach/mach.h>\n\n#include <dlfcn.h>\n\n#include \"../Components/FIRCLSGlobals.h\"\n#include \"FIRCLSFeatures.h\"\n\n#import <CommonCrypto/CommonHMAC.h>\n\nvoid FIRCLSLookupFunctionPointer(void* ptr, void (^block)(const char* name, const char* lib)) {\n  Dl_info info;\n\n  if (dladdr(ptr, &info) == 0) {\n    block(NULL, NULL);\n    return;\n  }\n\n  const char* name = \"unknown\";\n  const char* lib = \"unknown\";\n\n  if (info.dli_sname) {\n    name = info.dli_sname;\n  }\n\n  if (info.dli_fname) {\n    lib = info.dli_fname;\n  }\n\n  block(name, lib);\n}\n\nuint8_t FIRCLSNybbleFromChar(char c) {\n  if (c >= '0' && c <= '9') {\n    return c - '0';\n  }\n\n  if (c >= 'a' && c <= 'f') {\n    return c - 'a' + 10;\n  }\n\n  if (c >= 'A' && c <= 'F') {\n    return c - 'A' + 10;\n  }\n\n  return FIRCLSInvalidCharNybble;\n}\n\nbool FIRCLSReadMemory(vm_address_t src, void* dest, size_t len) {\n  if (!FIRCLSIsValidPointer(src)) {\n    return false;\n  }\n\n  vm_size_t readSize = len;\n\n  // Originally this was a `vm_read_overwrite` to protect against reading invalid memory.\n  // That can happen in the context of a crash reporter, but should not happen during normal\n  // ettrace operation. Replacing it with memcpy makes this about 5x faster\n  // return vm_read_overwrite(mach_task_self(), src, len, (pointer_t)dest, &readSize) == KERN_SUCCESS;\n  memcpy(dest, src, len);\n  return true;\n}\n\nbool FIRCLSReadString(vm_address_t src, char** dest, size_t maxlen) {\n  char c;\n  vm_address_t address;\n\n  if (!dest) {\n    return false;\n  }\n\n  // Walk the entire string.  Not certain this is perfect...\n  for (address = src; address < src + maxlen; ++address) {\n    if (!FIRCLSReadMemory(address, &c, 1)) {\n      return false;\n    }\n\n    if (c == 0) {\n      break;\n    }\n  }\n\n  *dest = (char*)src;\n\n  return true;\n}\n\nvoid FIRCLSDispatchAfter(float timeInSeconds, dispatch_queue_t queue, dispatch_block_t block) {\n  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeInSeconds * NSEC_PER_SEC)), queue,\n                 block);\n}\n\nbool FIRCLSUnlinkIfExists(const char* path) {\n  if (unlink(path) != 0) {\n    if (errno != ENOENT) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\nNSString* FIRCLSNormalizeUUID(NSString* value) {\n  return [[value stringByReplacingOccurrencesOfString:@\"-\" withString:@\"\"] lowercaseString];\n}\n\n// Redacts a UUID wrapped in parenthesis from a char* using strchr, which is async safe.\n// Ex.\n//   \"foo (bar) (45D62CC2-CFB5-4E33-AB61-B0684627F1B6) baz\"\n// becomes\n//   \"foo (bar) (********-****-****-****-************) baz\"\nvoid FIRCLSRedactUUID(char* value) {\n  if (value == NULL) {\n    return;\n  }\n  char* openParen = value;\n  // find the index of the first paren\n  while ((openParen = strchr(openParen, '(')) != NULL) {\n    // find index of the matching close paren\n    const char* closeParen = strchr(openParen, ')');\n    if (closeParen == NULL) {\n      break;\n    }\n    // if the distance between them is 37, traverse the characters\n    // and replace anything that is not a '-' with '*'\n    if (closeParen - openParen == 37) {\n      for (int i = 1; i < 37; ++i) {\n        if (*(openParen + i) != '-') {\n          *(openParen + i) = '*';\n        }\n      }\n      break;\n    }\n    openParen++;\n  }\n}\n\nvoid FIRCLSAddOperationAfter(float timeInSeconds, NSOperationQueue* queue, void (^block)(void)) {\n  dispatch_queue_t afterQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);\n  FIRCLSDispatchAfter(timeInSeconds, afterQueue, ^{\n    [queue addOperationWithBlock:block];\n  });\n}\n\n#if DEBUG\nvoid FIRCLSPrintAUUID(const uint8_t* value) {\n  CFUUIDRef uuid = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, *(CFUUIDBytes*)value);\n\n  NSString* string = CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid));\n\n  CFRelease(uuid);\n\n  FIRCLSDebugLog(@\"%@\", [[string stringByReplacingOccurrencesOfString:@\"-\"\n                                                           withString:@\"\"] lowercaseString]);\n}\n#endif\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind.c",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"FIRCLSUnwind.h\"\n#include \"../Helpers/FIRCLSFeatures.h\"\n#include \"../Components/FIRCLSGlobals.h\"\n#include \"../Helpers/FIRCLSUtility.h\"\n\n#include <mach/mach.h>\n#include <signal.h>\n#include <stdio.h>\n\n// Without a limit on the number of frames we unwind, there's a real possibility\n// we'll get stuck in an infinite loop. But, we still need pretty big limits,\n// because stacks can get quite big. Also, the stacks are different on the platforms.\n// These values were empirically determined (~525000 on OS X, ~65000 on iOS).\n#if TARGET_OS_EMBEDDED\nconst uint32_t FIRCLSUnwindMaxFrames = 100000;\n#else\nconst uint32_t FIRCLSUnwindMaxFrames = 600000;\n#endif\n\nconst uint32_t FIRCLSUnwindInfiniteRecursionCountThreshold = 10;\n\n#pragma mark Prototypes\nstatic bool FIRCLSUnwindNextFrameUsingAllStrategies(FIRCLSUnwindContext* context);\n#if CLS_COMPACT_UNWINDING_SUPPORTED\nstatic bool FIRCLSUnwindWithCompactUnwindInfo(FIRCLSUnwindContext* context);\n#endif\nbool FIRCLSUnwindContextHasValidPCAndSP(FIRCLSUnwindContext* context);\n\n#pragma mark - API\nbool FIRCLSUnwindInit(FIRCLSUnwindContext* context, FIRCLSThreadContext threadContext) {\n  if (!context) {\n    return false;\n  }\n\n  memset(context, 0, sizeof(FIRCLSUnwindContext));\n\n  context->registers = threadContext;\n\n  return true;\n}\n\nbool FIRCLSUnwindNextFrame(FIRCLSUnwindContext* context) {\n  if (!FIRCLSIsValidPointer(context)) {\n    FIRCLSSDKLog(\"Error: invalid inputs\\n\");\n    return false;\n  }\n\n  if (!FIRCLSUnwindContextHasValidPCAndSP(context)) {\n    // This is a special-case. It is possible to try to unwind a thread that has no stack (ie, is\n    // executing zero functions. I believe this happens when a thread has exited, but before the\n    // kernel has actually cleaned it up. This situation can only apply to the first frame. So, in\n    // that case, we don't count it as an error. But, if it happens mid-unwind, it's a problem.\n\n    if (context->frameCount == 0) {\n      FIRCLSSDKLog(\"Cancelling unwind for thread with invalid PC/SP\\n\");\n    } else {\n      FIRCLSSDKLog(\"Error: thread PC/SP invalid before unwind\\n\");\n    }\n\n    return false;\n  }\n\n  if (!FIRCLSUnwindNextFrameUsingAllStrategies(context)) {\n    FIRCLSSDKLogError(\"Failed to advance to the next frame\\n\");\n    return false;\n  }\n\n  uintptr_t pc = FIRCLSUnwindGetPC(context);\n  uintptr_t sp = FIRCLSUnwindGetStackPointer(context);\n\n  // Unwinding will complete when this is no longer a valid value\n  if (!FIRCLSIsValidPointer(pc)) {\n    return false;\n  }\n\n  // after unwinding, validate that we have a sane register value\n  if (!FIRCLSIsValidPointer(sp)) {\n    FIRCLSSDKLog(\"Error: SP (%p) isn't a valid pointer\\n\", (void*)sp);\n    return false;\n  }\n\n  // track repeating frames\n  if (context->lastFramePC == pc) {\n    context->repeatCount += 1;\n  } else {\n    context->repeatCount = 0;\n  }\n\n  context->frameCount += 1;\n  context->lastFramePC = pc;\n\n  return true;\n}\n\n#pragma mark - Register Accessors\nuintptr_t FIRCLSUnwindGetPC(FIRCLSUnwindContext* context) {\n  if (!FIRCLSIsValidPointer(context)) {\n    return 0;\n  }\n\n  return FIRCLSThreadContextGetPC(&context->registers);\n}\n\nuintptr_t FIRCLSUnwindGetStackPointer(FIRCLSUnwindContext* context) {\n  if (!FIRCLSIsValidPointer(context)) {\n    return 0;\n  }\n\n  return FIRCLSThreadContextGetStackPointer(&context->registers);\n}\n\nstatic uintptr_t FIRCLSUnwindGetFramePointer(FIRCLSUnwindContext* context) {\n  if (!FIRCLSIsValidPointer(context)) {\n    return 0;\n  }\n\n  return FIRCLSThreadContextGetFramePointer(&context->registers);\n}\n\nuint32_t FIRCLSUnwindGetFrameRepeatCount(FIRCLSUnwindContext* context) {\n  if (!FIRCLSIsValidPointer(context)) {\n    return 0;\n  }\n\n  return context->repeatCount;\n}\n\n#pragma mark - Unwind Strategies\nstatic bool FIRCLSUnwindNextFrameUsingAllStrategies(FIRCLSUnwindContext* context) {\n  if (!FIRCLSIsValidPointer(context)) {\n    FIRCLSSDKLogError(\"Arguments invalid\\n\");\n    return false;\n  }\n\n  if (context->frameCount >= FIRCLSUnwindMaxFrames) {\n    FIRCLSSDKLogWarn(\"Exceeded maximum number of frames\\n\");\n    return false;\n  }\n\n  uintptr_t pc = FIRCLSUnwindGetPC(context);\n\n  // Ok, what's going on here? libunwind's UnwindCursor<A,R>::setInfoBasedOnIPRegister has a\n  // parameter that, if true, does this subtraction. Despite the comments in the code\n  // (of 35.1), I found that the parameter was almost always set to true.\n  //\n  // I then ran into a problem when unwinding from _pthread_start -> thread_start. This\n  // is a common transition, which happens in pretty much every report. An extra frame\n  // was being generated, because the PC we get for _pthread_start was mapping to exactly\n  // one greater than the function's last byte, according to the compact unwind info. This\n  // resulted in using the wrong compact encoding, and picking the next function, which\n  // turned out to be dwarf instead of a frame pointer.\n\n  // So, the moral is - do the subtraction for all frames except the first. I haven't found\n  // a case where it produces an incorrect result. Also note that at first, I thought this would\n  // subtract one from the final addresses too. But, the end of this function will *compute* PC,\n  // so this value is used only to look up unwinding data.\n\n  if (context->frameCount > 0) {\n    --pc;\n    if (!FIRCLSThreadContextSetPC(&context->registers, pc)) {\n      FIRCLSSDKLogError(\"Unable to set PC\\n\");\n      return false;\n    }\n  }\n\n  if (!FIRCLSIsValidPointer(pc)) {\n    FIRCLSSDKLogError(\"PC is invalid\\n\");\n    return false;\n  }\n\n  // the first frame is special - as the registers we need\n  // are already loaded by definition\n  if (context->frameCount == 0) {\n    return true;\n  }\n\n#if CLS_COMPACT_UNWINDING_SUPPORTED\n  // attempt to advance to the next frame using compact unwinding, and\n  // only fall back to the frame pointer if that fails\n  if (FIRCLSUnwindWithCompactUnwindInfo(context)) {\n    return true;\n  }\n#endif\n\n  // If the frame pointer is zero, we cannot use an FP-based unwind and we can reasonably\n  // assume that we've just gotten to the end of the stack.\n  if (FIRCLSUnwindGetFramePointer(context) == 0) {\n    FIRCLSSDKLogWarn(\"FP is zero, aborting unwind\\n\");\n    // make sure to set the PC to zero, to indicate the unwind is complete\n    return FIRCLSThreadContextSetPC(&context->registers, 0);\n  }\n\n  // Only allow stack scanning (as a last resort) if we're on the first frame. All others\n  // are too likely to screw up.\n  if (FIRCLSUnwindWithFramePointer(&context->registers, context->frameCount == 1)) {\n    return true;\n  }\n\n  FIRCLSSDKLogError(\"Unable to use frame pointer\\n\");\n\n  return false;\n}\n\n#if CLS_COMPACT_UNWINDING_SUPPORTED\nstatic bool FIRCLSUnwindWithCompactUnwindInfo(FIRCLSUnwindContext* context) {\n  if (!context) {\n    return false;\n  }\n\n  // step one - find the image the current pc is within\n  FIRCLSBinaryImageRuntimeNode image;\n\n  uintptr_t pc = FIRCLSUnwindGetPC(context);\n\n  if (!FIRCLSBinaryImageSafeFindImageForAddress(pc, &image)) {\n    FIRCLSSDKLogWarn(\"Unable to find binary for %p\\n\", (void*)pc);\n    return false;\n  }\n\n#if CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME\n  FIRCLSSDKLogDebug(\"Binary image for %p at %p => %s\\n\", (void*)pc, image.baseAddress, image.name);\n#else\n  FIRCLSSDKLogDebug(\"Binary image for %p at %p\\n\", (void*)pc, image.baseAddress);\n#endif\n\n  if (!FIRCLSBinaryImageSafeHasUnwindInfo(&image)) {\n    FIRCLSSDKLogInfo(\"Binary image at %p has no unwind info\\n\", image.baseAddress);\n    return false;\n  }\n\n  if (!FIRCLSCompactUnwindInit(&context->compactUnwindState, image.unwindInfo, image.ehFrame,\n                               (uintptr_t)image.baseAddress)) {\n    FIRCLSSDKLogError(\"Unable to read unwind info\\n\");\n    return false;\n  }\n\n  // this function will actually attempt to find compact unwind info for the current PC,\n  // and use it to mutate the context register state\n  return FIRCLSCompactUnwindLookupAndCompute(&context->compactUnwindState, &context->registers);\n}\n#endif\n\n#pragma mark - Utility Functions\nbool FIRCLSUnwindContextHasValidPCAndSP(FIRCLSUnwindContext* context) {\n  return FIRCLSIsValidPointer(FIRCLSUnwindGetPC(context)) &&\n         FIRCLSIsValidPointer(FIRCLSUnwindGetStackPointer(context));\n}\n\n#if CLS_CPU_64BIT\n#define BASIC_INFO_TYPE vm_region_basic_info_64_t\n#define BASIC_INFO VM_REGION_BASIC_INFO_64\n#define BASIC_INFO_COUNT VM_REGION_BASIC_INFO_COUNT_64\n#define vm_region_query_fn vm_region_64\n#else\n#define BASIC_INFO_TYPE vm_region_basic_info_t\n#define BASIC_INFO VM_REGION_BASIC_INFO\n#define BASIC_INFO_COUNT VM_REGION_BASIC_INFO_COUNT\n#define vm_region_query_fn vm_region\n#endif\nbool FIRCLSUnwindIsAddressExecutable(vm_address_t address) {\n#if CLS_COMPACT_UNWINDING_SUPPORTED\n  FIRCLSBinaryImageRuntimeNode unusedNode;\n\n  return FIRCLSBinaryImageSafeFindImageForAddress(address, &unusedNode);\n#else\n  return true;\n#endif\n}\n\nbool FIRCLSUnwindFirstExecutableAddress(vm_address_t start,\n                                        vm_address_t end,\n                                        vm_address_t* foundAddress) {\n  // This function walks up the data on the stack, looking for the first value that is an address on\n  // an exectuable page.  This is a heurestic, and can hit false positives.\n\n  *foundAddress = 0;  // write in a 0\n\n  do {\n    vm_address_t address;\n\n    FIRCLSSDKLogDebug(\"Checking address %p => %p\\n\", (void*)start, (void*)*(uintptr_t*)start);\n\n    // if start isn't a valid pointer, don't even bother trying\n    if (FIRCLSIsValidPointer(start)) {\n      if (!FIRCLSReadMemory(start, &address, sizeof(void*))) {\n        // if we fail to read from the stack, we're done\n        return false;\n      }\n\n      FIRCLSSDKLogDebug(\"Checking for executable %p\\n\", (void*)address);\n      // when we find an exectuable address, we're finished\n      if (FIRCLSUnwindIsAddressExecutable(address)) {\n        *foundAddress = address;\n        return true;\n      }\n    }\n\n    start += sizeof(void*);  // move back up the stack\n\n  } while (start < end);\n\n  return false;\n}\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind.h",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include \"../Helpers/FIRCLSThreadState.h\"\n#include \"../Helpers/FIRCLSUtility.h\"\n#if CLS_COMPACT_UNWINDING_SUPPORTED\n#include \"../Unwind/Compact/FIRCLSCompactUnwind.h\"\n#endif\n#include <mach/vm_types.h>\n#include <stdbool.h>\n\n#include \"FIRCLSUnwind_arch.h\"\n\nextern const uint32_t FIRCLSUnwindMaxFrames;\n\nextern const uint32_t FIRCLSUnwindInfiniteRecursionCountThreshold;\n\ntypedef struct {\n  FIRCLSThreadContext registers;\n  uint32_t frameCount;\n#if CLS_COMPACT_UNWINDING_SUPPORTED\n  FIRCLSCompactUnwindContext compactUnwindState;\n#endif\n  uintptr_t lastFramePC;\n  uint32_t repeatCount;\n} FIRCLSUnwindContext;\n\n// API\nbool FIRCLSUnwindInit(FIRCLSUnwindContext* context, FIRCLSThreadContext threadContext);\n\nbool FIRCLSUnwindNextFrame(FIRCLSUnwindContext* context);\nuintptr_t FIRCLSUnwindGetPC(FIRCLSUnwindContext* context);\nuintptr_t FIRCLSUnwindGetStackPointer(FIRCLSUnwindContext* context);\nuint32_t FIRCLSUnwindGetFrameRepeatCount(FIRCLSUnwindContext* context);\n\n// utility functions\nbool FIRCLSUnwindIsAddressExecutable(vm_address_t address);\nbool FIRCLSUnwindFirstExecutableAddress(vm_address_t start,\n                                        vm_address_t end,\n                                        vm_address_t* foundAddress);\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_arch.h",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include \"../Helpers/FIRCLSFeatures.h\"\n#include \"../Helpers/FIRCLSThreadState.h\"\n#if CLS_COMPACT_UNWINDING_SUPPORTED\n#include \"../Unwind/Compact/FIRCLSCompactUnwind.h\"\n#endif\n\nbool FIRCLSUnwindWithFramePointer(FIRCLSThreadContext *registers, bool allowScanning);\nuintptr_t FIRCLSUnwindStackPointerFromFramePointer(uintptr_t framePtr);\n\n#if CLS_DWARF_UNWINDING_SUPPORTED\nuintptr_t FIRCLSCompactUnwindDwarfOffset(compact_unwind_encoding_t encoding);\nbool FIRCLSDwarfUnwindSetRegisterValue(FIRCLSThreadContext *registers,\n                                       uint64_t num,\n                                       uintptr_t value);\nuintptr_t FIRCLSDwarfUnwindGetRegisterValue(const FIRCLSThreadContext *registers, uint64_t num);\n#endif\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_arm.c",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"../Helpers/FIRCLSDefines.h\"\n#include \"../Helpers/FIRCLSFeatures.h\"\n#include \"FIRCLSUnwind.h\"\n#include \"FIRCLSUnwind_arch.h\"\n#include \"../Helpers/FIRCLSUtility.h\"\n\n#if CLS_CPU_ARM || CLS_CPU_ARM64\n\nstatic bool FIRCLSUnwindWithLRRegister(FIRCLSThreadContext* registers) {\n  if (!FIRCLSIsValidPointer(registers)) {\n    return false;\n  }\n\n  // Return address is in LR, SP is pointing to the next frame.\n  uintptr_t value = FIRCLSThreadContextGetLinkRegister(registers);\n\n  if (!FIRCLSIsValidPointer(value)) {\n    FIRCLSSDKLog(\"Error: LR value is invalid\\n\");\n    return false;\n  }\n\n  return FIRCLSThreadContextSetPC(registers, value);\n}\n\nbool FIRCLSUnwindWithFramePointer(FIRCLSThreadContext* registers, bool allowScanning) {\n  if (allowScanning) {\n    // The LR register does have the return address here, but there are situations where\n    // this can produce false matches. Better backend rules can fix this up in many cases.\n    if (FIRCLSUnwindWithLRRegister(registers)) {\n      return true;\n    } else {\n      // In this case, we're unable to use the LR. We don't want to just stop unwinding, so\n      // proceed with the normal, non-scanning path\n      FIRCLSSDKLog(\"Unable to use LR, skipping\\n\");\n    }\n  }\n\n  // read the values from the stack\n  const uintptr_t framePointer = FIRCLSThreadContextGetFramePointer(registers);\n  uintptr_t stack[2];\n\n  if (!FIRCLSReadMemory((vm_address_t)framePointer, stack, sizeof(stack))) {\n    // unable to read the first stack frame\n    FIRCLSSDKLog(\"Error: failed to read memory at address %p\\n\", (void*)framePointer);\n    return false;\n  }\n\n  if (!FIRCLSThreadContextSetPC(registers, stack[1])) {\n    return false;\n  }\n\n  if (!FIRCLSThreadContextSetFramePointer(registers, stack[0])) {\n    return false;\n  }\n\n  if (!FIRCLSThreadContextSetStackPointer(registers,\n                                          FIRCLSUnwindStackPointerFromFramePointer(framePointer))) {\n    return false;\n  }\n\n  return true;\n}\n\nuintptr_t FIRCLSUnwindStackPointerFromFramePointer(uintptr_t framePtr) {\n  // the stack pointer is the frame pointer plus the two saved pointers for the frame\n  return framePtr + 2 * sizeof(void*);\n}\n\n#if CLS_COMPACT_UNWINDING_SUPPORTED\nbool FIRCLSCompactUnwindComputeRegisters(FIRCLSCompactUnwindContext* context,\n                                         FIRCLSCompactUnwindResult* result,\n                                         FIRCLSThreadContext* registers) {\n  if (!context || !result || !registers) {\n    return false;\n  }\n\n  // Note that compact_uwnind_encoding.h has a few bugs in it prior to iOS 8.0.\n  // Only refer to the >= 8.0 header.\n  switch (result->encoding & UNWIND_ARM64_MODE_MASK) {\n    case UNWIND_ARM64_MODE_FRAMELESS:\n      // Interestingly, we also know the size of the stack frame, by\n      // using UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK. Is that useful?\n      return FIRCLSUnwindWithLRRegister(registers);\n      break;\n    case UNWIND_ARM64_MODE_DWARF:\n      return FIRCLSCompactUnwindDwarfFrame(\n          context, result->encoding & UNWIND_ARM64_DWARF_SECTION_OFFSET, registers);\n      break;\n    case UNWIND_ARM64_MODE_FRAME:\n      return FIRCLSUnwindWithFramePointer(registers, false);\n    default:\n      FIRCLSSDKLog(\"Invalid encoding 0x%x\\n\", result->encoding);\n      break;\n  }\n\n  return false;\n}\n#endif\n\n#if CLS_DWARF_UNWINDING_SUPPORTED\nuintptr_t FIRCLSDwarfUnwindGetRegisterValue(const FIRCLSThreadContext* registers, uint64_t num) {\n  switch (num) {\n    case CLS_DWARF_ARM64_X0:\n      return registers->__ss.__x[0];\n    case CLS_DWARF_ARM64_X1:\n      return registers->__ss.__x[1];\n    case CLS_DWARF_ARM64_X2:\n      return registers->__ss.__x[2];\n    case CLS_DWARF_ARM64_X3:\n      return registers->__ss.__x[3];\n    case CLS_DWARF_ARM64_X4:\n      return registers->__ss.__x[4];\n    case CLS_DWARF_ARM64_X5:\n      return registers->__ss.__x[5];\n    case CLS_DWARF_ARM64_X6:\n      return registers->__ss.__x[6];\n    case CLS_DWARF_ARM64_X7:\n      return registers->__ss.__x[7];\n    case CLS_DWARF_ARM64_X8:\n      return registers->__ss.__x[8];\n    case CLS_DWARF_ARM64_X9:\n      return registers->__ss.__x[9];\n    case CLS_DWARF_ARM64_X10:\n      return registers->__ss.__x[10];\n    case CLS_DWARF_ARM64_X11:\n      return registers->__ss.__x[11];\n    case CLS_DWARF_ARM64_X12:\n      return registers->__ss.__x[12];\n    case CLS_DWARF_ARM64_X13:\n      return registers->__ss.__x[13];\n    case CLS_DWARF_ARM64_X14:\n      return registers->__ss.__x[14];\n    case CLS_DWARF_ARM64_X15:\n      return registers->__ss.__x[15];\n    case CLS_DWARF_ARM64_X16:\n      return registers->__ss.__x[16];\n    case CLS_DWARF_ARM64_X17:\n      return registers->__ss.__x[17];\n    case CLS_DWARF_ARM64_X18:\n      return registers->__ss.__x[18];\n    case CLS_DWARF_ARM64_X19:\n      return registers->__ss.__x[19];\n    case CLS_DWARF_ARM64_X20:\n      return registers->__ss.__x[20];\n    case CLS_DWARF_ARM64_X21:\n      return registers->__ss.__x[21];\n    case CLS_DWARF_ARM64_X22:\n      return registers->__ss.__x[22];\n    case CLS_DWARF_ARM64_X23:\n      return registers->__ss.__x[23];\n    case CLS_DWARF_ARM64_X24:\n      return registers->__ss.__x[24];\n    case CLS_DWARF_ARM64_X25:\n      return registers->__ss.__x[25];\n    case CLS_DWARF_ARM64_X26:\n      return registers->__ss.__x[26];\n    case CLS_DWARF_ARM64_X27:\n      return registers->__ss.__x[27];\n    case CLS_DWARF_ARM64_X28:\n      return registers->__ss.__x[28];\n    case CLS_DWARF_ARM64_FP:\n      return FIRCLSThreadContextGetFramePointer(registers);\n    case CLS_DWARF_ARM64_LR:\n      return FIRCLSThreadContextGetLinkRegister(registers);\n    case CLS_DWARF_ARM64_SP:\n      return FIRCLSThreadContextGetStackPointer(registers);\n    default:\n      break;\n  }\n\n  FIRCLSSDKLog(\"Error: Unrecognized get register number %llu\\n\", num);\n\n  return 0;\n}\n\nbool FIRCLSDwarfUnwindSetRegisterValue(FIRCLSThreadContext* registers,\n                                       uint64_t num,\n                                       uintptr_t value) {\n  switch (num) {\n    case CLS_DWARF_ARM64_X0:\n      registers->__ss.__x[0] = value;\n      return true;\n    case CLS_DWARF_ARM64_X1:\n      registers->__ss.__x[1] = value;\n      return true;\n    case CLS_DWARF_ARM64_X2:\n      registers->__ss.__x[2] = value;\n      return true;\n    case CLS_DWARF_ARM64_X3:\n      registers->__ss.__x[3] = value;\n      return true;\n    case CLS_DWARF_ARM64_X4:\n      registers->__ss.__x[4] = value;\n      return true;\n    case CLS_DWARF_ARM64_X5:\n      registers->__ss.__x[5] = value;\n      return true;\n    case CLS_DWARF_ARM64_X6:\n      registers->__ss.__x[6] = value;\n      return true;\n    case CLS_DWARF_ARM64_X7:\n      registers->__ss.__x[7] = value;\n      return true;\n    case CLS_DWARF_ARM64_X8:\n      registers->__ss.__x[8] = value;\n      return true;\n    case CLS_DWARF_ARM64_X9:\n      registers->__ss.__x[9] = value;\n      return true;\n    case CLS_DWARF_ARM64_X10:\n      registers->__ss.__x[10] = value;\n      return true;\n    case CLS_DWARF_ARM64_X11:\n      registers->__ss.__x[11] = value;\n      return true;\n    case CLS_DWARF_ARM64_X12:\n      registers->__ss.__x[12] = value;\n      return true;\n    case CLS_DWARF_ARM64_X13:\n      registers->__ss.__x[13] = value;\n      return true;\n    case CLS_DWARF_ARM64_X14:\n      registers->__ss.__x[14] = value;\n      return true;\n    case CLS_DWARF_ARM64_X15:\n      registers->__ss.__x[15] = value;\n      return true;\n    case CLS_DWARF_ARM64_X16:\n      registers->__ss.__x[16] = value;\n      return true;\n    case CLS_DWARF_ARM64_X17:\n      registers->__ss.__x[17] = value;\n      return true;\n    case CLS_DWARF_ARM64_X18:\n      registers->__ss.__x[18] = value;\n      return true;\n    case CLS_DWARF_ARM64_X19:\n      registers->__ss.__x[19] = value;\n      return true;\n    case CLS_DWARF_ARM64_X20:\n      registers->__ss.__x[20] = value;\n      return true;\n    case CLS_DWARF_ARM64_X21:\n      registers->__ss.__x[21] = value;\n      return true;\n    case CLS_DWARF_ARM64_X22:\n      registers->__ss.__x[22] = value;\n      return true;\n    case CLS_DWARF_ARM64_X23:\n      registers->__ss.__x[23] = value;\n      return true;\n    case CLS_DWARF_ARM64_X24:\n      registers->__ss.__x[24] = value;\n      return true;\n    case CLS_DWARF_ARM64_X25:\n      registers->__ss.__x[25] = value;\n      return true;\n    case CLS_DWARF_ARM64_X26:\n      registers->__ss.__x[26] = value;\n      return true;\n    case CLS_DWARF_ARM64_X27:\n      registers->__ss.__x[27] = value;\n      return true;\n    case CLS_DWARF_ARM64_X28:\n      registers->__ss.__x[28] = value;\n      return true;\n    case CLS_DWARF_ARM64_FP:\n      FIRCLSThreadContextSetFramePointer(registers, value);\n      return true;\n    case CLS_DWARF_ARM64_SP:\n      FIRCLSThreadContextSetStackPointer(registers, value);\n      return true;\n    case CLS_DWARF_ARM64_LR:\n      // Here's what's going on. For x86, the \"return register\" is virtual. The architecture\n      // doesn't actually have one, but DWARF does have the concept. So, when the system\n      // tries to set the return register, we set the PC. You can see this behavior\n      // in the FIRCLSDwarfUnwindSetRegisterValue implemenation for that architecture. In the\n      // case of ARM64, the register is real. So, we have to be extra careful to make sure\n      // we update the PC here. Otherwise, when a DWARF unwind completes, it won't have\n      // changed the PC to the right value.\n      FIRCLSThreadContextSetLinkRegister(registers, value);\n      FIRCLSThreadContextSetPC(registers, value);\n      return true;\n    default:\n      break;\n  }\n\n  FIRCLSSDKLog(\"Unrecognized set register number %llu\\n\", num);\n\n  return false;\n}\n#endif\n\n#else\nINJECT_STRIP_SYMBOL(unwind_arm)\n#endif\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_x86.c",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"FIRCLSUnwind_x86.h\"\n#include \"../Helpers/FIRCLSDefines.h\"\n#include \"../Helpers/FIRCLSFeatures.h\"\n#include \"FIRCLSUnwind.h\"\n#include \"FIRCLSUnwind_arch.h\"\n#include \"../Helpers/FIRCLSUtility.h\"\n\n#if CLS_CPU_X86\n#include <mach-o/compact_unwind_encoding.h>\n\nstatic bool FIRCLSCompactUnwindBPFrame(compact_unwind_encoding_t encoding,\n                                       FIRCLSThreadContext* registers);\nstatic bool FIRCLSCompactUnwindFrameless(compact_unwind_encoding_t encoding,\n                                         FIRCLSThreadContext* registers,\n                                         uintptr_t functionStart,\n                                         bool indirect);\n\n#if CLS_COMPACT_UNWINDING_SUPPORTED\nbool FIRCLSCompactUnwindComputeRegisters(FIRCLSCompactUnwindContext* context,\n                                         FIRCLSCompactUnwindResult* result,\n                                         FIRCLSThreadContext* registers) {\n  if (!FIRCLSIsValidPointer(context) || !FIRCLSIsValidPointer(result) ||\n      !FIRCLSIsValidPointer(registers)) {\n    FIRCLSSDKLogError(\"invalid inputs\\n\");\n    return false;\n  }\n\n  FIRCLSSDKLogDebug(\"Computing registers for encoding %x\\n\", result->encoding);\n\n  switch (result->encoding & CLS_X86_MODE_MASK) {\n    case CLS_X86_MODE_BP_FRAME:\n      return FIRCLSCompactUnwindBPFrame(result->encoding, registers);\n    case CLS_X86_MODE_STACK_IMMD:\n      return FIRCLSCompactUnwindFrameless(result->encoding, registers, result->functionStart,\n                                          false);\n    case CLS_X86_MODE_STACK_IND:\n      return FIRCLSCompactUnwindFrameless(result->encoding, registers, result->functionStart, true);\n    case CLS_X86_MODE_DWARF:\n      return FIRCLSCompactUnwindDwarfFrame(context, result->encoding & CLS_X86_DWARF_SECTION_OFFSET,\n                                           registers);\n    default:\n      FIRCLSSDKLogError(\"Invalid encoding %x\\n\", result->encoding);\n      break;\n  }\n\n  return false;\n}\n#endif\n\nstatic bool FIRCLSCompactUnwindBPFrame(compact_unwind_encoding_t encoding,\n                                       FIRCLSThreadContext* registers) {\n  // this is the plain-vanilla frame pointer process\n\n  // uint32_t offset = GET_BITS_WITH_MASK(encoding, UNWIND_X86_EBP_FRAME_OFFSET);\n  // uint32_t locations = GET_BITS_WITH_MASK(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);\n\n  // TODO: pretty sure we do need to restore registers here, so that if a subsequent frame needs\n  // these results, they will be correct\n\n  // Checkout CompactUnwinder.hpp in libunwind for how to do this. Since we don't make use of any of\n  // those registers for a stacktrace only, there's nothing we need do with them.\n\n  // read the values from the stack\n  const uintptr_t framePointer = FIRCLSThreadContextGetFramePointer(registers);\n  uintptr_t stack[2];\n\n  if (!FIRCLSReadMemory((vm_address_t)framePointer, stack, sizeof(stack))) {\n    // unable to read the first stack frame\n    FIRCLSSDKLog(\"Error: failed to read memory at address %p\\n\", (void*)framePointer);\n    return false;\n  }\n\n  if (!FIRCLSThreadContextSetPC(registers, stack[1])) {\n    return false;\n  }\n\n  if (!FIRCLSThreadContextSetFramePointer(registers, stack[0])) {\n    return false;\n  }\n\n  if (!FIRCLSThreadContextSetStackPointer(registers,\n                                          FIRCLSUnwindStackPointerFromFramePointer(framePointer))) {\n    return false;\n  }\n\n  return true;\n}\n\nbool FIRCLSUnwindWithStackScanning(FIRCLSThreadContext* registers) {\n  vm_address_t start = (vm_address_t)FIRCLSThreadContextGetStackPointer(registers);\n  vm_address_t end = (vm_address_t)FIRCLSThreadContextGetFramePointer(registers);\n\n  uintptr_t newPC = 0;\n\n  if (!FIRCLSUnwindFirstExecutableAddress(start, end, (vm_address_t*)&newPC)) {\n    return false;\n  }\n\n  return FIRCLSThreadContextSetPC(registers, newPC);\n}\n\nbool FIRCLSUnwindWithFramePointer(FIRCLSThreadContext* registers, bool allowScanning) {\n  // Here's an interesting case. We've just processed the first frame, and it did\n  // not have any unwind info. If that first function did not allocate\n  // a stack frame, we'll \"skip\" the caller. This might sound unlikely, but it actually\n  // happens a lot in practice.\n\n  // Sooo, one thing we can do is try to stack the stack for things that look like return\n  // addresses. Normally, this technique will hit many false positives. But, if we do it\n  // only for the second frame, and only when we don't have other unwind info available.\n\n  if (allowScanning) {\n    FIRCLSSDKLogInfo(\"Attempting stack scan\\n\");\n    if (FIRCLSUnwindWithStackScanning(registers)) {\n      FIRCLSSDKLogInfo(\"Stack scan successful\\n\");\n      return true;\n    }\n  }\n\n  // If we ever do anything else with the encoding, we need to be sure\n  // to set it up right.\n  return FIRCLSCompactUnwindBPFrame(CLS_X86_MODE_BP_FRAME, registers);\n}\n\nuintptr_t FIRCLSUnwindStackPointerFromFramePointer(uintptr_t framePtr) {\n  // the stack pointer is the frame pointer plus the two saved pointers for the frame\n  return framePtr + 2 * sizeof(void*);\n}\n\n#if CLS_COMPACT_UNWINDING_SUPPORTED || CLS_DWARF_UNWINDING_SUPPORTED\nuintptr_t FIRCLSDwarfUnwindGetRegisterValue(const FIRCLSThreadContext* registers, uint64_t num) {\n  switch (num) {\n#if CLS_CPU_X86_64\n    case CLS_DWARF_X86_64_RAX:\n      return registers->__ss.__rax;\n    case CLS_DWARF_X86_64_RDX:\n      return registers->__ss.__rdx;\n    case CLS_DWARF_X86_64_RCX:\n      return registers->__ss.__rcx;\n    case CLS_DWARF_X86_64_RBX:\n      return registers->__ss.__rbx;\n    case CLS_DWARF_X86_64_RSI:\n      return registers->__ss.__rsi;\n    case CLS_DWARF_X86_64_RDI:\n      return registers->__ss.__rdi;\n    case CLS_DWARF_X86_64_RBP:\n      return registers->__ss.__rbp;\n    case CLS_DWARF_X86_64_RSP:\n      return registers->__ss.__rsp;\n    case CLS_DWARF_X86_64_R8:\n      return registers->__ss.__r8;\n    case CLS_DWARF_X86_64_R9:\n      return registers->__ss.__r9;\n    case CLS_DWARF_X86_64_R10:\n      return registers->__ss.__r10;\n    case CLS_DWARF_X86_64_R11:\n      return registers->__ss.__r11;\n    case CLS_DWARF_X86_64_R12:\n      return registers->__ss.__r12;\n    case CLS_DWARF_X86_64_R13:\n      return registers->__ss.__r13;\n    case CLS_DWARF_X86_64_R14:\n      return registers->__ss.__r14;\n    case CLS_DWARF_X86_64_R15:\n      return registers->__ss.__r15;\n    case CLS_DWARF_X86_64_RET_ADDR:\n      return registers->__ss.__rip;\n#elif CLS_CPU_I386\n    case CLS_DWARF_X86_EAX:\n      return registers->__ss.__eax;\n    case CLS_DWARF_X86_ECX:\n      return registers->__ss.__ecx;\n    case CLS_DWARF_X86_EDX:\n      return registers->__ss.__edx;\n    case CLS_DWARF_X86_EBX:\n      return registers->__ss.__ebx;\n    case CLS_DWARF_X86_EBP:\n      return registers->__ss.__ebp;\n    case CLS_DWARF_X86_ESP:\n      return registers->__ss.__esp;\n    case CLS_DWARF_X86_ESI:\n      return registers->__ss.__esi;\n    case CLS_DWARF_X86_EDI:\n      return registers->__ss.__edi;\n    case CLS_DWARF_X86_RET_ADDR:\n      return registers->__ss.__eip;\n#endif\n    default:\n      break;\n  }\n\n  FIRCLSSDKLog(\"Error: Unrecognized get register number %llu\\n\", num);\n\n  return 0;\n}\n\nbool FIRCLSDwarfUnwindSetRegisterValue(FIRCLSThreadContext* registers,\n                                       uint64_t num,\n                                       uintptr_t value) {\n  switch (num) {\n#if CLS_CPU_X86_64\n    case CLS_DWARF_X86_64_RAX:\n      registers->__ss.__rax = value;\n      return true;\n    case CLS_DWARF_X86_64_RDX:\n      registers->__ss.__rdx = value;\n      return true;\n    case CLS_DWARF_X86_64_RCX:\n      registers->__ss.__rcx = value;\n      return true;\n    case CLS_DWARF_X86_64_RBX:\n      registers->__ss.__rbx = value;\n      return true;\n    case CLS_DWARF_X86_64_RSI:\n      registers->__ss.__rsi = value;\n      return true;\n    case CLS_DWARF_X86_64_RDI:\n      registers->__ss.__rdi = value;\n      return true;\n    case CLS_DWARF_X86_64_RBP:\n      registers->__ss.__rbp = value;\n      return true;\n    case CLS_DWARF_X86_64_RSP:\n      registers->__ss.__rsp = value;\n      return true;\n    case CLS_DWARF_X86_64_R8:\n      registers->__ss.__r8 = value;\n      return true;\n    case CLS_DWARF_X86_64_R9:\n      registers->__ss.__r9 = value;\n      return true;\n    case CLS_DWARF_X86_64_R10:\n      registers->__ss.__r10 = value;\n      return true;\n    case CLS_DWARF_X86_64_R11:\n      registers->__ss.__r11 = value;\n      return true;\n    case CLS_DWARF_X86_64_R12:\n      registers->__ss.__r12 = value;\n      return true;\n    case CLS_DWARF_X86_64_R13:\n      registers->__ss.__r13 = value;\n      return true;\n    case CLS_DWARF_X86_64_R14:\n      registers->__ss.__r14 = value;\n      return true;\n    case CLS_DWARF_X86_64_R15:\n      registers->__ss.__r15 = value;\n      return true;\n    case CLS_DWARF_X86_64_RET_ADDR:\n      registers->__ss.__rip = value;\n      return true;\n#elif CLS_CPU_I386\n    case CLS_DWARF_X86_EAX:\n      registers->__ss.__eax = value;\n      return true;\n    case CLS_DWARF_X86_ECX:\n      registers->__ss.__ecx = value;\n      return true;\n    case CLS_DWARF_X86_EDX:\n      registers->__ss.__edx = value;\n      return true;\n    case CLS_DWARF_X86_EBX:\n      registers->__ss.__ebx = value;\n      return true;\n    case CLS_DWARF_X86_EBP:\n      registers->__ss.__ebp = value;\n      return true;\n    case CLS_DWARF_X86_ESP:\n      registers->__ss.__esp = value;\n      return true;\n    case CLS_DWARF_X86_ESI:\n      registers->__ss.__esi = value;\n      return true;\n    case CLS_DWARF_X86_EDI:\n      registers->__ss.__edi = value;\n      return true;\n    case CLS_DWARF_X86_RET_ADDR:\n      registers->__ss.__eip = value;\n      return true;\n#endif\n    default:\n      break;\n  }\n\n  FIRCLSSDKLog(\"Unrecognized set register number %llu\\n\", num);\n\n  return false;\n}\n#endif\n\n#if CLS_COMPACT_UNWINDING_SUPPORTED\nbool FIRCLSCompactUnwindComputeStackSize(const compact_unwind_encoding_t encoding,\n                                         const uintptr_t functionStart,\n                                         const bool indirect,\n                                         uint32_t* const stackSize) {\n  if (!FIRCLSIsValidPointer(stackSize)) {\n    FIRCLSSDKLog(\"Error: invalid inputs\\n\");\n    return false;\n  }\n\n  const uint32_t stackSizeEncoded = GET_BITS_WITH_MASK(encoding, CLS_X86_FRAMELESS_STACK_SIZE);\n\n  if (!indirect) {\n    *stackSize = stackSizeEncoded * sizeof(void*);\n    return true;\n  }\n\n  const vm_address_t sublAddress = functionStart + stackSizeEncoded;\n  uint32_t sublValue = 0;\n\n  if (!FIRCLSReadMemory(sublAddress, &sublValue, sizeof(uint32_t))) {\n    FIRCLSSDKLog(\"Error: unable to read subl value\\n\");\n    return false;\n  }\n\n  const uint32_t stackAdjust = GET_BITS_WITH_MASK(encoding, CLS_X86_FRAMELESS_STACK_ADJUST);\n\n  *stackSize = sublValue + stackAdjust * sizeof(void*);\n\n  return true;\n}\n\nbool FIRCLSCompactUnwindDecompressPermutation(const compact_unwind_encoding_t encoding,\n                                              uintptr_t permutatedRegisters[const static 6]) {\n  const uint32_t regCount = GET_BITS_WITH_MASK(encoding, CLS_X86_FRAMELESS_STACK_REG_COUNT);\n  uint32_t permutation = GET_BITS_WITH_MASK(encoding, CLS_X86_FRAMELESS_STACK_REG_PERMUTATION);\n\n  switch (regCount) {\n    case 6:\n      permutatedRegisters[0] = permutation / 120;\n      permutation -= (permutatedRegisters[0] * 120);\n      permutatedRegisters[1] = permutation / 24;\n      permutation -= (permutatedRegisters[1] * 24);\n      permutatedRegisters[2] = permutation / 6;\n      permutation -= (permutatedRegisters[2] * 6);\n      permutatedRegisters[3] = permutation / 2;\n      permutation -= (permutatedRegisters[3] * 2);\n      permutatedRegisters[4] = permutation;\n      permutatedRegisters[5] = 0;\n      break;\n    case 5:\n      permutatedRegisters[0] = permutation / 120;\n      permutation -= (permutatedRegisters[0] * 120);\n      permutatedRegisters[1] = permutation / 24;\n      permutation -= (permutatedRegisters[1] * 24);\n      permutatedRegisters[2] = permutation / 6;\n      permutation -= (permutatedRegisters[2] * 6);\n      permutatedRegisters[3] = permutation / 2;\n      permutation -= (permutatedRegisters[3] * 2);\n      permutatedRegisters[4] = permutation;\n      break;\n    case 4:\n      permutatedRegisters[0] = permutation / 60;\n      permutation -= (permutatedRegisters[0] * 60);\n      permutatedRegisters[1] = permutation / 12;\n      permutation -= (permutatedRegisters[1] * 12);\n      permutatedRegisters[2] = permutation / 3;\n      permutation -= (permutatedRegisters[2] * 3);\n      permutatedRegisters[3] = permutation;\n      break;\n    case 3:\n      permutatedRegisters[0] = permutation / 20;\n      permutation -= (permutatedRegisters[0] * 20);\n      permutatedRegisters[1] = permutation / 4;\n      permutation -= (permutatedRegisters[1] * 4);\n      permutatedRegisters[2] = permutation;\n      break;\n    case 2:\n      permutatedRegisters[0] = permutation / 5;\n      permutation -= (permutatedRegisters[0] * 5);\n      permutatedRegisters[1] = permutation;\n      break;\n    case 1:\n      permutatedRegisters[0] = permutation;\n      break;\n    case 0:\n      break;\n    default:\n      FIRCLSSDKLog(\"Error: unhandled number of register permutations for encoding %x\\n\", encoding);\n      return false;\n  }\n\n  return true;\n}\n\nbool FIRCLSCompactUnwindRemapRegisters(const compact_unwind_encoding_t encoding,\n                                       uintptr_t permutatedRegisters[const static 6],\n                                       uintptr_t savedRegisters[const static 6]) {\n  const uint32_t regCount = GET_BITS_WITH_MASK(encoding, CLS_X86_FRAMELESS_STACK_REG_COUNT);\n\n  if (regCount > 6) {\n    FIRCLSSDKLog(\"Error: invalid register number count %d\\n\", regCount);\n    return false;\n  }\n\n  // Re-number the registers\n\n  // You are probably wondering, what the hell is this algorithm even doing? It is\n  // taken from libunwind's implemenation that does the same thing.\n  bool used[7] = {false, false, false, false, false, false, false};\n  for (uint32_t i = 0; i < regCount; ++i) {\n    int renum = 0;\n    for (int u = 1; u < 7; ++u) {\n      if (!used[u]) {\n        if (renum == permutatedRegisters[i]) {\n          savedRegisters[i] = u;\n          used[u] = true;\n          break;\n        }\n        ++renum;\n      }\n    }\n  }\n\n  return true;\n}\n\nbool FIRCLSCompactUnwindRestoreRegisters(compact_unwind_encoding_t encoding,\n                                         FIRCLSThreadContext* registers,\n                                         uint32_t stackSize,\n                                         const uintptr_t savedRegisters[const static 6],\n                                         uintptr_t* address) {\n  if (!FIRCLSIsValidPointer(registers) || !FIRCLSIsValidPointer(address)) {\n    FIRCLSSDKLog(\"Error: invalid inputs\\n\");\n    return false;\n  }\n\n  const uint32_t regCount = GET_BITS_WITH_MASK(encoding, CLS_X86_FRAMELESS_STACK_REG_COUNT);\n\n  // compute initial address of saved registers\n  *address = FIRCLSThreadContextGetStackPointer(registers) + stackSize - sizeof(void*) -\n             sizeof(void*) * regCount;\n  uintptr_t value = 0;\n\n  for (uint32_t i = 0; i < regCount; ++i) {\n    value = 0;\n\n    switch (savedRegisters[i]) {\n      case CLS_X86_REG_RBP:\n        if (!FIRCLSReadMemory((vm_address_t)*address, (void*)&value, sizeof(uintptr_t))) {\n          FIRCLSSDKLog(\"Error: unable to read memory to set register\\n\");\n          return false;\n        }\n\n        if (!FIRCLSThreadContextSetFramePointer(registers, value)) {\n          FIRCLSSDKLog(\"Error: unable to set FP\\n\");\n          return false;\n        }\n        break;\n      default:\n        // here, we are restoring a register we don't need for unwinding\n        FIRCLSSDKLog(\"Error: skipping a restore of register %d at %p\\n\", (int)savedRegisters[i],\n                     (void*)*address);\n        break;\n    }\n\n    *address += sizeof(void*);\n  }\n\n  return true;\n}\n\nstatic bool FIRCLSCompactUnwindFrameless(compact_unwind_encoding_t encoding,\n                                         FIRCLSThreadContext* registers,\n                                         uintptr_t functionStart,\n                                         bool indirect) {\n  FIRCLSSDKLog(\"Frameless unwind encountered with encoding %x\\n\", encoding);\n\n  uint32_t stackSize = 0;\n  if (!FIRCLSCompactUnwindComputeStackSize(encoding, functionStart, indirect, &stackSize)) {\n    FIRCLSSDKLog(\"Error: unable to compute stack size for encoding %x\\n\", encoding);\n    return false;\n  }\n\n  uintptr_t permutatedRegisters[6];\n\n  memset(permutatedRegisters, 0, sizeof(permutatedRegisters));\n  if (!FIRCLSCompactUnwindDecompressPermutation(encoding, permutatedRegisters)) {\n    FIRCLSSDKLog(\"Error: unable to decompress registers %x\\n\", encoding);\n    return false;\n  }\n\n  uintptr_t savedRegisters[6];\n\n  memset(savedRegisters, 0, sizeof(savedRegisters));\n  if (!FIRCLSCompactUnwindRemapRegisters(encoding, permutatedRegisters, savedRegisters)) {\n    FIRCLSSDKLog(\"Error: unable to remap registers %x\\n\", encoding);\n    return false;\n  }\n\n  uintptr_t address = 0;\n\n  if (!FIRCLSCompactUnwindRestoreRegisters(encoding, registers, stackSize, savedRegisters,\n                                           &address)) {\n    FIRCLSSDKLog(\"Error: unable to restore registers\\n\");\n    return false;\n  }\n\n  FIRCLSSDKLog(\"SP is %p and we are reading %p\\n\",\n               (void*)FIRCLSThreadContextGetStackPointer(registers), (void*)address);\n  // read the value from the stack, now that we know the address to read\n  uintptr_t value = 0;\n  if (!FIRCLSReadMemory((vm_address_t)address, (void*)&value, sizeof(uintptr_t))) {\n    FIRCLSSDKLog(\"Error: unable to read memory to set register\\n\");\n    return false;\n  }\n\n  FIRCLSSDKLog(\"Read PC to be %p\\n\", (void*)value);\n  if (!FIRCLSIsValidPointer(value)) {\n    FIRCLSSDKLog(\"Error: computed PC is invalid\\n\");\n    return false;\n  }\n\n  return FIRCLSThreadContextSetPC(registers, value) &&\n         FIRCLSThreadContextSetStackPointer(registers, address + sizeof(void*));\n}\n#endif\n\n#else\nINJECT_STRIP_SYMBOL(unwind_x86)\n#endif\n"
  },
  {
    "path": "Unwinding/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_x86.h",
    "content": "// Copyright 2019 Google\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include \"../Helpers/FIRCLSFeatures.h\"\n\n// Add some abstraction to compact unwinding, because compact\n// unwinding is nearly identical between 32 and 64 bit\n#if CLS_CPU_X86_64\n\n#define CLS_X86_MODE_MASK UNWIND_X86_64_MODE_MASK\n#define CLS_X86_MODE_BP_FRAME UNWIND_X86_64_MODE_RBP_FRAME\n#define CLS_X86_MODE_STACK_IMMD UNWIND_X86_64_MODE_STACK_IMMD\n#define CLS_X86_MODE_STACK_IND UNWIND_X86_64_MODE_STACK_IND\n#define CLS_X86_MODE_DWARF UNWIND_X86_64_MODE_DWARF\n\n#define CLS_X86_BP_FRAME_REGISTERS UNWIND_X86_64_RBP_FRAME_REGISTERS\n#define CLS_X86_BP_FRAME_OFFSET UNWIND_X86_64_RBP_FRAME_OFFSET\n\n#define CLS_X86_FRAMELESS_STACK_SIZE UNWIND_X86_64_FRAMELESS_STACK_SIZE\n#define CLS_X86_FRAMELESS_STACK_ADJUST UNWIND_X86_64_FRAMELESS_STACK_ADJUST\n#define CLS_X86_FRAMELESS_STACK_REG_COUNT UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT\n#define CLS_X86_FRAMELESS_STACK_REG_PERMUTATION UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION\n\n#define CLS_X86_DWARF_SECTION_OFFSET UNWIND_X86_64_DWARF_SECTION_OFFSET\n\n#define CLS_X86_REG_RBP UNWIND_X86_64_REG_RBP\n\n#else\n\n#define CLS_X86_MODE_MASK UNWIND_X86_MODE_MASK\n#define CLS_X86_MODE_BP_FRAME UNWIND_X86_MODE_EBP_FRAME\n#define CLS_X86_MODE_STACK_IMMD UNWIND_X86_MODE_STACK_IMMD\n#define CLS_X86_MODE_STACK_IND UNWIND_X86_MODE_STACK_IND\n#define CLS_X86_MODE_DWARF UNWIND_X86_MODE_DWARF\n\n#define CLS_X86_BP_FRAME_REGISTERS UNWIND_X86_RBP_FRAME_REGISTERS\n#define CLS_X86_BP_FRAME_OFFSET UNWIND_X86_RBP_FRAME_OFFSET\n\n#define CLS_X86_FRAMELESS_STACK_SIZE UNWIND_X86_FRAMELESS_STACK_SIZE\n#define CLS_X86_FRAMELESS_STACK_ADJUST UNWIND_X86_FRAMELESS_STACK_ADJUST\n#define CLS_X86_FRAMELESS_STACK_REG_COUNT UNWIND_X86_FRAMELESS_STACK_REG_COUNT\n#define CLS_X86_FRAMELESS_STACK_REG_PERMUTATION UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION\n\n#define CLS_X86_DWARF_SECTION_OFFSET UNWIND_X86_DWARF_SECTION_OFFSET\n\n#define CLS_X86_REG_RBP UNWIND_X86_REG_EBP\n\n#endif\n\n#if CLS_COMPACT_UNWINDING_SUPPORTED\nbool FIRCLSCompactUnwindComputeStackSize(const compact_unwind_encoding_t encoding,\n                                         const uintptr_t functionStart,\n                                         const bool indirect,\n                                         uint32_t* const stackSize);\nbool FIRCLSCompactUnwindDecompressPermutation(const compact_unwind_encoding_t encoding,\n                                              uintptr_t permutatedRegisters[const static 6]);\nbool FIRCLSCompactUnwindRestoreRegisters(compact_unwind_encoding_t encoding,\n                                         FIRCLSThreadContext* registers,\n                                         uint32_t stackSize,\n                                         const uintptr_t savedRegisters[const static 6],\n                                         uintptr_t* address);\n#endif\n"
  },
  {
    "path": "Unwinding/Crashlytics/LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additions Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n================================================================================\n\nThe following copyright from Hewlett-Packard Development Company, L.P.\napplies to the dwarf.h file in third_party/libunwind\n\n   libunwind - a platform-independent unwind library\n   Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P.\n        Contributed by David Mosberger-Tang <davidm@hpl.hp.com>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Unwinding/Crashlytics/ProtoSupport/Protos/crashlytics.options",
    "content": "# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Options for crashlytics.proto\n\ngoogle_crashlytics.Report.sdk_version type:FT_POINTER\ngoogle_crashlytics.Report.gmp_app_id type:FT_POINTER\ngoogle_crashlytics.Report.installation_uuid type:FT_POINTER\ngoogle_crashlytics.Report.build_version type:FT_POINTER\ngoogle_crashlytics.Report.display_version type:FT_POINTER\ngoogle_crashlytics.FilesPayload.File.filename type:FT_POINTER\ngoogle_crashlytics.FilesPayload.File.contents type:FT_POINTER\ngoogle_crashlytics.FilesPayload.files type:FT_POINTER\n"
  },
  {
    "path": "Unwinding/Crashlytics/ProtoSupport/Protos/crashlytics.proto",
    "content": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto3\";\n\npackage google_crashlytics;\n\nenum Platforms {\n  UNKNOWN_PLATFORM = 0;\n  IOS = 1;\n  TVOS = 2;\n  MAC_OS_X = 5;\n}\n\nmessage Report {\n  // SDK version that generated this session\n  string sdk_version = 1;\n\n  // GMP App ID\n  string gmp_app_id = 3;\n\n  // General device type which generated the Event\n  Platforms platform = 4;\n\n  // Unique device generated guid for application install.\n  string installation_uuid = 5;\n\n  // App build version.\n  string build_version = 6;\n\n  // Developer-supplied application version.\n  string display_version = 7;\n\n  FilesPayload apple_payload = 10;\n}\n\n// Session data represented as a set of log and metadata files.\nmessage FilesPayload {\n  message File {\n    string filename = 1;\n    bytes contents = 2;\n  }\n\n  repeated File files = 1;\n}"
  },
  {
    "path": "Unwinding/Crashlytics/ProtoSupport/generate_crashlytics_protos.sh",
    "content": "#!/bin/bash\n\n# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Example usage:\n# ./build_protos <path to nanopb>\n\n# Dependencies: git, protobuf, python-protobuf, pyinstaller\n\nreadonly DIR=\"$( git rev-parse --show-toplevel )\"\n\n# Current release of nanopb being used  to build the CCT protos\nreadonly NANOPB_VERSION=\"0.3.9.8\"\nreadonly NANOPB_TEMPDIR=\"${DIR}/Crashlytics/nanopb_temp\"\n\nreadonly LIBRARY_DIR=\"${DIR}/Crashlytics/Crashlytics/\"\nreadonly PROTO_DIR=\"${DIR}/Crashlytics/ProtoSupport/Protos/\"\nreadonly PROTOGEN_DIR=\"${DIR}/Crashlytics/Protogen/\"\n\nrm -rf \"${NANOPB_TEMPDIR}\"\n\necho \"Downloading nanopb...\"\ngit clone --branch \"${NANOPB_VERSION}\" https://github.com/nanopb/nanopb.git \"${NANOPB_TEMPDIR}\"\n\necho \"Building nanopb...\"\npushd \"${NANOPB_TEMPDIR}\"\n./tools/make_mac_package.sh\nGIT_DESCRIPTION=`git describe --always`-macosx-x86\nNANOPB_BIN_DIR=\"dist/${GIT_DESCRIPTION}\"\npopd\n\necho \"Removing existing CCT protos...\"\nrm -rf \"${PROTOGEN_DIR}/*\"\n\necho \"Generating CCT protos...\"\npython \"${DIR}/Crashlytics/ProtoSupport/proto_generator.py\" \\\n  --nanopb \\\n  --protos_dir=\"${PROTO_DIR}\" \\\n  --pythonpath=\"${NANOPB_TEMPDIR}/${NANOPB_BIN_DIR}/generator\" \\\n  --output_dir=\"${PROTOGEN_DIR}\" \\\n  --include=\"${PROTO_DIR}\"\n\nrm -rf \"${NANOPB_TEMPDIR}\"\n"
  },
  {
    "path": "Unwinding/Crashlytics/ProtoSupport/nanopb_objc_generator.py",
    "content": "#!/usr/bin/env python\n\n# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Generates and massages protocol buffer outputs.\n\"\"\"\n\nfrom __future__ import print_function\n\nimport sys\n\nimport io\nimport nanopb_generator as nanopb\nimport os\nimport os.path\nimport shlex\n\nfrom google.protobuf.descriptor_pb2 import FieldDescriptorProto\n\n# The plugin_pb2 package loads descriptors on import, but doesn't defend\n# against multiple imports. Reuse the plugin package as loaded by the\n# nanopb_generator.\nplugin_pb2 = nanopb.plugin_pb2\nnanopb_pb2 = nanopb.nanopb_pb2\n\n\ndef main():\n  # Parse request\n  data = io.open(sys.stdin.fileno(), 'rb').read()\n  request = plugin_pb2.CodeGeneratorRequest.FromString(data)\n\n  # Preprocess inputs, changing types and nanopb defaults\n  options = nanopb_parse_options(request)\n  use_anonymous_oneof(request)\n  use_bytes_for_strings(request)\n\n  # Generate code\n  parsed_files = nanopb_parse_files(request, options)\n  results = nanopb_generate(request, options, parsed_files)\n  response = nanopb_write(results)\n\n  # Write to stdout\n  io.open(sys.stdout.fileno(), 'wb').write(response.SerializeToString())\n\n\ndef use_anonymous_oneof(request):\n  \"\"\"Use anonymous unions for oneofs if they're the only one in a message.\n\n  Equivalent to setting this option on messages where it applies:\n\n    option (nanopb).anonymous_oneof = true;\n\n  Args:\n    request: A CodeGeneratorRequest from protoc. The descriptors are modified\n      in place.\n  \"\"\"\n  for _, message_type in iterate_messages(request):\n    if len(message_type.oneof_decl) == 1:\n      ext = message_type.options.Extensions[nanopb_pb2.nanopb_msgopt]\n      ext.anonymous_oneof = True\n\n\ndef use_bytes_for_strings(request):\n  \"\"\"Always use the bytes type instead of string.\n\n  By default, nanopb renders proto strings as having the C type char* and does\n  not include a separate size field, getting the length of the string via\n  strlen(). Unfortunately this prevents using strings with embedded nulls,\n  which is something the wire format supports.\n\n  Fortunately, string and bytes proto fields are identical on the wire and\n  nanopb's bytes representation does have an explicit length, so this function\n  changes the types of all string fields to bytes. The generated code will now\n  contain pb_bytes_array_t.\n\n  There's no nanopb or proto option to control this behavior. The equivalent\n  would be to hand edit all the .proto files :-(.\n\n  Args:\n    request: A CodeGeneratorRequest from protoc. The descriptors are modified\n      in place.\n  \"\"\"\n  for names, message_type in iterate_messages(request):\n    for field in message_type.field:\n      if field.type == FieldDescriptorProto.TYPE_STRING:\n        field.type = FieldDescriptorProto.TYPE_BYTES\n\n\ndef iterate_messages(request):\n  \"\"\"Iterates over all messages in all files in the request.\n\n  Args:\n    request: A CodeGeneratorRequest passed by protoc.\n\n  Yields:\n    names: a nanopb.Names object giving a qualified name for the message\n    message_type: a DescriptorProto for the message.\n  \"\"\"\n  for fdesc in request.proto_file:\n    for names, message_type in nanopb.iterate_messages(fdesc):\n      yield names, message_type\n\n\ndef nanopb_parse_options(request):\n  \"\"\"Parses nanopb_generator command-line options from the given request.\n\n  Args:\n    request: A CodeGeneratorRequest passed by protoc.\n\n  Returns:\n    Nanopb's options object, obtained via optparser.\n  \"\"\"\n  # Parse options the same as nanopb_generator.main_plugin() does.\n  args = shlex.split(request.parameter)\n  options, _ = nanopb.optparser.parse_args(args)\n\n  # Force certain options\n  options.extension = '.nanopb'\n  options.verbose = True\n  \n  # Replicate options setup from nanopb_generator.main_plugin.\n  nanopb.Globals.verbose_options = options.verbose\n\n  # Google's protoc does not currently indicate the full path of proto files.\n  # Instead always add the main file path to the search dirs, that works for\n  # the common case.\n  options.options_path.append(os.path.dirname(request.file_to_generate[0]))\n  return options\n\n\ndef nanopb_parse_files(request, options):\n  \"\"\"Parses the files in the given request into nanopb ProtoFile objects.\n\n  Args:\n    request: A CodeGeneratorRequest, as passed by protoc.\n    options: The command-line options from nanopb_parse_options.\n\n  Returns:\n    A dictionary of filename to nanopb.ProtoFile objects, each one representing\n    the parsed form of a FileDescriptor in the request.\n  \"\"\"\n  # Process any include files first, in order to have them\n  # available as dependencies\n  parsed_files = {}\n  for fdesc in request.proto_file:\n    parsed_files[fdesc.name] = nanopb.parse_file(fdesc.name, fdesc, options)\n\n  return parsed_files\n\n\ndef nanopb_generate(request, options, parsed_files):\n  \"\"\"Generates C sources from the given parsed files.\n\n  Args:\n    request: A CodeGeneratorRequest, as passed by protoc.\n    options: The command-line options from nanopb_parse_options.\n    parsed_files: A dictionary of filename to nanopb.ProtoFile, as returned by\n      nanopb_parse_files().\n\n  Returns:\n    A list of nanopb output dictionaries, each one representing the code\n    generation result for each file to generate. The output dictionaries have\n    the following form:\n\n        {\n          'headername': Name of header file, ending in .h,\n          'headerdata': Contents of the header file,\n          'sourcename': Name of the source code file, ending in .c,\n          'sourcedata': Contents of the source code file\n        }\n  \"\"\"\n  output = []\n\n  for filename in request.file_to_generate:\n    for fdesc in request.proto_file:\n      if fdesc.name == filename:\n        results = nanopb.process_file(filename, fdesc, options, parsed_files)\n        output.append(results)\n\n  return output\n\n\ndef nanopb_write(results):\n  \"\"\"Translates nanopb output dictionaries to a CodeGeneratorResponse.\n\n  Args:\n    results: A list of generated source dictionaries, as returned by\n      nanopb_generate().\n\n  Returns:\n    A CodeGeneratorResponse describing the result of the code generation\n    process to protoc.\n  \"\"\"\n  response = plugin_pb2.CodeGeneratorResponse()\n\n  for result in results:\n    f = response.file.add()\n    f.name = result['headername']\n    f.content = result['headerdata']\n\n    f = response.file.add()\n    f.name = result['sourcename']\n    f.content = result['sourcedata']\n\n  return response\n\n\nif __name__ == '__main__':\n  main()"
  },
  {
    "path": "Unwinding/Crashlytics/ProtoSupport/proto_generator.py",
    "content": "#! /usr/bin/env python\n\n# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Generates and massages protocol buffer outputs.\n\nExample usage:\n\npython Crashlytics/ProtoSupport/build_protos.py \\\n  --nanopb \\\n  --protos_dir=Crashlytics/Classes/Protos/ \\\n  --pythonpath=~/Downloads/nanopb-0.3.9.2-macosx-x86/generator/ \\\n  --output_dir=Crashlytics/Protogen/\n\"\"\"\n\nfrom __future__ import print_function\n\nimport sys\n\nimport argparse\nimport os\nimport os.path\nimport re\nimport subprocess\n\nOBJC_GENERATOR='nanopb_objc_generator.py'\n\nCOPYRIGHT_NOTICE = '''\n/*\n * Copyright 2019 Google\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'''.lstrip()\n\n\ndef main():\n  parser = argparse.ArgumentParser(\n      description='Generates proto messages.')\n  parser.add_argument(\n      '--nanopb', action='store_true',\n      help='Generates nanopb messages.')\n  parser.add_argument(\n      '--objc', action='store_true',\n      help='Generates Objective-C messages.')\n  parser.add_argument(\n      '--protos_dir',\n      help='Source directory containing .proto files.')\n  parser.add_argument(\n      '--output_dir', '-d',\n      help='Directory to write files; subdirectories will be created.')\n  parser.add_argument(\n      '--protoc', default='protoc',\n      help='Location of the protoc executable')\n  parser.add_argument(\n      '--pythonpath',\n      help='Location of the protoc python library.')\n  parser.add_argument(\n      '--include', '-I', action='append', default=[],\n      help='Adds INCLUDE to the proto path.')\n\n\n  args = parser.parse_args()\n  if args.nanopb is None and args.objc is None:\n    parser.print_help()\n    sys.exit(1)\n\n  if args.protos_dir is None:\n    root_dir = os.path.abspath(os.path.dirname(__file__))\n    args.protos_dir = os.path.join(root_dir, 'protos')\n\n  if args.output_dir is None:\n    root_dir = os.path.abspath(os.path.dirname(__file__))\n    args.output_dir = os.path.join(root_dir, 'protogen-please-supply-an-outputdir')\n\n  all_proto_files = collect_files(args.protos_dir, '.proto')\n  if args.nanopb:\n    NanopbGenerator(args, all_proto_files).run()\n\n  proto_files = remove_well_known_protos(all_proto_files)\n\n  if args.objc:\n    ObjcProtobufGenerator(args, proto_files).run()\n\n\nclass NanopbGenerator(object):\n  \"\"\"Builds and runs the nanopb plugin to protoc.\"\"\"\n\n  def __init__(self, args, proto_files):\n    self.args = args\n    self.proto_files = proto_files\n\n  def run(self):\n    \"\"\"Performs the action of the generator.\"\"\"\n\n    nanopb_out = os.path.join(self.args.output_dir, 'nanopb')\n    mkdir(nanopb_out)\n\n    self.__run_generator(nanopb_out)\n\n    sources = collect_files(nanopb_out, '.nanopb.h', '.nanopb.c')\n    post_process_files(\n        sources,\n        add_copyright,\n        nanopb_remove_extern_c,\n        nanopb_rename_delete,\n        nanopb_use_module_import\n    )\n\n  def __run_generator(self, out_dir):\n    \"\"\"Invokes protoc using the nanopb plugin.\"\"\"\n    cmd = protoc_command(self.args)\n\n    gen = os.path.join(os.path.dirname(__file__), OBJC_GENERATOR)\n    cmd.append('--plugin=protoc-gen-nanopb=%s' % gen)\n\n    nanopb_flags = [\n        '--extension=.nanopb',\n        '--source-extension=.c',\n        '--no-timestamp'\n    ]\n    nanopb_flags.extend(['-I%s' % path for path in self.args.include])\n    cmd.append('--nanopb_out=%s:%s' % (' '.join(nanopb_flags), out_dir))\n\n    cmd.extend(self.proto_files)\n    run_protoc(self.args, cmd)\n\n\ndef protoc_command(args):\n  \"\"\"Composes the initial protoc command-line including its include path.\"\"\"\n  cmd = [args.protoc]\n  if args.include is not None:\n    cmd.extend(['-I=%s' % path for path in args.include])\n  return cmd\n\n\ndef run_protoc(args, cmd):\n  \"\"\"Actually runs the given protoc command.\n\n  Args:\n    args: The command-line args (including pythonpath)\n    cmd: The command to run expressed as a list of strings\n  \"\"\"\n\n  kwargs = {}\n  if args.pythonpath:\n    env = os.environ.copy()\n    old_path = env.get('PYTHONPATH')\n    env['PYTHONPATH'] = os.path.expanduser(args.pythonpath)\n    if old_path is not None:\n      env['PYTHONPATH'] += os.pathsep + old_path\n    kwargs['env'] = env\n\n  try:\n    print(subprocess.check_output(cmd, stderr=subprocess.STDOUT, **kwargs))\n  except subprocess.CalledProcessError as error:\n    print('command failed: ', ' '.join(cmd), '\\nerror: ', error.output)\n\n\ndef remove_well_known_protos(filenames):\n  \"\"\"Remove \"well-known\" protos for objc.\n\n  On those platforms we get these for free as a part of the protobuf runtime.\n  We only need them for nanopb.\n\n  Args:\n    filenames: A list of filenames, each naming a .proto file.\n\n  Returns:\n    The filenames with members of google/protobuf removed.\n  \"\"\"\n\n  return [f for f in filenames if 'protos/google/protobuf/' not in f]\n\n\ndef post_process_files(filenames, *processors):\n  for filename in filenames:\n    lines = []\n    with open(filename, 'r') as fd:\n      lines = fd.readlines()\n\n    for processor in processors:\n      lines = processor(lines)\n\n    write_file(filename, lines)\n\n\ndef write_file(filename, lines):\n  mkdir(os.path.dirname(filename))\n  with open(filename, 'w') as fd:\n    fd.write(''.join(lines))\n\n\ndef add_copyright(lines):\n  \"\"\"Adds a copyright notice to the lines.\"\"\"\n  result = [COPYRIGHT_NOTICE, '\\n']\n  result.extend(lines)\n  return result\n\n\ndef nanopb_remove_extern_c(lines):\n  \"\"\"Removes extern \"C\" directives from nanopb code.\n\n  Args:\n    lines: A nanobp-generated source file, split into lines.\n  Returns:\n    A list of strings, similar to the input but modified to remove extern \"C\".\n  \"\"\"\n  result = []\n  state = 'initial'\n  for line in lines:\n    if state == 'initial':\n      if '#ifdef __cplusplus' in line:\n        state = 'in-ifdef'\n        continue\n\n      result.append(line)\n\n    elif state == 'in-ifdef':\n      if '#endif' in line:\n        state = 'initial'\n\n  return result\n\n\ndef nanopb_rename_delete(lines):\n  \"\"\"Renames a delete symbol to delete_.\n\n  If a proto uses a field named 'delete', nanopb happily uses that in the\n  message definition. Works fine for C; not so much for C++.\n\n  Args:\n    lines: The lines to fix.\n\n  Returns:\n    The lines, fixed.\n  \"\"\"\n  delete_keyword = re.compile(r'\\bdelete\\b')\n  return [delete_keyword.sub('delete_', line) for line in lines]\n\n\ndef nanopb_use_module_import(lines):\n  \"\"\"Changes #include <pb.h> to include <nanopb/pb.h>\"\"\" # Don't let Copybara alter these lines.\n  return [line.replace('#include <pb.h>', '{}include <nanopb/pb.h>'.format(\"#\")) for line in lines]\n\n\ndef strip_trailing_whitespace(lines):\n  \"\"\"Removes trailing whitespace from the given lines.\"\"\"\n  return [line.rstrip() + '\\n' for line in lines]\n\n\ndef objc_flatten_imports(lines):\n  \"\"\"Flattens the import statements for compatibility with CocoaPods.\"\"\"\n\n  long_import = re.compile(r'#import \".*/')\n  return [long_import.sub('#import \"', line) for line in lines]\n\n\ndef objc_strip_extension_registry(lines):\n  \"\"\"Removes extensionRegistry methods from the classes.\"\"\"\n  skip = False\n  result = []\n  for line in lines:\n    if '+ (GPBExtensionRegistry*)extensionRegistry {' in line:\n      skip = True\n    if not skip:\n      result.append(line)\n    elif line == '}\\n':\n      skip = False\n\n  return result\n\n\ndef collect_files(root_dir, *extensions):\n  \"\"\"Finds files with the given extensions in the root_dir.\n\n  Args:\n    root_dir: The directory from which to start traversing.\n    *extensions: Filename extensions (including the leading dot) to find.\n\n  Returns:\n    A list of filenames, all starting with root_dir, that have one of the given\n    extensions.\n  \"\"\"\n  result = []\n  for root, _, files in os.walk(root_dir):\n    for basename in files:\n      for ext in extensions:\n        if basename.endswith(ext):\n          filename = os.path.join(root, basename)\n          result.append(filename)\n  return result\n\n\ndef mkdir(dirname):\n  if not os.path.isdir(dirname):\n    os.makedirs(dirname)\n\n\nif __name__ == '__main__':\n  main()"
  },
  {
    "path": "Unwinding/Crashlytics/Public/Unwinding.h",
    "content": "//\n//  Header.h\n//  \n//\n//  Created by Itay Brenner on 2/6/23.\n//\n\n#ifndef Header_h\n#define Header_h\n\n\n#endif /* Header_h */\n"
  },
  {
    "path": "Unwinding/Crashlytics/README.md",
    "content": "# Firebase Crashlytics SDK\n\n## Development\n\nFollow the subsequent instructions to develop, debug, unit test, and\nintegration test FirebaseCrashlytics:\n\n### Prereqs\n\n- At least CocoaPods 1.6.0\n- Install [cocoapods-generate](https://github.com/square/cocoapods-generate)\n- For nanopb and GDT:\n    - `brew install protobuf nanopb-generator`\n    - `easy_install protobuf python`\n\n### To Develop\n\n- Run `Crashlytics/generate_project.sh`\n- `open gen/FirebaseCrashlytics/FirebaseCrashlytics.xcworkspace`\n\nYou're now in an Xcode workspace generate for building, debugging and\ntesting the FirebaseCrashlytics CocoaPod.\n\n### Running Unit Tests\n\nOpen the generated workspace, choose the FirebaseCrashlytics-Unit-unit scheme and press Command-u.\n\n### Changing crash report uploads (using GDT)\n\n#### Update report proto\n\nIf the crash report proto needs to be updated, follow these instructions:\n\n- Update `ProtoSupport/Protos/crashlytics.proto` with the new changes\n- Depending on the type of fields added/removed, also update `ProtoSupport/Protos/crashlytics.options`.\n `CALLBACK` type fields in crashlytics.nanopb.c needs to be changed to `POINTER`\n (through the options file). Known field types that require an entry in crashlytics.options are\n `strings`, `repeated` and `bytes`.\n- Run `generate_project.sh` to update the nanopb .c/.h files."
  },
  {
    "path": "Unwinding/Crashlytics/third_party/libunwind/LICENSE",
    "content": "Permission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Unwinding/Crashlytics/third_party/libunwind/dwarf.h",
    "content": "/* libunwind - a platform-independent unwind library\n   Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P.\n        Contributed by David Mosberger-Tang <davidm@hpl.hp.com>\n\nThis file is part of libunwind.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */\n\n#pragma once\n\n//\n#define DWARF_EXTENDED_LENGTH_FLAG (0xffffffff)\n#define DWARF_CIE_ID_CIE_FLAG (0)\n\n// Exception Handling Pointer Encoding constants\n#define DW_EH_PE_VALUE_MASK (0x0F)\n#define DW_EH_PE_RELATIVE_OFFSET_MASK (0x70)\n\n// Register Definitions\n#define DW_EN_MAX_REGISTER_NUMBER (120)\n\nenum {\n  DW_EH_PE_ptr = 0x00,\n  DW_EH_PE_uleb128 = 0x01,\n  DW_EH_PE_udata2 = 0x02,\n  DW_EH_PE_udata4 = 0x03,\n  DW_EH_PE_udata8 = 0x04,\n  DW_EH_PE_signed = 0x08,\n  DW_EH_PE_sleb128 = 0x09,\n  DW_EH_PE_sdata2 = 0x0A,\n  DW_EH_PE_sdata4 = 0x0B,\n  DW_EH_PE_sdata8 = 0x0C,\n\n  DW_EH_PE_absptr = 0x00,\n  DW_EH_PE_pcrel = 0x10,\n  DW_EH_PE_textrel = 0x20,\n  DW_EH_PE_datarel = 0x30,\n  DW_EH_PE_funcrel = 0x40,\n  DW_EH_PE_aligned = 0x50,\n  DW_EH_PE_indirect = 0x80,\n  DW_EH_PE_omit = 0xFF\n};\n\n// Unwind Instructions\n\n#define DW_CFA_OPCODE_MASK (0xC0)\n#define DW_CFA_OPERAND_MASK (0x3F)\n\nenum {\n  DW_CFA_nop = 0x0,\n  DW_CFA_set_loc = 0x1,\n  DW_CFA_advance_loc1 = 0x2,\n  DW_CFA_advance_loc2 = 0x3,\n  DW_CFA_advance_loc4 = 0x4,\n  DW_CFA_offset_extended = 0x5,\n  DW_CFA_restore_extended = 0x6,\n  DW_CFA_undefined = 0x7,\n  DW_CFA_same_value = 0x8,\n  DW_CFA_register = 0x9,\n  DW_CFA_remember_state = 0xA,\n  DW_CFA_restore_state = 0xB,\n  DW_CFA_def_cfa = 0xC,\n  DW_CFA_def_cfa_register = 0xD,\n  DW_CFA_def_cfa_offset = 0xE,\n  DW_CFA_def_cfa_expression = 0xF,\n  DW_CFA_expression = 0x10,\n  DW_CFA_offset_extended_sf = 0x11,\n  DW_CFA_def_cfa_sf = 0x12,\n  DW_CFA_def_cfa_offset_sf = 0x13,\n  DW_CFA_val_offset = 0x14,\n  DW_CFA_val_offset_sf = 0x15,\n  DW_CFA_val_expression = 0x16,\n\n  // opcode is in high 2 bits, operand in is lower 6 bits\n  DW_CFA_advance_loc = 0x40,  // operand is delta\n  DW_CFA_offset = 0x80,       // operand is register\n  DW_CFA_restore = 0xC0,      // operand is register\n\n  // GNU extensions\n  DW_CFA_GNU_window_save = 0x2D,\n  DW_CFA_GNU_args_size = 0x2E,\n  DW_CFA_GNU_negative_offset_extended = 0x2F\n};\n\n// Expression Instructions\nenum {\n  DW_OP_addr = 0x03,\n  DW_OP_deref = 0x06,\n  DW_OP_const1u = 0x08,\n  DW_OP_const1s = 0x09,\n  DW_OP_const2u = 0x0A,\n  DW_OP_const2s = 0x0B,\n  DW_OP_const4u = 0x0C,\n  DW_OP_const4s = 0x0D,\n  DW_OP_const8u = 0x0E,\n  DW_OP_const8s = 0x0F,\n  DW_OP_constu = 0x10,\n  DW_OP_consts = 0x11,\n  DW_OP_dup = 0x12,\n  DW_OP_drop = 0x13,\n  DW_OP_over = 0x14,\n  DW_OP_pick = 0x15,\n  DW_OP_swap = 0x16,\n  DW_OP_rot = 0x17,\n  DW_OP_xderef = 0x18,\n  DW_OP_abs = 0x19,\n  DW_OP_and = 0x1A,\n  DW_OP_div = 0x1B,\n  DW_OP_minus = 0x1C,\n  DW_OP_mod = 0x1D,\n  DW_OP_mul = 0x1E,\n  DW_OP_neg = 0x1F,\n  DW_OP_not = 0x20,\n  DW_OP_or = 0x21,\n  DW_OP_plus = 0x22,\n  DW_OP_plus_uconst = 0x23,\n  DW_OP_shl = 0x24,\n  DW_OP_shr = 0x25,\n  DW_OP_shra = 0x26,\n  DW_OP_xor = 0x27,\n  DW_OP_skip = 0x2F,\n  DW_OP_bra = 0x28,\n  DW_OP_eq = 0x29,\n  DW_OP_ge = 0x2A,\n  DW_OP_gt = 0x2B,\n  DW_OP_le = 0x2C,\n  DW_OP_lt = 0x2D,\n  DW_OP_ne = 0x2E,\n  DW_OP_lit0 = 0x30,\n  DW_OP_lit1 = 0x31,\n  DW_OP_lit2 = 0x32,\n  DW_OP_lit3 = 0x33,\n  DW_OP_lit4 = 0x34,\n  DW_OP_lit5 = 0x35,\n  DW_OP_lit6 = 0x36,\n  DW_OP_lit7 = 0x37,\n  DW_OP_lit8 = 0x38,\n  DW_OP_lit9 = 0x39,\n  DW_OP_lit10 = 0x3A,\n  DW_OP_lit11 = 0x3B,\n  DW_OP_lit12 = 0x3C,\n  DW_OP_lit13 = 0x3D,\n  DW_OP_lit14 = 0x3E,\n  DW_OP_lit15 = 0x3F,\n  DW_OP_lit16 = 0x40,\n  DW_OP_lit17 = 0x41,\n  DW_OP_lit18 = 0x42,\n  DW_OP_lit19 = 0x43,\n  DW_OP_lit20 = 0x44,\n  DW_OP_lit21 = 0x45,\n  DW_OP_lit22 = 0x46,\n  DW_OP_lit23 = 0x47,\n  DW_OP_lit24 = 0x48,\n  DW_OP_lit25 = 0x49,\n  DW_OP_lit26 = 0x4A,\n  DW_OP_lit27 = 0x4B,\n  DW_OP_lit28 = 0x4C,\n  DW_OP_lit29 = 0x4D,\n  DW_OP_lit30 = 0x4E,\n  DW_OP_lit31 = 0x4F,\n  DW_OP_reg0 = 0x50,\n  DW_OP_reg1 = 0x51,\n  DW_OP_reg2 = 0x52,\n  DW_OP_reg3 = 0x53,\n  DW_OP_reg4 = 0x54,\n  DW_OP_reg5 = 0x55,\n  DW_OP_reg6 = 0x56,\n  DW_OP_reg7 = 0x57,\n  DW_OP_reg8 = 0x58,\n  DW_OP_reg9 = 0x59,\n  DW_OP_reg10 = 0x5A,\n  DW_OP_reg11 = 0x5B,\n  DW_OP_reg12 = 0x5C,\n  DW_OP_reg13 = 0x5D,\n  DW_OP_reg14 = 0x5E,\n  DW_OP_reg15 = 0x5F,\n  DW_OP_reg16 = 0x60,\n  DW_OP_reg17 = 0x61,\n  DW_OP_reg18 = 0x62,\n  DW_OP_reg19 = 0x63,\n  DW_OP_reg20 = 0x64,\n  DW_OP_reg21 = 0x65,\n  DW_OP_reg22 = 0x66,\n  DW_OP_reg23 = 0x67,\n  DW_OP_reg24 = 0x68,\n  DW_OP_reg25 = 0x69,\n  DW_OP_reg26 = 0x6A,\n  DW_OP_reg27 = 0x6B,\n  DW_OP_reg28 = 0x6C,\n  DW_OP_reg29 = 0x6D,\n  DW_OP_reg30 = 0x6E,\n  DW_OP_reg31 = 0x6F,\n  DW_OP_breg0 = 0x70,\n  DW_OP_breg1 = 0x71,\n  DW_OP_breg2 = 0x72,\n  DW_OP_breg3 = 0x73,\n  DW_OP_breg4 = 0x74,\n  DW_OP_breg5 = 0x75,\n  DW_OP_breg6 = 0x76,\n  DW_OP_breg7 = 0x77,\n  DW_OP_breg8 = 0x78,\n  DW_OP_breg9 = 0x79,\n  DW_OP_breg10 = 0x7A,\n  DW_OP_breg11 = 0x7B,\n  DW_OP_breg12 = 0x7C,\n  DW_OP_breg13 = 0x7D,\n  DW_OP_breg14 = 0x7E,\n  DW_OP_breg15 = 0x7F,\n  DW_OP_breg16 = 0x80,\n  DW_OP_breg17 = 0x81,\n  DW_OP_breg18 = 0x82,\n  DW_OP_breg19 = 0x83,\n  DW_OP_breg20 = 0x84,\n  DW_OP_breg21 = 0x85,\n  DW_OP_breg22 = 0x86,\n  DW_OP_breg23 = 0x87,\n  DW_OP_breg24 = 0x88,\n  DW_OP_breg25 = 0x89,\n  DW_OP_breg26 = 0x8A,\n  DW_OP_breg27 = 0x8B,\n  DW_OP_breg28 = 0x8C,\n  DW_OP_breg29 = 0x8D,\n  DW_OP_breg30 = 0x8E,\n  DW_OP_breg31 = 0x8F,\n  DW_OP_regx = 0x90,\n  DW_OP_fbreg = 0x91,\n  DW_OP_bregx = 0x92,\n  DW_OP_piece = 0x93,\n  DW_OP_deref_size = 0x94,\n  DW_OP_xderef_size = 0x95,\n  DW_OP_nop = 0x96,\n  DW_OP_push_object_addres = 0x97,\n  DW_OP_call2 = 0x98,\n  DW_OP_call4 = 0x99,\n  DW_OP_call_ref = 0x9A,\n  DW_OP_lo_user = 0xE0,\n  DW_OP_APPLE_uninit = 0xF0,\n  DW_OP_hi_user = 0xFF\n};\n"
  },
  {
    "path": "Unwinding/FirebaseCrashlytics.podspec",
    "content": "Pod::Spec.new do |s|\n  s.name             = 'FirebaseCrashlytics'\n  s.version          = '8.13.0'\n  s.summary          = 'Best and lightest-weight crash reporting for mobile, desktop and tvOS.'\n  s.description      = 'Firebase Crashlytics helps you track, prioritize, and fix stability issues that erode app quality.'\n  s.homepage         = 'https://firebase.google.com/'\n  s.license          = { :type => 'Apache', :file => 'Crashlytics/LICENSE' }\n  s.authors          = 'Google, Inc.'\n  s.source           = {\n    :git => 'https://github.com/firebase/firebase-ios-sdk.git',\n    :tag => 'CocoaPods-' + s.version.to_s\n  }\n\n  ios_deployment_target = '9.0'\n  osx_deployment_target = '10.12'\n  tvos_deployment_target = '10.0'\n  watchos_deployment_target = '6.0'\n\n  s.ios.deployment_target = ios_deployment_target\n  s.osx.deployment_target = osx_deployment_target\n  s.tvos.deployment_target = tvos_deployment_target\n  s.watchos.deployment_target = watchos_deployment_target\n\n  s.cocoapods_version = '>= 1.4.0'\n  s.prefix_header_file = false\n\n  s.source_files = [\n    'Crashlytics/Crashlytics/**/*.{c,h,m,mm}',\n    'Crashlytics/Protogen/**/*.{c,h,m,mm}',\n    'Crashlytics/Shared/**/*.{c,h,m,mm}',\n    'Crashlytics/third_party/**/*.{c,h,m,mm}',\n    'FirebaseCore/Sources/Private/*.h',\n    'FirebaseInstallations/Source/Library/Private/*.h',\n    'Interop/Analytics/Public/*.h',\n  ]\n\n  s.public_header_files = [\n    'Crashlytics/Crashlytics/Public/FirebaseCrashlytics/*.h'\n  ]\n\n  s.preserve_paths = [\n    'Crashlytics/README.md',\n    'run',\n    'upload-symbols',\n  ]\n\n  # Ensure the run script and upload-symbols are callable via\n  # ${PODS_ROOT}/FirebaseCrashlytics/<name>\n  s.prepare_command = <<-PREPARE_COMMAND_END\n  PREPARE_COMMAND_END\n\n  s.libraries = 'c++', 'z'\n  s.ios.frameworks = 'Security', 'SystemConfiguration'\n  s.macos.frameworks = 'Security', 'SystemConfiguration'\n  s.osx.frameworks = 'Security', 'SystemConfiguration'\n  s.watchos.frameworks = 'Security'\n\n  s.ios.pod_target_xcconfig = {\n    'GCC_C_LANGUAGE_STANDARD' => 'c99',\n    'GCC_PREPROCESSOR_DEFINITIONS' =>\n      'CLS_SDK_NAME=\"Crashlytics iOS SDK\" ' +\n      # For nanopb:\n      'PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1',\n    'HEADER_SEARCH_PATHS' => '\"${PODS_TARGET_SRCROOT}\"',\n  }\n\n  s.osx.pod_target_xcconfig = {\n    'GCC_C_LANGUAGE_STANDARD' => 'c99',\n    'GCC_PREPROCESSOR_DEFINITIONS' =>\n      'CLS_SDK_NAME=\"Crashlytics Mac SDK\" ' +\n      # For nanopb:\n      'PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1',\n    'HEADER_SEARCH_PATHS' => '\"${PODS_TARGET_SRCROOT}\"',\n  }\n\n  s.tvos.pod_target_xcconfig = {\n    'GCC_C_LANGUAGE_STANDARD' => 'c99',\n    'GCC_PREPROCESSOR_DEFINITIONS' =>\n      'CLS_SDK_NAME=\"Crashlytics tvOS SDK\" ' +\n      # For nanopb:\n      'PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1',\n    'HEADER_SEARCH_PATHS' => '\"${PODS_TARGET_SRCROOT}\"',\n  }\n\n  s.watchos.pod_target_xcconfig = {\n    'GCC_C_LANGUAGE_STANDARD' => 'c99',\n    'GCC_PREPROCESSOR_DEFINITIONS' =>\n      'CLS_SDK_NAME=\"Crashlytics watchOS SDK\" ' +\n      # For nanopb:\n      'PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1',\n    'OTHER_LD_FLAGS' => '$(inherited) -sectcreate __TEXT __info_plist',\n    'HEADER_SEARCH_PATHS' => '\"${PODS_TARGET_SRCROOT}\"',\n  }\n\n  s.test_spec 'unit' do |unit_tests|\n    unit_tests.scheme = { :code_coverage => true }\n    # Unit tests can't run on watchOS.\n    unit_tests.platforms = {\n      :ios => ios_deployment_target,\n      :osx => osx_deployment_target,\n      :tvos => tvos_deployment_target\n    }\n    unit_tests.source_files = 'Crashlytics/UnitTests/*.[mh]',\n                              'Crashlytics/UnitTests/*/*.[mh]'\n    unit_tests.resources = 'Crashlytics/UnitTests/Data/*',\n                           'Crashlytics/UnitTests/*.clsrecord',\n                           'Crashlytics/UnitTests/FIRCLSMachO/data/*'\n  end\nend\n"
  },
  {
    "path": "Unwinding/LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "build.sh",
    "content": "xcodebuild archive \\\n -scheme ETTrace \\\n -archivePath ./ETTrace-iphonesimulator.xcarchive \\\n -sdk iphonesimulator \\\n -destination 'generic/platform=iOS Simulator' \\\n BUILD_LIBRARY_FOR_DISTRIBUTION=YES \\\n INSTALL_PATH='Library/Frameworks' \\\n SKIP_INSTALL=NO \\\n CLANG_CXX_LANGUAGE_STANDARD=c++17\n\nxcodebuild archive \\\n -scheme ETTrace \\\n -archivePath ./ETTrace-iphoneos.xcarchive \\\n -sdk iphoneos \\\n -destination 'generic/platform=iOS' \\\n BUILD_LIBRARY_FOR_DISTRIBUTION=YES \\\n INSTALL_PATH='Library/Frameworks' \\\n SKIP_INSTALL=NO \\\n CLANG_CXX_LANGUAGE_STANDARD=c++17\n\nxcodebuild archive \\\n -scheme ETTrace \\\n -archivePath ./ETTrace-tvOSSimulator.xcarchive \\\n -sdk appletvsimulator \\\n -destination 'generic/platform=tvOS Simulator' \\\n BUILD_LIBRARY_FOR_DISTRIBUTION=YES \\\n INSTALL_PATH='Library/Frameworks' \\\n SKIP_INSTALL=NO \\\n CLANG_CXX_LANGUAGE_STANDARD=c++17\n\nxcodebuild archive \\\n -scheme ETTrace \\\n -archivePath ./ETTrace-visionOSSimulator.xcarchive \\\n -sdk xrsimulator \\\n -destination 'generic/platform=visionOS Simulator' \\\n BUILD_LIBRARY_FOR_DISTRIBUTION=YES \\\n INSTALL_PATH='Library/Frameworks' \\\n SKIP_INSTALL=NO \\\n CLANG_CXX_LANGUAGE_STANDARD=c++17\n\nxcodebuild -create-xcframework \\\n -framework ./ETTrace-iphonesimulator.xcarchive/Products/Library/Frameworks/ETTrace.framework \\\n -framework ./ETTrace-iphoneos.xcarchive/Products/Library/Frameworks/ETTrace.framework \\\n -framework ./ETTrace-visionOSSimulator.xcarchive/Products/Library/Frameworks/ETTrace.framework \\\n -framework ./ETTrace-tvOSSimulator.xcarchive/Products/Library/Frameworks/ETTrace.framework \\\n -output ./ETTrace.xcframework"
  },
  {
    "path": "build_runner.sh",
    "content": "xcodebuild archive \\\n -scheme ETTraceRunner \\\n -archivePath ./ETTraceRunner.xcarchive \\\n -sdk macosx \\\n -destination 'generic/platform=macOS' \\\n SKIP_INSTALL=NO\n\ncodesign --entitlements ./ETTrace/ETTraceRunner/ETTraceRunner.entitlements -f -s $SIGNING_IDENTITY ETTraceRunner.xcarchive/Products/usr/local/bin/ETTraceRunner\n\ncp ETTraceRunner.xcarchive/Products/usr/local/bin/ETTraceRunner ETTraceRunner"
  }
]