Skip to content

Commit 14fcdbd

Browse files
committed
[Backtracing][Linux] Tidy a few things up after review.
Mike and Max made various helpful suggestions, so I've added and updated various comments and amended the code to cope with partial reads and writes. rdar://110261430
1 parent e5f4c20 commit 14fcdbd

File tree

3 files changed

+85
-26
lines changed

3 files changed

+85
-26
lines changed

include/swift/Runtime/CrashInfo.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,34 +27,62 @@ namespace backtrace {
2727
extern "C" {
2828
#endif
2929

30+
// Note: The "pointers" below are pointers in a different process's address
31+
// space, which might not even share our bitness. That is why they are
32+
// `uint64_t`s, rather than pointers or `uintptr_t`s.
33+
34+
// The address of this structure in memory is passed to swift-backtrace.
3035
struct CrashInfo {
36+
// The thread ID for the crashing thread.
3137
uint64_t crashing_thread;
38+
39+
// The signal number corresponding to this crash.
3240
uint64_t signal;
41+
42+
// The fault address.
3343
uint64_t fault_address;
3444

3545
#ifdef __APPLE__
46+
// Points to the mcontext_t structure for the crashing thread; other
47+
// threads' contexts can be recovered using Mach APIs later.
3648
uint64_t mctx;
3749
#elif defined(__linux__)
50+
// The head of the thread list; points at a "struct thread" (see below).
3851
uint64_t thread_list;
3952
#endif
4053
};
4154

4255
#ifdef __linux__
4356

57+
// A memory server request packet.
4458
struct memserver_req {
59+
// Address to read.
4560
uint64_t addr;
61+
62+
// Number of bytes to read.
4663
uint64_t len;
4764
};
4865

66+
// A memory server response packet.
4967
struct memserver_resp {
68+
// Address that was read from.
5069
uint64_t addr;
70+
71+
// Number of bytes, *or* negative to indicate an error.
5172
int64_t len;
52-
/* Then len bytes of data */
73+
74+
// Followed by len bytes of data if len > 0
5375
};
5476

77+
// Holds details of a running thread.
5578
struct thread {
79+
// Points at the next thread.
5680
uint64_t next;
81+
82+
// The thread ID for this thread.
5783
int64_t tid;
84+
85+
// Points to the Linux ucontext_t structure.
5886
uint64_t uctx;
5987
};
6088

stdlib/public/runtime/Backtrace.cpp

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -857,23 +857,26 @@ _swift_isThunkFunction(const char *mangledName) {
857857
/// @param mangledName is the symbol name to be demangled.
858858
/// @param mangledNameLength is the length of this name.
859859
/// @param outputBuffer is a pointer to a buffer in which to place the result.
860-
/// @param outputBufferSize points to a variable that will be filled in with
861-
/// the size of the allocated buffer (NOT the length of the result).
860+
/// @param outputBufferSize points to a variable that contains the size of the
861+
/// output buffer.
862862
/// @param status returns the status codes defined in the C++ ABI.
863863
///
864-
/// If outputBuffer and outputBufferSize are both nullptr, this function will
865-
/// allocate memory for the result using malloc().
866-
///
867-
/// If outputBuffer is nullptr but outputBufferSize is not, the function will
868-
/// fill outputBufferSize with the required buffer size and return nullptr.
864+
/// If outputBuffer is nullptr, the function will allocate memory for the
865+
/// result using malloc(). In this case, outputBufferSize may be nullptr;
866+
/// if it is *not* nullptr, it will be set to the size of buffer that was
867+
/// allocated. This is not necessarily the length of the string (it may be
868+
/// somewhat higher).
869869
///
870870
/// Otherwise, the result will be written into the output buffer, and the
871871
/// size of the result will be written into outputBufferSize. If the buffer
872872
/// is too small, the result will be truncated, but outputBufferSize will
873873
/// still be set to the number of bytes that would have been required to
874874
/// copy out the full result (including a trailing NUL).
875875
///
876-
/// @returns `true` if demangling was successful.
876+
/// The unusual behaviour here is a consequence of the way the C++ ABI's
877+
/// demangling function works.
878+
///
879+
/// @returns a pointer to the output if demangling was successful.
877880
SWIFT_RUNTIME_STDLIB_SPI char *
878881
_swift_backtrace_demangle(const char *mangledName,
879882
size_t mangledNameLength,
@@ -882,6 +885,11 @@ _swift_backtrace_demangle(const char *mangledName,
882885
int *status) {
883886
llvm::StringRef name = llvm::StringRef(mangledName, mangledNameLength);
884887

888+
// You must provide buffer size if you're providing your own output buffer
889+
if (outputBuffer && !outputBufferSize) {
890+
return nullptr;
891+
}
892+
885893
if (Demangle::isSwiftSymbol(name)) {
886894
// This is a Swift mangling
887895
auto options = DemangleOptions::SimplifiedUIDemangleOptions();
@@ -926,6 +934,8 @@ _swift_backtrace_demangle(const char *mangledName,
926934
::memcpy(outputBuffer, result, toCopy);
927935
outputBuffer[toCopy] = '\0';
928936

937+
free(result);
938+
929939
*status = 0;
930940
return outputBuffer;
931941
}

stdlib/public/runtime/CrashHandlerLinux.cpp

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -67,22 +67,44 @@ int memserver_start();
6767
int memserver_entry(void *);
6868
bool run_backtracer(int fd);
6969

70-
int safe_read(int fd, void *buf, size_t len) {
71-
int ret;
72-
do {
73-
ret = read(fd, buf, len);
74-
} while (ret < 0 && errno == EINTR);
70+
ssize_t safe_read(int fd, void *buf, size_t len) {
71+
uint8_t *ptr = (uint8_t *)buf;
72+
uint8_t *end = ptr + len;
73+
ssize_t total = 0;
74+
75+
while (ptr < end) {
76+
ssize_t ret;
77+
do {
78+
ret = read(fd, buf, len);
79+
} while (ret < 0 && errno == EINTR);
80+
if (ret < 0)
81+
return ret;
82+
total += ret;
83+
ptr += ret;
84+
len -= ret;
85+
}
7586

76-
return ret;
87+
return total;
7788
}
7889

79-
int safe_write(int fd, const void *buf, size_t len) {
80-
int ret;
81-
do {
82-
ret = write(fd, buf, len);
83-
} while (ret < 0 && errno == EINTR);
90+
ssize_t safe_write(int fd, const void *buf, size_t len) {
91+
const uint8_t *ptr = (uint8_t *)buf;
92+
const uint8_t *end = ptr + len;
93+
ssize_t total = 0;
94+
95+
while (ptr < end) {
96+
ssize_t ret;
97+
do {
98+
ret = write(fd, buf, len);
99+
} while (ret < 0 && errno == EINTR);
100+
if (ret < 0)
101+
return ret;
102+
total += ret;
103+
ptr += ret;
104+
len -= ret;
105+
}
84106

85-
return ret;
107+
return total;
86108
}
87109

88110
CrashInfo crashInfo;
@@ -195,8 +217,7 @@ handle_fatal_signal(int signum,
195217
// Start the memory server
196218
int fd = memserver_start();
197219

198-
/* Start the backtracer; this will suspend the process, so there's no need
199-
to try to suspend other threads from here. */
220+
// Actually start the backtracer
200221
run_backtracer(fd);
201222

202223
#if !MEMSERVER_USE_PROCESS
@@ -276,15 +297,15 @@ getdents(int fd, void *buf, size_t bufsiz)
276297
}
277298

278299
/* Stop all other threads in this process; we do this by establishing a
279-
signal handler for SIGUSR1, then iterating through the threads sending
280-
SIGUSR1.
300+
signal handler for SIGPROF, then iterating through the threads sending
301+
SIGPROF.
281302
282303
Finding the other threads is a pain, because Linux has no actual API
283304
for that; instead, you have to read /proc. Unfortunately, opendir()
284305
and readdir() are not async signal safe, so we get to do this with
285306
the getdents system call instead.
286307
287-
The SIGUSR1 signals also serve to build the thread list. */
308+
The SIGPROF signals also serve to build the thread list. */
288309
void
289310
suspend_other_threads(struct thread *self)
290311
{

0 commit comments

Comments
 (0)