Skip to content

[Backtracing] Implement API per SE-0419 #78516

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -695,10 +695,6 @@ option(SWIFT_IMPLICIT_CONCURRENCY_IMPORT
"Implicitly import the Swift concurrency module"
TRUE)

option(SWIFT_IMPLICIT_BACKTRACING_IMPORT
"Implicitly import the Swift backtracing module"
FALSE)

option(SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY
"Enable build of the Swift concurrency module"
FALSE)
Expand Down Expand Up @@ -731,6 +727,10 @@ option(SWIFT_ENABLE_SYNCHRONIZATION
"Enable build of the Swift Synchronization module"
FALSE)

option(SWIFT_ENABLE_RUNTIME_MODULE
"Build the Swift Runtime module"
FALSE)

option(SWIFT_ENABLE_VOLATILE
"Enable build of the Swift Volatile module"
FALSE)
Expand Down Expand Up @@ -865,11 +865,6 @@ if (CMAKE_Swift_COMPILER)
SWIFT_SUPPORTS_DISABLE_IMPLICIT_STRING_PROCESSING_MODULE_IMPORT)
message(STATUS " Implicit 'string-processing' import: ${SWIFT_SUPPORTS_DISABLE_IMPLICIT_STRING_PROCESSING_MODULE_IMPORT}")

# Same for _Backtracing.
swift_supports_implicit_module("backtracing"
SWIFT_SUPPORTS_DISABLE_IMPLICIT_BACKTRACING_MODULE_IMPORT)
message(STATUS " Implicit 'backtracing' import: ${SWIFT_SUPPORTS_DISABLE_IMPLICIT_BACKTRACING_MODULE_IMPORT}")

