@descryy/runtime-adapter-swift 0.0.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +6 -0
- package/dist/swift-frame-parsing.d.ts +33 -88
- package/dist/swift-frame-parsing.d.ts.map +1 -1
- package/dist/swift-frame-parsing.js +75 -193
- package/dist/swift-frame-parsing.js.map +1 -1
- package/dist/swift-runtime-adapter.d.ts +5 -8
- package/dist/swift-runtime-adapter.d.ts.map +1 -1
- package/dist/swift-runtime-adapter.js +5 -8
- package/dist/swift-runtime-adapter.js.map +1 -1
- package/dist/swift-source-location-resolver.d.ts +19 -44
- package/dist/swift-source-location-resolver.d.ts.map +1 -1
- package/dist/swift-source-location-resolver.js +20 -47
- package/dist/swift-source-location-resolver.js.map +1 -1
- package/dist/swift-stack-trace-parser.d.ts +34 -82
- package/dist/swift-stack-trace-parser.d.ts.map +1 -1
- package/dist/swift-stack-trace-parser.js +35 -91
- package/dist/swift-stack-trace-parser.js.map +1 -1
- package/package.json +8 -3
package/LICENSE
ADDED
|
@@ -1,71 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Swift crash-handler trace line shapes —
|
|
3
|
-
*
|
|
4
|
-
* that feeds `SourceLocationResolver`.
|
|
2
|
+
* Swift crash-handler trace line shapes — LineClassifier for ExceptionBlockDetector,
|
|
3
|
+
* plus raw-text-to-frame-parts extraction feeding SourceLocationResolver.
|
|
5
4
|
*
|
|
6
|
-
* Grammar verified against
|
|
7
|
-
*
|
|
5
|
+
* Grammar verified against swiftlang/swift's test/Backtracing/FatalError.swift
|
|
6
|
+
* (a FileCheck test asserting real interpreter backtrace output) and
|
|
7
|
+
* stdlib/public/runtime/RuntimeErrorReporting.cpp (confirms frames are demangled
|
|
8
|
+
* by the runtime's own embedded demangler before printing — see point 4).
|
|
9
|
+
* No swift toolchain ran here; see test/fixtures/PROVENANCE.md.
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* at 0x<addr> ***`, then `Thread N [...] crashed:`, then (per the
|
|
26
|
-
* `FileCheck` test's plain `CHECK` rather than `CHECK-NEXT` between
|
|
27
|
-
* the thread line and frame 0) a blank line, before real frames start.
|
|
28
|
-
* PHP needed one bridging line (`Stack trace:`); C#'s `InnerException`
|
|
29
|
-
* case needed one (` ---> `). Swift needs two, plus tolerating a blank
|
|
30
|
-
* line — the same family of gap, escalated. Reported via
|
|
31
|
-
* `LineClassifier.isBridge` (RT-048), which `ExceptionBlockDetector`
|
|
32
|
-
* only ever consults while a header is already being held, so neither
|
|
33
|
-
* bridging line can open a block from idle on its own —
|
|
34
|
-
* `createSwiftLineClassifier` tracks a small stage counter (which of
|
|
35
|
-
* the two bridge lines is next) rather than a single boolean, since
|
|
36
|
-
* "confirm bridge line 1, then bridge line 2" is a short sequence, not
|
|
37
|
-
* a single yes/no; `isFrame` itself stays a plain, stateless check for
|
|
38
|
-
* the real frame shape.
|
|
39
|
-
* 2. **A frame is `<index> [flags]* 0x<addr> <symbol> + <offset> in
|
|
40
|
-
* <image>`, optionally ` at <file>:<line>:<col>`.** Index 0 is the
|
|
41
|
-
* innermost frame (the trap site itself) and the sequence counts
|
|
42
|
-
* upward to the process entry point — most-recent-first already, the
|
|
43
|
-
* same convention as V8/JVM/Go/Rust/C#; CPython remains the only
|
|
44
|
-
* reversing outlier in this codebase. `[ra]` and `[system]` are real,
|
|
45
|
-
* disclosed flags (a return-address frame; a Swift-runtime-internal
|
|
46
|
-
* frame) and are not stripped — they stay in `raw`, and are not
|
|
47
|
-
* parsed into anything structured because nothing in this contract
|
|
48
|
-
* has a field for them.
|
|
49
|
-
* 3. **The location suffix can be a real `file:line:col`, or a pseudo-path
|
|
50
|
-
* with neither** — `<compiler-generated>` for a compiler-synthesized
|
|
51
|
-
* frame (confirmed verbatim in the cited test: `static
|
|
52
|
-
* FatalError.$main() ... at {{.*}}/<compiler-generated>`), the same
|
|
53
|
-
* "real text, not a real source position" case CPython's pseudo-files
|
|
54
|
-
* already established a precedent for. Both shapes are handled: a
|
|
55
|
-
* `file:line:col` suffix is parsed into real fields; anything else
|
|
56
|
-
* after `at ` is kept as `file` verbatim with `line`/`column` null,
|
|
57
|
-
* never a fabricated position.
|
|
58
|
-
* 4. **"Mangled symbol names" turns out to mean something narrower than
|
|
59
|
-
* it sounds, verified rather than assumed from the lane brief.**
|
|
60
|
-
* `RuntimeErrorReporting.cpp`'s `dumpStackTraceEntry` demangles via
|
|
61
|
-
* `__cxa_demangle`/Swift's own `demangleSymbolAsString` automatically
|
|
62
|
-
* whenever symbol lookup succeeds — no external `swift-demangle`
|
|
63
|
-
* binary is needed, and the cited test's own frames are all
|
|
64
|
-
* already-demangled (`level5()`, `static FatalError.main()`). A raw
|
|
65
|
-
* `$s...`-prefixed name surviving into this text specifically means
|
|
66
|
-
* the runtime's own demangler failed on it, not that demangling was
|
|
67
|
-
* skipped. This parser does not attempt demangling itself either way
|
|
68
|
-
* — whatever text is there, verbatim, is `functionName`.
|
|
11
|
+
* 1. Header and first frame are separated by TWO bridging lines, not one:
|
|
12
|
+
* `*** Program crashed: <reason> at 0x<addr> ***`, then `Thread N ... crashed:`,
|
|
13
|
+
* then a blank line before real frames. Handled via LineClassifier.isBridge
|
|
14
|
+
* (RT-048) with a stage counter; ExceptionBlockDetector only consults it while
|
|
15
|
+
* a header is already held, so neither bridging line opens a block from idle.
|
|
16
|
+
* 2. A frame is `<index> [flags]* 0x<addr> <symbol> + <offset> in <image>`,
|
|
17
|
+
* optionally ` at <file>:<line>:<col>`. Index 0 is innermost (most-recent-first,
|
|
18
|
+
* like V8/JVM/Go/Rust/C#; CPython is the outlier here). `[ra]`/`[system]` flags
|
|
19
|
+
* stay verbatim in `raw`, unparsed — no field for them in this contract.
|
|
20
|
+
* 3. Location suffix is either real `file:line:col` or a pseudo-path like
|
|
21
|
+
* `<compiler-generated>` (confirmed in the cited test). Kept verbatim as
|
|
22
|
+
* `file` with line/column null — never a fabricated position.
|
|
23
|
+
* 4. "Mangled symbol names" is narrower than it sounds: dumpStackTraceEntry
|
|
24
|
+
* demangles automatically whenever lookup succeeds, so a raw `$s...` name
|
|
25
|
+
* surviving here means the runtime's own demangler failed, not that
|
|
26
|
+
* demangling was skipped. This parser never demangles itself either way.
|
|
69
27
|
*/
|
|
70
28
|
export interface RawSwiftFrame {
|
|
71
29
|
/** Whatever text the runtime printed for this frame — demangled, a fallback token, or (rarely) still mangled. Never rewritten. */
|
|
@@ -90,31 +48,18 @@ export declare function parseSwiftHeaderPosition(line: string): {
|
|
|
90
48
|
readonly line: number;
|
|
91
49
|
} | null;
|
|
92
50
|
/**
|
|
93
|
-
* Stateful
|
|
94
|
-
*
|
|
51
|
+
* Stateful bridge between header and first frame (point 1 above), via
|
|
52
|
+
* LineClassifier.isBridge (RT-048).
|
|
95
53
|
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* all. The signal line is accepted as optional so both shapes work.
|
|
102
|
-
* `ExceptionBlockDetector` only ever consults `isBridge` while a header is
|
|
103
|
-
* already being held, so `*** Program crashed...` and `Thread N ...
|
|
104
|
-
* crashed:` cannot open a block on their own from idle — that guarantee is
|
|
105
|
-
* now the state machine's, not this classifier's to re-derive, which is
|
|
106
|
-
* what let `isFrame` shrink back to a plain, stateless check for the real
|
|
107
|
-
* frame shape: it no longer has to also recognise the bridge, only the
|
|
108
|
-
* thing that ends it.
|
|
54
|
+
* RT-122: a real crash's bridge is three lines, not two — the Signal line
|
|
55
|
+
* precedes `*** Program crashed ***` and isn't in the transcribed FileCheck
|
|
56
|
+
* test; missing it produced no evidence at all, so it's accepted as optional.
|
|
57
|
+
* isBridge is only consulted while a header is already held, so isFrame stays
|
|
58
|
+
* a plain stateless check for the frame shape alone.
|
|
109
59
|
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* — but does not confirm — a blank line at each gap; the test's own
|
|
114
|
-
* comment formatting cannot be trusted to mirror the real output's
|
|
115
|
-
* spacing. Rather than guess a specific count and position, every stage
|
|
116
|
-
* of the bridge accepts an arbitrary run of blank lines before the
|
|
117
|
-
* pattern it expects.
|
|
60
|
+
* Blank lines are tolerated at every stage of the bridge — the FileCheck
|
|
61
|
+
* test's `CHECK` (not `CHECK-NEXT`) formatting permits but doesn't confirm
|
|
62
|
+
* exact spacing, so no specific count/position is assumed.
|
|
118
63
|
*/
|
|
119
64
|
export declare function createSwiftLineClassifier(): {
|
|
120
65
|
isHeader(line: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swift-frame-parsing.d.ts","sourceRoot":"","sources":["../src/swift-frame-parsing.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"swift-frame-parsing.d.ts","sourceRoot":"","sources":["../src/swift-frame-parsing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,WAAW,aAAa;IAC5B,kIAAkI;IAClI,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,yGAAyG;IACzG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,+GAA+G;IAC/G,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AA6DD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,4HAA4H;AAC5H,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED,oGAAoG;AACpG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,2FAA2F;AAC3F,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,yGAAyG;AACzG,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAO9G;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,IAAI;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CA2DA;AAED,yFAAyF;AACzF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA6BlE"}
|
|
@@ -1,162 +1,79 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Swift crash-handler trace line shapes —
|
|
3
|
-
*
|
|
4
|
-
* that feeds `SourceLocationResolver`.
|
|
2
|
+
* Swift crash-handler trace line shapes — LineClassifier for ExceptionBlockDetector,
|
|
3
|
+
* plus raw-text-to-frame-parts extraction feeding SourceLocationResolver.
|
|
5
4
|
*
|
|
6
|
-
* Grammar verified against
|
|
7
|
-
*
|
|
5
|
+
* Grammar verified against swiftlang/swift's test/Backtracing/FatalError.swift
|
|
6
|
+
* (a FileCheck test asserting real interpreter backtrace output) and
|
|
7
|
+
* stdlib/public/runtime/RuntimeErrorReporting.cpp (confirms frames are demangled
|
|
8
|
+
* by the runtime's own embedded demangler before printing — see point 4).
|
|
9
|
+
* No swift toolchain ran here; see test/fixtures/PROVENANCE.md.
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* at 0x<addr> ***`, then `Thread N [...] crashed:`, then (per the
|
|
26
|
-
* `FileCheck` test's plain `CHECK` rather than `CHECK-NEXT` between
|
|
27
|
-
* the thread line and frame 0) a blank line, before real frames start.
|
|
28
|
-
* PHP needed one bridging line (`Stack trace:`); C#'s `InnerException`
|
|
29
|
-
* case needed one (` ---> `). Swift needs two, plus tolerating a blank
|
|
30
|
-
* line — the same family of gap, escalated. Reported via
|
|
31
|
-
* `LineClassifier.isBridge` (RT-048), which `ExceptionBlockDetector`
|
|
32
|
-
* only ever consults while a header is already being held, so neither
|
|
33
|
-
* bridging line can open a block from idle on its own —
|
|
34
|
-
* `createSwiftLineClassifier` tracks a small stage counter (which of
|
|
35
|
-
* the two bridge lines is next) rather than a single boolean, since
|
|
36
|
-
* "confirm bridge line 1, then bridge line 2" is a short sequence, not
|
|
37
|
-
* a single yes/no; `isFrame` itself stays a plain, stateless check for
|
|
38
|
-
* the real frame shape.
|
|
39
|
-
* 2. **A frame is `<index> [flags]* 0x<addr> <symbol> + <offset> in
|
|
40
|
-
* <image>`, optionally ` at <file>:<line>:<col>`.** Index 0 is the
|
|
41
|
-
* innermost frame (the trap site itself) and the sequence counts
|
|
42
|
-
* upward to the process entry point — most-recent-first already, the
|
|
43
|
-
* same convention as V8/JVM/Go/Rust/C#; CPython remains the only
|
|
44
|
-
* reversing outlier in this codebase. `[ra]` and `[system]` are real,
|
|
45
|
-
* disclosed flags (a return-address frame; a Swift-runtime-internal
|
|
46
|
-
* frame) and are not stripped — they stay in `raw`, and are not
|
|
47
|
-
* parsed into anything structured because nothing in this contract
|
|
48
|
-
* has a field for them.
|
|
49
|
-
* 3. **The location suffix can be a real `file:line:col`, or a pseudo-path
|
|
50
|
-
* with neither** — `<compiler-generated>` for a compiler-synthesized
|
|
51
|
-
* frame (confirmed verbatim in the cited test: `static
|
|
52
|
-
* FatalError.$main() ... at {{.*}}/<compiler-generated>`), the same
|
|
53
|
-
* "real text, not a real source position" case CPython's pseudo-files
|
|
54
|
-
* already established a precedent for. Both shapes are handled: a
|
|
55
|
-
* `file:line:col` suffix is parsed into real fields; anything else
|
|
56
|
-
* after `at ` is kept as `file` verbatim with `line`/`column` null,
|
|
57
|
-
* never a fabricated position.
|
|
58
|
-
* 4. **"Mangled symbol names" turns out to mean something narrower than
|
|
59
|
-
* it sounds, verified rather than assumed from the lane brief.**
|
|
60
|
-
* `RuntimeErrorReporting.cpp`'s `dumpStackTraceEntry` demangles via
|
|
61
|
-
* `__cxa_demangle`/Swift's own `demangleSymbolAsString` automatically
|
|
62
|
-
* whenever symbol lookup succeeds — no external `swift-demangle`
|
|
63
|
-
* binary is needed, and the cited test's own frames are all
|
|
64
|
-
* already-demangled (`level5()`, `static FatalError.main()`). A raw
|
|
65
|
-
* `$s...`-prefixed name surviving into this text specifically means
|
|
66
|
-
* the runtime's own demangler failed on it, not that demangling was
|
|
67
|
-
* skipped. This parser does not attempt demangling itself either way
|
|
68
|
-
* — whatever text is there, verbatim, is `functionName`.
|
|
11
|
+
* 1. Header and first frame are separated by TWO bridging lines, not one:
|
|
12
|
+
* `*** Program crashed: <reason> at 0x<addr> ***`, then `Thread N ... crashed:`,
|
|
13
|
+
* then a blank line before real frames. Handled via LineClassifier.isBridge
|
|
14
|
+
* (RT-048) with a stage counter; ExceptionBlockDetector only consults it while
|
|
15
|
+
* a header is already held, so neither bridging line opens a block from idle.
|
|
16
|
+
* 2. A frame is `<index> [flags]* 0x<addr> <symbol> + <offset> in <image>`,
|
|
17
|
+
* optionally ` at <file>:<line>:<col>`. Index 0 is innermost (most-recent-first,
|
|
18
|
+
* like V8/JVM/Go/Rust/C#; CPython is the outlier here). `[ra]`/`[system]` flags
|
|
19
|
+
* stay verbatim in `raw`, unparsed — no field for them in this contract.
|
|
20
|
+
* 3. Location suffix is either real `file:line:col` or a pseudo-path like
|
|
21
|
+
* `<compiler-generated>` (confirmed in the cited test). Kept verbatim as
|
|
22
|
+
* `file` with line/column null — never a fabricated position.
|
|
23
|
+
* 4. "Mangled symbol names" is narrower than it sounds: dumpStackTraceEntry
|
|
24
|
+
* demangles automatically whenever lookup succeeds, so a raw `$s...` name
|
|
25
|
+
* surviving here means the runtime's own demangler failed, not that
|
|
26
|
+
* demangling was skipped. This parser never demangles itself either way.
|
|
69
27
|
*/
|
|
70
28
|
/** Not anchored to the start of the line, same reasoning as every other adapter's header pattern: a logger prefix may precede it. */
|
|
71
29
|
const HEADER_PATTERN = /Fatal error: (?<message>.+)$/;
|
|
72
30
|
const PROGRAM_CRASHED_PATTERN = /^\*\*\* Program crashed: .+ \*\*\*$/;
|
|
73
31
|
/**
|
|
74
|
-
* `*** Signal 4: Backtracing from 0x787be4d66895... done ***` —
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* from the bridge was total: the held header met a line matching nothing,
|
|
80
|
-
* the block closed before a single frame, and a real Swift crash produced
|
|
81
|
-
* no evidence at all — not an error, no evidence.
|
|
82
|
-
*
|
|
83
|
-
* Optional rather than required, because the transcribed fixtures (and
|
|
84
|
-
* whatever produced them) do not have it, and a bridge that demands it
|
|
85
|
-
* would trade one silence for another.
|
|
32
|
+
* `*** Signal 4: Backtracing from 0x787be4d66895... done ***` — printed
|
|
33
|
+
* BEFORE `*** Program crashed ***`. Measured on a real Swift 6.1.2 Linux
|
|
34
|
+
* crash (RT-122), absent from the FileCheck test this was transcribed from;
|
|
35
|
+
* without it the block closed before a single frame and produced no evidence.
|
|
36
|
+
* Optional, not required, since the transcribed fixtures lack it.
|
|
86
37
|
*/
|
|
87
38
|
const SIGNAL_BACKTRACING_PATTERN = /^\*\*\* Signal \d+: Backtracing from 0x[0-9a-fA-F]+\.\.\. done \*\*\*$/;
|
|
88
39
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* When the trap happens off the main thread, Swift dumps *every* thread and
|
|
94
|
-
* only one carries `crashed`. The uncrashed ones come first. So the two
|
|
95
|
-
* spellings are different facts, not formatting: `isSwiftCrashedThreadLine`
|
|
96
|
-
* is what says which stack actually trapped.
|
|
40
|
+
* Thread header: `Thread 1 crashed:`, `Thread 0 "server" crashed:`, or
|
|
41
|
+
* (measured, no fixture case) `Thread 0 "server":` with no `crashed`.
|
|
42
|
+
* Off-main-thread traps dump every thread, uncrashed ones first —
|
|
43
|
+
* `isSwiftCrashedThreadLine` picks the one that actually trapped.
|
|
97
44
|
*/
|
|
98
45
|
const THREAD_PATTERN = /^Thread \d+(?:\s+"[^"]*")?(?:\s+crashed)?:\s*$/;
|
|
99
46
|
const THREAD_CRASHED_PATTERN = /^Thread \d+.*crashed:\s*$/;
|
|
100
47
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* extension keeps this from matching a `log`-style `02:11:06` timestamp.
|
|
106
|
-
* The transcribed fixtures carry no position at all — that is a real
|
|
107
|
-
* difference between the two shapes, and it degrades to "no declared
|
|
108
|
-
* position" rather than to a guess.
|
|
48
|
+
* Position from Swift's own `Fatal error:` line: `server/server.swift:10:
|
|
49
|
+
* Fatal error: ...`. Requires the `.swift` extension so this doesn't match
|
|
50
|
+
* a log-style `02:11:06` timestamp. Fixtures carry no position — degrades
|
|
51
|
+
* to null, never a guess.
|
|
109
52
|
*/
|
|
110
53
|
const HEADER_POSITION_PATTERN = /(?<file>\S+\.swift):(?<line>\d+):\s+Fatal error:/;
|
|
111
54
|
/**
|
|
112
55
|
* `<index> [flag]* 0x<hex> <symbol> + <offset> in <image>[ at <location>]`.
|
|
113
|
-
* The mandatory
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
* status line never contains a hex address in this exact position.
|
|
117
|
-
* `symbol` is greedy — correct because the ` + <offset> in <image>`
|
|
118
|
-
* suffix is anchored and unique per line, so backtracking finds it
|
|
119
|
-
* regardless of what the symbol text itself contains.
|
|
56
|
+
* The mandatory hex address guards against the numbered-log-line hazard
|
|
57
|
+
* (PHP's `#N`, C#'s bare `at`). `symbol` is greedy — safe because the
|
|
58
|
+
* trailing ` + <offset> in <image>` is anchored and unique per line.
|
|
120
59
|
*/
|
|
121
60
|
const FRAME_PATTERN = /^\d+\s+(?:\[[\w-]+\]\s+)*0x[0-9a-fA-F]+\s+(?<symbol>.+?)(?:\s\+\s\d+)?\s+in\s+(?<image>\S+)(?:\s+at\s+(?<location>.+))?\s*$/;
|
|
122
61
|
/**
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* 6.1.2 dump this parser was built against had an offset, because this
|
|
128
|
-
* machine has libc symbols; the CI runner does not, so `Thread 0`'s first
|
|
129
|
-
* frame is `<unknown> in libc.so.6`. `isSwiftFrameLine` gates
|
|
130
|
-
* `LineClassifier.isFrame`, so the block closed on that line, at 247
|
|
131
|
-
* characters, and the crashed thread three blocks below was never captured.
|
|
132
|
-
*
|
|
133
|
-
* `symbol` became lazy (`.+?`) with the offset optional. Greedy plus
|
|
134
|
-
* optional would let `.+` swallow ` + 309` and report the offset as part of
|
|
135
|
-
* the function name.
|
|
62
|
+
* `+ <offset>` is optional — requiring it took CI red (RT-133): a frame
|
|
63
|
+
* the backtracer can't name prints `<unknown> in <image>` with no offset
|
|
64
|
+
* (CI runner has no libc symbols). `symbol` became lazy (`.+?`) so
|
|
65
|
+
* greedy+optional doesn't swallow ` + 309` into the function name.
|
|
136
66
|
*/
|
|
137
67
|
const UNNAMED_SYMBOL = "<unknown>";
|
|
138
68
|
/**
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* The cost was **not** just unparsed frames. `isSwiftFrameLine` gates
|
|
149
|
-
* `LineClassifier.isFrame`, so the exception block closed at the thread
|
|
150
|
-
* header and `payload.raw` was **247 characters** — the addresses were
|
|
151
|
-
* never captured either, so nothing downstream could resolve them offline.
|
|
152
|
-
* A degradation that also destroys the evidence of itself.
|
|
153
|
-
*
|
|
154
|
-
* Safe against the numbered-log-line hazard for the same reason
|
|
155
|
-
* `FRAME_PATTERN` is, and anchored at both ends so it cannot swallow the
|
|
156
|
-
* `Registers:` and `Images:` sections that follow the crashed thread in the
|
|
157
|
-
* same slice: register lines begin with a register name (`rax`, ` r8`) and
|
|
158
|
-
* image lines with `0x`, so neither can present a leading frame index.
|
|
159
|
-
* Asserted, not assumed — see this package's unsymbolicated-dump test.
|
|
69
|
+
* Same line with no symbol at all: `4 [ra] 0x00005b3df1642b54`. Measured,
|
|
70
|
+
* took CI red (RT-126): when the backtracer can't symbolicate (`symbolicate=off`,
|
|
71
|
+
* unreadable images) it still unwinds and prints addresses only. Worse than
|
|
72
|
+
* unparsed frames — the exception block closed at the thread header and
|
|
73
|
+
* `payload.raw` was only 247 characters, so the addresses were lost too.
|
|
74
|
+
* Anchored at both ends so it can't swallow the following `Registers:`/
|
|
75
|
+
* `Images:` sections (register lines start with a register name, image
|
|
76
|
+
* lines with `0x`, neither has a leading frame index).
|
|
160
77
|
*/
|
|
161
78
|
const UNSYMBOLICATED_FRAME_PATTERN = /^\d+\s+(?:\[[\w-]+\]\s+)*0x[0-9a-fA-F]+\s*$/;
|
|
162
79
|
/** A real source position: `file:line:col`. Anchored at both ends so a bare pseudo-path like `<compiler-generated>` never matches. */
|
|
@@ -191,52 +108,31 @@ export function parseSwiftHeaderPosition(line) {
|
|
|
191
108
|
return { file, line: lineNumber };
|
|
192
109
|
}
|
|
193
110
|
/**
|
|
194
|
-
* Stateful
|
|
195
|
-
*
|
|
111
|
+
* Stateful bridge between header and first frame (point 1 above), via
|
|
112
|
+
* LineClassifier.isBridge (RT-048).
|
|
196
113
|
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
* all. The signal line is accepted as optional so both shapes work.
|
|
203
|
-
* `ExceptionBlockDetector` only ever consults `isBridge` while a header is
|
|
204
|
-
* already being held, so `*** Program crashed...` and `Thread N ...
|
|
205
|
-
* crashed:` cannot open a block on their own from idle — that guarantee is
|
|
206
|
-
* now the state machine's, not this classifier's to re-derive, which is
|
|
207
|
-
* what let `isFrame` shrink back to a plain, stateless check for the real
|
|
208
|
-
* frame shape: it no longer has to also recognise the bridge, only the
|
|
209
|
-
* thing that ends it.
|
|
114
|
+
* RT-122: a real crash's bridge is three lines, not two — the Signal line
|
|
115
|
+
* precedes `*** Program crashed ***` and isn't in the transcribed FileCheck
|
|
116
|
+
* test; missing it produced no evidence at all, so it's accepted as optional.
|
|
117
|
+
* isBridge is only consulted while a header is already held, so isFrame stays
|
|
118
|
+
* a plain stateless check for the frame shape alone.
|
|
210
119
|
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* — but does not confirm — a blank line at each gap; the test's own
|
|
215
|
-
* comment formatting cannot be trusted to mirror the real output's
|
|
216
|
-
* spacing. Rather than guess a specific count and position, every stage
|
|
217
|
-
* of the bridge accepts an arbitrary run of blank lines before the
|
|
218
|
-
* pattern it expects.
|
|
120
|
+
* Blank lines are tolerated at every stage of the bridge — the FileCheck
|
|
121
|
+
* test's `CHECK` (not `CHECK-NEXT`) formatting permits but doesn't confirm
|
|
122
|
+
* exact spacing, so no specific count/position is assumed.
|
|
219
123
|
*/
|
|
220
124
|
export function createSwiftLineClassifier() {
|
|
221
125
|
/**
|
|
222
|
-
* 0 =
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
* Three stages rather than RT-048's two, because the real backtracer
|
|
227
|
-
* prints one more line than the transcribed fixtures do. Stage 1 accepts
|
|
228
|
-
* either, which is what makes the signal line optional without a
|
|
229
|
-
* separate code path.
|
|
126
|
+
* 0 = idle; 1 = expecting signal line or "*** Program crashed"; 2 = signal
|
|
127
|
+
* seen, expecting "*** Program crashed"; 3 = expecting "Thread N ...:".
|
|
128
|
+
* Three stages (not RT-048's two) because the real backtracer prints one
|
|
129
|
+
* extra line; stage 1 accepts either, making the signal line optional.
|
|
230
130
|
*/
|
|
231
131
|
let stage = 0;
|
|
232
132
|
/**
|
|
233
|
-
* Whether a thread stack is
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
* opens a bare `stack-trace` block on one), so the blank-line and
|
|
237
|
-
* thread-header tolerance below MUST be gated on having already seen a
|
|
238
|
-
* real frame or a bridged thread line. Ungated, every blank line in
|
|
239
|
-
* ordinary output would open a block.
|
|
133
|
+
* Whether a thread stack is open. `isFrame` is also consulted from idle
|
|
134
|
+
* (ExceptionBlockDetector opens a bare block on one), so the tolerance
|
|
135
|
+
* below must be gated on this — else every blank line would open a block.
|
|
240
136
|
*/
|
|
241
137
|
let inStack = false;
|
|
242
138
|
return {
|
|
@@ -255,12 +151,8 @@ export function createSwiftLineClassifier() {
|
|
|
255
151
|
inStack = true;
|
|
256
152
|
return true;
|
|
257
153
|
}
|
|
258
|
-
//
|
|
259
|
-
//
|
|
260
|
-
// the intervening blank and `Thread N ...:` lines, the block ends at
|
|
261
|
-
// the first thread boundary and keeps only the stack of a thread that
|
|
262
|
-
// did not crash. Which thread's frames are then USED is the parser's
|
|
263
|
-
// decision, not this one's; this only keeps them in the block.
|
|
154
|
+
// A crash off the main thread dumps every thread, trapped one not first
|
|
155
|
+
// (RT-122) — accept blank/Thread-header lines mid-stack so its frames survive.
|
|
264
156
|
if (inStack && (line.trim() === "" || isSwiftThreadLine(line)))
|
|
265
157
|
return true;
|
|
266
158
|
inStack = false;
|
|
@@ -279,9 +171,7 @@ export function createSwiftLineClassifier() {
|
|
|
279
171
|
}
|
|
280
172
|
if (stage === 3 && isSwiftThreadLine(line)) {
|
|
281
173
|
stage = 0;
|
|
282
|
-
//
|
|
283
|
-
// frame 0 — so the stack is already "open" as far as isFrame is
|
|
284
|
-
// concerned, or that blank would close what the bridge just built.
|
|
174
|
+
// Mark the stack open now, or the blank line before frame 0 would close it.
|
|
285
175
|
inStack = true;
|
|
286
176
|
return true;
|
|
287
177
|
}
|
|
@@ -294,24 +184,17 @@ export function createSwiftLineClassifier() {
|
|
|
294
184
|
export function parseSwiftFrame(line) {
|
|
295
185
|
const match = FRAME_PATTERN.exec(line);
|
|
296
186
|
if (!match?.groups) {
|
|
297
|
-
//
|
|
298
|
-
// rather than one of them carrying the address as a stand-in. The
|
|
299
|
-
// frame's own text is kept verbatim as `StackFrame.raw` by the caller,
|
|
300
|
-
// which is where an address belongs — it is evidence, not a location.
|
|
187
|
+
// Unsymbolicated: nothing to name. Address stays in caller's StackFrame.raw as evidence.
|
|
301
188
|
return UNSYMBOLICATED_FRAME_PATTERN.test(line) ? { functionName: null, file: null, line: null, column: null } : null;
|
|
302
189
|
}
|
|
303
190
|
const symbol = match.groups["symbol"];
|
|
304
191
|
if (symbol === undefined)
|
|
305
192
|
return null;
|
|
306
|
-
//
|
|
307
|
-
// we.** Passing the literal through would put a symbol into evidence that
|
|
308
|
-
// resolves against no graph node and reads, to anything downstream, like
|
|
309
|
-
// a function actually called that. `SourceLocation.functionName` is
|
|
310
|
-
// nullable for exactly this.
|
|
193
|
+
// `<unknown>` means the backtracer has no name — don't pass it through as a real symbol.
|
|
311
194
|
const named = symbol === UNNAMED_SYMBOL ? null : symbol;
|
|
312
195
|
const location = match.groups["location"];
|
|
313
196
|
if (location === undefined) {
|
|
314
|
-
// No `at ...` suffix
|
|
197
|
+
// No `at ...` suffix — this image had no debug info.
|
|
315
198
|
return { functionName: named, file: null, line: null, column: null };
|
|
316
199
|
}
|
|
317
200
|
const withLineCol = FILE_LINE_COL_PATTERN.exec(location);
|
|
@@ -323,8 +206,7 @@ export function parseSwiftFrame(line) {
|
|
|
323
206
|
return { functionName: named, file, line: lineNumber, column: columnNumber };
|
|
324
207
|
}
|
|
325
208
|
}
|
|
326
|
-
//
|
|
327
|
-
// location — real text, kept verbatim as `file`, never a fabricated line.
|
|
209
|
+
// Pseudo-path or otherwise unparseable location — kept verbatim as `file`, never fabricated.
|
|
328
210
|
return { functionName: named, file: location, line: null, column: null };
|
|
329
211
|
}
|
|
330
212
|
//# sourceMappingURL=swift-frame-parsing.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swift-frame-parsing.js","sourceRoot":"","sources":["../src/swift-frame-parsing.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"swift-frame-parsing.js","sourceRoot":"","sources":["../src/swift-frame-parsing.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The concrete
|
|
2
|
+
* The concrete RuntimeAdapter for Swift.
|
|
3
3
|
*
|
|
4
|
-
* Needed no seam change: RT-037's already-nullable
|
|
5
|
-
*
|
|
6
|
-
* a
|
|
7
|
-
*
|
|
8
|
-
* this language needed from the classifier — the longest such bridge in
|
|
9
|
-
* this seam so far (PHP needed one line, C#'s `InnerException` needed
|
|
10
|
-
* one; Swift needs two, plus a tolerated blank line).
|
|
4
|
+
* Needed no seam change: RT-037's already-nullable SourceLocation/
|
|
5
|
+
* RawFrameLocation fields let a no-debug-info frame attach to null rather
|
|
6
|
+
* than a fabricated location. See swift-frame-parsing.ts for the two-line
|
|
7
|
+
* header/first-frame bridge (longest in this seam so far — PHP/C# need one).
|
|
11
8
|
*/
|
|
12
9
|
import type { RuntimeAdapter } from "@descryy/runtime-backend-observation";
|
|
13
10
|
export interface SwiftRuntimeAdapterOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swift-runtime-adapter.d.ts","sourceRoot":"","sources":["../src/swift-runtime-adapter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"swift-runtime-adapter.d.ts","sourceRoot":"","sources":["../src/swift-runtime-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,sCAAsC,CAAC;AAQhG,MAAM,WAAW,0BAA0B;IACzC,qGAAqG;IACrG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,0BAA+B,GAAG,cAAc,CAwBlG"}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The concrete
|
|
2
|
+
* The concrete RuntimeAdapter for Swift.
|
|
3
3
|
*
|
|
4
|
-
* Needed no seam change: RT-037's already-nullable
|
|
5
|
-
*
|
|
6
|
-
* a
|
|
7
|
-
*
|
|
8
|
-
* this language needed from the classifier — the longest such bridge in
|
|
9
|
-
* this seam so far (PHP needed one line, C#'s `InnerException` needed
|
|
10
|
-
* one; Swift needs two, plus a tolerated blank line).
|
|
4
|
+
* Needed no seam change: RT-037's already-nullable SourceLocation/
|
|
5
|
+
* RawFrameLocation fields let a no-debug-info frame attach to null rather
|
|
6
|
+
* than a fabricated location. See swift-frame-parsing.ts for the two-line
|
|
7
|
+
* header/first-frame bridge (longest in this seam so far — PHP/C# need one).
|
|
11
8
|
*/
|
|
12
9
|
import { createLogCollector } from "@descryy/runtime-backend-observation";
|
|
13
10
|
import { createSwiftLineClassifier } from "./swift-frame-parsing.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swift-runtime-adapter.js","sourceRoot":"","sources":["../src/swift-runtime-adapter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"swift-runtime-adapter.js","sourceRoot":"","sources":["../src/swift-runtime-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAE1E,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAC;AACxF,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAO5E,MAAM,UAAU,yBAAyB,CAAC,UAAsC,EAAE;IAChF,MAAM,sBAAsB,GAAG,iCAAiC,EAAE,CAAC;IACnE,MAAM,gBAAgB,GAAG,2BAA2B,CAAC,sBAAsB,CAAC,CAAC;IAE7E,SAAS,uBAAuB,CAAC,OAAuC;QACtE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAC5B,kBAAkB,CAAC;YACjB,MAAM;YACN,6DAA6D;YAC7D,6DAA6D;YAC7D,+CAA+C;YAC/C,UAAU,EAAE,yBAAyB,EAAE;YACvC,gBAAgB;YAChB,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvE,CAAC,CACH,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,OAAO;QACjB,gBAAgB;QAChB,sBAAsB;QACtB,uBAAuB;KACxB,CAAC;AACJ,CAAC"}
|
|
@@ -1,53 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Swift
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* text, before it is ever captured here — confirmed against
|
|
9
|
-
* `RuntimeErrorReporting.cpp`'s `dumpStackTraceEntry`, which looks the
|
|
10
|
-
* frame up and either finds a location or doesn't, before this adapter's
|
|
11
|
-
* code runs at all.
|
|
2
|
+
* Swift SourceLocationResolver — the second adapter to use
|
|
3
|
+
* `side-artifact-resolved`/`-missing` (RT-041's C# was the first): resolving
|
|
4
|
+
* means reading a verdict the runtime already reached, not performing one.
|
|
5
|
+
* The crash handler symbolicates each frame against debug info (embedded
|
|
6
|
+
* DWARF on Linux, `.dSYM` on Apple) before this text is ever captured —
|
|
7
|
+
* confirmed against RuntimeErrorReporting.cpp's dumpStackTraceEntry.
|
|
12
8
|
*
|
|
13
|
-
* - `file`/`line` present
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* frame always COULD have carried a location if debug info had been
|
|
18
|
-
* present for its image, unlike a language with no such mechanism at
|
|
19
|
-
* all. This is RT-041's own distinction (a managed C# frame vs. a PHP
|
|
20
|
-
* `[internal function]` frame), carried over rather than re-derived,
|
|
21
|
-
* because the underlying shape — a runtime that resolves against debug
|
|
22
|
-
* info before printing, rather than always capturing a location or
|
|
23
|
-
* never capturing one — is the same.
|
|
9
|
+
* - `file`/`line` present -> debug info matched -> `side-artifact-resolved`.
|
|
10
|
+
* - `file`/`line` absent -> `side-artifact-missing`, not `self-contained` —
|
|
11
|
+
* a compiled Swift frame always COULD have carried a location, unlike a
|
|
12
|
+
* language with no such mechanism (RT-041's C#-vs-PHP-internal-frame distinction).
|
|
24
13
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* binary's `LC_UUID` load command (or comparing embedded DWARF checksums)
|
|
28
|
-
* is real, scoped work this adapter does not attempt without a `swift`
|
|
29
|
-
* toolchain to build and verify it against.
|
|
14
|
+
* `side-artifact-stale` is not detected — verifying a `.dSYM` UUID against
|
|
15
|
+
* the binary's LC_UUID needs a swift toolchain this adapter doesn't have.
|
|
30
16
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* but never the specific `.dSYM` bundle or debug-info path that resolved
|
|
34
|
-
* it, so there is nothing to name without guessing one — the same
|
|
35
|
-
* reasoning as C#'s resolver, which cannot name a `.pdb` for the same
|
|
36
|
-
* kind of reason.
|
|
17
|
+
* `resolvedVia` stays null — the text names the image (`in libswiftCore.dylib`)
|
|
18
|
+
* but never the specific `.dSYM`/debug-info path, same as C#'s resolver.
|
|
37
19
|
*
|
|
38
|
-
* `column`
|
|
39
|
-
*
|
|
40
|
-
* fact — `RawFrameLocation.column` is passed straight through rather than
|
|
41
|
-
* discarded, because nothing here needs to derive or guess it the way a
|
|
42
|
-
* V8 source map's column sometimes does.
|
|
20
|
+
* `column` IS populated, unlike prior adapters' resolvers — Swift's
|
|
21
|
+
* `file:line:col` is a real captured fact, passed straight through.
|
|
43
22
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* enough information to name an origin for that frame — it simply isn't
|
|
48
|
-
* a source line — which is a different claim from "no debug info was
|
|
49
|
-
* available at all" and this resolver treats it that way rather than
|
|
50
|
-
* conflating the two.
|
|
23
|
+
* A `<compiler-generated>` pseudo-path (swift-frame-parsing.ts point 3)
|
|
24
|
+
* reports `side-artifact-resolved`, not `-missing`, even with null line/column
|
|
25
|
+
* — the crash handler did have enough info to name an origin, it just isn't a source line.
|
|
51
26
|
*/
|
|
52
27
|
import type { SourceLocationResolver } from "@descryy/runtime-backend-observation";
|
|
53
28
|
export declare function createSwiftSourceLocationResolver(): SourceLocationResolver;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swift-source-location-resolver.d.ts","sourceRoot":"","sources":["../src/swift-source-location-resolver.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"swift-source-location-resolver.d.ts","sourceRoot":"","sources":["../src/swift-source-location-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,KAAK,EAAoB,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAErG,wBAAgB,iCAAiC,IAAI,sBAAsB,CAc1E"}
|
|
@@ -1,59 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Swift
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* text, before it is ever captured here — confirmed against
|
|
9
|
-
* `RuntimeErrorReporting.cpp`'s `dumpStackTraceEntry`, which looks the
|
|
10
|
-
* frame up and either finds a location or doesn't, before this adapter's
|
|
11
|
-
* code runs at all.
|
|
2
|
+
* Swift SourceLocationResolver — the second adapter to use
|
|
3
|
+
* `side-artifact-resolved`/`-missing` (RT-041's C# was the first): resolving
|
|
4
|
+
* means reading a verdict the runtime already reached, not performing one.
|
|
5
|
+
* The crash handler symbolicates each frame against debug info (embedded
|
|
6
|
+
* DWARF on Linux, `.dSYM` on Apple) before this text is ever captured —
|
|
7
|
+
* confirmed against RuntimeErrorReporting.cpp's dumpStackTraceEntry.
|
|
12
8
|
*
|
|
13
|
-
* - `file`/`line` present
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* frame always COULD have carried a location if debug info had been
|
|
18
|
-
* present for its image, unlike a language with no such mechanism at
|
|
19
|
-
* all. This is RT-041's own distinction (a managed C# frame vs. a PHP
|
|
20
|
-
* `[internal function]` frame), carried over rather than re-derived,
|
|
21
|
-
* because the underlying shape — a runtime that resolves against debug
|
|
22
|
-
* info before printing, rather than always capturing a location or
|
|
23
|
-
* never capturing one — is the same.
|
|
9
|
+
* - `file`/`line` present -> debug info matched -> `side-artifact-resolved`.
|
|
10
|
+
* - `file`/`line` absent -> `side-artifact-missing`, not `self-contained` —
|
|
11
|
+
* a compiled Swift frame always COULD have carried a location, unlike a
|
|
12
|
+
* language with no such mechanism (RT-041's C#-vs-PHP-internal-frame distinction).
|
|
24
13
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* binary's `LC_UUID` load command (or comparing embedded DWARF checksums)
|
|
28
|
-
* is real, scoped work this adapter does not attempt without a `swift`
|
|
29
|
-
* toolchain to build and verify it against.
|
|
14
|
+
* `side-artifact-stale` is not detected — verifying a `.dSYM` UUID against
|
|
15
|
+
* the binary's LC_UUID needs a swift toolchain this adapter doesn't have.
|
|
30
16
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* but never the specific `.dSYM` bundle or debug-info path that resolved
|
|
34
|
-
* it, so there is nothing to name without guessing one — the same
|
|
35
|
-
* reasoning as C#'s resolver, which cannot name a `.pdb` for the same
|
|
36
|
-
* kind of reason.
|
|
17
|
+
* `resolvedVia` stays null — the text names the image (`in libswiftCore.dylib`)
|
|
18
|
+
* but never the specific `.dSYM`/debug-info path, same as C#'s resolver.
|
|
37
19
|
*
|
|
38
|
-
* `column`
|
|
39
|
-
*
|
|
40
|
-
* fact — `RawFrameLocation.column` is passed straight through rather than
|
|
41
|
-
* discarded, because nothing here needs to derive or guess it the way a
|
|
42
|
-
* V8 source map's column sometimes does.
|
|
20
|
+
* `column` IS populated, unlike prior adapters' resolvers — Swift's
|
|
21
|
+
* `file:line:col` is a real captured fact, passed straight through.
|
|
43
22
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* enough information to name an origin for that frame — it simply isn't
|
|
48
|
-
* a source line — which is a different claim from "no debug info was
|
|
49
|
-
* available at all" and this resolver treats it that way rather than
|
|
50
|
-
* conflating the two.
|
|
23
|
+
* A `<compiler-generated>` pseudo-path (swift-frame-parsing.ts point 3)
|
|
24
|
+
* reports `side-artifact-resolved`, not `-missing`, even with null line/column
|
|
25
|
+
* — the crash handler did have enough info to name an origin, it just isn't a source line.
|
|
51
26
|
*/
|
|
52
27
|
export function createSwiftSourceLocationResolver() {
|
|
53
28
|
return {
|
|
54
|
-
// Async to satisfy the interface;
|
|
55
|
-
// the crash handler already did (or didn't) resolve this frame before
|
|
56
|
-
// the text was captured.
|
|
29
|
+
// Async to satisfy the interface; the crash handler already resolved (or didn't) before capture.
|
|
57
30
|
resolve(frame) {
|
|
58
31
|
return Promise.resolve({
|
|
59
32
|
file: frame.file,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swift-source-location-resolver.js","sourceRoot":"","sources":["../src/swift-source-location-resolver.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"swift-source-location-resolver.js","sourceRoot":"","sources":["../src/swift-source-location-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAKH,MAAM,UAAU,iCAAiC;IAC/C,OAAO;QACL,iGAAiG;QACjG,OAAO,CAAC,KAAuB;YAC7B,OAAO,OAAO,CAAC,OAAO,CAAC;gBACrB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,WAAW,EAAE,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,wBAAwB;gBACrF,WAAW,EAAE,IAAI;aAClB,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,87 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Composes the pure Swift line-recognition functions
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* Thread 0 "server": <- still sitting in accept(); did not crash
|
|
29
|
-
* 0 … accept + 77 in libc.so.6
|
|
30
|
-
* …
|
|
31
|
-
* Thread 1 crashed: <- the one that trapped
|
|
32
|
-
* 0 … _assertionFailure … in libswiftCore.so
|
|
33
|
-
* 4 … readCustomerName(_:) … at …/server.swift:10:28
|
|
34
|
-
* ```
|
|
35
|
-
*
|
|
36
|
-
* Reading every frame-shaped line, as this parser used to, concatenates
|
|
37
|
-
* two unrelated call chains into one — the identical fabricated-call-
|
|
38
|
-
* relationship error `adapter-go` refuses for goroutines — and, worse,
|
|
39
|
-
* puts a thread that did not crash at the head of the list, so
|
|
40
|
-
* `Evidence.sourceLocation` names `accept` in libc. The other threads stay
|
|
41
|
-
* in the block's raw text, disclosed rather than merged or discarded.
|
|
42
|
-
*
|
|
43
|
-
* **2. `primaryFrameIndex` points at the position the header declares.**
|
|
44
|
-
* Swift's `Fatal error:` line on Linux carries one —
|
|
45
|
-
* `server/server.swift:10: Fatal error: …` — and frames 0..3 above the
|
|
46
|
-
* application frame are `_assertionFailure` closures inside
|
|
47
|
-
* `libswiftCore.so`. `collector.ts` reports `frames[0]` as
|
|
48
|
-
* `Evidence.sourceLocation`, so unrooted this names a Swift-runtime
|
|
49
|
-
* closure with no source location at all.
|
|
50
|
-
*
|
|
51
|
-
* This is an exact join on a position the runtime printed, not a
|
|
52
|
-
* classification of images as "runtime" or "application": nothing here
|
|
53
|
-
* looks at `libswiftCore.so`. It matches on **basename and line** — the
|
|
54
|
-
* header's `#file` is `Module/File.swift` while a frame carries the
|
|
55
|
-
* absolute path, so neither is a prefix of the other, and Swift's header
|
|
56
|
-
* (unlike Rust's) has no column to tighten it with. That is a weaker join
|
|
57
|
-
* than Rust's and is stated as such. The transcribed fixtures declare no
|
|
58
|
-
* position at all, so the index stays `0` for them — the honest outcome,
|
|
59
|
-
* not a fallback.
|
|
60
|
-
*
|
|
61
|
-
* ## An unsymbolicated dump is a stack, not the absence of one (RT-126/128)
|
|
62
|
-
*
|
|
63
|
-
* When the backtracer cannot symbolicate it still unwinds correctly and
|
|
64
|
-
* prints every frame as a bare address. Those frames are parsed and kept,
|
|
65
|
-
* with `file`, `line`, `functionName` and `column` all null and the address
|
|
2
|
+
* Composes the pure Swift line-recognition functions (swift-frame-parsing.ts)
|
|
3
|
+
* with a SourceLocationResolver to implement the async StackTraceParser contract.
|
|
4
|
+
*
|
|
5
|
+
* No reversal — frame 0 is the trap site, counting up to `main` (V8/JVM/Go/
|
|
6
|
+
* Rust/C# convention; CPython is the outlier).
|
|
7
|
+
*
|
|
8
|
+
* `fidelity` is always `"synchronous"` — the crash handler walks the actual
|
|
9
|
+
* trapped thread and cannot show a logical caller across a Task/actor hop
|
|
10
|
+
* the runtime didn't preserve, the same limit every other adapter shares.
|
|
11
|
+
*
|
|
12
|
+
* Two things a real crash (Swift 6.1.2 Linux, RT-122) added beyond the transcript:
|
|
13
|
+
*
|
|
14
|
+
* 1. Only the thread marked `crashed:` becomes frames. Off-main-thread traps
|
|
15
|
+
* dump every thread, uncrashed ones first — reading every frame-shaped
|
|
16
|
+
* line would concatenate unrelated call chains and put an uncrashed
|
|
17
|
+
* thread's frame at `Evidence.sourceLocation`. Other threads stay in the
|
|
18
|
+
* block's raw text, disclosed not discarded.
|
|
19
|
+
* 2. `primaryFrameIndex` points at the position the header declares
|
|
20
|
+
* (`server/server.swift:10: Fatal error: ...`), matched on basename+line
|
|
21
|
+
* only — the header path and a frame's absolute path aren't prefixes of
|
|
22
|
+
* each other, and Swift's header has no column to tighten with, so this
|
|
23
|
+
* join is weaker than Rust's. Transcribed fixtures declare no position,
|
|
24
|
+
* so the index stays 0 for them — honest, not a fallback.
|
|
25
|
+
*
|
|
26
|
+
* An unsymbolicated dump is a stack, not the absence of one (RT-126/128):
|
|
27
|
+
* frames with null file/line/functionName/column are still kept, address
|
|
66
28
|
* preserved in `StackFrame.raw`. `fidelity` stays `"synchronous"` — the
|
|
67
|
-
* chain is
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* `primaryFrameLocation` (RT-087) turns that frame into a null
|
|
76
|
-
* `Evidence.sourceLocation` rather than a location naming nothing.
|
|
77
|
-
*
|
|
78
|
-
* **Selecting a thread is not the same as choosing an index, and only one
|
|
79
|
-
* of the two became a view.** `primaryFrameIndex` (RT-083) keeps every
|
|
80
|
-
* frame of the crashed thread and points at the failure site. The
|
|
81
|
-
* *uncrashed* threads are still excluded from `frames` entirely, because
|
|
82
|
-
* they are not frames of this stack at all — including them would assert a
|
|
83
|
-
* call relationship between independent threads, which no index can undo.
|
|
84
|
-
* They remain in `payload.raw`.
|
|
29
|
+
* chain is accurate, only names are missing. Returning `null` instead (old
|
|
30
|
+
* behavior) made "a Swift crash we couldn't name" indistinguishable from
|
|
31
|
+
* "not a Swift crash". `primaryFrameIndex` stays 0 with no names to root against.
|
|
32
|
+
*
|
|
33
|
+
* Selecting a thread and choosing an index are different: `primaryFrameIndex`
|
|
34
|
+
* (RT-083) points within the crashed thread's frames; uncrashed threads are
|
|
35
|
+
* excluded from `frames` entirely, not merely deprioritized, since including
|
|
36
|
+
* them would assert a call relationship between independent threads.
|
|
85
37
|
*/
|
|
86
38
|
import type { SourceLocationResolver, StackTraceParser } from "@descryy/runtime-backend-observation";
|
|
87
39
|
export declare function createSwiftStackTraceParser(resolver: SourceLocationResolver): StackTraceParser;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swift-stack-trace-parser.d.ts","sourceRoot":"","sources":["../src/swift-stack-trace-parser.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"swift-stack-trace-parser.d.ts","sourceRoot":"","sources":["../src/swift-stack-trace-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AA+BrG,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,sBAAsB,GAAG,gBAAgB,CA2C9F"}
|
|
@@ -1,99 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Composes the pure Swift line-recognition functions
|
|
3
|
-
*
|
|
4
|
-
* the full async `StackTraceParser` contract.
|
|
2
|
+
* Composes the pure Swift line-recognition functions (swift-frame-parsing.ts)
|
|
3
|
+
* with a SourceLocationResolver to implement the async StackTraceParser contract.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* recent-first already, the same convention as V8, the JVM, Go, Rust and
|
|
9
|
-
* C#; CPython remains the only reversing outlier in this codebase.
|
|
5
|
+
* No reversal — frame 0 is the trap site, counting up to `main` (V8/JVM/Go/
|
|
6
|
+
* Rust/C# convention; CPython is the outlier).
|
|
10
7
|
*
|
|
11
|
-
* `fidelity` is always
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* hard limit every other adapter in this codebase shares. Claiming
|
|
15
|
-
* `"concurrency-fragmented"` without an explicit marker for it would be a
|
|
16
|
-
* guess this project refuses to make.
|
|
8
|
+
* `fidelity` is always `"synchronous"` — the crash handler walks the actual
|
|
9
|
+
* trapped thread and cannot show a logical caller across a Task/actor hop
|
|
10
|
+
* the runtime didn't preserve, the same limit every other adapter shares.
|
|
17
11
|
*
|
|
18
|
-
*
|
|
12
|
+
* Two things a real crash (Swift 6.1.2 Linux, RT-122) added beyond the transcript:
|
|
19
13
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
14
|
+
* 1. Only the thread marked `crashed:` becomes frames. Off-main-thread traps
|
|
15
|
+
* dump every thread, uncrashed ones first — reading every frame-shaped
|
|
16
|
+
* line would concatenate unrelated call chains and put an uncrashed
|
|
17
|
+
* thread's frame at `Evidence.sourceLocation`. Other threads stay in the
|
|
18
|
+
* block's raw text, disclosed not discarded.
|
|
19
|
+
* 2. `primaryFrameIndex` points at the position the header declares
|
|
20
|
+
* (`server/server.swift:10: Fatal error: ...`), matched on basename+line
|
|
21
|
+
* only — the header path and a frame's absolute path aren't prefixes of
|
|
22
|
+
* each other, and Swift's header has no column to tighten with, so this
|
|
23
|
+
* join is weaker than Rust's. Transcribed fixtures declare no position,
|
|
24
|
+
* so the index stays 0 for them — honest, not a fallback.
|
|
22
25
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* uncrashed ones are printed **first**:
|
|
26
|
-
*
|
|
27
|
-
* ```
|
|
28
|
-
* Thread 0 "server": <- still sitting in accept(); did not crash
|
|
29
|
-
* 0 … accept + 77 in libc.so.6
|
|
30
|
-
* …
|
|
31
|
-
* Thread 1 crashed: <- the one that trapped
|
|
32
|
-
* 0 … _assertionFailure … in libswiftCore.so
|
|
33
|
-
* 4 … readCustomerName(_:) … at …/server.swift:10:28
|
|
34
|
-
* ```
|
|
35
|
-
*
|
|
36
|
-
* Reading every frame-shaped line, as this parser used to, concatenates
|
|
37
|
-
* two unrelated call chains into one — the identical fabricated-call-
|
|
38
|
-
* relationship error `adapter-go` refuses for goroutines — and, worse,
|
|
39
|
-
* puts a thread that did not crash at the head of the list, so
|
|
40
|
-
* `Evidence.sourceLocation` names `accept` in libc. The other threads stay
|
|
41
|
-
* in the block's raw text, disclosed rather than merged or discarded.
|
|
42
|
-
*
|
|
43
|
-
* **2. `primaryFrameIndex` points at the position the header declares.**
|
|
44
|
-
* Swift's `Fatal error:` line on Linux carries one —
|
|
45
|
-
* `server/server.swift:10: Fatal error: …` — and frames 0..3 above the
|
|
46
|
-
* application frame are `_assertionFailure` closures inside
|
|
47
|
-
* `libswiftCore.so`. `collector.ts` reports `frames[0]` as
|
|
48
|
-
* `Evidence.sourceLocation`, so unrooted this names a Swift-runtime
|
|
49
|
-
* closure with no source location at all.
|
|
50
|
-
*
|
|
51
|
-
* This is an exact join on a position the runtime printed, not a
|
|
52
|
-
* classification of images as "runtime" or "application": nothing here
|
|
53
|
-
* looks at `libswiftCore.so`. It matches on **basename and line** — the
|
|
54
|
-
* header's `#file` is `Module/File.swift` while a frame carries the
|
|
55
|
-
* absolute path, so neither is a prefix of the other, and Swift's header
|
|
56
|
-
* (unlike Rust's) has no column to tighten it with. That is a weaker join
|
|
57
|
-
* than Rust's and is stated as such. The transcribed fixtures declare no
|
|
58
|
-
* position at all, so the index stays `0` for them — the honest outcome,
|
|
59
|
-
* not a fallback.
|
|
60
|
-
*
|
|
61
|
-
* ## An unsymbolicated dump is a stack, not the absence of one (RT-126/128)
|
|
62
|
-
*
|
|
63
|
-
* When the backtracer cannot symbolicate it still unwinds correctly and
|
|
64
|
-
* prints every frame as a bare address. Those frames are parsed and kept,
|
|
65
|
-
* with `file`, `line`, `functionName` and `column` all null and the address
|
|
26
|
+
* An unsymbolicated dump is a stack, not the absence of one (RT-126/128):
|
|
27
|
+
* frames with null file/line/functionName/column are still kept, address
|
|
66
28
|
* preserved in `StackFrame.raw`. `fidelity` stays `"synchronous"` — the
|
|
67
|
-
* chain is
|
|
68
|
-
*
|
|
69
|
-
*
|
|
29
|
+
* chain is accurate, only names are missing. Returning `null` instead (old
|
|
30
|
+
* behavior) made "a Swift crash we couldn't name" indistinguishable from
|
|
31
|
+
* "not a Swift crash". `primaryFrameIndex` stays 0 with no names to root against.
|
|
70
32
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* `primaryFrameLocation` (RT-087) turns that frame into a null
|
|
76
|
-
* `Evidence.sourceLocation` rather than a location naming nothing.
|
|
77
|
-
*
|
|
78
|
-
* **Selecting a thread is not the same as choosing an index, and only one
|
|
79
|
-
* of the two became a view.** `primaryFrameIndex` (RT-083) keeps every
|
|
80
|
-
* frame of the crashed thread and points at the failure site. The
|
|
81
|
-
* *uncrashed* threads are still excluded from `frames` entirely, because
|
|
82
|
-
* they are not frames of this stack at all — including them would assert a
|
|
83
|
-
* call relationship between independent threads, which no index can undo.
|
|
84
|
-
* They remain in `payload.raw`.
|
|
33
|
+
* Selecting a thread and choosing an index are different: `primaryFrameIndex`
|
|
34
|
+
* (RT-083) points within the crashed thread's frames; uncrashed threads are
|
|
35
|
+
* excluded from `frames` entirely, not merely deprioritized, since including
|
|
36
|
+
* them would assert a call relationship between independent threads.
|
|
85
37
|
*/
|
|
86
38
|
import { isSwiftCrashedThreadLine, isSwiftThreadLine, parseSwiftFrame, parseSwiftHeaderPosition, } from "./swift-frame-parsing.js";
|
|
87
39
|
const basename = (path) => path.split("/").pop() ?? path;
|
|
88
40
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* but none marked `crashed` is refused a choice: every line is returned,
|
|
94
|
-
* which keeps the pre-existing behaviour rather than picking a thread on
|
|
95
|
-
* no evidence. Swift has always marked one in every shape observed, so
|
|
96
|
-
* that branch is a guard, not a supported case.
|
|
41
|
+
* Lines belonging to the thread that actually trapped. No thread headers at
|
|
42
|
+
* all (transcribed fixtures) is one implicit block, all lines returned.
|
|
43
|
+
* Thread headers with none marked `crashed` also returns everything rather
|
|
44
|
+
* than guessing — a guard, not an observed case.
|
|
97
45
|
*/
|
|
98
46
|
function crashedThreadLines(lines) {
|
|
99
47
|
const start = lines.findIndex(isSwiftCrashedThreadLine);
|
|
@@ -117,8 +65,7 @@ export function createSwiftStackTraceParser(resolver) {
|
|
|
117
65
|
/** Index of the frame at the header's own declared position, or -1 when nothing declares or matches one — reported as `primaryFrameIndex: 0`, stated rather than defaulted. */
|
|
118
66
|
let declaredSite = -1;
|
|
119
67
|
for (const line of crashedThreadLines(lines)) {
|
|
120
|
-
//
|
|
121
|
-
// parts of the block, none fabricated into a frame.
|
|
68
|
+
// Header and bridging lines parse to null here — never fabricated into a frame.
|
|
122
69
|
const raw = parseSwiftFrame(line);
|
|
123
70
|
if (raw === null)
|
|
124
71
|
continue;
|
|
@@ -129,8 +76,7 @@ export function createSwiftStackTraceParser(resolver) {
|
|
|
129
76
|
functionName: raw.functionName,
|
|
130
77
|
});
|
|
131
78
|
frames.push({ location, raw: line });
|
|
132
|
-
// First match only: recursion would repeat
|
|
133
|
-
// down, and the outermost repetition is not where execution was.
|
|
79
|
+
// First match only: recursion would repeat this position further down.
|
|
134
80
|
if (declaredSite === -1 &&
|
|
135
81
|
declared !== null &&
|
|
136
82
|
raw.file !== null &&
|
|
@@ -140,9 +86,7 @@ export function createSwiftStackTraceParser(resolver) {
|
|
|
140
86
|
}
|
|
141
87
|
}
|
|
142
88
|
if (frames.length === 0) {
|
|
143
|
-
// Nothing frame-shaped
|
|
144
|
-
// trace, per this interface's contract: null, never an empty-but-
|
|
145
|
-
// structured StackTrace standing in for "couldn't parse".
|
|
89
|
+
// Nothing frame-shaped — null, never an empty StackTrace standing in for "couldn't parse".
|
|
146
90
|
return null;
|
|
147
91
|
}
|
|
148
92
|
return { fidelity: "synchronous", frames, primaryFrameIndex: declaredSite > 0 ? declaredSite : 0 };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swift-stack-trace-parser.js","sourceRoot":"","sources":["../src/swift-stack-trace-parser.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"swift-stack-trace-parser.js","sourceRoot":"","sources":["../src/swift-stack-trace-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAKH,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,eAAe,EACf,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAElC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC;AAEzE;;;;;GAKG;AACH,SAAS,kBAAkB,CAAC,KAAwB;IAClD,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;IACxD,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAE/B,IAAI,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;IACvB,KAAK,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9C,IAAI,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC;YACjC,GAAG,GAAG,CAAC,CAAC;YACR,MAAM;QACR,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,QAAgC;IAC1E,OAAO;QACL,KAAK,CAAC,KAAK,CAAC,OAAe;YACzB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;YAErF,MAAM,MAAM,GAAiB,EAAE,CAAC;YAChC,+KAA+K;YAC/K,IAAI,YAAY,GAAG,CAAC,CAAC,CAAC;YAEtB,KAAK,MAAM,IAAI,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC7C,gFAAgF;gBAChF,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;gBAClC,IAAI,GAAG,KAAK,IAAI;oBAAE,SAAS;gBAE3B,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC;oBACtC,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,YAAY,EAAE,GAAG,CAAC,YAAY;iBAC/B,CAAC,CAAC;gBACH,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;gBAErC,uEAAuE;gBACvE,IACE,YAAY,KAAK,CAAC,CAAC;oBACnB,QAAQ,KAAK,IAAI;oBACjB,GAAG,CAAC,IAAI,KAAK,IAAI;oBACjB,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI;oBAC1B,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAC9C,CAAC;oBACD,YAAY,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;YAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,2FAA2F;gBAC3F,OAAO,IAAI,CAAC;YACd,CAAC;YAED,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,iBAAiB,EAAE,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACrG,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@descryy/runtime-adapter-swift",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Swift runtime adapter: crash-handler trace parsing, side-artifact-aware source locations, the concrete RuntimeAdapter for Swift.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/descryhq-wq/descry-runtime.git",
|
|
10
|
+
"directory": "packages/adapter-swift"
|
|
11
|
+
},
|
|
7
12
|
"engines": {
|
|
8
13
|
"node": ">=22.5"
|
|
9
14
|
},
|
|
@@ -24,7 +29,7 @@
|
|
|
24
29
|
"build": "tsc -b"
|
|
25
30
|
},
|
|
26
31
|
"dependencies": {
|
|
27
|
-
"@descryy/runtime-contracts": "
|
|
28
|
-
"@descryy/runtime-backend-observation": "
|
|
32
|
+
"@descryy/runtime-contracts": "0.3.1",
|
|
33
|
+
"@descryy/runtime-backend-observation": "0.3.1"
|
|
29
34
|
}
|
|
30
35
|
}
|