├── .github
├── FUNDING.yml
└── workflows
│ └── ci.yml
├── .gitignore
├── .spi.yml
├── .swiftpm
└── xcode
│ └── xcshareddata
│ └── xcschemes
│ └── Meter.xcscheme
├── CODE_OF_CONDUCT.md
├── LICENSE
├── Package.swift
├── README.md
├── Sources
├── BinaryImage
│ ├── BinaryImage.h
│ └── BinaryImage.m
└── Meter
│ ├── CallStackTree.swift
│ ├── DiagnosticPayload.swift
│ ├── DlfcnSymbolicator.swift
│ ├── ExceptionInfo.swift
│ └── Symbolicator.swift
└── Tests
├── BinaryImageTests
└── BinaryImageTests.swift
└── MeterTests
├── DiagnosticTests.swift
├── Resources
├── exception.log
├── ios_uncaugh_exception_payload.json
├── real_report.json
├── xcode_simulated.crash
├── xcode_simulated.json
├── xcode_simulated_macOS_12.json
└── xcode_simulated_macOS_13.json
└── SymbolicationTests.swift
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [mattmassicotte]
2 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths-ignore:
8 | - 'README.md'
9 | - 'CODE_OF_CONDUCT.md'
10 | - '.editorconfig'
11 | - '.spi.yml'
12 | pull_request:
13 | branches:
14 | - main
15 |
16 | env:
17 | DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer
18 |
19 | jobs:
20 | test-13:
21 | name: Test macOS 13
22 | runs-on: macOS-13
23 | strategy:
24 | matrix:
25 | destination:
26 | - "platform=macOS"
27 | # the test copies files, and that fails to built for other platforms for some reason...
28 | # - "platform=iOS Simulator,name=iPhone 12"
29 | # - "platform=watchOS Simulator,name=Apple Watch Series 6 (40mm)"
30 |
31 | steps:
32 | - uses: actions/checkout@v4
33 | - name: Test platform ${{ matrix.destination }}
34 | run: set -o pipefail && xcodebuild -scheme Meter -destination "${{ matrix.destination }}" test | xcbeautify
35 |
36 | test-14:
37 | name: Test macOS 14
38 | runs-on: macOS-14
39 | strategy:
40 | matrix:
41 | destination:
42 | - "platform=macOS"
43 |
44 | steps:
45 | - uses: actions/checkout@v4
46 | - name: Test platform ${{ matrix.destination }}
47 | run: set -o pipefail && xcodebuild -scheme Meter -destination "${{ matrix.destination }}" test | xcbeautify
48 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | xcuserdata/
5 | DerivedData/
6 | .swiftpm/configuration/registries.json
7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8 | .netrc
9 |
--------------------------------------------------------------------------------
/.spi.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | builder:
3 | configs:
4 | - documentation_targets: [Meter]
5 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcshareddata/xcschemes/Meter.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
57 |
63 |
64 |
65 |
71 |
77 |
78 |
79 |
85 |
91 |
92 |
93 |
99 |
105 |
106 |
107 |
113 |
119 |
120 |
121 |
122 |
123 |
130 |
131 |
137 |
138 |
139 |
140 |
142 |
148 |
149 |
150 |
152 |
158 |
159 |
160 |
161 |
162 |
172 |
173 |
179 |
180 |
186 |
187 |
188 |
189 |
191 |
192 |
195 |
196 |
197 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at support@chimehq.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2020, Chime
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.9
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "Meter",
7 | platforms: [
8 | .macOS(.v10_13),
9 | .iOS(.v12),
10 | .tvOS(.v12),
11 | .watchOS(.v4),
12 | .visionOS(.v1),
13 | ],
14 | products: [
15 | .library(name: "Meter", targets: ["Meter"]),
16 | ],
17 | dependencies: [],
18 | targets: [
19 | .target(name: "BinaryImage", publicHeadersPath: "."),
20 | .target(name: "Meter", dependencies: ["BinaryImage"]),
21 | .testTarget(
22 | name: "MeterTests",
23 | dependencies: ["Meter"],
24 | resources: [
25 | .copy("Resources"),
26 | ]
27 | ),
28 | .testTarget(
29 | name: "BinaryImageTests",
30 | dependencies: ["BinaryImage"]
31 | ),
32 | ]
33 | )
34 |
35 | let swiftSettings: [SwiftSetting] = [
36 | .enableExperimentalFeature("StrictConcurrency")
37 | ]
38 |
39 | for target in package.targets {
40 | var settings = target.swiftSettings ?? []
41 | settings.append(contentsOf: swiftSettings)
42 | target.swiftSettings = settings
43 | }
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [![Build Status][build status badge]][build status]
4 | [![Platforms][platforms badge]][platforms]
5 | [![Documentation][documentation badge]][documentation]
6 | [![Matrix][matrix badge]][matrix]
7 |
8 |
9 |
10 | # Meter
11 |
12 | Meter is a companion library to [MetricKit](https://developer.apple.com/documentation/metrickit). It aims to provide the following capabilities:
13 |
14 | - API for `MXCallStackTree`
15 | - Types for `MXDiagnostic` emulation and coding
16 | - `MXMetricManager`-like interface for unsupported platforms
17 | - On-device symbolication
18 | - Account for MetricKit inconsistencies across platforms and types
19 | - Support for custom exception reporting
20 |
21 | If you're also looking for a way to transmit MetricKit data to your server, have a look at [MeterReporter](https://github.com/ChimeHQ/MeterReporter). It uses Meter under the hood, and takes care of the details.
22 |
23 | ## Integration
24 |
25 | ```swift
26 | dependencies: [
27 | .package(url: "https://github.com/ChimeHQ/Meter")
28 | ]
29 | ```
30 |
31 | ## Expanded API
32 |
33 | The MetricKit API for crash reporting is unwieldy. In particular, `MXCallStackTree` lacks any kind of interface for interacting with its structure. Meter includes some classes that make it easier to work with. In addition to providing an API for `MXCallStackTree`, Meter includes types to emulate and parse MetricKit diagnostics.
34 |
35 | ```swift
36 | let data = mxTree.jsonRepresentation()
37 | let tree = try CallStackTree.from(data: data)
38 |
39 | for frame in tree.callStacks[0].frames {
40 | print("\(frame.address) \(frame.binaryName) \(frame.binaryUUID)")
41 | }
42 | ```
43 |
44 | ## Custom Exceptions
45 |
46 | As of iOS 17, macOS 14, visionOS 1.0, MetricKit does capture uncaught [NSExceptions](https://developer.apple.com/documentation/metrickit/mxcrashdiagnostic/4172947-exceptionreason). To help support older OSes, and exceptions that do not originate from places that are covered by this feature, custom exceptions are also supported. These can be created from an `NSException` object, which will capture all the needed runtime information to emulate a standard `CallStack`.
47 |
48 | How you actually get access to the `NSException` is not defined by Meter. But, if you have one, the `CrashDiagnostic` type also includes an `exceptionInfo` property that can accept one of these for easy encoding.
49 |
50 | ## On-Device Symbolication
51 |
52 | The stack traces provided by MetricKit, like other types of crash logs, are not symbolicated. There are a bunch of different ways to tackle this problem, but one very convenient option is just to do it as a post-processing step on the device where the crash occurred. This does come, however, with one major drawback. It only works when you still have access to the same binaries. OS updates will almost certainly change all the OS binaries. The same is true for an app update, though in that case, an off-line symbolication step using a dSYM is still doable.
53 |
54 | Meter provides an API for performing symbolication, via the `Symbolicator` protocol. The core of this protocol should be usable to symbolicate any address, and is not tied to MetricKit. But, the protocol also does include a number of convenience methods that can operate on the various MetricKit classes. The result uses the Meter's wrapper classes to return `Frame` instances which include a `symbolInfo` property. This property can be accessed directly or just re-encoded for transport.
55 |
56 | ```swift
57 | let symbolicator = DlfcnSymbolicator()
58 | let symPayload = symbolicator.symbolicate(payload: diagnosticPayload)
59 | ```
60 |
61 | ### DlfcnSymbolicator
62 |
63 | This class implements the `Symbolicator` protocol, and uses the functions with `dlfcn.h` to determine symbol/offset. This works, but does have some limitations. First, it relies on looking up symbols in the **currently executing** process, so it will only work if the needed binary is currently loaded.
64 |
65 | Second, these functions return `` for some binary's symbols on iOS. I know the symbol information is still accessible from the binary, so it's unclear why this is done.
66 |
67 | This is a relatively inexpensive symbolication pass, and is a first effort. Further work here is definitely necessary.
68 |
69 | ## Contributing and Collaboration
70 |
71 | I would love to hear from you! Issues or pull requests work great. Both a [Matrix space][matrix] and [Discord][discord] are available for live help, but I have a strong bias towards answering in the form of documentation. You can also find me on [mastodon](https://mastodon.social/@mattiem).
72 |
73 | I prefer collaboration, and would love to find ways to work together if you have a similar project.
74 |
75 | I prefer indentation with tabs for improved accessibility. But, I'd rather you use the system you want and make a PR than hesitate because of whitespace.
76 |
77 | By participating in this project you agree to abide by the [Contributor Code of Conduct](CODE_OF_CONDUCT.md).
78 |
79 | [build status]: https://github.com/ChimeHQ/Meter/actions
80 | [build status badge]: https://github.com/ChimeHQ/Meter/workflows/CI/badge.svg
81 | [platforms]: https://swiftpackageindex.com/ChimeHQ/Meter
82 | [platforms badge]: https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FChimeHQ%2FMeter%2Fbadge%3Ftype%3Dplatforms
83 | [documentation]: https://swiftpackageindex.com/ChimeHQ/Meter/main/documentation
84 | [documentation badge]: https://img.shields.io/badge/Documentation-DocC-blue
85 | [matrix]: https://matrix.to/#/%23chimehq%3Amatrix.org
86 | [matrix badge]: https://img.shields.io/matrix/chimehq%3Amatrix.org?label=Matrix
87 | [discord]: https://discord.gg/esFpX6sErJ
88 |
--------------------------------------------------------------------------------
/Sources/BinaryImage/BinaryImage.h:
--------------------------------------------------------------------------------
1 | #ifndef BinaryImage_h
2 | #define BinaryImage_h
3 |
4 | #define _Noescape __attribute__((noescape))
5 | #define ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin")
6 | #define ASSUME_NONNULL_END _Pragma("clang assume_nonnull end")
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #if __OBJC__
13 | #import
14 | #endif
15 |
16 | ASSUME_NONNULL_BEGIN
17 |
18 | typedef struct {
19 | uintptr_t address;
20 | intptr_t loadAddress;
21 | uintptr_t length;
22 | } MachODataRegion;
23 |
24 | typedef struct {
25 | const uint8_t* uuid;
26 | intptr_t slide;
27 | MachODataRegion ehFrameRegion;
28 | MachODataRegion unwindInfoRegion;
29 | uintptr_t loadAddress;
30 | uintptr_t textSize;
31 | const char* path;
32 | } MachOData;
33 |
34 | #if __LP64__
35 | typedef struct mach_header_64 MachOHeader;
36 | typedef struct section_64 MachOSection;
37 | typedef struct segment_command_64 SegmentCommand;
38 | typedef struct section_64 Section;
39 |
40 | const static uint32_t LCSegment = LC_SEGMENT_64;
41 | #else
42 | typedef struct mach_header MachOHeader;
43 | typedef struct section MachOSection;
44 | typedef struct segment_command SegmentCommand;
45 | typedef struct section Section;
46 |
47 | const static uint32_t LCSegment = LC_SEGMENT;
48 | #endif
49 |
50 | typedef struct {
51 | const char* name;
52 | const MachOHeader* header;
53 | } BinaryImage;
54 |
55 | typedef void (^BinaryImageIterator)(BinaryImage image, bool* stop);
56 |
57 | void BinaryImageEnumerateLoadedImages(_Noescape BinaryImageIterator iterator);
58 |
59 | typedef void (^BinaryImageLoadCommandIterator)(const struct load_command* lcmd, uint32_t cmdCode, bool* stop);
60 |
61 | void BinaryImageEnumerateLoadCommands(const MachOHeader* header, _Noescape BinaryImageLoadCommandIterator iterator);
62 |
63 | uint8_t* _Nullable BinaryImageGetUUIDBytesFromLoadCommand(const struct load_command* lcmd, uint32_t cmdCode);
64 |
65 | #if __OBJC__
66 | NSUUID* _Nullable BinaryuImageUUIDFromLoadCommand(const struct load_command* lcmd, uint32_t cmdCode);
67 | NSUUID* BinaryImageGetUUID(const MachOHeader* header);
68 |
69 | void LogException(NSException* exception, NSString* path);
70 | #endif
71 |
72 | ASSUME_NONNULL_END
73 |
74 | #endif /* BinaryImage_h */
75 |
--------------------------------------------------------------------------------
/Sources/BinaryImage/BinaryImage.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #include "BinaryImage.h"
4 | #include
5 |
6 | void BinaryImageEnumerateLoadedImages(BinaryImageIterator iterator) {
7 | for (uint32_t i = 0; i < _dyld_image_count(); ++i) {
8 | BinaryImage image = {0};
9 |
10 | image.name = _dyld_get_image_name(i);
11 | image.header = (MachOHeader*)_dyld_get_image_header(i);
12 |
13 | bool stop = false;
14 |
15 | iterator(image, &stop);
16 |
17 | if (stop) {
18 | break;
19 | }
20 | }
21 | }
22 |
23 | void BinaryImageEnumerateLoadCommands(const MachOHeader* header, BinaryImageLoadCommandIterator iterator) {
24 | if (header == NULL) {
25 | return;
26 | }
27 |
28 | const uint8_t *ptr = (uint8_t *)header + sizeof(MachOHeader);
29 |
30 | for (uint32_t i = 0; i < header->ncmds; ++i) {
31 | const struct load_command* const lcmd = (struct load_command*)ptr;
32 | const uint32_t cmdCode = lcmd->cmd & ~LC_REQ_DYLD;
33 |
34 | bool stop = false;
35 |
36 | iterator(lcmd, cmdCode, &stop);
37 |
38 | if (stop) {
39 | break;
40 | }
41 |
42 | ptr += lcmd->cmdsize;
43 | }
44 | }
45 |
46 | uint8_t* BinaryImageGetUUIDBytesFromLoadCommand(const struct load_command* lcmd, uint32_t cmdCode) {
47 | if (lcmd == NULL || cmdCode != LC_UUID) {
48 | return NULL;
49 | }
50 |
51 | return ((struct uuid_command*)lcmd)->uuid;
52 | }
53 |
54 | NSUUID* BinaryuImageUUIDFromLoadCommand(const struct load_command* lcmd, uint32_t cmdCode) {
55 | const uint8_t* bytes = BinaryImageGetUUIDBytesFromLoadCommand(lcmd, cmdCode);
56 |
57 | return [[NSUUID alloc] initWithUUIDBytes:bytes];
58 | }
59 |
60 | NSUUID* BinaryImageGetUUID(const MachOHeader* header) {
61 | __block NSUUID* uuid = nil;
62 |
63 | BinaryImageEnumerateLoadCommands(header, ^(const struct load_command * _Nonnull lcmd, uint32_t cmdCode, bool * _Nonnull stop) {
64 | switch (cmdCode) {
65 | case LC_UUID:
66 | uuid = BinaryuImageUUIDFromLoadCommand(lcmd, cmdCode);
67 | *stop = true;
68 | break;
69 | }
70 | });
71 |
72 | return uuid;
73 | }
74 |
75 | bool BinaryImageGetData(const MachOHeader* header, const char* path, MachOData* data) {
76 | if (header == NULL || data == NULL) {
77 | return false;
78 | }
79 |
80 | const uint8_t *ptr = (uint8_t *)header + sizeof(MachOHeader);
81 |
82 | data->loadAddress = (uintptr_t)header;
83 | data->path = path;
84 |
85 | return true;
86 | }
87 |
--------------------------------------------------------------------------------
/Sources/Meter/CallStackTree.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | #if canImport(MetricKit)
3 | import MetricKit
4 | #endif
5 |
6 | public struct Binary: Codable, Hashable {
7 | public var uuid: UUID
8 | public var loadAddress: Int
9 | public var approximateSize: Int
10 | public var name: String?
11 |
12 | public init(uuid: UUID, loadAddress: Int, approximateSize: Int, name: String?) {
13 | self.uuid = uuid
14 | self.loadAddress = loadAddress
15 | self.approximateSize = approximateSize
16 | self.name = name
17 | }
18 |
19 | public init?(uuid: String, loadAddress: Int, approximateSize: Int, name: String?) {
20 | guard let value = UUID(uuidString: uuid) else { return nil }
21 |
22 | self.uuid = value
23 | self.loadAddress = loadAddress
24 | self.approximateSize = approximateSize
25 | self.name = name
26 | }
27 |
28 | public var addressRange: Range {
29 | return loadAddress.. Bool {
33 | return addressRange.contains(address)
34 | }
35 | }
36 |
37 | public struct Frame: Codable, Hashable {
38 | public var binaryUUID: UUID?
39 | public var offsetIntoBinaryTextSegment: Int?
40 | public var sampleCount: Int?
41 | public var binaryName: String?
42 | public var address: UInt64
43 | public var subFrames: [Frame]?
44 | public var symbolInfo: [SymbolInfo]?
45 |
46 | public init(binaryUUID: UUID? = nil, offsetIntoBinaryTextSegment: Int? = nil, sampleCount: Int? = nil, binaryName: String? = nil, address: UInt64, subFrames: [Frame]?, symbolInfo: [SymbolInfo]? = nil) {
47 | self.binaryUUID = binaryUUID
48 | self.offsetIntoBinaryTextSegment = offsetIntoBinaryTextSegment
49 | self.sampleCount = sampleCount
50 | self.binaryName = binaryName
51 | self.address = address
52 | self.subFrames = subFrames
53 | self.symbolInfo = symbolInfo
54 | }
55 |
56 | init(frame: Frame, symbolInfo: [SymbolInfo], subFrames: [Frame]?) {
57 | self.binaryUUID = frame.binaryUUID
58 | self.offsetIntoBinaryTextSegment = frame.offsetIntoBinaryTextSegment
59 | self.sampleCount = frame.sampleCount
60 | self.binaryName = frame.binaryName
61 | self.address = frame.address
62 | self.subFrames = subFrames
63 | self.symbolInfo = symbolInfo
64 | }
65 |
66 | public var flattenedFrames: [Frame] {
67 | return subFrames?.flatMap({ [$0] + $0.flattenedFrames }) ?? []
68 | }
69 |
70 | public func binary(withOffsetAsLoadAddress: Bool) -> Binary? {
71 | guard
72 | let uuid = binaryUUID,
73 | let offset = offsetIntoBinaryTextSegment
74 | else {
75 | return nil
76 | }
77 |
78 | let loadAddress = withOffsetAsLoadAddress ? offset : Int(address) - offset
79 | let size = Int(address) - loadAddress + 1
80 |
81 | return Binary(uuid: uuid,
82 | loadAddress: loadAddress,
83 | approximateSize: size,
84 | name: binaryName)
85 | }
86 |
87 | public var isSimulated: Bool {
88 | return binaryName == "testBinaryName"
89 | }
90 | }
91 |
92 | public class CallStack: NSObject, Codable {
93 | /// Indicates which thread caused the crash
94 | public var threadAttributed: Bool?
95 | public var rootFrames: [Frame]
96 |
97 | enum CodingKeys: String, CodingKey {
98 | case threadAttributed
99 | case rootFrames = "callStackRootFrames"
100 | }
101 |
102 | public init(threadAttributed: Bool, rootFrames: [Frame]) {
103 | self.threadAttributed = threadAttributed
104 | self.rootFrames = rootFrames
105 | }
106 |
107 | /// Returns a single array of Frame objects
108 | public var frames: [Frame] {
109 | return rootFrames.flatMap({ [$0] + $0.flattenedFrames })
110 | }
111 |
112 | public var isSimulated: Bool {
113 | return rootFrames.first?.isSimulated == true
114 | }
115 | }
116 |
117 | public class CallStackTree: Codable {
118 | public let callStacks: [CallStack]
119 | public let callStackPerThread: Bool
120 |
121 | public static func from(data: Data) throws -> CallStackTree {
122 | return try JSONDecoder().decode(CallStackTree.self, from: data)
123 | }
124 |
125 | #if canImport(MetricKit)
126 | @available(iOS 14.0, macOS 12.0, visionOS 1.0, *)
127 | @available(tvOS, unavailable)
128 | @available(watchOS, unavailable)
129 | public static func from(callStackTree: MXCallStackTree) throws -> CallStackTree {
130 | let data = callStackTree.jsonRepresentation()
131 |
132 | return try from(data: data)
133 | }
134 | #endif
135 |
136 | public init(callStacks: [CallStack], callStackPerThread: Bool) {
137 | self.callStacks = callStacks
138 | self.callStackPerThread = callStackPerThread
139 | }
140 |
141 | public func jsonRepresentation() -> Data {
142 | do {
143 | return try JSONEncoder().encode(self)
144 | } catch {
145 | return Data()
146 | }
147 | }
148 |
149 | public var isSimulated: Bool {
150 | return callStacks.first?.isSimulated == true
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/Sources/Meter/DiagnosticPayload.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | #if canImport(MetricKit)
3 | import MetricKit
4 | #endif
5 |
6 | public class DiagnosticPayload: Codable {
7 | private static let dateFormatter: DateFormatter = {
8 | let formatter = DateFormatter()
9 |
10 | // "2020-10-10 19:35:24 +0000"
11 | formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
12 |
13 | return formatter
14 | }()
15 |
16 | private static let dateWithoutMillisFormatter: DateFormatter = {
17 | let formatter = DateFormatter()
18 |
19 | // "2020-10-10 19:35:24"
20 | formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
21 |
22 | return formatter
23 | }()
24 |
25 | enum CodingKeys: String, CodingKey {
26 | case timeStampBegin
27 | case timeStampEnd
28 | case crashDiagnostics
29 | case hangDiagnostics
30 | case cpuExceptionDiagnostics
31 | case diskWriteExceptionDiagnostics
32 | }
33 |
34 | public let timeStampBegin: Date
35 | public let timeStampEnd: Date
36 | public let crashDiagnostics: [CrashDiagnostic]?
37 | public let hangDiagnostics: [HangDiagnostic]?
38 | public let cpuExceptionDiagnostics: [CPUExceptionDiagnostic]?
39 | public let diskWriteExceptionDiagnostics: [DiskWriteExceptionDiagnostic]?
40 |
41 | public static func from(data: Data) throws -> DiagnosticPayload {
42 | let decoder = JSONDecoder()
43 |
44 | decoder.dateDecodingStrategy = .custom({ decoder in
45 | let container = try decoder.singleValueContainer()
46 | let string = try container.decode(String.self)
47 |
48 | if let date = DiagnosticPayload.dateFormatter.date(from: string) {
49 | return date
50 | }
51 |
52 | if let date = DiagnosticPayload.dateWithoutMillisFormatter.date(from: string) {
53 | return date
54 | }
55 |
56 | throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)")
57 | })
58 |
59 | return try decoder.decode(DiagnosticPayload.self, from: data)
60 | }
61 |
62 | #if canImport(MetricKit)
63 | @available(iOS 14.0, macOS 12.0, visionOS 1.0, *)
64 | @available(tvOS, unavailable)
65 | @available(watchOS, unavailable)
66 | public static func from(payload: MXDiagnosticPayload) throws -> DiagnosticPayload {
67 | let data = payload.jsonRepresentation()
68 |
69 | return try from(data: data)
70 | }
71 | #endif
72 |
73 | public init(timeStampBegin: Date, timeStampEnd: Date, crashDiagnostics: [CrashDiagnostic]?, hangDiagnostics: [HangDiagnostic]?, cpuExceptionDiagnostics: [CPUExceptionDiagnostic]?, diskWriteExceptionDiagnostics: [DiskWriteExceptionDiagnostic]?) {
74 | self.timeStampBegin = timeStampBegin
75 | self.timeStampEnd = timeStampEnd
76 | self.crashDiagnostics = crashDiagnostics
77 | self.hangDiagnostics = hangDiagnostics
78 | self.cpuExceptionDiagnostics = cpuExceptionDiagnostics
79 | self.diskWriteExceptionDiagnostics = diskWriteExceptionDiagnostics
80 | }
81 |
82 | public func jsonRepresentation() -> Data {
83 | let encoder = JSONEncoder()
84 |
85 | encoder.dateEncodingStrategy = .formatted(DiagnosticPayload.dateFormatter)
86 |
87 | return (try? encoder.encode(self)) ?? Data()
88 | }
89 |
90 | public var isSimulated: Bool {
91 | if crashDiagnostics?.allSatisfy({ $0.isSimulated }) == false {
92 | return false
93 | }
94 |
95 | if hangDiagnostics?.allSatisfy({ $0.isSimulated }) == false {
96 | return false
97 | }
98 |
99 | if cpuExceptionDiagnostics?.allSatisfy({ $0.isSimulated }) == false {
100 | return false
101 | }
102 |
103 | if diskWriteExceptionDiagnostics?.allSatisfy({ $0.isSimulated }) == false {
104 | return false
105 | }
106 |
107 | return true
108 | }
109 | }
110 |
111 | public extension DiagnosticPayload {
112 | var dateRange: Range {
113 | return timeStampBegin.. {
123 | return timeStampBegin.. Data {
242 | return (try? JSONEncoder().encode(self)) ?? Data()
243 | }
244 | }
245 |
246 | public class CrashDiagnostic: Codable {
247 | public let version: String
248 | private let internalMetaData: CrashMetaData
249 | public let callStackTree: CallStackTree
250 |
251 | /// Per-binary crash-related information
252 | ///
253 | /// This is available only for convenience when encoding. The value
254 | /// must be set externally.
255 | public var applicationSpecificInformation: [String: [String]]?
256 |
257 | /// Backtraces that are available only within the crashing process
258 | ///
259 | /// This is available only for convenience when encoding. The value
260 | /// must be set externally.
261 | public var exceptionInfo: ExceptionInfo?
262 |
263 | enum CodingKeys: String, CodingKey {
264 | case version
265 | case internalMetaData = "diagnosticMetaData"
266 | case callStackTree = "callStackTree"
267 | case applicationSpecificInformation
268 | case exceptionInfo
269 | }
270 |
271 | public init(metaData: CrashMetaData, callStackTree: CallStackTree) {
272 | self.version = "1.0.0"
273 | self.internalMetaData = metaData
274 | self.callStackTree = callStackTree
275 | }
276 |
277 | public var applicationVersion: String {
278 | return internalMetaData.applicationVersion
279 | }
280 |
281 | public var virtualMemoryRegionInfo: String? {
282 | return internalMetaData.virtualMemoryRegionInfo
283 | }
284 |
285 | public var metaData: CrashMetaData {
286 | return internalMetaData
287 | }
288 |
289 | public var terminationReason: String? {
290 | return internalMetaData.terminationReason
291 | }
292 |
293 | public var signal: NSNumber? {
294 | return internalMetaData.signal.map({ NSNumber(value: $0) })
295 | }
296 |
297 | public var exceptionReason: ObjectiveCExceptionReason? {
298 | return internalMetaData.exceptionReason
299 | }
300 |
301 | public var exceptionCode: NSNumber? {
302 | return internalMetaData.exceptionCode.map({ NSNumber(value: $0) })
303 | }
304 |
305 | public var exceptionType: NSNumber? {
306 | return internalMetaData.exceptionType.map({ NSNumber(value: $0) })
307 | }
308 |
309 | public func jsonRepresentation() -> Data {
310 | return (try? JSONEncoder().encode(self)) ?? Data()
311 | }
312 |
313 | public var isSimulated: Bool {
314 | return callStackTree.isSimulated
315 | }
316 |
317 | /// Determine how `offsetIntoBinaryTextSegment` data should be intrepreted.
318 | public var usesOffsetAsLoadAddress: Bool {
319 | let osVersion = metaData.osVersion
320 |
321 | if osVersion.hasPrefix("macOS 12") {
322 | return true
323 | }
324 |
325 | if osVersion.hasPrefix("iPhone OS 14") || osVersion.hasPrefix("iPhone OS 15") {
326 | return true
327 | }
328 |
329 | return false
330 | }
331 | }
332 |
333 | public class HangMetaData: Codable {
334 | public let deviceType: String
335 | public let applicationBuildVersion: String
336 | public let applicationVersion: String
337 | public let osVersion: String
338 | public let platformArchitecture: String
339 | public let regionFormat: String
340 | private let hangDuration: String
341 |
342 | enum CodingKeys: String, CodingKey {
343 | case applicationBuildVersion = "appBuildVersion"
344 | case applicationVersion = "appVersion"
345 | case osVersion
346 | case platformArchitecture
347 | case regionFormat
348 | case deviceType
349 | case hangDuration
350 | }
351 |
352 | public init(deviceType: String, applicationBuildVersion: String, applicationVersion: String, osVersion: String, platformArchitecture: String, regionFormat: String) {
353 | self.deviceType = deviceType
354 | self.applicationBuildVersion = applicationBuildVersion
355 | self.applicationVersion = applicationVersion
356 | self.osVersion = osVersion
357 | self.platformArchitecture = platformArchitecture
358 | self.regionFormat = regionFormat
359 | self.hangDuration = ""
360 | }
361 |
362 | public init(diagnostic: HangDiagnostic) {
363 | self.deviceType = diagnostic.metaData.deviceType
364 | self.applicationBuildVersion = diagnostic.metaData.applicationBuildVersion
365 | self.applicationVersion = diagnostic.applicationVersion
366 | self.osVersion = diagnostic.metaData.osVersion
367 | self.platformArchitecture = diagnostic.metaData.platformArchitecture
368 | self.regionFormat = diagnostic.metaData.regionFormat
369 | self.hangDuration = ""
370 | }
371 |
372 | public func jsonRepresentation() -> Data {
373 | return (try? JSONEncoder().encode(self)) ?? Data()
374 | }
375 | }
376 |
377 | public class HangDiagnostic: Codable {
378 | public let version: String
379 | private let internalMetaData: HangMetaData
380 | public let callStackTree: CallStackTree
381 |
382 | enum CodingKeys: String, CodingKey {
383 | case version
384 | case internalMetaData = "diagnosticMetaData"
385 | case callStackTree
386 | }
387 |
388 | public init(metaData: HangMetaData, callStackTree: CallStackTree) {
389 | self.version = "1.0.0"
390 | self.internalMetaData = metaData
391 | self.callStackTree = callStackTree
392 | }
393 |
394 | public func jsonRepresentation() -> Data {
395 | return (try? JSONEncoder().encode(self)) ?? Data()
396 | }
397 |
398 | public var metaData: HangMetaData {
399 | return internalMetaData
400 | }
401 |
402 | public var applicationVersion: String {
403 | return internalMetaData.applicationVersion
404 | }
405 |
406 | public var isSimulated: Bool {
407 | return callStackTree.isSimulated
408 | }
409 | }
410 |
411 | public class CPUExceptionMetaData: Codable {
412 | public let deviceType: String
413 | public let applicationBuildVersion: String
414 | public let applicationVersion: String
415 | public let osVersion: String
416 | public let platformArchitecture: String
417 | public let regionFormat: String
418 | private let totalCPUTime: String
419 | private let totalSampledTime: String
420 |
421 | enum CodingKeys: String, CodingKey {
422 | case applicationBuildVersion = "appBuildVersion"
423 | case applicationVersion = "appVersion"
424 | case osVersion
425 | case platformArchitecture
426 | case regionFormat
427 | case deviceType
428 | case totalCPUTime
429 | case totalSampledTime
430 | }
431 |
432 | public init(deviceType: String, applicationBuildVersion: String, applicationVersion: String, osVersion: String, platformArchitecture: String, regionFormat: String) {
433 | self.deviceType = deviceType
434 | self.applicationBuildVersion = applicationBuildVersion
435 | self.applicationVersion = applicationVersion
436 | self.osVersion = osVersion
437 | self.platformArchitecture = platformArchitecture
438 | self.regionFormat = regionFormat
439 | self.totalCPUTime = ""
440 | self.totalSampledTime = ""
441 | }
442 |
443 | public init(diagnostic: CPUExceptionDiagnostic) {
444 | self.deviceType = diagnostic.metaData.deviceType
445 | self.applicationBuildVersion = diagnostic.metaData.applicationBuildVersion
446 | self.applicationVersion = diagnostic.applicationVersion
447 | self.osVersion = diagnostic.metaData.osVersion
448 | self.platformArchitecture = diagnostic.metaData.platformArchitecture
449 | self.regionFormat = diagnostic.metaData.regionFormat
450 | self.totalCPUTime = ""
451 | self.totalSampledTime = ""
452 | }
453 |
454 | public func jsonRepresentation() -> Data {
455 | return (try? JSONEncoder().encode(self)) ?? Data()
456 | }
457 | }
458 |
459 | public class CPUExceptionDiagnostic: Codable {
460 | public let version: String
461 | private let internalMetaData: CPUExceptionMetaData
462 | public let callStackTree: CallStackTree
463 |
464 | enum CodingKeys: String, CodingKey {
465 | case version
466 | case internalMetaData = "diagnosticMetaData"
467 | case callStackTree
468 | }
469 |
470 | public init(metaData: CPUExceptionMetaData, callStackTree: CallStackTree) {
471 | self.version = "1.0.0"
472 | self.internalMetaData = metaData
473 | self.callStackTree = callStackTree
474 | }
475 |
476 | public func jsonRepresentation() -> Data {
477 | return (try? JSONEncoder().encode(self)) ?? Data()
478 | }
479 |
480 | public var metaData: CPUExceptionMetaData {
481 | return internalMetaData
482 | }
483 |
484 | public var applicationVersion: String {
485 | return internalMetaData.applicationVersion
486 | }
487 |
488 | public var isSimulated: Bool {
489 | return callStackTree.isSimulated
490 | }
491 | }
492 |
493 | public class DiskWriteExceptionMetaData: Codable {
494 | public let deviceType: String
495 | public let applicationBuildVersion: String
496 | public let applicationVersion: String
497 | public let osVersion: String
498 | public let platformArchitecture: String
499 | public let regionFormat: String
500 | private let writesCaused: String
501 |
502 | enum CodingKeys: String, CodingKey {
503 | case applicationBuildVersion = "appBuildVersion"
504 | case applicationVersion = "appVersion"
505 | case osVersion
506 | case platformArchitecture
507 | case regionFormat
508 | case deviceType
509 | case writesCaused
510 | }
511 |
512 | public init(deviceType: String, applicationBuildVersion: String, applicationVersion: String, osVersion: String, platformArchitecture: String, regionFormat: String) {
513 | self.deviceType = deviceType
514 | self.applicationBuildVersion = applicationBuildVersion
515 | self.applicationVersion = applicationVersion
516 | self.osVersion = osVersion
517 | self.platformArchitecture = platformArchitecture
518 | self.regionFormat = regionFormat
519 | self.writesCaused = ""
520 | }
521 |
522 | public init(diagnostic: DiskWriteExceptionDiagnostic) {
523 | self.deviceType = diagnostic.metaData.deviceType
524 | self.applicationBuildVersion = diagnostic.metaData.applicationBuildVersion
525 | self.applicationVersion = diagnostic.applicationVersion
526 | self.osVersion = diagnostic.metaData.osVersion
527 | self.platformArchitecture = diagnostic.metaData.platformArchitecture
528 | self.regionFormat = diagnostic.metaData.regionFormat
529 | self.writesCaused = ""
530 | }
531 |
532 | public func jsonRepresentation() -> Data {
533 | return (try? JSONEncoder().encode(self)) ?? Data()
534 | }
535 | }
536 |
537 | public class DiskWriteExceptionDiagnostic: Codable {
538 | public let version: String
539 | private let internalMetaData: DiskWriteExceptionMetaData
540 | public let callStackTree: CallStackTree
541 |
542 | enum CodingKeys: String, CodingKey {
543 | case version
544 | case internalMetaData = "diagnosticMetaData"
545 | case callStackTree
546 | }
547 |
548 | public init(metaData: DiskWriteExceptionMetaData, callStackTree: CallStackTree) {
549 | self.version = "1.0.0"
550 | self.internalMetaData = metaData
551 | self.callStackTree = callStackTree
552 | }
553 |
554 | public func jsonRepresentation() -> Data {
555 | return (try? JSONEncoder().encode(self)) ?? Data()
556 | }
557 |
558 | public var metaData: DiskWriteExceptionMetaData {
559 | return internalMetaData
560 | }
561 |
562 | public var applicationVersion: String {
563 | return internalMetaData.applicationVersion
564 | }
565 |
566 | public var isSimulated: Bool {
567 | return callStackTree.isSimulated
568 | }
569 | }
570 |
--------------------------------------------------------------------------------
/Sources/Meter/DlfcnSymbolicator.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import BinaryImage
3 |
4 | extension BinaryImage {
5 | static var imageMap: [UUID: BinaryImage] {
6 | var map: [UUID: BinaryImage] = [:]
7 |
8 | BinaryImageEnumerateLoadedImages { image, _ in
9 | BinaryImageEnumerateLoadCommands(image.header) { lcmd, code, stop in
10 | switch code {
11 | case UInt32(LC_UUID):
12 | if let uuid = BinaryuImageUUIDFromLoadCommand(lcmd, code) {
13 | map[uuid] = image
14 | }
15 |
16 | stop.pointee = true
17 | default:
18 | break
19 | }
20 | }
21 | }
22 |
23 | return map
24 | }
25 | }
26 |
27 | public class DlfcnSymbolicator {
28 | private lazy var imageMap: [UUID: BinaryImage] = BinaryImage.imageMap
29 |
30 | public init() {
31 | }
32 | }
33 |
34 | struct DynamicLibraryInfo {
35 | private let info: Dl_info
36 | private let address: Int
37 |
38 | init?(address: Int) {
39 | self.address = address
40 |
41 | let ptr = UnsafeRawPointer(bitPattern: address)
42 | var infoObj = Dl_info()
43 |
44 | guard dladdr(ptr, &infoObj) != 0 else {
45 | return nil
46 | }
47 |
48 | self.info = infoObj
49 | }
50 |
51 | init() {
52 | self.info = Dl_info(dli_fname: nil, dli_fbase: nil, dli_sname: nil, dli_saddr: nil)
53 | self.address = 0
54 | }
55 |
56 | var symbolName: String? {
57 | return info.dli_sname.map(String.init(cString:))
58 | }
59 |
60 | var symbolOffset: Int {
61 | return address - symbolAddress
62 | }
63 |
64 | var symbolAddress: Int {
65 | return info.dli_saddr.map({ Int(bitPattern: $0) }) ?? 0
66 | }
67 |
68 | var path: String? {
69 | return info.dli_fname.map(String.init(cString:))
70 | }
71 |
72 | var machOHeader: UnsafePointer? {
73 | return info.dli_fbase
74 | .map({ $0.bindMemory(to: MachOHeader.self, capacity: 1) })
75 | .map({ UnsafePointer($0) })
76 | }
77 |
78 | var loadAddress: Int {
79 | return info.dli_fbase.map({ Int(bitPattern: $0) }) ?? 0
80 | }
81 |
82 | func getUUID() -> UUID? {
83 | return machOHeader.map({ BinaryImageGetUUID($0) })
84 | }
85 |
86 | func makeBinary() -> Binary? {
87 | guard let uuid = getUUID() else {
88 | return nil
89 | }
90 |
91 | let size = address - loadAddress
92 |
93 | let name = path?.components(separatedBy: "/").last
94 |
95 | return Binary(uuid: uuid, loadAddress: loadAddress, approximateSize: size, name: name)
96 | }
97 |
98 | var symbolInfo: SymbolInfo {
99 | return SymbolInfo(symbol: symbolName ?? "", offset: symbolOffset)
100 | }
101 | }
102 |
103 | extension DlfcnSymbolicator: Symbolicator {
104 | public func symbolicate(address: UInt64, in target: SymbolicationTarget) -> [SymbolInfo] {
105 | guard let loadedImage = imageMap[target.uuid] else {
106 | return []
107 | }
108 |
109 | let loadAddress = Int(bitPattern: loadedImage.header)
110 | let relativeAddress = address - UInt64(target.loadAddress)
111 | let processAddress = UInt64(loadAddress) + relativeAddress
112 |
113 | if processAddress > Int.max {
114 | return []
115 | }
116 |
117 | guard let info = DynamicLibraryInfo(address: Int(processAddress)) else {
118 | return []
119 | }
120 |
121 | return [info.symbolInfo]
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/Sources/Meter/ExceptionInfo.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import BinaryImage
3 |
4 | public struct ExceptionInfo: Codable {
5 | public let name: String
6 | public let reason: String
7 | public let backtrace: [Frame]
8 |
9 | public init(name: String, reason: String, backtrace: [Frame]) {
10 | self.name = name
11 | self.reason = reason
12 | self.backtrace = backtrace
13 | }
14 |
15 | public init(exception: NSException) {
16 | self.name = exception.name.rawValue
17 | self.reason = exception.reason ?? ""
18 | self.backtrace = [exception.frameRepresentation]
19 | }
20 |
21 | public func write(to url: URL) throws {
22 | let data = try JSONEncoder().encode(self)
23 |
24 | try data.write(to: url)
25 | }
26 |
27 | public func matchesCrashDiagnostic(_ diagnostic: CrashDiagnostic) -> Bool {
28 | // ok, yes, this is insufficient
29 | return true
30 | }
31 | }
32 |
33 | extension NSException {
34 | public var frameRepresentation: Frame {
35 | let addresses = callStackReturnAddresses.map({ $0.intValue })
36 |
37 | var binaryMap = [Int : Binary]()
38 |
39 | var frame: Frame? = nil
40 |
41 | // both creating the DynamicLibraryInfo and calling makeBinary are
42 | // expensive. Use a simple cache to avoid one of those operations.
43 |
44 | for addr in addresses.reversed() {
45 | let info = DynamicLibraryInfo(address: addr) ?? DynamicLibraryInfo()
46 |
47 | let binary = binaryMap[info.loadAddress] ?? info.makeBinary()
48 | let loadAddress = binary?.loadAddress ?? info.loadAddress
49 |
50 | if binary != nil && info.loadAddress > 0 {
51 | binaryMap[info.loadAddress] = binary
52 | }
53 |
54 | let subFrames = [frame].compactMap({ $0 })
55 |
56 | // have to account for the change in recent platforms to make this an actual offset
57 | let offset: Int
58 |
59 | if #available(macOS 13, iOS 16, *) {
60 | offset = addr - loadAddress
61 | } else {
62 | offset = loadAddress
63 | }
64 |
65 | frame = Frame(binaryUUID: binary?.uuid,
66 | offsetIntoBinaryTextSegment: offset,
67 | binaryName: binary?.name,
68 | address: UInt64(addr),
69 | subFrames: subFrames,
70 | symbolInfo: [info.symbolInfo])
71 | }
72 |
73 | return frame ?? Frame(address: 0, subFrames: nil)
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Sources/Meter/Symbolicator.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import BinaryImage
3 |
4 | public struct SymbolInfo: Codable, Hashable {
5 | public var offset: Int?
6 | public var symbol: String
7 | public var demangledSymbol: String?
8 | public var file: String?
9 | public var lineNumber: Int?
10 |
11 | public init(symbol: String, demangledSymbol: String? = nil, offset: Int? = nil, file: String? = nil, lineNumber: Int? = nil) {
12 | self.symbol = symbol
13 | self.demangledSymbol = demangledSymbol
14 | self.offset = offset
15 | self.file = file
16 | self.lineNumber = lineNumber
17 | }
18 | }
19 |
20 | extension SymbolInfo: CustomDebugStringConvertible {
21 | public var debugDescription: String {
22 | var str = ""
37 | }
38 | }
39 |
40 | public struct SymbolicationTarget: Hashable {
41 | public var uuid: UUID
42 | public var loadAddress: Int
43 | public var path: String?
44 |
45 | public init(uuid: UUID, loadAddress: Int, path: String? = nil) {
46 | self.uuid = uuid
47 | self.loadAddress = loadAddress
48 | self.path = path
49 | }
50 |
51 | public init?(uuid: String, loadAddress: Int, path: String? = nil) {
52 | guard let value = UUID(uuidString: uuid) else { return nil }
53 |
54 | self.uuid = value
55 | self.loadAddress = loadAddress
56 | self.path = path
57 | }
58 | }
59 |
60 | public protocol Symbolicator {
61 | func symbolicate(address: UInt64, in target: SymbolicationTarget) -> [SymbolInfo]
62 | }
63 |
64 | public extension Frame {
65 | func symbolicationTarget(withOffsetAsLoadAddress: Bool) -> SymbolicationTarget? {
66 | let binary = binary(withOffsetAsLoadAddress: withOffsetAsLoadAddress)
67 |
68 | return binary.map({ SymbolicationTarget(uuid: $0.uuid, loadAddress: $0.loadAddress, path: $0.name) })
69 | }
70 | }
71 |
72 | public extension Symbolicator {
73 | func symbolicate(frame: Frame, withOffsetAsLoadAddress: Bool) -> Frame {
74 | let subframes = frame.subFrames ?? []
75 | let symSubframes = subframes.map({ symbolicate(frame: $0, withOffsetAsLoadAddress: withOffsetAsLoadAddress) })
76 |
77 | if frame.address > Int.max {
78 | return Frame(frame: frame, symbolInfo: [], subFrames: symSubframes)
79 | }
80 |
81 | let addr = Int(frame.address)
82 | let target = frame.symbolicationTarget(withOffsetAsLoadAddress: withOffsetAsLoadAddress)
83 | let info = target.map({ symbolicate(address: UInt64(addr), in: $0) }) ?? []
84 |
85 | return Frame(frame: frame, symbolInfo: info, subFrames: symSubframes)
86 | }
87 |
88 | func symbolicate(callStack: CallStack, withOffsetAsLoadAddress: Bool) -> CallStack {
89 | let symFrames = callStack.rootFrames.map({ symbolicate(frame: $0, withOffsetAsLoadAddress: withOffsetAsLoadAddress) })
90 | let attributed = callStack.threadAttributed ?? false
91 |
92 | return CallStack(threadAttributed: attributed, rootFrames: symFrames)
93 | }
94 |
95 | func symbolicate(tree: CallStackTree, withOffsetAsLoadAddress: Bool) -> CallStackTree {
96 | let stacks = tree.callStacks.map({ symbolicate(callStack: $0, withOffsetAsLoadAddress: withOffsetAsLoadAddress) })
97 |
98 | return CallStackTree(callStacks: stacks,
99 | callStackPerThread: tree.callStackPerThread)
100 | }
101 |
102 | func symbolicate(diagnostic: CrashDiagnostic) -> CrashDiagnostic {
103 | let metadata = CrashMetaData(diagnostic: diagnostic)
104 |
105 | let useOffsets = diagnostic.usesOffsetAsLoadAddress
106 | let symTree = symbolicate(tree: diagnostic.callStackTree, withOffsetAsLoadAddress: useOffsets)
107 |
108 | return CrashDiagnostic(metaData: metadata, callStackTree: symTree)
109 | }
110 |
111 | func symbolicate(diagnostic: HangDiagnostic) -> HangDiagnostic {
112 | let metadata = HangMetaData(diagnostic: diagnostic)
113 |
114 | let symTree = symbolicate(tree: diagnostic.callStackTree, withOffsetAsLoadAddress: false)
115 |
116 | return HangDiagnostic(metaData: metadata, callStackTree: symTree)
117 | }
118 |
119 | func symbolicate(diagnostic: DiskWriteExceptionDiagnostic) -> DiskWriteExceptionDiagnostic {
120 | let metadata = DiskWriteExceptionMetaData(diagnostic: diagnostic)
121 |
122 | let symTree = symbolicate(tree: diagnostic.callStackTree, withOffsetAsLoadAddress: false)
123 |
124 | return DiskWriteExceptionDiagnostic(metaData: metadata, callStackTree: symTree)
125 | }
126 |
127 | func symbolicate(diagnostic: CPUExceptionDiagnostic) -> CPUExceptionDiagnostic {
128 | let metadata = CPUExceptionMetaData(diagnostic: diagnostic)
129 |
130 | let symTree = symbolicate(tree: diagnostic.callStackTree, withOffsetAsLoadAddress: false)
131 |
132 | return CPUExceptionDiagnostic(metaData: metadata, callStackTree: symTree)
133 | }
134 |
135 | func symbolicate(payload: DiagnosticPayload) -> DiagnosticPayload {
136 | let symCrashDiagnostics = payload.crashDiagnostics?.map({ symbolicate(diagnostic: $0) })
137 | let symHangDiagnostics = payload.hangDiagnostics?.map({ symbolicate(diagnostic: $0) })
138 | let symCPUDiagnostics = payload.cpuExceptionDiagnostics?.map({ symbolicate(diagnostic: $0) })
139 | let diskWriteDiagnostics = payload.diskWriteExceptionDiagnostics?.map({ symbolicate(diagnostic: $0) })
140 |
141 | return DiagnosticPayload(timeStampBegin: payload.timeStampBegin,
142 | timeStampEnd: payload.timeStampEnd,
143 | crashDiagnostics: symCrashDiagnostics,
144 | hangDiagnostics: symHangDiagnostics,
145 | cpuExceptionDiagnostics: symCPUDiagnostics,
146 | diskWriteExceptionDiagnostics: diskWriteDiagnostics)
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/Tests/BinaryImageTests/BinaryImageTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import BinaryImage
3 |
4 | final class BinaryImageTests: XCTestCase {
5 | func testIteration() {
6 | var images: [BinaryImage] = []
7 |
8 | BinaryImageEnumerateLoadedImages { image, _ in
9 | images.append(image)
10 | }
11 |
12 | let matchingImage = images.first { image in
13 | let name = String(cString: image.name)
14 |
15 | return name == "/usr/lib/system/libsystem_kernel.dylib"
16 | }
17 |
18 | XCTAssertNotNil(matchingImage)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Tests/MeterTests/DiagnosticTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import Meter
3 |
4 | final class DiagnosticTests: XCTestCase {
5 | func testReadingSimulatedCrashDiagnosticsData() throws {
6 | let url = try XCTUnwrap(Bundle.module.url(forResource: "xcode_simulated", withExtension: "json", subdirectory: "Resources"))
7 | let data = try Data(contentsOf: url, options: [])
8 |
9 | let payload = try XCTUnwrap(DiagnosticPayload.from(data: data))
10 |
11 | XCTAssertEqual(payload.timeStampBegin, Date(timeIntervalSince1970: 1602358524))
12 | XCTAssertEqual(payload.timeStampEnd, Date(timeIntervalSince1970: 1602358524))
13 | XCTAssertTrue(payload.isSimulated)
14 |
15 | XCTAssertEqual(payload.crashDiagnostics?.count, 1)
16 | let crashDiagnostic = try XCTUnwrap(payload.crashDiagnostics?[0])
17 |
18 | XCTAssertEqual(crashDiagnostic.metaData.applicationBuildVersion, "1")
19 | XCTAssertEqual(crashDiagnostic.metaData.osVersion, "iPhone OS 14.0.1 (18A393)")
20 | if #available(iOS 14.0, macOS 12.0, *) {
21 | XCTAssertEqual(crashDiagnostic.metaData.platformArchitecture, "arm64")
22 | }
23 | XCTAssertEqual(crashDiagnostic.metaData.regionFormat, "CA")
24 | XCTAssertEqual(crashDiagnostic.virtualMemoryRegionInfo?.hasPrefix("0 is not in any region"), true)
25 | XCTAssertEqual(crashDiagnostic.applicationVersion, "1.0")
26 | XCTAssertEqual(crashDiagnostic.terminationReason, "Namespace SIGNAL, Code 0xb")
27 | XCTAssertEqual(crashDiagnostic.signal, 11)
28 | XCTAssertEqual(crashDiagnostic.exceptionCode, 0)
29 | XCTAssertEqual(crashDiagnostic.exceptionType, 1)
30 | XCTAssertTrue(crashDiagnostic.isSimulated)
31 | XCTAssertTrue(crashDiagnostic.usesOffsetAsLoadAddress)
32 |
33 | let tree = crashDiagnostic.callStackTree
34 |
35 | XCTAssertTrue(tree.callStackPerThread)
36 | XCTAssertEqual(tree.callStacks.count, 1);
37 |
38 | let callStack = tree.callStacks[0]
39 |
40 | XCTAssertTrue(callStack.threadAttributed == true)
41 | XCTAssertEqual(callStack.rootFrames.count, 1)
42 |
43 | let frame = callStack.rootFrames[0]
44 |
45 | XCTAssertEqual(frame.binaryUUID, UUID(uuidString: "CDB53DDB-2337-4933-B62F-4356E6174AF0"))
46 | XCTAssertEqual(frame.offsetIntoBinaryTextSegment, 123)
47 | XCTAssertEqual(frame.sampleCount, 20)
48 | XCTAssertEqual(frame.binaryName, "testBinaryName")
49 | XCTAssertEqual(frame.address, 74565)
50 | }
51 |
52 | func testReadingSimulatedMacOSCrashDiagnosticsData() throws {
53 | let url = try XCTUnwrap(Bundle.module.url(forResource: "xcode_simulated_macOS_12", withExtension: "json", subdirectory: "Resources"))
54 | let data = try Data(contentsOf: url, options: [])
55 |
56 | let payload = try XCTUnwrap(DiagnosticPayload.from(data: data))
57 |
58 | XCTAssertTrue(payload.isSimulated)
59 |
60 | XCTAssertEqual(payload.crashDiagnostics?.count, 1)
61 |
62 | let crashDiagnostic = try XCTUnwrap(payload.crashDiagnostics?[0])
63 |
64 | XCTAssertTrue(crashDiagnostic.isSimulated)
65 | XCTAssertTrue(crashDiagnostic.usesOffsetAsLoadAddress)
66 |
67 | // on macOS, these dates are encoded in local time, which is a real pain
68 | let formatter = DateFormatter()
69 |
70 | formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
71 |
72 | XCTAssertEqual(payload.timeStampBegin, formatter.date(from: "2022-03-07 07:30:54"))
73 | XCTAssertEqual(payload.timeStampEnd, formatter.date(from: "2022-03-07 07:30:54"))
74 | }
75 |
76 | func testReadingSimulatedMacOS13CrashDiagnosticsData() throws {
77 | let url = try XCTUnwrap(Bundle.module.url(forResource: "xcode_simulated_macOS_13", withExtension: "json", subdirectory: "Resources"))
78 | let data = try Data(contentsOf: url, options: [])
79 |
80 | let payload = try XCTUnwrap(DiagnosticPayload.from(data: data))
81 |
82 | XCTAssertTrue(payload.isSimulated)
83 |
84 | XCTAssertEqual(payload.crashDiagnostics?.count, 1)
85 |
86 | let crashDiagnostic = try XCTUnwrap(payload.crashDiagnostics?[0])
87 |
88 | XCTAssertTrue(crashDiagnostic.isSimulated)
89 | XCTAssertFalse(crashDiagnostic.usesOffsetAsLoadAddress)
90 | }
91 |
92 | func testReadingSimulatedHangDiagnosticsData() throws {
93 | let url = try XCTUnwrap(Bundle.module.url(forResource: "xcode_simulated", withExtension: "json", subdirectory: "Resources"))
94 | let data = try Data(contentsOf: url, options: [])
95 |
96 | let payload = try XCTUnwrap(DiagnosticPayload.from(data: data))
97 |
98 | XCTAssertTrue(payload.isSimulated)
99 |
100 | XCTAssertEqual(payload.hangDiagnostics?.count, 1)
101 | let diagnostic = try XCTUnwrap(payload.hangDiagnostics?[0])
102 |
103 | XCTAssertEqual(diagnostic.metaData.applicationBuildVersion, "1")
104 | XCTAssertTrue(diagnostic.isSimulated)
105 |
106 | let tree = diagnostic.callStackTree
107 |
108 | XCTAssertTrue(tree.callStackPerThread)
109 | XCTAssertEqual(tree.callStacks.count, 1);
110 |
111 | let callStack = tree.callStacks[0]
112 |
113 | XCTAssertTrue(callStack.threadAttributed == true)
114 | XCTAssertEqual(callStack.rootFrames.count, 1)
115 |
116 | let frame = callStack.rootFrames[0]
117 |
118 | XCTAssertEqual(frame.offsetIntoBinaryTextSegment, 123)
119 | XCTAssertEqual(frame.address, 74565)
120 | }
121 |
122 | func testReadingSimulatedCPUExceptionDiagnosticsData() throws {
123 | let url = try XCTUnwrap(Bundle.module.url(forResource: "xcode_simulated", withExtension: "json", subdirectory: "Resources"))
124 | let data = try Data(contentsOf: url, options: [])
125 |
126 | let payload = try XCTUnwrap(DiagnosticPayload.from(data: data))
127 |
128 | XCTAssertTrue(payload.isSimulated)
129 |
130 | XCTAssertEqual(payload.cpuExceptionDiagnostics?.count, 1)
131 | let diagnostic = try XCTUnwrap(payload.cpuExceptionDiagnostics?[0])
132 |
133 | XCTAssertEqual(diagnostic.metaData.applicationBuildVersion, "1")
134 | XCTAssertTrue(diagnostic.isSimulated)
135 |
136 | let tree = diagnostic.callStackTree
137 |
138 | XCTAssertFalse(tree.callStackPerThread)
139 | XCTAssertEqual(tree.callStacks.count, 1);
140 |
141 | let callStack = tree.callStacks[0]
142 |
143 | XCTAssertNil(callStack.threadAttributed)
144 | XCTAssertEqual(callStack.rootFrames.count, 1)
145 | }
146 |
147 | func testRealPayloadWithSubframes() throws {
148 | let url = try XCTUnwrap(Bundle.module.url(forResource: "real_report", withExtension: "json", subdirectory: "Resources"))
149 | let data = try Data(contentsOf: url, options: [])
150 | let payload = try DiagnosticPayload.from(data: data)
151 |
152 | XCTAssertFalse(payload.isSimulated)
153 |
154 | XCTAssertEqual(payload.crashDiagnostics?.count, 2)
155 |
156 | let crashDiagnostic = try XCTUnwrap(payload.crashDiagnostics?[0])
157 |
158 | XCTAssertFalse(crashDiagnostic.isSimulated)
159 |
160 | let tree = crashDiagnostic.callStackTree
161 |
162 | XCTAssertTrue(crashDiagnostic.usesOffsetAsLoadAddress)
163 |
164 | XCTAssertTrue(tree.callStackPerThread)
165 | XCTAssertEqual(tree.callStacks.count, 10);
166 |
167 | let callStack = tree.callStacks[0]
168 |
169 | XCTAssertTrue(callStack.threadAttributed == true)
170 | XCTAssertEqual(callStack.rootFrames.count, 1)
171 | XCTAssertEqual(callStack.rootFrames[0].subFrames?.count, 1)
172 |
173 | let frames = callStack.frames
174 |
175 | XCTAssertEqual(frames.count, 32)
176 |
177 | // guard against 64bit addresses
178 | #if !os(watchOS)
179 | XCTAssertEqual(frames[0].binaryUUID, UUID(uuidString: "9156BE86-D4B6-3A81-8460-8728FA38C978"))
180 | XCTAssertEqual(frames[0].offsetIntoBinaryTextSegment, 6859616256)
181 | XCTAssertEqual(frames[0].sampleCount, 1)
182 | XCTAssertEqual(frames[0].binaryName, "libswiftCore.dylib")
183 | XCTAssertEqual(frames[0].address, 6859816880)
184 |
185 | XCTAssertEqual(frames[1].binaryUUID, UUID(uuidString: "9156BE86-D4B6-3A81-8460-8728FA38C978"))
186 | XCTAssertEqual(frames[1].offsetIntoBinaryTextSegment, 6859616256)
187 | XCTAssertEqual(frames[1].sampleCount, 1)
188 | XCTAssertEqual(frames[1].binaryName, "libswiftCore.dylib")
189 | XCTAssertEqual(frames[1].address, 6859816880)
190 |
191 | XCTAssertEqual(frames[30].binaryUUID, UUID(uuidString: "444F912B-06E7-395E-9E6E-D947B07401AC"))
192 | XCTAssertEqual(frames[30].offsetIntoBinaryTextSegment, 4303568896)
193 | XCTAssertEqual(frames[30].sampleCount, 1)
194 | XCTAssertEqual(frames[30].binaryName, "MetricKitTest")
195 | XCTAssertEqual(frames[30].address, 4303603064)
196 |
197 | XCTAssertEqual(frames[31].binaryUUID, UUID(uuidString: "77E57314-8A58-3064-90C0-8AF9A4745430"))
198 | XCTAssertEqual(frames[31].offsetIntoBinaryTextSegment, 6795280384)
199 | XCTAssertEqual(frames[31].sampleCount, 1)
200 | XCTAssertEqual(frames[31].binaryName, "libdyld.dylib")
201 | XCTAssertEqual(frames[31].address, 6795285912)
202 | #endif
203 | }
204 |
205 | func testVeryLargeAddress() throws {
206 | let value = """
207 | {
208 | "timeStampEnd" : "2022-10-07 14:41:00",
209 | "timeStampBegin" : "2022-10-07 14:41:00",
210 | "hangDiagnostics" : [
211 | {
212 | "version" : "1.0.0",
213 | "callStackTree" : {
214 | "callStacks" : [
215 | {
216 | "threadAttributed" : true,
217 | "callStackRootFrames" : [
218 | {
219 | "binaryUUID" : "1A111731-DB37-3138-BB6A-4F8CC7551374",
220 | "offsetIntoBinaryTextSegment" : 239784,
221 | "sampleCount" : 434,
222 | "subFrames" : [],
223 | "binaryName" : "kernel.release.t8103",
224 | "address" : 18446741874824079528
225 | }
226 | ]
227 | }
228 | ],
229 | "callStackPerThread" : true
230 | },
231 | "diagnosticMetaData" : {
232 | "appBuildVersion" : "1",
233 | "appVersion" : "1.0.0",
234 | "regionFormat" : "US",
235 | "hangDuration" : "7 sec",
236 | "osVersion" : "macOS 13.0 (22A5358e)",
237 | "deviceType" : "Macmini9,1",
238 | "bundleIdentifier" : "x.y.x",
239 | "platformArchitecture" : "arm64e"
240 | }
241 | }
242 | ]
243 | }
244 | """
245 | let data = try XCTUnwrap(value.data(using: .utf8))
246 |
247 | XCTAssertNoThrow(try DiagnosticPayload.from(data: data))
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/Tests/MeterTests/Resources/exception.log:
--------------------------------------------------------------------------------
1 | 1 {"type":"objc","name":"c29tZXRoaW5n","reason":"a2Fib29t","time_ms":1646487651000}
2 | 2 {"pc":7268352440}
3 | 2 {"pc":7265540024}
4 | 2 {"pc":7268527652}
5 | 2 {"pc":4309232304}
6 | 2 {"pc":4309232612}
7 | 2 {"pc":7315379200}
8 | 2 {"pc":7315378688}
9 | 2 {"pc":7315378440}
10 | 2 {"pc":7315378172}
11 | 2 {"pc":7315377960}
12 | 2 {"pc":7315365224}
13 | 2 {"pc":7315363460}
14 | 2 {"pc":7315363064}
15 | 2 {"pc":7315359584}
16 | 2 {"pc":7315352500}
17 | 2 {"pc":7314776068}
18 | 2 {"pc":7314773004}
19 | 2 {"pc":7314768752}
20 | 2 {"pc":7317622800}
21 | 2 {"pc":7313201628}
22 | 2 {"pc":7313010824}
23 | 2 {"pc":4309233164}
24 | 2 {"pc":4309233108}
25 | 2 {"pc":4309233384}
26 | 2 {"pc":4313256180}
27 | 3 {"address":7267287040, "uuid":"16FADF8B-A3AF-3E0C-B5FB-CE9A9CDC8747", "path":"/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"}
28 | 3 {"address":7265443840, "uuid":"2EB32E1D-90CB-3256-8681-9034085A06A6", "path":"/usr/lib/libobjc.A.dylib"}
29 | 3 {"address":7267287040, "uuid":"16FADF8B-A3AF-3E0C-B5FB-CE9A9CDC8747", "path":"/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"}
30 | 3 {"address":4309204992, "uuid":"EED71F69-9B36-34C0-97D2-ACA3784A7FB8", "path":"/Users/USER/Library/Developer/Xcode/DerivedData/MetricKitTest-cqwuvojqzcbwndfiobiaxddtbmzd/Build/Products/Debug/MetricKitTest.app/Contents/MacOS/MetricKitTest"}
31 | 3 {"address":7312994304, "uuid":"1B65B629-6D3B-3FCD-AD43-E5683A9F94B7", "path":"/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit"}
32 | 3 {"address":4309204992, "uuid":"EED71F69-9B36-34C0-97D2-ACA3784A7FB8", "path":"/Users/USER/Library/Developer/Xcode/DerivedData/MetricKitTest-cqwuvojqzcbwndfiobiaxddtbmzd/Build/Products/Debug/MetricKitTest.app/Contents/MacOS/MetricKitTest"}
33 |
34 |
--------------------------------------------------------------------------------
/Tests/MeterTests/Resources/ios_uncaugh_exception_payload.json:
--------------------------------------------------------------------------------
1 | {
2 | "timeStampEnd" : "2020-10-12 03:59:00 +0000",
3 | "timeStampBegin" : "2020-10-11 04:00:00 +0000",
4 | "crashDiagnostics" : [
5 | {
6 | "version" : "1.0.0",
7 | "callStackTree" : {
8 | "callStacks" : [
9 | {
10 | "threadAttributed" : true,
11 | "callStackRootFrames" : [
12 | {
13 | "binaryUUID" : "7C9A24C6-CB84-3053-8481-DA167DA8FB9C",
14 | "offsetIntoBinaryTextSegment" : 4331307008,
15 | "sampleCount" : 1,
16 | "subFrames" : [
17 | {
18 | "binaryUUID" : "7C9A24C6-CB84-3053-8481-DA167DA8FB9C",
19 | "offsetIntoBinaryTextSegment" : 4331307008,
20 | "sampleCount" : 1,
21 | "subFrames" : [
22 | {
23 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
24 | "offsetIntoBinaryTextSegment" : 6900813824,
25 | "sampleCount" : 1,
26 | "subFrames" : [
27 | {
28 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
29 | "offsetIntoBinaryTextSegment" : 6900813824,
30 | "sampleCount" : 1,
31 | "subFrames" : [
32 | {
33 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
34 | "offsetIntoBinaryTextSegment" : 6900813824,
35 | "sampleCount" : 1,
36 | "subFrames" : [
37 | {
38 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
39 | "offsetIntoBinaryTextSegment" : 6900813824,
40 | "sampleCount" : 1,
41 | "subFrames" : [
42 | {
43 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
44 | "offsetIntoBinaryTextSegment" : 6900813824,
45 | "sampleCount" : 1,
46 | "subFrames" : [
47 | {
48 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
49 | "offsetIntoBinaryTextSegment" : 6900813824,
50 | "sampleCount" : 1,
51 | "subFrames" : [
52 | {
53 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
54 | "offsetIntoBinaryTextSegment" : 6900813824,
55 | "sampleCount" : 1,
56 | "subFrames" : [
57 | {
58 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
59 | "offsetIntoBinaryTextSegment" : 6900813824,
60 | "sampleCount" : 1,
61 | "subFrames" : [
62 | {
63 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
64 | "offsetIntoBinaryTextSegment" : 6900813824,
65 | "sampleCount" : 1,
66 | "subFrames" : [
67 | {
68 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
69 | "offsetIntoBinaryTextSegment" : 6900813824,
70 | "sampleCount" : 1,
71 | "subFrames" : [
72 | {
73 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
74 | "offsetIntoBinaryTextSegment" : 6829649920,
75 | "sampleCount" : 1,
76 | "subFrames" : [
77 | {
78 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
79 | "offsetIntoBinaryTextSegment" : 6829649920,
80 | "sampleCount" : 1,
81 | "subFrames" : [
82 | {
83 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
84 | "offsetIntoBinaryTextSegment" : 6829649920,
85 | "sampleCount" : 1,
86 | "subFrames" : [
87 | {
88 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
89 | "offsetIntoBinaryTextSegment" : 6829649920,
90 | "sampleCount" : 1,
91 | "subFrames" : [
92 | {
93 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
94 | "offsetIntoBinaryTextSegment" : 6829649920,
95 | "sampleCount" : 1,
96 | "subFrames" : [
97 | {
98 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
99 | "offsetIntoBinaryTextSegment" : 6829649920,
100 | "sampleCount" : 1,
101 | "subFrames" : [
102 | {
103 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
104 | "offsetIntoBinaryTextSegment" : 6829649920,
105 | "sampleCount" : 1,
106 | "subFrames" : [
107 | {
108 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
109 | "offsetIntoBinaryTextSegment" : 6829649920,
110 | "sampleCount" : 1,
111 | "subFrames" : [
112 | {
113 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
114 | "offsetIntoBinaryTextSegment" : 6829649920,
115 | "sampleCount" : 1,
116 | "subFrames" : [
117 | {
118 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
119 | "offsetIntoBinaryTextSegment" : 6797963264,
120 | "sampleCount" : 1,
121 | "subFrames" : [
122 | {
123 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
124 | "offsetIntoBinaryTextSegment" : 6797963264,
125 | "sampleCount" : 1,
126 | "subFrames" : [
127 | {
128 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
129 | "offsetIntoBinaryTextSegment" : 6797963264,
130 | "sampleCount" : 1,
131 | "subFrames" : [
132 | {
133 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
134 | "offsetIntoBinaryTextSegment" : 6797963264,
135 | "sampleCount" : 1,
136 | "subFrames" : [
137 | {
138 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
139 | "offsetIntoBinaryTextSegment" : 6797963264,
140 | "sampleCount" : 1,
141 | "subFrames" : [
142 | {
143 | "binaryUUID" : "7762C161-3C1B-3E56-BA75-9CB873604898",
144 | "offsetIntoBinaryTextSegment" : 7168679936,
145 | "sampleCount" : 1,
146 | "subFrames" : [
147 | {
148 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
149 | "offsetIntoBinaryTextSegment" : 6829649920,
150 | "sampleCount" : 1,
151 | "subFrames" : [
152 | {
153 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
154 | "offsetIntoBinaryTextSegment" : 6829649920,
155 | "sampleCount" : 1,
156 | "subFrames" : [
157 | {
158 | "binaryUUID" : "7C9A24C6-CB84-3053-8481-DA167DA8FB9C",
159 | "offsetIntoBinaryTextSegment" : 4331307008,
160 | "sampleCount" : 1,
161 | "subFrames" : [
162 | {
163 | "binaryUUID" : "77E57314-8A58-3064-90C0-8AF9A4745430",
164 | "offsetIntoBinaryTextSegment" : 6795280384,
165 | "sampleCount" : 1,
166 | "binaryName" : "libdyld.dylib",
167 | "address" : 6795285912
168 | }
169 | ],
170 | "binaryName" : "MetricKitTest",
171 | "address" : 4331338160
172 | }
173 | ],
174 | "binaryName" : "UIKitCore",
175 | "address" : 6841341400
176 | }
177 | ],
178 | "binaryName" : "UIKitCore",
179 | "address" : 6841319428
180 | }
181 | ],
182 | "binaryName" : "GraphicsServices",
183 | "address" : 7168693656
184 | }
185 | ],
186 | "binaryName" : "CoreFoundation",
187 | "address" : 6798565888
188 | }
189 | ],
190 | "binaryName" : "CoreFoundation",
191 | "address" : 6798568000
192 | }
193 | ],
194 | "binaryName" : "CoreFoundation",
195 | "address" : 6798591112
196 | }
197 | ],
198 | "binaryName" : "CoreFoundation",
199 | "address" : 6798594368
200 | }
201 | ],
202 | "binaryName" : "CoreFoundation",
203 | "address" : 6798594624
204 | }
205 | ],
206 | "binaryName" : "UIKitCore",
207 | "address" : 6841959552
208 | }
209 | ],
210 | "binaryName" : "UIKitCore",
211 | "address" : 6841995712
212 | }
213 | ],
214 | "binaryName" : "UIKitCore",
215 | "address" : 6841977376
216 | }
217 | ],
218 | "binaryName" : "UIKitCore",
219 | "address" : 6841444660
220 | }
221 | ],
222 | "binaryName" : "UIKitCore",
223 | "address" : 6841595188
224 | }
225 | ],
226 | "binaryName" : "UIKitCore",
227 | "address" : 6836611588
228 | }
229 | ],
230 | "binaryName" : "UIKitCore",
231 | "address" : 6836612372
232 | }
233 | ],
234 | "binaryName" : "UIKitCore",
235 | "address" : 6841884856
236 | }
237 | ],
238 | "binaryName" : "UIKitCore",
239 | "address" : 6836652972
240 | }
241 | ],
242 | "binaryName" : "SwiftUI",
243 | "address" : 6909904924
244 | }
245 | ],
246 | "binaryName" : "SwiftUI",
247 | "address" : 6909901984
248 | }
249 | ],
250 | "binaryName" : "SwiftUI",
251 | "address" : 6909909448
252 | }
253 | ],
254 | "binaryName" : "SwiftUI",
255 | "address" : 6905817256
256 | }
257 | ],
258 | "binaryName" : "SwiftUI",
259 | "address" : 6905526492
260 | }
261 | ],
262 | "binaryName" : "SwiftUI",
263 | "address" : 6905585528
264 | }
265 | ],
266 | "binaryName" : "SwiftUI",
267 | "address" : 6905585556
268 | }
269 | ],
270 | "binaryName" : "SwiftUI",
271 | "address" : 6905585528
272 | }
273 | ],
274 | "binaryName" : "SwiftUI",
275 | "address" : 6907555208
276 | }
277 | ],
278 | "binaryName" : "SwiftUI",
279 | "address" : 6902833692
280 | }
281 | ],
282 | "binaryName" : "MetricKitTest",
283 | "address" : 4331345300
284 | }
285 | ],
286 | "binaryName" : "MetricKitTest",
287 | "address" : 4331327932
288 | }
289 | ]
290 | },
291 | {
292 | "threadAttributed" : false,
293 | "callStackRootFrames" : [
294 | {
295 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
296 | "offsetIntoBinaryTextSegment" : 7966019584,
297 | "sampleCount" : 1,
298 | "binaryName" : "libsystem_pthread.dylib",
299 | "address" : 7966079084
300 | }
301 | ]
302 | },
303 | {
304 | "threadAttributed" : false,
305 | "callStackRootFrames" : [
306 | {
307 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
308 | "offsetIntoBinaryTextSegment" : 7966019584,
309 | "sampleCount" : 1,
310 | "binaryName" : "libsystem_pthread.dylib",
311 | "address" : 7966079084
312 | }
313 | ]
314 | },
315 | {
316 | "threadAttributed" : false,
317 | "callStackRootFrames" : [
318 | {
319 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
320 | "offsetIntoBinaryTextSegment" : 7966019584,
321 | "sampleCount" : 1,
322 | "binaryName" : "libsystem_pthread.dylib",
323 | "address" : 7966079084
324 | }
325 | ]
326 | },
327 | {
328 | "threadAttributed" : false,
329 | "callStackRootFrames" : [
330 | {
331 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
332 | "offsetIntoBinaryTextSegment" : 7966019584,
333 | "sampleCount" : 1,
334 | "binaryName" : "libsystem_pthread.dylib",
335 | "address" : 7966079084
336 | }
337 | ]
338 | },
339 | {
340 | "threadAttributed" : false,
341 | "callStackRootFrames" : [
342 | {
343 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
344 | "offsetIntoBinaryTextSegment" : 7966019584,
345 | "sampleCount" : 1,
346 | "binaryName" : "libsystem_pthread.dylib",
347 | "address" : 7966079084
348 | }
349 | ]
350 | },
351 | {
352 | "threadAttributed" : false,
353 | "callStackRootFrames" : [
354 | {
355 | "binaryUUID" : "82DBF088-D587-35CF-9598-15F79C12EAA4",
356 | "offsetIntoBinaryTextSegment" : 7513526272,
357 | "sampleCount" : 1,
358 | "subFrames" : [
359 | {
360 | "binaryUUID" : "82DBF088-D587-35CF-9598-15F79C12EAA4",
361 | "offsetIntoBinaryTextSegment" : 7513526272,
362 | "sampleCount" : 1,
363 | "subFrames" : [
364 | {
365 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
366 | "offsetIntoBinaryTextSegment" : 6797963264,
367 | "sampleCount" : 1,
368 | "subFrames" : [
369 | {
370 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
371 | "offsetIntoBinaryTextSegment" : 6797963264,
372 | "sampleCount" : 1,
373 | "subFrames" : [
374 | {
375 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
376 | "offsetIntoBinaryTextSegment" : 6797963264,
377 | "sampleCount" : 1,
378 | "subFrames" : [
379 | {
380 | "binaryUUID" : "4B7B9C0A-BAD3-348D-95A3-94784BFED02E",
381 | "offsetIntoBinaryTextSegment" : 6817447936,
382 | "sampleCount" : 1,
383 | "subFrames" : [
384 | {
385 | "binaryUUID" : "4B7B9C0A-BAD3-348D-95A3-94784BFED02E",
386 | "offsetIntoBinaryTextSegment" : 6817447936,
387 | "sampleCount" : 1,
388 | "subFrames" : [
389 | {
390 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
391 | "offsetIntoBinaryTextSegment" : 6829649920,
392 | "sampleCount" : 1,
393 | "subFrames" : [
394 | {
395 | "binaryUUID" : "4B7B9C0A-BAD3-348D-95A3-94784BFED02E",
396 | "offsetIntoBinaryTextSegment" : 6817447936,
397 | "sampleCount" : 1,
398 | "subFrames" : [
399 | {
400 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
401 | "offsetIntoBinaryTextSegment" : 7966019584,
402 | "sampleCount" : 1,
403 | "subFrames" : [
404 | {
405 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
406 | "offsetIntoBinaryTextSegment" : 7966019584,
407 | "sampleCount" : 1,
408 | "binaryName" : "libsystem_pthread.dylib",
409 | "address" : 7966079104
410 | }
411 | ],
412 | "binaryName" : "libsystem_pthread.dylib",
413 | "address" : 7966059376
414 | }
415 | ],
416 | "binaryName" : "Foundation",
417 | "address" : 6818909256
418 | }
419 | ],
420 | "binaryName" : "UIKitCore",
421 | "address" : 6842022396
422 | }
423 | ],
424 | "binaryName" : "Foundation",
425 | "address" : 6817481048
426 | }
427 | ],
428 | "binaryName" : "Foundation",
429 | "address" : 6817481336
430 | }
431 | ],
432 | "binaryName" : "CoreFoundation",
433 | "address" : 6798565888
434 | }
435 | ],
436 | "binaryName" : "CoreFoundation",
437 | "address" : 6798568400
438 | }
439 | ],
440 | "binaryName" : "CoreFoundation",
441 | "address" : 6798591820
442 | }
443 | ],
444 | "binaryName" : "libsystem_kernel.dylib",
445 | "address" : 7513537736
446 | }
447 | ],
448 | "binaryName" : "libsystem_kernel.dylib",
449 | "address" : 7513540804
450 | }
451 | ]
452 | },
453 | {
454 | "threadAttributed" : false,
455 | "callStackRootFrames" : [
456 | {
457 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
458 | "offsetIntoBinaryTextSegment" : 7966019584,
459 | "sampleCount" : 1,
460 | "binaryName" : "libsystem_pthread.dylib",
461 | "address" : 7966079084
462 | }
463 | ]
464 | },
465 | {
466 | "threadAttributed" : false,
467 | "callStackRootFrames" : [
468 | {
469 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
470 | "offsetIntoBinaryTextSegment" : 7966019584,
471 | "sampleCount" : 1,
472 | "binaryName" : "libsystem_pthread.dylib",
473 | "address" : 7966079084
474 | }
475 | ]
476 | },
477 | {
478 | "threadAttributed" : false,
479 | "callStackRootFrames" : [
480 | {
481 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
482 | "offsetIntoBinaryTextSegment" : 7966019584,
483 | "sampleCount" : 1,
484 | "binaryName" : "libsystem_pthread.dylib",
485 | "address" : 7966079084
486 | }
487 | ]
488 | }
489 | ],
490 | "callStackPerThread" : true
491 | },
492 | "diagnosticMetaData" : {
493 | "appBuildVersion" : "1",
494 | "appVersion" : "1.0",
495 | "regionFormat" : "CA",
496 | "exceptionType" : 1,
497 | "osVersion" : "iPhone OS 14.0.1 (18A393)",
498 | "deviceType" : "iPhone10,2",
499 | "signal" : 11,
500 | "exceptionCode" : 1,
501 | "platformArchitecture" : "arm64",
502 | "terminationReason" : "Namespace SIGNAL, Code 0xb",
503 | "virtualMemoryRegionInfo" : "0 is not in any region. Bytes before following region: 4331307008\n REGION TYPE START - END [ VSIZE] PRT\/MAX SHRMOD REGION DETAIL\n UNUSED SPACE AT START\n---> \n __TEXT 1022a8000-102300000 [ 352K] r-x\/r-x SM=COW ...MetricKitTest"
504 | }
505 | }
506 | ]
507 | }
508 |
--------------------------------------------------------------------------------
/Tests/MeterTests/Resources/real_report.json:
--------------------------------------------------------------------------------
1 | {
2 | "timeStampEnd" : "2020-10-11 03:59:00 +0000",
3 | "timeStampBegin" : "2020-10-10 04:00:00 +0000",
4 | "crashDiagnostics" : [
5 | {
6 | "version" : "1.0.0",
7 | "callStackTree" : {
8 | "callStacks" : [
9 | {
10 | "threadAttributed" : true,
11 | "callStackRootFrames" : [
12 | {
13 | "binaryUUID" : "9156BE86-D4B6-3A81-8460-8728FA38C978",
14 | "offsetIntoBinaryTextSegment" : 6859616256,
15 | "sampleCount" : 1,
16 | "subFrames" : [
17 | {
18 | "binaryUUID" : "9156BE86-D4B6-3A81-8460-8728FA38C978",
19 | "offsetIntoBinaryTextSegment" : 6859616256,
20 | "sampleCount" : 1,
21 | "subFrames" : [
22 | {
23 | "binaryUUID" : "444F912B-06E7-395E-9E6E-D947B07401AC",
24 | "offsetIntoBinaryTextSegment" : 4303568896,
25 | "sampleCount" : 1,
26 | "subFrames" : [
27 | {
28 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
29 | "offsetIntoBinaryTextSegment" : 6900813824,
30 | "sampleCount" : 1,
31 | "subFrames" : [
32 | {
33 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
34 | "offsetIntoBinaryTextSegment" : 6900813824,
35 | "sampleCount" : 1,
36 | "subFrames" : [
37 | {
38 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
39 | "offsetIntoBinaryTextSegment" : 6900813824,
40 | "sampleCount" : 1,
41 | "subFrames" : [
42 | {
43 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
44 | "offsetIntoBinaryTextSegment" : 6900813824,
45 | "sampleCount" : 1,
46 | "subFrames" : [
47 | {
48 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
49 | "offsetIntoBinaryTextSegment" : 6900813824,
50 | "sampleCount" : 1,
51 | "subFrames" : [
52 | {
53 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
54 | "offsetIntoBinaryTextSegment" : 6900813824,
55 | "sampleCount" : 1,
56 | "subFrames" : [
57 | {
58 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
59 | "offsetIntoBinaryTextSegment" : 6900813824,
60 | "sampleCount" : 1,
61 | "subFrames" : [
62 | {
63 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
64 | "offsetIntoBinaryTextSegment" : 6900813824,
65 | "sampleCount" : 1,
66 | "subFrames" : [
67 | {
68 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
69 | "offsetIntoBinaryTextSegment" : 6900813824,
70 | "sampleCount" : 1,
71 | "subFrames" : [
72 | {
73 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
74 | "offsetIntoBinaryTextSegment" : 6900813824,
75 | "sampleCount" : 1,
76 | "subFrames" : [
77 | {
78 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
79 | "offsetIntoBinaryTextSegment" : 6829649920,
80 | "sampleCount" : 1,
81 | "subFrames" : [
82 | {
83 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
84 | "offsetIntoBinaryTextSegment" : 6829649920,
85 | "sampleCount" : 1,
86 | "subFrames" : [
87 | {
88 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
89 | "offsetIntoBinaryTextSegment" : 6829649920,
90 | "sampleCount" : 1,
91 | "subFrames" : [
92 | {
93 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
94 | "offsetIntoBinaryTextSegment" : 6829649920,
95 | "sampleCount" : 1,
96 | "subFrames" : [
97 | {
98 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
99 | "offsetIntoBinaryTextSegment" : 6829649920,
100 | "sampleCount" : 1,
101 | "subFrames" : [
102 | {
103 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
104 | "offsetIntoBinaryTextSegment" : 6829649920,
105 | "sampleCount" : 1,
106 | "subFrames" : [
107 | {
108 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
109 | "offsetIntoBinaryTextSegment" : 6829649920,
110 | "sampleCount" : 1,
111 | "subFrames" : [
112 | {
113 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
114 | "offsetIntoBinaryTextSegment" : 6829649920,
115 | "sampleCount" : 1,
116 | "subFrames" : [
117 | {
118 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
119 | "offsetIntoBinaryTextSegment" : 6829649920,
120 | "sampleCount" : 1,
121 | "subFrames" : [
122 | {
123 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
124 | "offsetIntoBinaryTextSegment" : 6797963264,
125 | "sampleCount" : 1,
126 | "subFrames" : [
127 | {
128 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
129 | "offsetIntoBinaryTextSegment" : 6797963264,
130 | "sampleCount" : 1,
131 | "subFrames" : [
132 | {
133 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
134 | "offsetIntoBinaryTextSegment" : 6797963264,
135 | "sampleCount" : 1,
136 | "subFrames" : [
137 | {
138 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
139 | "offsetIntoBinaryTextSegment" : 6797963264,
140 | "sampleCount" : 1,
141 | "subFrames" : [
142 | {
143 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
144 | "offsetIntoBinaryTextSegment" : 6797963264,
145 | "sampleCount" : 1,
146 | "subFrames" : [
147 | {
148 | "binaryUUID" : "7762C161-3C1B-3E56-BA75-9CB873604898",
149 | "offsetIntoBinaryTextSegment" : 7168679936,
150 | "sampleCount" : 1,
151 | "subFrames" : [
152 | {
153 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
154 | "offsetIntoBinaryTextSegment" : 6829649920,
155 | "sampleCount" : 1,
156 | "subFrames" : [
157 | {
158 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
159 | "offsetIntoBinaryTextSegment" : 6829649920,
160 | "sampleCount" : 1,
161 | "subFrames" : [
162 | {
163 | "binaryUUID" : "444F912B-06E7-395E-9E6E-D947B07401AC",
164 | "offsetIntoBinaryTextSegment" : 4303568896,
165 | "sampleCount" : 1,
166 | "subFrames" : [
167 | {
168 | "binaryUUID" : "77E57314-8A58-3064-90C0-8AF9A4745430",
169 | "offsetIntoBinaryTextSegment" : 6795280384,
170 | "sampleCount" : 1,
171 | "binaryName" : "libdyld.dylib",
172 | "address" : 6795285912
173 | }
174 | ],
175 | "binaryName" : "MetricKitTest",
176 | "address" : 4303603064
177 | }
178 | ],
179 | "binaryName" : "UIKitCore",
180 | "address" : 6841341400
181 | }
182 | ],
183 | "binaryName" : "UIKitCore",
184 | "address" : 6841319428
185 | }
186 | ],
187 | "binaryName" : "GraphicsServices",
188 | "address" : 7168693656
189 | }
190 | ],
191 | "binaryName" : "CoreFoundation",
192 | "address" : 6798565888
193 | }
194 | ],
195 | "binaryName" : "CoreFoundation",
196 | "address" : 6798568000
197 | }
198 | ],
199 | "binaryName" : "CoreFoundation",
200 | "address" : 6798591112
201 | }
202 | ],
203 | "binaryName" : "CoreFoundation",
204 | "address" : 6798594368
205 | }
206 | ],
207 | "binaryName" : "CoreFoundation",
208 | "address" : 6798594624
209 | }
210 | ],
211 | "binaryName" : "UIKitCore",
212 | "address" : 6841959552
213 | }
214 | ],
215 | "binaryName" : "UIKitCore",
216 | "address" : 6841995712
217 | }
218 | ],
219 | "binaryName" : "UIKitCore",
220 | "address" : 6841977376
221 | }
222 | ],
223 | "binaryName" : "UIKitCore",
224 | "address" : 6841444660
225 | }
226 | ],
227 | "binaryName" : "UIKitCore",
228 | "address" : 6841595188
229 | }
230 | ],
231 | "binaryName" : "UIKitCore",
232 | "address" : 6836611588
233 | }
234 | ],
235 | "binaryName" : "UIKitCore",
236 | "address" : 6836612372
237 | }
238 | ],
239 | "binaryName" : "UIKitCore",
240 | "address" : 6841884856
241 | }
242 | ],
243 | "binaryName" : "UIKitCore",
244 | "address" : 6836652972
245 | }
246 | ],
247 | "binaryName" : "SwiftUI",
248 | "address" : 6909904924
249 | }
250 | ],
251 | "binaryName" : "SwiftUI",
252 | "address" : 6909901984
253 | }
254 | ],
255 | "binaryName" : "SwiftUI",
256 | "address" : 6909909448
257 | }
258 | ],
259 | "binaryName" : "SwiftUI",
260 | "address" : 6905817256
261 | }
262 | ],
263 | "binaryName" : "SwiftUI",
264 | "address" : 6905526492
265 | }
266 | ],
267 | "binaryName" : "SwiftUI",
268 | "address" : 6905585528
269 | }
270 | ],
271 | "binaryName" : "SwiftUI",
272 | "address" : 6905585556
273 | }
274 | ],
275 | "binaryName" : "SwiftUI",
276 | "address" : 6905585528
277 | }
278 | ],
279 | "binaryName" : "SwiftUI",
280 | "address" : 6907555208
281 | }
282 | ],
283 | "binaryName" : "SwiftUI",
284 | "address" : 6902833692
285 | }
286 | ],
287 | "binaryName" : "MetricKitTest",
288 | "address" : 4303609936
289 | }
290 | ],
291 | "binaryName" : "libswiftCore.dylib",
292 | "address" : 6859816880
293 | }
294 | ],
295 | "binaryName" : "libswiftCore.dylib",
296 | "address" : 6859816880
297 | }
298 | ]
299 | },
300 | {
301 | "threadAttributed" : false,
302 | "callStackRootFrames" : [
303 | {
304 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
305 | "offsetIntoBinaryTextSegment" : 7966019584,
306 | "sampleCount" : 1,
307 | "binaryName" : "libsystem_pthread.dylib",
308 | "address" : 7966079084
309 | }
310 | ]
311 | },
312 | {
313 | "threadAttributed" : false,
314 | "callStackRootFrames" : [
315 | {
316 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
317 | "offsetIntoBinaryTextSegment" : 7966019584,
318 | "sampleCount" : 1,
319 | "binaryName" : "libsystem_pthread.dylib",
320 | "address" : 7966079084
321 | }
322 | ]
323 | },
324 | {
325 | "threadAttributed" : false,
326 | "callStackRootFrames" : [
327 | {
328 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
329 | "offsetIntoBinaryTextSegment" : 7966019584,
330 | "sampleCount" : 1,
331 | "binaryName" : "libsystem_pthread.dylib",
332 | "address" : 7966079084
333 | }
334 | ]
335 | },
336 | {
337 | "threadAttributed" : false,
338 | "callStackRootFrames" : [
339 | {
340 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
341 | "offsetIntoBinaryTextSegment" : 7966019584,
342 | "sampleCount" : 1,
343 | "binaryName" : "libsystem_pthread.dylib",
344 | "address" : 7966079084
345 | }
346 | ]
347 | },
348 | {
349 | "threadAttributed" : false,
350 | "callStackRootFrames" : [
351 | {
352 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
353 | "offsetIntoBinaryTextSegment" : 7966019584,
354 | "sampleCount" : 1,
355 | "binaryName" : "libsystem_pthread.dylib",
356 | "address" : 7966079084
357 | }
358 | ]
359 | },
360 | {
361 | "threadAttributed" : false,
362 | "callStackRootFrames" : [
363 | {
364 | "binaryUUID" : "82DBF088-D587-35CF-9598-15F79C12EAA4",
365 | "offsetIntoBinaryTextSegment" : 7513526272,
366 | "sampleCount" : 1,
367 | "subFrames" : [
368 | {
369 | "binaryUUID" : "82DBF088-D587-35CF-9598-15F79C12EAA4",
370 | "offsetIntoBinaryTextSegment" : 7513526272,
371 | "sampleCount" : 1,
372 | "subFrames" : [
373 | {
374 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
375 | "offsetIntoBinaryTextSegment" : 6797963264,
376 | "sampleCount" : 1,
377 | "subFrames" : [
378 | {
379 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
380 | "offsetIntoBinaryTextSegment" : 6797963264,
381 | "sampleCount" : 1,
382 | "subFrames" : [
383 | {
384 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
385 | "offsetIntoBinaryTextSegment" : 6797963264,
386 | "sampleCount" : 1,
387 | "subFrames" : [
388 | {
389 | "binaryUUID" : "4B7B9C0A-BAD3-348D-95A3-94784BFED02E",
390 | "offsetIntoBinaryTextSegment" : 6817447936,
391 | "sampleCount" : 1,
392 | "subFrames" : [
393 | {
394 | "binaryUUID" : "4B7B9C0A-BAD3-348D-95A3-94784BFED02E",
395 | "offsetIntoBinaryTextSegment" : 6817447936,
396 | "sampleCount" : 1,
397 | "subFrames" : [
398 | {
399 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
400 | "offsetIntoBinaryTextSegment" : 6829649920,
401 | "sampleCount" : 1,
402 | "subFrames" : [
403 | {
404 | "binaryUUID" : "4B7B9C0A-BAD3-348D-95A3-94784BFED02E",
405 | "offsetIntoBinaryTextSegment" : 6817447936,
406 | "sampleCount" : 1,
407 | "subFrames" : [
408 | {
409 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
410 | "offsetIntoBinaryTextSegment" : 7966019584,
411 | "sampleCount" : 1,
412 | "subFrames" : [
413 | {
414 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
415 | "offsetIntoBinaryTextSegment" : 7966019584,
416 | "sampleCount" : 1,
417 | "binaryName" : "libsystem_pthread.dylib",
418 | "address" : 7966079104
419 | }
420 | ],
421 | "binaryName" : "libsystem_pthread.dylib",
422 | "address" : 7966059376
423 | }
424 | ],
425 | "binaryName" : "Foundation",
426 | "address" : 6818909256
427 | }
428 | ],
429 | "binaryName" : "UIKitCore",
430 | "address" : 6842022396
431 | }
432 | ],
433 | "binaryName" : "Foundation",
434 | "address" : 6817481048
435 | }
436 | ],
437 | "binaryName" : "Foundation",
438 | "address" : 6817481336
439 | }
440 | ],
441 | "binaryName" : "CoreFoundation",
442 | "address" : 6798565888
443 | }
444 | ],
445 | "binaryName" : "CoreFoundation",
446 | "address" : 6798568400
447 | }
448 | ],
449 | "binaryName" : "CoreFoundation",
450 | "address" : 6798591820
451 | }
452 | ],
453 | "binaryName" : "libsystem_kernel.dylib",
454 | "address" : 7513537736
455 | }
456 | ],
457 | "binaryName" : "libsystem_kernel.dylib",
458 | "address" : 7513540804
459 | }
460 | ]
461 | },
462 | {
463 | "threadAttributed" : false,
464 | "callStackRootFrames" : [
465 | {
466 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
467 | "offsetIntoBinaryTextSegment" : 7966019584,
468 | "sampleCount" : 1,
469 | "binaryName" : "libsystem_pthread.dylib",
470 | "address" : 7966079084
471 | }
472 | ]
473 | },
474 | {
475 | "threadAttributed" : false,
476 | "callStackRootFrames" : [
477 | {
478 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
479 | "offsetIntoBinaryTextSegment" : 7966019584,
480 | "sampleCount" : 1,
481 | "binaryName" : "libsystem_pthread.dylib",
482 | "address" : 7966079084
483 | }
484 | ]
485 | },
486 | {
487 | "threadAttributed" : false,
488 | "callStackRootFrames" : [
489 | {
490 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
491 | "offsetIntoBinaryTextSegment" : 7966019584,
492 | "sampleCount" : 1,
493 | "binaryName" : "libsystem_pthread.dylib",
494 | "address" : 7966079084
495 | }
496 | ]
497 | }
498 | ],
499 | "callStackPerThread" : true
500 | },
501 | "diagnosticMetaData" : {
502 | "appBuildVersion" : "1",
503 | "appVersion" : "1.0",
504 | "regionFormat" : "CA",
505 | "exceptionType" : 6,
506 | "osVersion" : "iPhone OS 14.0.1 (18A393)",
507 | "deviceType" : "iPhone10,2",
508 | "signal" : 5,
509 | "exceptionCode" : 1,
510 | "platformArchitecture" : "arm64",
511 | "terminationReason" : "Namespace SIGNAL, Code 0x5"
512 | }
513 | },
514 | {
515 | "version" : "1.0.0",
516 | "callStackTree" : {
517 | "callStacks" : [
518 | {
519 | "threadAttributed" : true,
520 | "callStackRootFrames" : [
521 | {
522 | "binaryUUID" : "444F912B-06E7-395E-9E6E-D947B07401AC",
523 | "offsetIntoBinaryTextSegment" : 4303978496,
524 | "sampleCount" : 1,
525 | "subFrames" : [
526 | {
527 | "binaryUUID" : "444F912B-06E7-395E-9E6E-D947B07401AC",
528 | "offsetIntoBinaryTextSegment" : 4303978496,
529 | "sampleCount" : 1,
530 | "subFrames" : [
531 | {
532 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
533 | "offsetIntoBinaryTextSegment" : 6900813824,
534 | "sampleCount" : 1,
535 | "subFrames" : [
536 | {
537 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
538 | "offsetIntoBinaryTextSegment" : 6900813824,
539 | "sampleCount" : 1,
540 | "subFrames" : [
541 | {
542 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
543 | "offsetIntoBinaryTextSegment" : 6900813824,
544 | "sampleCount" : 1,
545 | "subFrames" : [
546 | {
547 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
548 | "offsetIntoBinaryTextSegment" : 6900813824,
549 | "sampleCount" : 1,
550 | "subFrames" : [
551 | {
552 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
553 | "offsetIntoBinaryTextSegment" : 6900813824,
554 | "sampleCount" : 1,
555 | "subFrames" : [
556 | {
557 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
558 | "offsetIntoBinaryTextSegment" : 6900813824,
559 | "sampleCount" : 1,
560 | "subFrames" : [
561 | {
562 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
563 | "offsetIntoBinaryTextSegment" : 6900813824,
564 | "sampleCount" : 1,
565 | "subFrames" : [
566 | {
567 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
568 | "offsetIntoBinaryTextSegment" : 6900813824,
569 | "sampleCount" : 1,
570 | "subFrames" : [
571 | {
572 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
573 | "offsetIntoBinaryTextSegment" : 6900813824,
574 | "sampleCount" : 1,
575 | "subFrames" : [
576 | {
577 | "binaryUUID" : "E9BDE7BE-9A61-3A4C-A7D8-F14F9B216DD1",
578 | "offsetIntoBinaryTextSegment" : 6900813824,
579 | "sampleCount" : 1,
580 | "subFrames" : [
581 | {
582 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
583 | "offsetIntoBinaryTextSegment" : 6829649920,
584 | "sampleCount" : 1,
585 | "subFrames" : [
586 | {
587 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
588 | "offsetIntoBinaryTextSegment" : 6829649920,
589 | "sampleCount" : 1,
590 | "subFrames" : [
591 | {
592 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
593 | "offsetIntoBinaryTextSegment" : 6829649920,
594 | "sampleCount" : 1,
595 | "subFrames" : [
596 | {
597 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
598 | "offsetIntoBinaryTextSegment" : 6829649920,
599 | "sampleCount" : 1,
600 | "subFrames" : [
601 | {
602 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
603 | "offsetIntoBinaryTextSegment" : 6829649920,
604 | "sampleCount" : 1,
605 | "subFrames" : [
606 | {
607 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
608 | "offsetIntoBinaryTextSegment" : 6829649920,
609 | "sampleCount" : 1,
610 | "subFrames" : [
611 | {
612 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
613 | "offsetIntoBinaryTextSegment" : 6829649920,
614 | "sampleCount" : 1,
615 | "subFrames" : [
616 | {
617 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
618 | "offsetIntoBinaryTextSegment" : 6829649920,
619 | "sampleCount" : 1,
620 | "subFrames" : [
621 | {
622 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
623 | "offsetIntoBinaryTextSegment" : 6829649920,
624 | "sampleCount" : 1,
625 | "subFrames" : [
626 | {
627 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
628 | "offsetIntoBinaryTextSegment" : 6797963264,
629 | "sampleCount" : 1,
630 | "subFrames" : [
631 | {
632 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
633 | "offsetIntoBinaryTextSegment" : 6797963264,
634 | "sampleCount" : 1,
635 | "subFrames" : [
636 | {
637 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
638 | "offsetIntoBinaryTextSegment" : 6797963264,
639 | "sampleCount" : 1,
640 | "subFrames" : [
641 | {
642 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
643 | "offsetIntoBinaryTextSegment" : 6797963264,
644 | "sampleCount" : 1,
645 | "subFrames" : [
646 | {
647 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
648 | "offsetIntoBinaryTextSegment" : 6797963264,
649 | "sampleCount" : 1,
650 | "subFrames" : [
651 | {
652 | "binaryUUID" : "7762C161-3C1B-3E56-BA75-9CB873604898",
653 | "offsetIntoBinaryTextSegment" : 7168679936,
654 | "sampleCount" : 1,
655 | "subFrames" : [
656 | {
657 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
658 | "offsetIntoBinaryTextSegment" : 6829649920,
659 | "sampleCount" : 1,
660 | "subFrames" : [
661 | {
662 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
663 | "offsetIntoBinaryTextSegment" : 6829649920,
664 | "sampleCount" : 1,
665 | "subFrames" : [
666 | {
667 | "binaryUUID" : "444F912B-06E7-395E-9E6E-D947B07401AC",
668 | "offsetIntoBinaryTextSegment" : 4303978496,
669 | "sampleCount" : 1,
670 | "subFrames" : [
671 | {
672 | "binaryUUID" : "77E57314-8A58-3064-90C0-8AF9A4745430",
673 | "offsetIntoBinaryTextSegment" : 6795280384,
674 | "sampleCount" : 1,
675 | "binaryName" : "libdyld.dylib",
676 | "address" : 6795285912
677 | }
678 | ],
679 | "binaryName" : "MetricKitTest",
680 | "address" : 4304012664
681 | }
682 | ],
683 | "binaryName" : "UIKitCore",
684 | "address" : 6841341400
685 | }
686 | ],
687 | "binaryName" : "UIKitCore",
688 | "address" : 6841319428
689 | }
690 | ],
691 | "binaryName" : "GraphicsServices",
692 | "address" : 7168693656
693 | }
694 | ],
695 | "binaryName" : "CoreFoundation",
696 | "address" : 6798565888
697 | }
698 | ],
699 | "binaryName" : "CoreFoundation",
700 | "address" : 6798568000
701 | }
702 | ],
703 | "binaryName" : "CoreFoundation",
704 | "address" : 6798591112
705 | }
706 | ],
707 | "binaryName" : "CoreFoundation",
708 | "address" : 6798594368
709 | }
710 | ],
711 | "binaryName" : "CoreFoundation",
712 | "address" : 6798594624
713 | }
714 | ],
715 | "binaryName" : "UIKitCore",
716 | "address" : 6841959552
717 | }
718 | ],
719 | "binaryName" : "UIKitCore",
720 | "address" : 6841995712
721 | }
722 | ],
723 | "binaryName" : "UIKitCore",
724 | "address" : 6841977376
725 | }
726 | ],
727 | "binaryName" : "UIKitCore",
728 | "address" : 6841444660
729 | }
730 | ],
731 | "binaryName" : "UIKitCore",
732 | "address" : 6841595188
733 | }
734 | ],
735 | "binaryName" : "UIKitCore",
736 | "address" : 6836611588
737 | }
738 | ],
739 | "binaryName" : "UIKitCore",
740 | "address" : 6836612372
741 | }
742 | ],
743 | "binaryName" : "UIKitCore",
744 | "address" : 6841884856
745 | }
746 | ],
747 | "binaryName" : "UIKitCore",
748 | "address" : 6836652972
749 | }
750 | ],
751 | "binaryName" : "SwiftUI",
752 | "address" : 6909904924
753 | }
754 | ],
755 | "binaryName" : "SwiftUI",
756 | "address" : 6909901984
757 | }
758 | ],
759 | "binaryName" : "SwiftUI",
760 | "address" : 6909909448
761 | }
762 | ],
763 | "binaryName" : "SwiftUI",
764 | "address" : 6905817256
765 | }
766 | ],
767 | "binaryName" : "SwiftUI",
768 | "address" : 6905526492
769 | }
770 | ],
771 | "binaryName" : "SwiftUI",
772 | "address" : 6905585528
773 | }
774 | ],
775 | "binaryName" : "SwiftUI",
776 | "address" : 6905585556
777 | }
778 | ],
779 | "binaryName" : "SwiftUI",
780 | "address" : 6905585528
781 | }
782 | ],
783 | "binaryName" : "SwiftUI",
784 | "address" : 6907555208
785 | }
786 | ],
787 | "binaryName" : "SwiftUI",
788 | "address" : 6902833692
789 | }
790 | ],
791 | "binaryName" : "MetricKitTest",
792 | "address" : 4304019804
793 | }
794 | ],
795 | "binaryName" : "MetricKitTest",
796 | "address" : 4304003120
797 | }
798 | ]
799 | },
800 | {
801 | "threadAttributed" : false,
802 | "callStackRootFrames" : [
803 | {
804 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
805 | "offsetIntoBinaryTextSegment" : 7966019584,
806 | "sampleCount" : 1,
807 | "binaryName" : "libsystem_pthread.dylib",
808 | "address" : 7966079084
809 | }
810 | ]
811 | },
812 | {
813 | "threadAttributed" : false,
814 | "callStackRootFrames" : [
815 | {
816 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
817 | "offsetIntoBinaryTextSegment" : 7966019584,
818 | "sampleCount" : 1,
819 | "binaryName" : "libsystem_pthread.dylib",
820 | "address" : 7966079084
821 | }
822 | ]
823 | },
824 | {
825 | "threadAttributed" : false,
826 | "callStackRootFrames" : [
827 | {
828 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
829 | "offsetIntoBinaryTextSegment" : 7966019584,
830 | "sampleCount" : 1,
831 | "binaryName" : "libsystem_pthread.dylib",
832 | "address" : 7966079084
833 | }
834 | ]
835 | },
836 | {
837 | "threadAttributed" : false,
838 | "callStackRootFrames" : [
839 | {
840 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
841 | "offsetIntoBinaryTextSegment" : 7966019584,
842 | "sampleCount" : 1,
843 | "binaryName" : "libsystem_pthread.dylib",
844 | "address" : 7966079084
845 | }
846 | ]
847 | },
848 | {
849 | "threadAttributed" : false,
850 | "callStackRootFrames" : [
851 | {
852 | "binaryUUID" : "82DBF088-D587-35CF-9598-15F79C12EAA4",
853 | "offsetIntoBinaryTextSegment" : 7513526272,
854 | "sampleCount" : 1,
855 | "subFrames" : [
856 | {
857 | "binaryUUID" : "82DBF088-D587-35CF-9598-15F79C12EAA4",
858 | "offsetIntoBinaryTextSegment" : 7513526272,
859 | "sampleCount" : 1,
860 | "subFrames" : [
861 | {
862 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
863 | "offsetIntoBinaryTextSegment" : 6797963264,
864 | "sampleCount" : 1,
865 | "subFrames" : [
866 | {
867 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
868 | "offsetIntoBinaryTextSegment" : 6797963264,
869 | "sampleCount" : 1,
870 | "subFrames" : [
871 | {
872 | "binaryUUID" : "F80FCA31-BF76-3293-8BC6-1729588AE8B6",
873 | "offsetIntoBinaryTextSegment" : 6797963264,
874 | "sampleCount" : 1,
875 | "subFrames" : [
876 | {
877 | "binaryUUID" : "4B7B9C0A-BAD3-348D-95A3-94784BFED02E",
878 | "offsetIntoBinaryTextSegment" : 6817447936,
879 | "sampleCount" : 1,
880 | "subFrames" : [
881 | {
882 | "binaryUUID" : "4B7B9C0A-BAD3-348D-95A3-94784BFED02E",
883 | "offsetIntoBinaryTextSegment" : 6817447936,
884 | "sampleCount" : 1,
885 | "subFrames" : [
886 | {
887 | "binaryUUID" : "00EA1426-38F7-3FD2-BE01-04EBD44ECA35",
888 | "offsetIntoBinaryTextSegment" : 6829649920,
889 | "sampleCount" : 1,
890 | "subFrames" : [
891 | {
892 | "binaryUUID" : "4B7B9C0A-BAD3-348D-95A3-94784BFED02E",
893 | "offsetIntoBinaryTextSegment" : 6817447936,
894 | "sampleCount" : 1,
895 | "subFrames" : [
896 | {
897 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
898 | "offsetIntoBinaryTextSegment" : 7966019584,
899 | "sampleCount" : 1,
900 | "subFrames" : [
901 | {
902 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
903 | "offsetIntoBinaryTextSegment" : 7966019584,
904 | "sampleCount" : 1,
905 | "binaryName" : "libsystem_pthread.dylib",
906 | "address" : 7966079104
907 | }
908 | ],
909 | "binaryName" : "libsystem_pthread.dylib",
910 | "address" : 7966059376
911 | }
912 | ],
913 | "binaryName" : "Foundation",
914 | "address" : 6818909256
915 | }
916 | ],
917 | "binaryName" : "UIKitCore",
918 | "address" : 6842022396
919 | }
920 | ],
921 | "binaryName" : "Foundation",
922 | "address" : 6817481048
923 | }
924 | ],
925 | "binaryName" : "Foundation",
926 | "address" : 6817481336
927 | }
928 | ],
929 | "binaryName" : "CoreFoundation",
930 | "address" : 6798565888
931 | }
932 | ],
933 | "binaryName" : "CoreFoundation",
934 | "address" : 6798568400
935 | }
936 | ],
937 | "binaryName" : "CoreFoundation",
938 | "address" : 6798591820
939 | }
940 | ],
941 | "binaryName" : "libsystem_kernel.dylib",
942 | "address" : 7513537736
943 | }
944 | ],
945 | "binaryName" : "libsystem_kernel.dylib",
946 | "address" : 7513540804
947 | }
948 | ]
949 | },
950 | {
951 | "threadAttributed" : false,
952 | "callStackRootFrames" : [
953 | {
954 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
955 | "offsetIntoBinaryTextSegment" : 7966019584,
956 | "sampleCount" : 1,
957 | "binaryName" : "libsystem_pthread.dylib",
958 | "address" : 7966079084
959 | }
960 | ]
961 | },
962 | {
963 | "threadAttributed" : false,
964 | "callStackRootFrames" : [
965 | {
966 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
967 | "offsetIntoBinaryTextSegment" : 7966019584,
968 | "sampleCount" : 1,
969 | "binaryName" : "libsystem_pthread.dylib",
970 | "address" : 7966079084
971 | }
972 | ]
973 | },
974 | {
975 | "threadAttributed" : false,
976 | "callStackRootFrames" : [
977 | {
978 | "binaryUUID" : "9A318917-27DB-312E-91D6-81661034FCC6",
979 | "offsetIntoBinaryTextSegment" : 7966019584,
980 | "sampleCount" : 1,
981 | "binaryName" : "libsystem_pthread.dylib",
982 | "address" : 7966079084
983 | }
984 | ]
985 | }
986 | ],
987 | "callStackPerThread" : true
988 | },
989 | "diagnosticMetaData" : {
990 | "appBuildVersion" : "1",
991 | "appVersion" : "1.0",
992 | "regionFormat" : "CA",
993 | "exceptionType" : 1,
994 | "osVersion" : "iPhone OS 14.0.1 (18A393)",
995 | "deviceType" : "iPhone10,2",
996 | "signal" : 11,
997 | "exceptionCode" : 1,
998 | "platformArchitecture" : "arm64",
999 | "terminationReason" : "Namespace SIGNAL, Code 0xb",
1000 | "virtualMemoryRegionInfo" : "0 is not in any region. Bytes before following region: 4303978496\n REGION TYPE START - END [ VSIZE] PRT\/MAX SHRMOD REGION DETAIL\n UNUSED SPACE AT START\n---> \n __TEXT 100898000-1008e4000 [ 304K] r-x\/r-x SM=COW ...MetricKitTest"
1001 | }
1002 | }
1003 | ]
1004 | }
1005 |
--------------------------------------------------------------------------------
/Tests/MeterTests/Resources/xcode_simulated.crash:
--------------------------------------------------------------------------------
1 | Process: testBinaryName [0]
2 | Path:
3 | Identifier: testBinaryName
4 | Version: 1
5 | Code Type: ARM-64 (Native)
6 | Parent Process: ??? [0]
7 | Responsible: testBinaryName [0]
8 | User ID: 0
9 |
10 |
--------------------------------------------------------------------------------
/Tests/MeterTests/Resources/xcode_simulated.json:
--------------------------------------------------------------------------------
1 | {
2 | "crashDiagnostics" : [
3 | {
4 | "version" : "1.0.0",
5 | "callStackTree" : {
6 | "callStacks" : [
7 | {
8 | "threadAttributed" : true,
9 | "callStackRootFrames" : [
10 | {
11 | "binaryUUID" : "CDB53DDB-2337-4933-B62F-4356E6174AF0",
12 | "offsetIntoBinaryTextSegment" : 123,
13 | "sampleCount" : 20,
14 | "binaryName" : "testBinaryName",
15 | "address" : 74565
16 | }
17 | ]
18 | }
19 | ],
20 | "callStackPerThread" : true
21 | },
22 | "diagnosticMetaData" : {
23 | "appBuildVersion" : "1",
24 | "appVersion" : "1.0",
25 | "regionFormat" : "CA",
26 | "exceptionType" : 1,
27 | "osVersion" : "iPhone OS 14.0.1 (18A393)",
28 | "deviceType" : "iPhone10,2",
29 | "signal" : 11,
30 | "exceptionCode" : 0,
31 | "platformArchitecture" : "arm64",
32 | "terminationReason" : "Namespace SIGNAL, Code 0xb",
33 | "virtualMemoryRegionInfo" : "0 is not in any region. Bytes before following region: 4000000000 REGION TYPE START - END [ VSIZE] PRT\/MAX SHRMOD REGION DETAIL UNUSED SPACE AT START ---> __TEXT 0000000000000000-0000000000000000 [ 32K] r-x\/r-x SM=COW ...pp\/Test"
34 | }
35 | }
36 | ],
37 | "timeStampEnd" : "2020-10-10 19:35:24 +0000",
38 | "hangDiagnostics" : [
39 | {
40 | "version" : "1.0.0",
41 | "callStackTree" : {
42 | "callStacks" : [
43 | {
44 | "threadAttributed" : true,
45 | "callStackRootFrames" : [
46 | {
47 | "binaryUUID" : "CDB53DDB-2337-4933-B62F-4356E6174AF0",
48 | "offsetIntoBinaryTextSegment" : 123,
49 | "sampleCount" : 20,
50 | "binaryName" : "testBinaryName",
51 | "address" : 74565
52 | }
53 | ]
54 | }
55 | ],
56 | "callStackPerThread" : true
57 | },
58 | "diagnosticMetaData" : {
59 | "appBuildVersion" : "1",
60 | "appVersion" : "1.0",
61 | "regionFormat" : "CA",
62 | "hangDuration" : "20 secs.",
63 | "osVersion" : "iPhone OS 14.0.1 (18A393)",
64 | "deviceType" : "iPhone10,2",
65 | "platformArchitecture" : "arm64"
66 | }
67 | }
68 | ],
69 | "cpuExceptionDiagnostics" : [
70 | {
71 | "version" : "1.0.0",
72 | "callStackTree" : {
73 | "callStacks" : [
74 | {
75 | "callStackRootFrames" : [
76 | {
77 | "binaryUUID" : "50E04296-0089-4EA2-9125-9C7E2D6CE8E6",
78 | "offsetIntoBinaryTextSegment" : 123,
79 | "sampleCount" : 20,
80 | "binaryName" : "testBinaryName",
81 | "address" : 74565
82 | }
83 | ]
84 | }
85 | ],
86 | "callStackPerThread" : false
87 | },
88 | "diagnosticMetaData" : {
89 | "appBuildVersion" : "1",
90 | "appVersion" : "1.0",
91 | "regionFormat" : "CA",
92 | "osVersion" : "iPhone OS 14.0.1 (18A393)",
93 | "deviceType" : "iPhone10,2",
94 | "platformArchitecture" : "arm64",
95 | "totalCPUTime" : "20 secs.",
96 | "totalSampledTime" : "20 secs."
97 | }
98 | }
99 | ],
100 | "timeStampBegin" : "2020-10-10 19:35:24 +0000",
101 | "diskWriteExceptionDiagnostics" : [
102 | {
103 | "version" : "1.0.0",
104 | "callStackTree" : {
105 | "callStacks" : [
106 | {
107 | "callStackRootFrames" : [
108 | {
109 | "binaryUUID" : "50E04296-0089-4EA2-9125-9C7E2D6CE8E6",
110 | "offsetIntoBinaryTextSegment" : 123,
111 | "sampleCount" : 20,
112 | "binaryName" : "testBinaryName",
113 | "address" : 74565
114 | }
115 | ]
116 | }
117 | ],
118 | "callStackPerThread" : false
119 | },
120 | "diagnosticMetaData" : {
121 | "appBuildVersion" : "1",
122 | "appVersion" : "1.0",
123 | "regionFormat" : "CA",
124 | "writesCaused" : "2,000 byte",
125 | "osVersion" : "iPhone OS 14.0.1 (18A393)",
126 | "deviceType" : "iPhone10,2",
127 | "platformArchitecture" : "arm64"
128 | }
129 | }
130 | ]
131 | }
--------------------------------------------------------------------------------
/Tests/MeterTests/Resources/xcode_simulated_macOS_12.json:
--------------------------------------------------------------------------------
1 | {
2 | "crashDiagnostics" : [
3 | {
4 | "version" : "1.0.0",
5 | "callStackTree" : {
6 | "callStacks" : [
7 | {
8 | "threadAttributed" : true,
9 | "callStackRootFrames" : [
10 | {
11 | "binaryUUID" : "3AAA2D7B-FA6A-43EE-BCD3-0CB87D7AD39A",
12 | "offsetIntoBinaryTextSegment" : 123,
13 | "sampleCount" : 20,
14 | "binaryName" : "testBinaryName",
15 | "address" : 74565
16 | }
17 | ]
18 | }
19 | ],
20 | "callStackPerThread" : true
21 | },
22 | "diagnosticMetaData" : {
23 | "platformArchitecture" : "arm64e",
24 | "terminationReason" : "Namespace SIGNAL, Code 0xb",
25 | "exceptionType" : 1,
26 | "appBuildVersion" : "",
27 | "osVersion" : "macOS 12.2 (21D49)",
28 | "bundleIdentifier" : "com.chimehq.MetricKitTest",
29 | "deviceType" : "MacBookPro18,3",
30 | "exceptionCode" : 0,
31 | "virtualMemoryRegionInfo" : "0 is not in any region. Bytes before following region: 4000000000 REGION TYPE START - END [ VSIZE] PRT\/MAX SHRMOD REGION DETAIL UNUSED SPACE AT START ---> __TEXT 0000000000000000-0000000000000000 [ 32K] r-x\/r-x SM=COW ...pp\/Test",
32 | "signal" : 11,
33 | "appVersion" : "",
34 | "regionFormat" : "CA"
35 | }
36 | }
37 | ],
38 | "timeStampEnd" : "2022-03-07 07:30:54",
39 | "hangDiagnostics" : [
40 | {
41 | "version" : "1.0.0",
42 | "callStackTree" : {
43 | "callStacks" : [
44 | {
45 | "threadAttributed" : true,
46 | "callStackRootFrames" : [
47 | {
48 | "binaryUUID" : "3AAA2D7B-FA6A-43EE-BCD3-0CB87D7AD39A",
49 | "offsetIntoBinaryTextSegment" : 123,
50 | "sampleCount" : 20,
51 | "binaryName" : "testBinaryName",
52 | "address" : 74565
53 | }
54 | ]
55 | }
56 | ],
57 | "callStackPerThread" : true
58 | },
59 | "diagnosticMetaData" : {
60 | "appBuildVersion" : "",
61 | "appVersion" : "",
62 | "regionFormat" : "CA",
63 | "hangDuration" : "20 sec",
64 | "osVersion" : "macOS 12.2 (21D49)",
65 | "deviceType" : "MacBookPro18,3",
66 | "bundleIdentifier" : "com.chimehq.MetricKitTest",
67 | "platformArchitecture" : "arm64e"
68 | }
69 | }
70 | ],
71 | "cpuExceptionDiagnostics" : [
72 | {
73 | "version" : "1.0.0",
74 | "callStackTree" : {
75 | "callStacks" : [
76 | {
77 | "callStackRootFrames" : [
78 | {
79 | "binaryUUID" : "D80E43C4-AF16-4F1A-8296-C59FC0C054D0",
80 | "offsetIntoBinaryTextSegment" : 123,
81 | "sampleCount" : 20,
82 | "binaryName" : "testBinaryName",
83 | "address" : 74565
84 | }
85 | ]
86 | }
87 | ],
88 | "callStackPerThread" : false
89 | },
90 | "diagnosticMetaData" : {
91 | "appBuildVersion" : "",
92 | "appVersion" : "",
93 | "regionFormat" : "CA",
94 | "osVersion" : "macOS 12.2 (21D49)",
95 | "deviceType" : "MacBookPro18,3",
96 | "bundleIdentifier" : "com.chimehq.MetricKitTest",
97 | "platformArchitecture" : "arm64e",
98 | "totalCPUTime" : "20 sec",
99 | "totalSampledTime" : "20 sec"
100 | }
101 | }
102 | ],
103 | "timeStampBegin" : "2022-03-07 07:30:54",
104 | "diskWriteExceptionDiagnostics" : [
105 | {
106 | "version" : "1.0.0",
107 | "callStackTree" : {
108 | "callStacks" : [
109 | {
110 | "callStackRootFrames" : [
111 | {
112 | "binaryUUID" : "D80E43C4-AF16-4F1A-8296-C59FC0C054D0",
113 | "offsetIntoBinaryTextSegment" : 123,
114 | "sampleCount" : 20,
115 | "binaryName" : "testBinaryName",
116 | "address" : 74565
117 | }
118 | ]
119 | }
120 | ],
121 | "callStackPerThread" : false
122 | },
123 | "diagnosticMetaData" : {
124 | "appBuildVersion" : "",
125 | "appVersion" : "",
126 | "regionFormat" : "CA",
127 | "writesCaused" : "2000 byte",
128 | "osVersion" : "macOS 12.2 (21D49)",
129 | "deviceType" : "MacBookPro18,3",
130 | "bundleIdentifier" : "com.chimehq.MetricKitTest",
131 | "platformArchitecture" : "arm64e"
132 | }
133 | }
134 | ]
135 | }
136 |
--------------------------------------------------------------------------------
/Tests/MeterTests/Resources/xcode_simulated_macOS_13.json:
--------------------------------------------------------------------------------
1 | {
2 | "timeStampBegin" : "2022-09-28 10:59:30",
3 | "diskWriteExceptionDiagnostics" : [
4 | {
5 | "version" : "1.0.0",
6 | "callStackTree" : {
7 | "callStacks" : [
8 | {
9 | "callStackRootFrames" : [
10 | {
11 | "binaryUUID" : "97D62FA4-1DF8-4CF5-8D0B-13F0AD6E1C45",
12 | "offsetIntoBinaryTextSegment" : 123,
13 | "sampleCount" : 20,
14 | "binaryName" : "testBinaryName",
15 | "address" : 74565
16 | }
17 | ]
18 | }
19 | ],
20 | "callStackPerThread" : false
21 | },
22 | "diagnosticMetaData" : {
23 | "appBuildVersion" : "",
24 | "appVersion" : "",
25 | "regionFormat" : "CA",
26 | "writesCaused" : "2000 byte",
27 | "osVersion" : "macOS 13.0 (22A5358e)",
28 | "deviceType" : "MacBookPro18,3",
29 | "bundleIdentifier" : "com.chimehq.Edit",
30 | "platformArchitecture" : "arm64e"
31 | }
32 | }
33 | ],
34 | "timeStampEnd" : "2022-09-28 10:59:30",
35 | "hangDiagnostics" : [
36 | {
37 | "version" : "1.0.0",
38 | "callStackTree" : {
39 | "callStacks" : [
40 | {
41 | "threadAttributed" : true,
42 | "callStackRootFrames" : [
43 | {
44 | "binaryUUID" : "2B063BFD-1034-4DF1-A195-30DBC462B747",
45 | "offsetIntoBinaryTextSegment" : 123,
46 | "sampleCount" : 20,
47 | "binaryName" : "testBinaryName",
48 | "address" : 74565
49 | }
50 | ]
51 | }
52 | ],
53 | "callStackPerThread" : true
54 | },
55 | "diagnosticMetaData" : {
56 | "appBuildVersion" : "",
57 | "appVersion" : "",
58 | "regionFormat" : "CA",
59 | "hangDuration" : "20 sec",
60 | "osVersion" : "macOS 13.0 (22A5358e)",
61 | "deviceType" : "MacBookPro18,3",
62 | "bundleIdentifier" : "com.chimehq.Edit",
63 | "platformArchitecture" : "arm64e"
64 | }
65 | }
66 | ],
67 | "crashDiagnostics" : [
68 | {
69 | "version" : "1.0.0",
70 | "callStackTree" : {
71 | "callStacks" : [
72 | {
73 | "threadAttributed" : true,
74 | "callStackRootFrames" : [
75 | {
76 | "binaryUUID" : "2B063BFD-1034-4DF1-A195-30DBC462B747",
77 | "offsetIntoBinaryTextSegment" : 123,
78 | "sampleCount" : 20,
79 | "binaryName" : "testBinaryName",
80 | "address" : 74565
81 | }
82 | ]
83 | }
84 | ],
85 | "callStackPerThread" : true
86 | },
87 | "diagnosticMetaData" : {
88 | "platformArchitecture" : "arm64e",
89 | "terminationReason" : "Namespace SIGNAL, Code 0xb",
90 | "exceptionType" : 1,
91 | "appBuildVersion" : "",
92 | "osVersion" : "macOS 13.0 (22A5358e)",
93 | "bundleIdentifier" : "com.chimehq.Edit",
94 | "deviceType" : "MacBookPro18,3",
95 | "exceptionCode" : 0,
96 | "virtualMemoryRegionInfo" : "0 is not in any region. Bytes before following region: 4000000000 REGION TYPE START - END [ VSIZE] PRT\/MAX SHRMOD REGION DETAIL UNUSED SPACE AT START ---> __TEXT 0000000000000000-0000000000000000 [ 32K] r-x\/r-x SM=COW ...pp\/Test",
97 | "signal" : 11,
98 | "appVersion" : "",
99 | "regionFormat" : "CA"
100 | }
101 | }
102 | ],
103 | "cpuExceptionDiagnostics" : [
104 | {
105 | "version" : "1.0.0",
106 | "callStackTree" : {
107 | "callStacks" : [
108 | {
109 | "callStackRootFrames" : [
110 | {
111 | "binaryUUID" : "97D62FA4-1DF8-4CF5-8D0B-13F0AD6E1C45",
112 | "offsetIntoBinaryTextSegment" : 123,
113 | "sampleCount" : 20,
114 | "binaryName" : "testBinaryName",
115 | "address" : 74565
116 | }
117 | ]
118 | }
119 | ],
120 | "callStackPerThread" : false
121 | },
122 | "diagnosticMetaData" : {
123 | "appBuildVersion" : "",
124 | "appVersion" : "",
125 | "regionFormat" : "CA",
126 | "osVersion" : "macOS 13.0 (22A5358e)",
127 | "deviceType" : "MacBookPro18,3",
128 | "bundleIdentifier" : "com.chimehq.Edit",
129 | "platformArchitecture" : "arm64e",
130 | "totalCPUTime" : "20 sec",
131 | "totalSampledTime" : "20 sec"
132 | }
133 | }
134 | ],
135 | "appLaunchDiagnostics" : [
136 | {
137 | "version" : "1.0.0",
138 | "callStackTree" : {
139 | "callStacks" : [
140 | {
141 | "threadAttributed" : true,
142 | "callStackRootFrames" : [
143 | {
144 | "binaryUUID" : "2B063BFD-1034-4DF1-A195-30DBC462B747",
145 | "offsetIntoBinaryTextSegment" : 123,
146 | "sampleCount" : 20,
147 | "binaryName" : "testBinaryName",
148 | "address" : 74565
149 | }
150 | ]
151 | }
152 | ],
153 | "callStackPerThread" : true
154 | },
155 | "diagnosticMetaData" : {
156 | "appBuildVersion" : "",
157 | "appVersion" : "",
158 | "regionFormat" : "CA",
159 | "osVersion" : "macOS 13.0 (22A5358e)",
160 | "deviceType" : "MacBookPro18,3",
161 | "bundleIdentifier" : "com.chimehq.Edit",
162 | "launchDuration" : "20000 ms",
163 | "platformArchitecture" : "arm64e"
164 | }
165 | }
166 | ]
167 | }
--------------------------------------------------------------------------------
/Tests/MeterTests/SymbolicationTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import Meter
3 | import BinaryImage
4 |
5 | struct MockSymbolicator {
6 | typealias SymbolicationHandler = (UInt64, SymbolicationTarget) -> [SymbolInfo]
7 |
8 | var symbolicationHandler: SymbolicationHandler
9 |
10 | init(symbolicationHandler: @escaping SymbolicationHandler) {
11 | self.symbolicationHandler = symbolicationHandler
12 | }
13 | }
14 |
15 | extension MockSymbolicator: Symbolicator {
16 | func symbolicate(address: UInt64, in target: SymbolicationTarget) -> [SymbolInfo] {
17 | return symbolicationHandler(address, target)
18 | }
19 | }
20 |
21 | final class SymbolicationTests: XCTestCase {
22 | lazy var processImages = BinaryImage.imageMap
23 |
24 | // guard against 64bit addresses
25 | #if !os(watchOS)
26 | func testDlfcnSymbolicator() throws {
27 | // This is a fragile test, because it will only work when running
28 | // against 12.0 (21A344) on arm. I can think of a way to build a
29 | // more robust test, but it will take quite a bit of work.
30 | try XCTSkipUnless(ProcessInfo.processInfo.operatingSystemVersionString == "Version 12.0 (Build 21A344)")
31 |
32 | let symbolicator = DlfcnSymbolicator()
33 | let target = try XCTUnwrap(SymbolicationTarget(uuid: "17550b77-d255-389a-b779-906af75314b6",
34 | loadAddress: 0x19d0c4000,
35 | path: "/usr/lib/system/libsystem_kernel.dylib"))
36 |
37 | let infoArray = symbolicator.symbolicate(address: 0x19d0c59b4, in: target)
38 |
39 | XCTAssertEqual(infoArray.count, 1)
40 | XCTAssertEqual(infoArray[0].symbol, "mach_msg_trap")
41 | XCTAssertEqual(infoArray[0].offset, 8)
42 | XCTAssertNil(infoArray[0].lineNumber)
43 | XCTAssertNil(infoArray[0].file)
44 | }
45 | #endif
46 |
47 | func testDlfcnSymbolicatorWithAddressOutsideIntRange() throws {
48 | let randomImage = try XCTUnwrap(processImages.first)
49 |
50 | let symbolicator = DlfcnSymbolicator()
51 |
52 | // the load address and path are both bogus and will not match the UUID
53 | let target = try XCTUnwrap(
54 | SymbolicationTarget(
55 | uuid: randomImage.key,
56 | loadAddress: 0x19d0c4000,
57 | path: "/usr/lib/system/libsystem_kernel.dylib"
58 | )
59 | )
60 |
61 | // this will fail, but it should not crash
62 | let infoArray = symbolicator.symbolicate(address: UInt64(Int.max) + 1, in: target)
63 |
64 | XCTAssertEqual(infoArray.count, 0)
65 | }
66 |
67 | func testDlfcnSymbolicatorWithFrameAddressOutsideIntRange() throws {
68 | let symbolicator = DlfcnSymbolicator()
69 |
70 | // this will fail, but it should not crash
71 | let frame = Frame(address: UInt64(Int.max) + 1, subFrames: nil)
72 |
73 | _ = symbolicator.symbolicate(frame: frame, withOffsetAsLoadAddress: true)
74 | }
75 |
76 | func testTargetCalculation() throws {
77 | let uuid = UUID()
78 | let frame = Frame(binaryUUID: uuid,
79 | offsetIntoBinaryTextSegment: 100,
80 | binaryName: "binary",
81 | address: 105,
82 | subFrames: nil)
83 |
84 | let loadTarget = frame.symbolicationTarget(withOffsetAsLoadAddress: true)
85 |
86 | XCTAssertEqual(loadTarget, SymbolicationTarget(uuid: uuid, loadAddress: 100, path: "binary"))
87 |
88 | let offsetTarget = frame.symbolicationTarget(withOffsetAsLoadAddress: false)
89 |
90 | XCTAssertEqual(offsetTarget, SymbolicationTarget(uuid: uuid, loadAddress: 5, path: "binary"))
91 | }
92 |
93 | func testSymbolicateCallStack() throws {
94 | let uuidB = UUID()
95 | let frameB = Frame(binaryUUID: uuidB,
96 | offsetIntoBinaryTextSegment: 2000,
97 | sampleCount: 1,
98 | binaryName: "binaryB",
99 | address: 2020,
100 | subFrames: [])
101 | let uuidA = UUID()
102 | let frameA = Frame(binaryUUID: uuidA,
103 | offsetIntoBinaryTextSegment: 1000,
104 | sampleCount: 1,
105 | binaryName: "binaryA",
106 | address: 1015, subFrames: [frameB])
107 | let callStack = CallStack(threadAttributed: true, rootFrames: [frameA])
108 |
109 | let symbolInfoB = SymbolInfo(symbol: "symbolB", offset: 10)
110 | let symbolInfoA = SymbolInfo(symbol: "symbolA", offset: 10)
111 |
112 | let mockResults = [frameB.address: [symbolInfoB], frameA.address: [symbolInfoA]]
113 |
114 | let mockSymbolicator = MockSymbolicator { addr, _ in
115 | return mockResults[addr]!
116 | }
117 |
118 | let symbolicatedStack = mockSymbolicator.symbolicate(callStack: callStack, withOffsetAsLoadAddress: true)
119 |
120 | XCTAssertEqual(symbolicatedStack.threadAttributed, true)
121 | XCTAssertEqual(symbolicatedStack.rootFrames.count, 1)
122 | XCTAssertEqual(symbolicatedStack.rootFrames[0].symbolInfo?.count, 1)
123 | XCTAssertEqual(symbolicatedStack.rootFrames[0].symbolInfo?[0].symbol, "symbolA")
124 | XCTAssertEqual(symbolicatedStack.rootFrames[0].symbolInfo?[0].offset, 10)
125 |
126 | XCTAssertEqual(symbolicatedStack.rootFrames[0].subFrames?.count, 1)
127 | XCTAssertEqual(symbolicatedStack.rootFrames[0].subFrames?[0].symbolInfo?[0].symbol, "symbolB")
128 | XCTAssertEqual(symbolicatedStack.rootFrames[0].subFrames?[0].symbolInfo?[0].offset, 10)
129 | }
130 |
131 | func testUsingCrashOffsets() {
132 | let uuidA = UUID()
133 | let frameA = Frame(binaryUUID: uuidA,
134 | offsetIntoBinaryTextSegment: 15,
135 | sampleCount: 1,
136 | binaryName: "binaryA",
137 | address: 1015, subFrames: [])
138 | let callStack = CallStack(threadAttributed: true, rootFrames: [frameA])
139 | let tree = CallStackTree(callStacks: [callStack], callStackPerThread: true)
140 |
141 | let offsetCrashMetaData = CrashMetaData(deviceType: "",
142 | applicationBuildVersion: "",
143 | applicationVersion: "",
144 | osVersion: "macOS 13.0 (22A5358e)",
145 | platformArchitecture: "",
146 | regionFormat: "",
147 | isTestFlightApp: nil,
148 | lowPowerModeEnabled: nil,
149 | pid: nil,
150 | virtualMemoryRegionInfo: nil,
151 | exceptionType: nil,
152 | terminationReason: nil,
153 | exceptionCode: nil,
154 | signal: nil,
155 | exceptionReason: nil)
156 | let offsetCrashDiagnotic = CrashDiagnostic(metaData: offsetCrashMetaData, callStackTree: tree)
157 |
158 | let absoluteCrashMetaData = CrashMetaData(deviceType: "",
159 | applicationBuildVersion: "",
160 | applicationVersion: "",
161 | osVersion: "macOS 12.1",
162 | platformArchitecture: "",
163 | regionFormat: "",
164 | isTestFlightApp: nil,
165 | lowPowerModeEnabled: nil,
166 | pid: nil,
167 | virtualMemoryRegionInfo: nil,
168 | exceptionType: nil,
169 | terminationReason: nil,
170 | exceptionCode: nil,
171 | signal: nil,
172 | exceptionReason: nil)
173 | let absoluteCrashDiagnotic = CrashDiagnostic(metaData: absoluteCrashMetaData, callStackTree: tree)
174 |
175 | var symbolicationTarget: SymbolicationTarget? = nil
176 |
177 | let mockSymbolicator = MockSymbolicator { _, target in
178 | symbolicationTarget = target
179 |
180 | return []
181 | }
182 |
183 | _ = mockSymbolicator.symbolicate(diagnostic: offsetCrashDiagnotic)
184 |
185 | XCTAssertEqual(symbolicationTarget?.loadAddress, 1000)
186 |
187 | _ = mockSymbolicator.symbolicate(diagnostic: absoluteCrashDiagnotic)
188 |
189 | XCTAssertEqual(symbolicationTarget?.loadAddress, 15)
190 | }
191 |
192 | func testSymbolicatesAllDiagnosticTypes() throws {
193 | let url = try XCTUnwrap(Bundle.module.url(forResource: "xcode_simulated", withExtension: "json", subdirectory: "Resources"))
194 | let data = try Data(contentsOf: url, options: [])
195 | let payload = try XCTUnwrap(DiagnosticPayload.from(data: data))
196 |
197 | let symbolInfo = SymbolInfo(symbol: "symSymbol", offset: 10)
198 |
199 | let mockResults = [UInt64(74565): [symbolInfo]]
200 |
201 | let mockSymbolicator = MockSymbolicator { addr, _ in
202 | return mockResults[addr]!
203 | }
204 |
205 | let symPayload = mockSymbolicator.symbolicate(payload: payload)
206 |
207 | XCTAssertEqual(symPayload.crashDiagnostics?[0].callStackTree.callStacks[0].rootFrames[0].symbolInfo, [symbolInfo])
208 | XCTAssertEqual(symPayload.hangDiagnostics?[0].callStackTree.callStacks[0].rootFrames[0].symbolInfo, [symbolInfo])
209 | XCTAssertEqual(symPayload.cpuExceptionDiagnostics?[0].callStackTree.callStacks[0].rootFrames[0].symbolInfo, [symbolInfo])
210 | XCTAssertEqual(symPayload.diskWriteExceptionDiagnostics?[0].callStackTree.callStacks[0].rootFrames[0].symbolInfo, [symbolInfo])
211 | }
212 | }
213 |
--------------------------------------------------------------------------------