swift_get_package_cmo_support(
Swift_COMPILER_PACKAGE_CMO_SUPPORT)
message(STATUS " Package CMO: ${Swift_COMPILER_PACKAGE_CMO_SUPPORT}")
Expand Down Expand Up @@ -1393,6 +1388,8 @@ if(SWIFT_BUILD_STDLIB OR SWIFT_BUILD_SDK_OVERLAY)
message(STATUS "Observation Support: ${SWIFT_ENABLE_EXPERIMENTAL_OBSERVATION}")
message(STATUS "Synchronization Support: ${SWIFT_ENABLE_SYNCHRONIZATION}")
message(STATUS "Volatile Support: ${SWIFT_ENABLE_VOLATILE}")
message(STATUS "Pointer Bounds Support: ${SWIFT_ENABLE_EXPERIMENTAL_POINTER_BOUNDS}")
message(STATUS "Runtime Support: ${SWIFT_ENABLE_RUNTIME_MODULE}")
message(STATUS "")
else()
message(STATUS "Not building Swift standard library, SDK overlays, and runtime")
Expand Down
1 change: 0 additions & 1 deletion Runtimes/Core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ add_compile_options(
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xfrontend -enable-experimental-concise-pound-file>"
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xfrontend -enable-lexical-lifetimes=false>"
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xfrontend -disable-implicit-concurrency-module-import>"
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xfrontend -disable-implicit-backtracing-module-import>"
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xfrontend -disable-implicit-string-processing-module-import>"
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xfrontend -enforce-exclusivity=unchecked>"
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xfrontend -enable-ossa-modules>"
Expand Down
8 changes: 1 addition & 7 deletions cmake/modules/AddPureSwift.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,7 @@ function(_add_host_swift_compile_options name)
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xfrontend -disable-implicit-string-processing-module-import>")
endif()

# Same for backtracing
if (SWIFT_SUPPORTS_DISABLE_IMPLICIT_BACKTRACING_MODULE_IMPORT)
target_compile_options(${name} PRIVATE
"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xfrontend -disable-implicit-backtracing-module-import>")
endif()

if(SWIFT_ANALYZE_CODE_COVERAGE)
if(SWIFT_ANALYZE_CODE_COVERAGE)
set(_cov_flags $<$<COMPILE_LANGUAGE:Swift>:-profile-generate -profile-coverage-mapping>)
target_compile_options(${name} PRIVATE ${_cov_flags})
target_link_options(${name} PRIVATE ${_cov_flags})
Expand Down
11 changes: 11 additions & 0 deletions docs/Backtracing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -319,3 +319,14 @@ of the backtracer using

If the runtime is unable to locate the backtracer, it will allow your program to
crash as it would have done anyway.

Backtrace Storage
-----------------

Backtraces are stored internally in a format called :download:`Compact Backtrace
Format <CompactBacktraceFormat.md>`. This provides us with a way to store a
large number of frames in a much smaller space than would otherwise be possible.

Similarly, where we need to store address to image mappings, we
use :download:`Compact ImageMap Format <CompactImageMapFormat.md>` to minimise
storage requirements.
167 changes: 167 additions & 0 deletions docs/CompactBacktraceFormat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
Compact Backtrace Format
========================

We would like to be able to efficiently store and access backtraces,
but we also wish to minimise the memory used to store them. Since
backtraces typically contain a good deal of redundancy, it should be
possible to compress the data.

Compact Backtrace Format (CBF) is a binary format for holding a
backtrace; this specification addresses only the storage of the actual
stack backtrace, and it does not consider storage of ancillary data
(register contents, image lists and so on). Those will be dealt with
separately elsewhere.

## General Format

Compact Backtrace Format data is byte aligned and starts with an
information byte:

~~~
7 6 5 4 3 2 1 0
┌───────────────────────┬───────┐
│ version │ size │
└───────────────────────┴───────┘
~~~

The `version` field identifies the version of CBF that is in use; this
document describes version `0`. The `size` field is encqoded as
follows:

| `size` | Machine word size |
| :----: | :---------------- |
| 00 | 16-bit |
| 01 | 32-bit |
| 10 | 64-bit |
| 11 | Reserved |

This is followed by a series of instructions that tell the reader how
to decode subsequent data.

The first instruction that computes an address _must_ specify an
absolute address (the `a` bit must be set).

## Instructions

The following instructions are currently defined

| `opcode` | Mnemonic | Meaning |
| :--------: | :------- | :---------------------------------------- |
| `00000000` | `end` | Marks the end of the backtrace |
| `00000001` | `trunc` | As above, but the backtrace was truncated |
| `0000xxxx` | reserved | Reserved for future expansion |
| `0001axxx` | `pc` | A program counter value follows |
| `0010axxx` | `ra` | A return address value follows |
| `0011axxx` | `async` | An async resume point follows |
| `01xxxxxx` | `omit` | Indicates frames have been omitted |
| `1000xxxx` | `rep` | Repeat the previous frame |
| `1xxxxxxx` | reserved | Reserved for future expansion |

If the bit labelled `a` is set, it means that the address computation
is absolute rather than being relative to the previously computed
address.

### `end`/`trunc`

#### Encoding

~~~
7 6 5 4 3 2 1 0
┌───────────────────────────┬───┐
│ 0 0 0 0 0 0 0 │ t │ end (or trunc if t is 1)
└───────────────────────────┴───┘
~~~

#### Meaning

Marks the end of the backtrace data. If `t` is set, it indicates that
the backtrace was truncated at this point (for instance because we hit
a frame limit while capturing).

It is not strictly necessary to use the `end` instruction if the
CBF data is of a known length.

### `pc`, `ra`, `async`

#### Encoding

~~~
7 6 5 4 3 2 1 0
┌────────────────┬───┬──────────┐
│ 0 0 0 1 │ a │ count │ pc
└────────────────┴───┴──────────┘
┌────────────────┬───┬──────────┐
│ 0 0 1 0 │ a │ count │ ra
└────────────────┴───┴──────────┘
┌────────────────┬───┬──────────┐
│ 0 0 1 1 │ a │ count │ async
└────────────────┴───┴──────────┘
~~~

#### Meaning

Each of these instructions represents a frame on the stack. For `pc`
frames, the computed address is an actual program counter (aka
instruction pointer) value. `ra` instructions instead represent a
_return address_, the difference being that the program has not yet
executed that instruction. `async` instructions point at the entry
point of an async resume function, and are used when walking stacks on
systems that support `async`/`await` primitives that are implemented
by function splitting (typically an `async` instruction will point at
the start of a function containing the code immediately following an
`await`).

The next `count + 1` bytes following the instruction are an address
value. If `a` is set, the computed address is equal to the address
value. If `a` is not set, the computed address is equal to the
preceding computed address *plus* the address value.

Address values are sign-extended to the machine word width before
processing. Thus a single address byte with value `0xff` on a 32-bit
backtrace represents the address value `0xffffffff`.

### `omit`

#### Encoding

~~~
7 6 5 4 3 2 1 0
┌───────┬───┬───────────────────┐
│ 0 1 │ x │ count │ omit
└───────┴───┴───────────────────┘
~~~

#### Meaning

Indicates that a number of frames were skipped when capturing the
backtrace. This is used to allow a backtrace to include both the top
and bottom of the stack, without carrying every intervening frame, and
is useful to prevent the data from exploding where recursion has taken
place.

If `x` is `1`, the instruction is followed by `count + 1` bytes (up to the
machine word length) that are zero-extended to machine word length and
that represent a count of the number of frames that were omitted.

If `x` is `0`, `count + 1` is the number of frames that were omitted.

### `rep`

#### Encoding

~~~
7 6 5 4 3 2 1 0
┌────────────────┬───┬──────────┐
│ 1 0 0 0 │ x │ count │ repeat
└────────────────┴───┴──────────┘
~~~

#### Meaning

Repeat the previous frame.

If `x` is `1`, the instruction is followed by `count + 1` bytes that are zero
extended to machine word length and that represent a count of the number of
times to repeat the preceding frame.

If `x` is `0`, the previous frame should be repeated `count + 1` times.
Loading