Skip to content

[Backtracing] Add improved backtracing support for Swift crashes #64100

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 28 commits into from
Mar 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8a29657
[Runtime] [Darwin] Add code to trigger the external backtracer.
al45tair Feb 13, 2023
2f0e379
[Backtracing] Needed to add some code to this header for the runtime.
al45tair Feb 14, 2023
44be8cf
[Backtracing][Runtime] Disable backtraces for setuid binaries.
al45tair Feb 14, 2023
b9e79c4
[Backtracing][Runtime] Don't replace existing signal handlers.
al45tair Feb 15, 2023
b63a227
[Backtracing][Runtime] Renamed getAuxiliaryExecutablePath upstream.
al45tair Mar 2, 2023
6caea53
[Backtracing] Add _Backtracing library to the build.
al45tair Feb 13, 2023
55104aa
[Backtracing] Fix Windows assembler related problem.
al45tair Mar 2, 2023
fb830e0
[Backtracing][Windows] Use CRT, not MSVCRT.
al45tair Mar 2, 2023
36c2e4c
[Backtracing] Fix OS conditionals so iOS builds work.
al45tair Mar 3, 2023
0c5aeda
[Backtracing][Runtime] Fix Linux build to use getauxval(AT_SECURE).
al45tair Feb 20, 2023
4edfacb
[Backtracing][Runtime] Fix a copy/paste blunder.
al45tair Feb 28, 2023
1c3b8b5
[Backtracing] Various improvements following Johannes' remarks.
al45tair Feb 20, 2023
af57b2c
[Backtracing] Tweaks after Mike's remarks.
al45tair Feb 28, 2023
1e71f92
[Backtracing] Don't try to include <libproc.h> for iOS.
al45tair Mar 3, 2023
a2fc8c3
[Backtracing] Only include libproc.h if it's present.
al45tair Mar 3, 2023
44783e7
[Frontend] Add support for implicit import of _Backtracing
al45tair Feb 13, 2023
65a6aae
[Backtracing] Add `swift-backtrace` to the build.
al45tair Feb 13, 2023
f15011f
[Backtracing] Tweak output slightly.
al45tair Feb 20, 2023
dc6f97c
[Backtracing][Windows] Use CRT not MSVCRT.
al45tair Mar 2, 2023
24932f2
[Backtracing][Docs] Add an explanation of the workings of the backtra…
al45tair Feb 13, 2023
662c80e
[Backtracing][Docs] Add some information about signal handlers for ma…
al45tair Feb 15, 2023
43ac069
[Backtracing] Add control over symbol caching.
al45tair Feb 13, 2023
eb38d80
[Backtracing] Fix Windows build.
al45tair Mar 2, 2023
9e58b2c
[Backtracing] Make this macOS only for now.
al45tair Mar 3, 2023
aeb0323
[Backtracing] Fix i386 typo.
al45tair Mar 3, 2023
3ec2e67
[Backtracing] Really only build for OS X.
al45tair Mar 3, 2023
9c3ea84
[Backtracing] Disable implicit imports for executable builds.
al45tair Mar 3, 2023
eac93f9
[Backtracing][Tests] Apparently we don't see the dyld frame in CI.
al45tair Mar 4, 2023
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
24 changes: 22 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,22 @@ else()
set(CMAKE_JOB_POOL_LINK local_jobs)
endif()

ENABLE_LANGUAGE(C)
enable_language(C)
enable_language(CXX)

# On Windows, use MASM or MARMASM
set(SWIFT_ASM_DIALECT ASM)
set(SWIFT_ASM_EXT S)
if(CMAKE_SYSTEM_NAME STREQUAL Windows)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64")
set(SWIFT_ASM_DIALECT ASM_MARMASM)
else()
set(SWIFT_ASM_DIALECT ASM_MASM)
endif()
set(SWIFT_ASM_EXT asm)
endif()

enable_language(${SWIFT_ASM_DIALECT})

# Use C++14.
set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to conform to")
Expand Down Expand Up @@ -560,6 +575,10 @@ 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 @@ -618,7 +637,7 @@ if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
endif()

if(SWIFT_BUILT_STANDALONE)
project(Swift C CXX ASM)
project(Swift C CXX ${SWIFT_ASM_DIALECT})
endif()

