Skip to content

Commit 8e87541

Browse files
authored
Merge pull request swiftlang#79009 from al45tair/eng/PR-121430255
[Backtracing] Add JSON backtrace output
2 parents 9c598e8 + 6138992 commit 8e87541

File tree

14 files changed

+1273
-47
lines changed

14 files changed

+1273
-47
lines changed

docs/Backtracing.rst

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,17 @@ follows:
9292
| | | only has effect on platforms that have a symbol |
9393
| | | cache that can be controlled by the runtime. |
9494
+-----------------+---------+--------------------------------------------------+
95+
| format | text | Set to ``json`` to output JSON crash logs rather |
96+
| | | than plain text. |
97+
+-----------------+---------+--------------------------------------------------+
9598
| output-to | stdout | Set to ``stderr`` to send the backtrace to the |
9699
| | | standard error instead of standard output. This |
97100
| | | may be useful in some CI systems. |
101+
| | | |
102+
| | | You may also specify a path; if this points at a |
103+
| | | directory, the backtracer will generate unique |
104+
| | | filenames within that directory. Otherwise it |
105+
| | | is assumed to be a filename. |
98106
+-----------------+---------+--------------------------------------------------+
99107
| symbolicate | full | Options are ``full``, ``fast``, or ``off``. |
100108
| | | Full means to look up source locations and |
@@ -334,3 +342,200 @@ large number of frames in a much smaller space than would otherwise be possible.
334342
Similarly, where we need to store address to image mappings, we
335343
use :download:`Compact ImageMap Format <CompactImageMapFormat.md>` to minimise
336344
storage requirements.
345+
346+
JSON Crash Logs
347+
---------------
348+
349+
JSON crash logs are a structured crash log format that the backtracer is able
350+
to output. Note that addresses are represented in this format as hexadecimal
351+
strings, rather than as numbers, in order to avoid representational issues.
352+
Additionally, boolean fields that are ``false``, as well as fields whose
353+
values are unknown or empty, will normally be completely omitted to save space.
354+
355+
Where hexadecimal *values* are output, they will normally be prefixed with
356+
a ``0x`` prefix. Hexadecimal *data*, by contrast, such as captured memory or
357+
build IDs, will not have a prefix and will be formatted as a string with no
358+
whitespace.
359+
360+
Note that since JSON does not officially support hexadecimal, hexadecimal
361+
values will always be output as strings.
362+
363+
JSON crash logs will always contain the following top level fields:
364+
365+
+-------------------+--------------------------------------------------------+
366+
| Field | Value |
367+
+===================+========================================================+
368+
| timestamp | An ISO-8601 formatted timestamp, as a string. |
369+
+-------------------+--------------------------------------------------------+
370+
| kind | The string ``crashReport``. |
371+
+-------------------+--------------------------------------------------------+
372+
| description | A textual description of the crash or runtime failure. |
373+
+-------------------+--------------------------------------------------------+
374+
| faultAddress | The fault address associated with the crash. |
375+
+-------------------+--------------------------------------------------------+
376+
| platform | A string describing the platform; the first token |
377+
| | identifies the platform itself and is followed by |
378+
| | platform specific version information. |
379+
| | |
380+
| | e.g. "macOS 13.0 (22A380)", |
381+
| | "linux (Ubuntu 22.04.5 LTS)" |
382+
+-------------------+--------------------------------------------------------+
383+
| architecture | The name of the processor architecture for this crash. |
384+
+-------------------+--------------------------------------------------------+
385+
| threads | An array of thread records, one for each thread. |
386+
+-------------------+--------------------------------------------------------+
387+
388+
These will be followed by some or all of the following, according to the
389+
backtracer settings:
390+
391+
+-------------------+--------------------------------------------------------+
392+
| Field | Value |
393+
+===================+========================================================+
394+
| omittedThreads | A count of the number of threads that were omitted, if |
395+
| | the backtracer is set to give a backtrace only for the |
396+
| | crashed thread. Omitted if zero. |
397+
+-------------------+--------------------------------------------------------+
398+
| capturedMemory | A dictionary containing captured memory contents, if |
399+
| | any. This will not be present if the ``sanitize`` |
400+
| | setting is enabled, or if no data was captured. |
401+
| | |
402+
| | The dictionary is keyed by hexadecimal addresses, as |
403+
| | strings (with a ``0x`` prefix); the captured data is |
404+
| | also given as a hexadecimal string, but with no prefix |
405+
| | and no inter-byte whitespace. |
406+
| | |
407+
| | You should make no assumptions about the number of |
408+
| | bytes captured at each address; the backtracer will |
409+
| | currently attempt to grab 16 bytes, but this may |
410+
| | change if only a shorter range is available or in |
411+
| | future according to configuration parameters. |
412+
+-------------------+--------------------------------------------------------+
413+
| omittedImages | If ``images`` is set to ``mentioned``, this is an |
414+
| | integer giving the number of images whose details were |
415+
| | omitted from the crash log. |
416+
+-------------------+--------------------------------------------------------+
417+
| images | Unless ``images`` is ``none``, an array of records |
418+
| | describing the loaded images in the crashed process. |
419+
+-------------------+--------------------------------------------------------+
420+
| backtraceTime | The time taken to generate the crash report, in |
421+
| | seconds. |
422+
+-------------------+--------------------------------------------------------+
423+
424+
Thread Records
425+
^^^^^^^^^^^^^^
426+
427+
A thread record is a dictionary with the following fields:
428+
429+
+-------------------+--------------------------------------------------------+
430+
| Field | Value |
431+
+===================+========================================================+
432+
| name | The name of the thread. Omitted if no name. |
433+
+-------------------+--------------------------------------------------------+
434+
| crashed | ``true`` if the thread is the one that crashed, |
435+
| | omitted otherwise. |
436+
+-------------------+--------------------------------------------------------+
437+
| registers | A dictionary containing the register contents on the |
438+
| | crashed thread. |
439+
| | |
440+
| | The dictionary is keyed by architecture specific |
441+
| | register name; values are given as hexadecimal |
442+
| | strings (with a ``0x`` prefix). |
443+
| | |
444+
| | This field may be omitted for threads other than the |
445+
| | crashed thread, if the ``registers`` setting is set |
446+
| | to ``crashed``. |
447+
+-------------------+--------------------------------------------------------+
448+
| frames | An array of frames forming the backtrace for the |
449+
| | thread. |
450+
+-------------------+--------------------------------------------------------+
451+
452+
Each frame in the backtrace is described by a dictionary containing the
453+
following fields:
454+
455+
+-------------------+--------------------------------------------------------+
456+
| Field | Value |
457+
+===================+========================================================+
458+
| kind | ``programCounter`` if the frame address is a directly |
459+
| | captured program counter/instruction pointer. |
460+
| | |
461+
| | ``returnAddress`` if the frame address is a return |
462+
| | address. |
463+
| | |
464+
| | ``asyncResumePoint`` if the frame address is a |
465+
| | resumption point in an ``async`` function. |
466+
| | |
467+
| | ``omittedFrames`` if this is a frame omission record. |
468+
| | |
469+
| | ``truncated`` to indicate that the backtrace is |
470+
| | truncated at this point and that more frames were |
471+
| | available but not captured. |
472+
+-------------------+--------------------------------------------------------+
473+
| address | The frame address as a string (for records containing |
474+
| | an address). |
475+
+-------------------+--------------------------------------------------------+
476+
| count | The number of frames omitted at this point in the |
477+
| | backtrace (``omittedFrames`` only). |
478+
+-------------------+--------------------------------------------------------+
479+
480+
If the backtrace is symbolicated, the frame record may also contain the
481+
following additional information:
482+
483+
+-------------------+--------------------------------------------------------+
484+
| Field | Value |
485+
+===================+========================================================+
486+
| inlined | ``true`` if this frame is inlined, omitted otherwise. |
487+
+-------------------+--------------------------------------------------------+
488+
| runtimeFailure | ``true`` if this frame represents a Swift runtime |
489+
| | failure, omitted otherwise. |
490+
+-------------------+--------------------------------------------------------+
491+
| thunk | ``true`` if this frame is a compiler-generated thunk |
492+
| | function, omitted otherwise. |
493+
+-------------------+--------------------------------------------------------+
494+
| system | ``true`` if this frame is a system frame, omitted |
495+
| | otherwise. |
496+
+-------------------+--------------------------------------------------------+
497+
498+
If symbol lookup succeeded for the frame address, the following additional
499+
fields will be present:
500+
501+
+-------------------+--------------------------------------------------------+
502+
| Field | Value |
503+
+===================+========================================================+
504+
| symbol | The mangled name of the symbol corresponding to the |
505+
| | frame address. |
506+
+-------------------+--------------------------------------------------------+
507+
| offset | The offset from the symbol to the frame address. |
508+
+-------------------+--------------------------------------------------------+
509+
| description | If demangling is enabled, a human readable description |
510+
| | of the frame address, otherwise omitted. |
511+
+-------------------+--------------------------------------------------------+
512+
| image | The name of the image in which the symbol was found; |
513+
| | omitted if no corresponding image exists. |
514+
+-------------------+--------------------------------------------------------+
515+
| sourceLocation | If the source location of the symbol is known, a |
516+
| | dictionary containing ``file``, ``line`` and |
517+
| | ``column`` keys that identify the location of the |
518+
| | symbol in the source files. |
519+
+-------------------+--------------------------------------------------------+
520+
521+
Image Records
522+
^^^^^^^^^^^^^
523+
524+
An image record is a dictionary with the following fields:
525+
526+
+-------------------+--------------------------------------------------------+
527+
| Field | Value |
528+
+===================+========================================================+
529+
| name | The name of the image (omitted if not known). |
530+
+-------------------+--------------------------------------------------------+
531+
| buildId | The build ID (aka unique ID) of the image (omitted if |
532+
| | not known). Build IDs are formatted as un-prefixed |
533+
| | hexadecimal strings, with no inter-byte whitespace. |
534+
+-------------------+--------------------------------------------------------+
535+
| path | The path to the image (omitted if not known). |
536+
+-------------------+--------------------------------------------------------+
537+
| baseAddress | The base address of the image text, as a hexadecimal |
538+
| | string. |
539+
+-------------------+--------------------------------------------------------+
540+
| endOfText | The end of the image text, as a hexadecimal string. |
541+
+-------------------+--------------------------------------------------------+

