Skip to content

Commit 72f8aae

Browse files
committed
[Backtracing] Add an indication that we're working on a backtrace.
When we crash, emit a message straight away that says we're working on a backtrace. If starting the backtracer fails, report that also. Finally, add a duration to the messages output by the backtracer, so that we can see how long it took. rdar://118055527
1 parent ddd3522 commit 72f8aae

File tree

9 files changed

+396
-229
lines changed

9 files changed

+396
-229
lines changed

include/swift/Runtime/Backtrace.h

Lines changed: 5 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -31,114 +31,17 @@
3131

3232
#include <inttypes.h>
3333

34-
#ifdef _WIN32
35-
// For DWORD
36-
#define WIN32_LEAN_AND_MEAN
37-
#define NOMINMAX
38-
#include <windows.h>
39-
40-
// For wchar_t
41-
#include <cwchar>
42-
#endif
43-
4434
#ifdef __cplusplus
4535
namespace swift {
4636
namespace runtime {
4737
namespace backtrace {
4838
#endif
4939

50-
#ifdef _WIN32
51-
typedef wchar_t ArgChar;
52-
typedef DWORD ErrorCode;
53-
#else
54-
typedef char ArgChar;
55-
typedef int ErrorCode;
56-
#endif
57-
58-
SWIFT_RUNTIME_STDLIB_INTERNAL ErrorCode _swift_installCrashHandler();
59-
60-
#ifdef __linux__
61-
SWIFT_RUNTIME_STDLIB_INTERNAL bool _swift_spawnBacktracer(const ArgChar * const *argv, int memserver_fd);
62-
#else
63-
SWIFT_RUNTIME_STDLIB_INTERNAL bool _swift_spawnBacktracer(const ArgChar * const *argv);
64-
#endif
65-
66-
enum class UnwindAlgorithm {
67-
Auto = 0,
68-
Fast = 1,
69-
Precise = 2
70-
};
71-
72-
enum class OnOffTty {
73-
Off = 0,
74-
On = 1,
75-
TTY = 2
76-
};
77-
78-
enum class Preset {
79-
Auto = -1,
80-
Friendly = 0,
81-
Medium = 1,
82-
Full = 2
83-
};
84-
85-
enum class ThreadsToShow {
86-
Preset = -1,
87-
All = 0,
88-
Crashed = 1
89-
};
90-
91-
enum class RegistersToShow {
92-
Preset = -1,
93-
None = 0,
94-
All = 1,
95-
Crashed = 2
96-
};
97-
98-
enum class ImagesToShow {
99-
Preset = -1,
100-
None = 0,
101-
All = 1,
102-
Mentioned = 2
103-
};
104-
105-
enum class SanitizePaths {
106-
Preset = -1,
107-
Off = 0,
108-
On = 1
109-
};
110-
111-
enum class OutputTo {
112-
Auto = -1,
113-
Stdout = 0,
114-
Stderr = 2,
115-
};
116-
117-
struct BacktraceSettings {
118-
UnwindAlgorithm algorithm;
119-
OnOffTty enabled;
120-
bool demangle;
121-
OnOffTty interactive;
122-
OnOffTty color;
123-
unsigned timeout;
124-
ThreadsToShow threads;
125-
RegistersToShow registers;
126-
ImagesToShow images;
127-
unsigned limit;
128-
unsigned top;
129-
SanitizePaths sanitize;
130-
Preset preset;
131-
bool cache;
132-
OutputTo outputTo;
133-
const char *swiftBacktracePath;
134-
};
135-
136-
SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings;
137-
138-
inline bool _swift_backtrace_isEnabled() {
139-
return _swift_backtraceSettings.enabled == OnOffTty::On;
140-
}
141-
40+
// Test if a given function is a Swift thunk of some sort.
41+
//
42+
// @param mangledName is the name of the symbol to test.
43+
//
44+
// @returns true if the function is a thunk.
14245
SWIFT_RUNTIME_STDLIB_SPI
14346
bool _swift_backtrace_isThunkFunction(const char *mangledName);
14447

stdlib/public/libexec/swift-backtrace/main.swift

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,38 @@ internal struct SwiftBacktrace {
113113
}
114114
}
115115

116+
static func subtract(timespec ts: timespec, from: timespec) -> timespec {
117+
var sec = from.tv_sec - ts.tv_sec
118+
var nsec = from.tv_nsec - ts.tv_nsec
119+
if nsec < 0 {
120+
sec -= 1
121+
nsec += 1000000000
122+
}
123+
return timespec(tv_sec: sec, tv_nsec: nsec)
124+
}
125+
126+
// We can't use Foundation here, so there's no String(format:, ...)
127+
static func format(duration: timespec) -> String {
128+
let centisRounded = (duration.tv_nsec + 5000000) / 10000000
129+
let centis = centisRounded % 100
130+
let secs = duration.tv_sec + (centisRounded / 100)
131+
let d1 = centis / 10
132+
let d2 = centis % 10
133+
134+
return "\(secs).\(d1)\(d2)"
135+
}
136+
137+
static func measureDuration(_ body: () -> ()) -> timespec {
138+
var startTime = timespec()
139+
var endTime = timespec()
140+
141+
clock_gettime(CLOCK_MONOTONIC, &startTime)
142+
body()
143+
clock_gettime(CLOCK_MONOTONIC, &endTime)
144+
145+
return subtract(timespec: startTime, from: endTime)
146+
}
147+
116148
static func usage() {
117149
print("""
118150
usage: swift-backtrace [--unwind <algorithm>] [--demangle [<bool>]] [--interactive [<bool>]] [--color [<bool>]] [--timeout <seconds>] [--preset <preset>] [--threads [<bool>]] [--registers <registers>] [--images <images>] [--cache [<bool>]] [--output-to <stream>] --crashinfo <addr>
@@ -460,16 +492,25 @@ Generate a backtrace for the parent process.
460492
// want to do it *once* for all the backtraces we showed.
461493
formattingOptions = formattingOptions.showImages(.none)
462494

463-
target = Target(crashInfoAddr: crashInfoAddr,
464-
limit: args.limit, top: args.top,
465-
cache: args.cache)
495+
// Target's initializer fetches and symbolicates backtraces, so
496+
// we want to time that part here.
497+
let duration = measureDuration {
498+
target = Target(crashInfoAddr: crashInfoAddr,
499+
limit: args.limit, top: args.top,
500+
cache: args.cache)
466501

467-
currentThread = target!.crashingThreadNdx
502+
currentThread = target!.crashingThreadNdx
503+
}
468504

469505
printCrashLog()
470506

471507
writeln("")
472508

509+
let formattedDuration = format(duration: duration)
510+
511+
writeln("Backtrace took \(formattedDuration)s")
512+
writeln("")
513+
473514
if args.interactive {
474515
// Make sure we're line buffered
475516
setvbuf(stdout, nil, _IOLBF, 0)
@@ -611,7 +652,13 @@ Generate a backtrace for the parent process.
611652
description = "Program crashed: \(target.signalDescription) at \(hex(target.faultAddress))"
612653
}
613654

614-
writeln("")
655+
// Clear the message written by the crash handler
656+
if args.color {
657+
write("\r\u{1b}[0K")
658+
} else {
659+
write("\r" + String(repeating:" ", count: 80) + "\r")
660+
}
661+
615662
writeln(theme.crashReason(description))
616663

617664
var mentionedImages = Set<Int>()

stdlib/public/runtime/Backtrace.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
#include <cxxabi.h>
5959
#endif
6060

61+
#include "BacktracePrivate.h"
62+
6163
#define DEBUG_BACKTRACING_SETTINGS 0
6264

6365
#ifndef lengthof
@@ -987,6 +989,57 @@ _swift_spawnBacktracer(const ArgChar * const *argv)
987989
#endif
988990
}
989991

992+
// N.B. THIS FUNCTION MUST BE SAFE TO USE FROM A CRASH HANDLER. On Linux
993+
// and macOS, that means it must be async-signal-safe. On Windows, there
994+
// isn't an equivalent notion but a similar restriction applies.
995+
SWIFT_RUNTIME_STDLIB_INTERNAL void
996+
_swift_displayCrashMessage(int signum, const void *pc)
997+
{
998+
#if !SWIFT_BACKTRACE_ON_CRASH_SUPPORTED
999+
return;
1000+
#else
1001+
int fd = STDOUT_FILENO;
1002+
1003+
if (_swift_backtraceSettings.outputTo == OutputTo::Stderr)
1004+
fd = STDERR_FILENO;
1005+
1006+
const char *intro;
1007+
if (_swift_backtraceSettings.color == OnOffTty::On) {
1008+
intro = "\n💣 \033[91mProgram crashed: ";
1009+
} else {
1010+
intro = "\n*** Program crashed: ";
1011+
}
1012+
write(fd, intro, strlen(intro));
1013+
1014+
char sigbuf[30];
1015+
strcpy(sigbuf, "Signal ");
1016+
_swift_formatUnsigned((unsigned)signum, sigbuf + 7);
1017+
write(fd, sigbuf, strlen(sigbuf));
1018+
1019+
const char *message;
1020+
if (!pc) {
1021+
message = ": Backtracing";
1022+
} else {
1023+
message = ": Backtracing from 0x";
1024+
}
1025+
write(fd, message, strlen(message));
1026+
1027+
if (pc) {
1028+
char pcbuf[18];
1029+
_swift_formatAddress(pc, pcbuf);
1030+
write(fd, pcbuf, strlen(pcbuf));
1031+
}
1032+
1033+
const char *outro;
1034+
if (_swift_backtraceSettings.color == OnOffTty::On) {
1035+
outro = "...\033[0m";
1036+
} else {
1037+
outro = "...";
1038+
}
1039+
write(fd, outro, strlen(outro));
1040+
#endif
1041+
}
1042+
9901043
} // namespace backtrace
9911044
} // namespace runtime
9921045
} // namespace swift

0 commit comments

Comments
 (0)