if(MSVC OR "${CMAKE_SIMULATE_ID}" STREQUAL MSVC)
Expand Down Expand Up @@ -688,6 +707,7 @@ execute_process(COMMAND ${CMAKE_MAKE_PROGRAM} ${version_flag}
message(STATUS "CMake Make Program (${CMAKE_MAKE_PROGRAM}) Version: ${_CMAKE_MAKE_PROGRAM_VERSION}")
message(STATUS "C Compiler (${CMAKE_C_COMPILER}) Version: ${CMAKE_C_COMPILER_VERSION}")
message(STATUS "C++ Compiler (${CMAKE_CXX_COMPILER}) Version: ${CMAKE_CXX_COMPILER_VERSION}")
message(STATUS "Assembler (${CMAKE_${SWIFT_ASM_DIALECT}_COMPILER}) Version: ${CMAKE_${SWIFT_ASM_DIALECT}_COMPILER_VERSION}")
if (CMAKE_Swift_COMPILER)
message(STATUS "Swift Compiler (${CMAKE_Swift_COMPILER}) Version: ${CMAKE_Swift_COMPILER_VERSION}")
else()
Expand Down
20 changes: 20 additions & 0 deletions cmake/modules/SwiftImplicitImport.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Test if the Swift compiler supports -disable-implicit-<module>-module-import
function(swift_supports_implicit_module module_name out_var)
file(WRITE "${CMAKE_BINARY_DIR}/tmp/empty-check-${module_name}.swift" "")
execute_process(
COMMAND
"${CMAKE_Swift_COMPILER}"
-Xfrontend -disable-implicit-${module_name}-module-import
-c - -o /dev/null
INPUT_FILE
"${CMAKE_BINARY_DIR}/tmp/empty-check-${module_name}.swift"
OUTPUT_QUIET ERROR_QUIET
RESULT_VARIABLE
result
)
if(NOT result)
set("${out_var}" "TRUE" PARENT_SCOPE)
else()
set("${out_var}" "FALSE" PARENT_SCOPE)
endif()
endfunction()
227 changes: 227 additions & 0 deletions docs/Backtracing.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
Backtracing support in Swift
============================

When things go wrong, it's always useful to be able to get a backtrace showing
where the problem occurred in your program.

Broadly speaking there are three circumstances where you might want a backtrace,
namely:

* Program crashes
* Runtime errors
* Specific user-defined program events

Historically, Swift has tended to lean on operating system crash catching
support for the first two of these, and hasn't really provided any built-in
support for the latter. This is fine for Darwin, where the operating system
provides a comprehensive system-wide crash catching facility; it's just about OK
on Windows, which also has system-wide crash logging; but it isn't great
elsewhere, in particular on Linux where a lot of server-side Swift programs
currently rely on a separate package to provide them with some level of
backtrace support when errors happen.

What does Swift now support?
----------------------------

Swift now supports:

* Automatic crash catching and backtrace generation out of the box.
* Built-in symbolication.
* A choice of unwind algorithms, including "fast", DWARF and SEH.
* Interactive(!) crash/runtime error catching.

Crash catching is enabled by default, and won't interfere with any system-wide
crash reporters you might be using.

How do I configure backtracing?
-------------------------------

There is an environment variable, ``SWIFT_BACKTRACE``, that can be used to
configure Swift's crash catching and backtracing support. The variable should
contain a ``,``-separated list of ``key=value`` pairs. Supported keys are as
follows:

+-----------------+---------+--------------------------------------------------+
| Key | Default | Meaning |
+=================+=========+==================================================+
| enable | yes* | Set to ``no`` to disable crash catching, or |
| | | ``tty`` to enable only if stdin is a terminal. |
+-----------------+---------+--------------------------------------------------+
| demangle | yes | Set to ``no`` to disable demangling. |
+-----------------+---------+--------------------------------------------------+
| interactive | tty | Set to ``no`` to disable interaction, or ``yes`` |
| | | to enable always. |
+-----------------+---------+--------------------------------------------------+
| color | tty | Set to ``yes`` to enable always, or ``no`` to |
| | | disable. Uses ANSI escape sequences. |
+-----------------+---------+--------------------------------------------------+
| timeout | 30s | Time to wait for interaction when a crash |
| | | occurs. Setting this to ``none`` or ``0s`` will |
| | | disable interaction. |
+-----------------+---------+--------------------------------------------------+
| unwind | auto | Specifies which unwind algorithm to use. |
| | | ``auto`` means to choose appropriately for the |
| | | platform. Other options are ``fast``, which |
| | | does a naïve stack walk; and ``precise``, which |
| | | uses exception handling data to perform an |
| | | unwind. |
+-----------------+---------+--------------------------------------------------+
| preset | auto | Specifies which set of preset formatting options |
| | | to use. Options are ``friendly``, ``medium`` or |
| | | ``full``. ``auto`` means to use ``friendly`` if |
| | | interactive, and ``full`` otherwise. |
+-----------------+---------+--------------------------------------------------+
| sanitize | preset | If ``yes``, we will try to process paths to |
| | | remove PII. Exact behaviour is platform |
| | | dependent. |
+-----------------+---------+--------------------------------------------------+
| threads | preset | Options are ``all`` to show backtraces for every |
| | | thread, or ``crashed`` to show only the crashing |
| | | thread. |
+-----------------+---------+--------------------------------------------------+
| registers | preset | Options are ``none``, ``all`` or ``crashed``. |
+-----------------+---------+--------------------------------------------------+
| images | preset | Options are ``none``, ``all``, or ``mentioned``, |
| | | which only displays images mentioned in a |
| | | backtrace. |
+-----------------+---------+--------------------------------------------------+
| limit | 64 | Limits the length of the captured backtrace. See |
| | | below for a discussion of its behaviour. Can be |
| | | set to ``none`` to mean no limit. |
+-----------------+---------+--------------------------------------------------+
| top | 16 | Specify a minimum number of frames to capture |
| | | from the top of the stack. See below for more. |
+-----------------+---------+--------------------------------------------------+
| cache | yes | Set to ``no`` to disable symbol caching. This |
| | | only has effect on platforms that have a symbol |
| | | cache that can be controlled by the runtime. |
+-----------------+---------+--------------------------------------------------+
| swift-backtrace | | If specified, gives the full path to the |
| | | swift-backtrace binary to use for crashes. |
| | | Otherwise, Swift will locate the binary relative |
| | | to the runtime library, or using ``SWIFT_ROOT``. |
+-----------------+---------+--------------------------------------------------+

(*) On macOS, this defaults to ``tty`` rather than ``yes``.

Backtrace limits
----------------

The limit settings are provided both to prevent runaway backtraces and to allow
for a sensible backtrace to be produced even when a function has blown the stack
through excessive recursion.

Typically in the latter case you want to capture some frames at the top of the
stack so that you can see how the recursion was entered, and the frames at the
bottom of the stack where the actual fault occurred.

1. There are ``limit`` or fewer frames. In this case we will display all
the frames in the backtrace. Note that this _includes_ the case where there
are exactly ``limit`` frames.

2. There are more than ``limit`` frames.

a. ``top`` is ``0``. We will display the first ``limit - 1`` frames followed
by ``...`` to indicate that more frames exist.

b. ``top`` is less than ``limit - 1``. We will display ``limit - 1 - top``
frames from the bottom of the stack, then a ``...``, then ``top`` frames
from the top of the stack.

c. ``top`` is greater or equal to ``limit - 1``. We will display ``...``,
followed by ``limit - 1`` frames from the top of the stack.

For example, let's say we have a stack containing 10 frames numbered here 1 to
10, with 10 being the innermost frame. With ``limit`` set to 5, you would see::

10
9
8
7
...

With ``limit`` set to 5 and ``top`` to 2, you would instead see::

10
9
...
2
1

And with ``limit`` set to 5 and ``top`` to 4 or above, you would see::

...
4
3
2
1

What is the swift-backtrace binary?
-----------------------------------

``swift-backtrace`` is a program that gets invoked when your program crashes.
We do this because when a program crashes, it is potentially in an invalid state
and there is very little that is safe for us to do. By executing an external
helper program, we ensure that we do not interfere with the way the program was
going to crash (so that system-wide crash catchers will still generate the
correct information), and we are also able to use any functionality we need to
generate a decent backtrace, including symbolication (which might in general
require memory allocation, fetching and reading remote files and so on).

You shouldn't try to run ``swift-backtrace`` yourself; it has unusual
requirements, which vary from platform to platform. Instead, it will be
triggered automatically by the runtime.

System specifics
----------------

macOS
^^^^^

On macOS, we catch crashes and other events using a signal handler. At time of
writing, this is installed for the following signals:

+--------------+--------------------------+-------------------------------------+
| Signal | Description | Comment |
+====+=========+==========================+=====================================+
| 3 | SIGQUIT | Quit program | |
+----+---------+--------------------------+-------------------------------------+
| 4 | SIGILL | Illegal instruction | |
+----+---------+--------------------------+-------------------------------------+
| 5 | SIGTRAP | Trace trap | |
+----+---------+--------------------------+-------------------------------------+
| 6 | SIGABRT | Abort program | |
+----+---------+--------------------------+-------------------------------------+
| 8 | SIGFPE | Floating point exception | On Intel, integer divide by zero |
| | | | also triggers this. |
+----+---------+--------------------------+-------------------------------------+
| 10 | SIGBUS | Bus error | |
+----+---------+--------------------------+-------------------------------------+
| 11 | SIGSEGV | Segmentation violation | |
+----+---------+--------------------------+-------------------------------------+

If crash catching is enabled, the signal handler will be installed for any
process that links the Swift runtime. If you replace the handlers for any of
these signals, your program will no longer produce backtraces for program
failures that lead to the handler you have replaced.

Additionally, the runtime will configure an alternate signal handling stack, so
that stack overflows can be successfully trapped.

Note that the runtime will not install its signal handlers for a signal if it
finds that there is already a handler for that signal. Similarly if something
else has already configured an alternate signal stack, it will leave that
stack alone.

Once the backtracer has finished handling the crash, it will allow the crashing
program to continue and crash normally, which will result in the usual Crash
Reporter log file being generated.

Crash catching *cannot* be enabled for setuid binaries. This is intentional as
doing so might create a security hole.

Other Darwin (iOS, tvOS)
^^^^^^^^^^^^^^^^^^^^^^^^

Crash catching is not enabled for non-macOS Darwin. You should continue to look
at the system-provided crash logs.
3 changes: 3 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ documentation, please create a thread on the Swift forums under the
operations on [currency](/docs/Lexicon.md#currency-type) data types and
optimizes accordingly.
Includes a thorough discussion of the `@_semantics` attribute.
- Runtime specifics:
- [Backtracing.rst](/docs/Backtracing.rst):
Describes Swift's backtracing and crash catching support.

### SourceKit subsystems

Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticsFrontend.def
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ WARNING(warn_implicit_concurrency_import_failed,none,
"unable to perform implicit import of \"_Concurrency\" module: no such module found", ())
REMARK(warn_implicit_string_processing_import_failed,none,
"unable to perform implicit import of \"_StringProcessing\" module: no such module found", ())
REMARK(warn_implicit_backtracing_import_failed,none,
"unable to perform implicit import of \"_Backtracing\" module: no such module found", ())

ERROR(error_module_name_required,none, "-module-name is required", ())
ERROR(error_bad_module_name,none,
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/KnownIdentifiers.def
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,9 @@ IDENTIFIER(version)
IDENTIFIER_(StringProcessing)
IDENTIFIER(RegexBuilder)

// Backtracing
IDENTIFIER_(Backtracing)

// Distributed actors
IDENTIFIER(ActorID)
IDENTIFIER(ActorSystem)
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ namespace swift {
/// Disable the implicit import of the _StringProcessing module.
bool DisableImplicitStringProcessingModuleImport = false;

/// Disable the implicit import of the _Backtracing module.
bool DisableImplicitBacktracingModuleImport =
!SWIFT_IMPLICIT_BACKTRACING_IMPORT;

/// Should we check the target OSs of serialized modules to see that they're
/// new enough?
bool EnableTargetOSChecking = true;
Expand Down
2 changes: 2 additions & 0 deletions include/swift/Config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

#cmakedefine01 SWIFT_IMPLICIT_CONCURRENCY_IMPORT

#cmakedefine01 SWIFT_IMPLICIT_BACKTRACING_IMPORT

#cmakedefine01 SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED

#cmakedefine01 SWIFT_ENABLE_GLOBAL_ISEL_ARM64
Expand Down
12 changes: 12 additions & 0 deletions include/swift/Frontend/Frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,10 @@ class CompilerInvocation {
/// imported.
bool shouldImportSwiftStringProcessing() const;

/// Whether the Swift Backtracing support library should be implicitly
/// imported.
bool shouldImportSwiftBacktracing() const;

/// Performs input setup common to these tools:
/// sil-opt, sil-func-extractor, sil-llvm-gen, and sil-nm.
/// Return value includes the buffer so caller can keep it alive.
Expand Down Expand Up @@ -575,6 +579,14 @@ class CompilerInstance {
/// i.e. if it can be found.
bool canImportSwiftStringProcessing() const;

/// Verify that if an implicit import of the `Backtracing` module if
/// expected, it can actually be imported. Emit a warning, otherwise.
void verifyImplicitBacktracingImport();

/// Whether the Swift Backtracing support library can be imported
/// i.e. if it can be found.
bool canImportSwiftBacktracing() const;

/// Whether the CxxShim library can be imported
/// i.e. if it can be found.
bool canImportCxxShim() const;
Expand Down
8 changes: 8 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,14 @@ def disable_implicit_string_processing_module_import : Flag<["-"],
"disable-implicit-string-processing-module-import">,
HelpText<"Disable the implicit import of the _StringProcessing module.">;

def enable_implicit_backtracing_module_import : Flag<["-"],
"enable-implicit-backtracing-module-import">,
HelpText<"Enable the implicit import of the _Backtracing module.">;

def disable_implicit_backtracing_module_import : Flag<["-"],
"disable-implicit-backtracing-module-import">,
HelpText<"Disable the implicit import of the _Backtracing module.">;

def disable_arc_opts : Flag<["-"], "disable-arc-opts">,
HelpText<"Don't run SIL ARC optimization passes.">;
def disable_ossa_opts : Flag<["-"], "disable-ossa-opts">,
Expand Down
Loading