stdlib/public/RuntimeModule/Backtrace.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,30 @@ public struct Backtrace: CustomStringConvertible, Sendable {
194194
return "..."
195195
}
196196
}
197+
198+
/// A JSON description of this frame, less the surrounding braces.
199+
/// This is useful if you want to add extra information.
200+
@_spi(Internal)
201+
public var jsonBody: String {
202+
switch self {
203+
case let .programCounter(addr):
204+
return "\"kind\": \"programCounter\", \"address\": \"\(addr)\""
205+
case let .returnAddress(addr):
206+
return "\"kind\": \"returnAddress\", \"address\": \"\(addr)\""
207+
case let .asyncResumePoint(addr):
208+
return "\"kind\": \"asyncResumePoint\", \"address\": \"\(addr)\""
209+
case let .omittedFrames(count):
210+
return "\"kind\": \"omittedFrames\", \"count\": \(count)"
211+
case .truncated:
212+
return "\"kind\": \"truncated\""
213+
}
214+
}
215+
216+
/// A JSON description of this frame.
217+
@_spi(Internal)
218+
public var jsonDescription: String {
219+
return "{ \(jsonBody) }"
220+
}
197221
}
198222

199223
/// Represents an image loaded in the process's address space

stdlib/public/RuntimeModule/BacktraceFormatter.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,8 @@ private func untabify(_ s: String, tabWidth: Int = 8) -> String {
416416
/// @param path The path to sanitize.
417417
///
418418
/// @returns A string containing the sanitized path.
419-
private func sanitizePath(_ path: String) -> String {
419+
@_spi(Formatting)
420+
public func sanitizePath(_ path: String) -> String {
420421
#if os(macOS)
421422
return CRCopySanitizedPath(path,
422423
kCRSanitizePathGlobAllTypes

stdlib/public/libexec/swift-backtrace/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ set(BACKTRACING_COMPILE_FLAGS
3333
set(BACKTRACING_SOURCES
3434
main.swift
3535
AnsiColor.swift
36+
JSON.swift
3637
TargetMacOS.swift
3738
TargetLinux.swift
3839
Themes.swift

0 commit comments

Comments
 (0)