Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/angle
Path: blob/main_old/util/posix/crash_handler_posix.cpp
1693 views
1
//
2
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
3
// Use of this source code is governed by a BSD-style license that can be
4
// found in the LICENSE file.
5
//
6
// crash_handler_posix:
7
// ANGLE's crash handling and stack walking code. Modified from Skia's:
8
// https://github.com/google/skia/blob/master/tools/CrashHandler.cpp
9
//
10
11
#include "util/test_utils.h"
12
13
#include "common/FixedVector.h"
14
#include "common/angleutils.h"
15
#include "common/string_utils.h"
16
#include "common/system_utils.h"
17
18
#include <fcntl.h>
19
#include <inttypes.h>
20
#include <stdio.h>
21
#include <stdlib.h>
22
#include <sys/types.h>
23
#include <sys/wait.h>
24
#include <unistd.h>
25
#include <iostream>
26
27
#if !defined(ANGLE_PLATFORM_ANDROID) && !defined(ANGLE_PLATFORM_FUCHSIA)
28
# if defined(ANGLE_PLATFORM_APPLE)
29
// We only use local unwinding, so we can define this to select a faster implementation.
30
# define UNW_LOCAL_ONLY
31
# include <cxxabi.h>
32
# include <libunwind.h>
33
# include <signal.h>
34
# elif defined(ANGLE_PLATFORM_POSIX)
35
// We'd use libunwind here too, but it's a pain to get installed for
36
// both 32 and 64 bit on bots. Doesn't matter much: catchsegv is best anyway.
37
# include <cxxabi.h>
38
# include <dlfcn.h>
39
# include <execinfo.h>
40
# include <libgen.h>
41
# include <link.h>
42
# include <signal.h>
43
# include <string.h>
44
# endif // defined(ANGLE_PLATFORM_APPLE)
45
#endif // !defined(ANGLE_PLATFORM_ANDROID) && !defined(ANGLE_PLATFORM_FUCHSIA)
46
47
// This code snippet is coped from Chromium's base/posix/eintr_wrapper.h.
48
#if defined(NDEBUG)
49
# define HANDLE_EINTR(x) \
50
({ \
51
decltype(x) eintr_wrapper_result; \
52
do \
53
{ \
54
eintr_wrapper_result = (x); \
55
} while (eintr_wrapper_result == -1 && errno == EINTR); \
56
eintr_wrapper_result; \
57
})
58
#else
59
# define HANDLE_EINTR(x) \
60
({ \
61
int eintr_wrapper_counter = 0; \
62
decltype(x) eintr_wrapper_result; \
63
do \
64
{ \
65
eintr_wrapper_result = (x); \
66
} while (eintr_wrapper_result == -1 && errno == EINTR && \
67
eintr_wrapper_counter++ < 100); \
68
eintr_wrapper_result; \
69
})
70
#endif // NDEBUG
71
72
namespace angle
73
{
74
#if defined(ANGLE_PLATFORM_ANDROID) || defined(ANGLE_PLATFORM_FUCHSIA)
75
76
void PrintStackBacktrace()
77
{
78
// No implementations yet.
79
}
80
81
void InitCrashHandler(CrashCallback *callback)
82
{
83
// No implementations yet.
84
}
85
86
void TerminateCrashHandler()
87
{
88
// No implementations yet.
89
}
90
91
#else
92
namespace
93
{
94
CrashCallback *gCrashHandlerCallback;
95
} // namespace
96
97
# if defined(ANGLE_PLATFORM_APPLE)
98
99
void PrintStackBacktrace()
100
{
101
printf("Backtrace:\n");
102
103
unw_context_t context;
104
unw_getcontext(&context);
105
106
unw_cursor_t cursor;
107
unw_init_local(&cursor, &context);
108
109
while (unw_step(&cursor) > 0)
110
{
111
static const size_t kMax = 256;
112
char mangled[kMax], demangled[kMax];
113
unw_word_t offset;
114
unw_get_proc_name(&cursor, mangled, kMax, &offset);
115
116
int ok;
117
size_t len = kMax;
118
abi::__cxa_demangle(mangled, demangled, &len, &ok);
119
120
printf(" %s (+0x%zx)\n", ok == 0 ? demangled : mangled, (size_t)offset);
121
}
122
printf("\n");
123
}
124
125
static void Handler(int sig)
126
{
127
if (gCrashHandlerCallback)
128
{
129
(*gCrashHandlerCallback)();
130
}
131
132
printf("\nSignal %d:\n", sig);
133
PrintStackBacktrace();
134
135
// Exit NOW. Don't notify other threads, don't call anything registered with atexit().
136
_Exit(sig);
137
}
138
139
# elif defined(ANGLE_PLATFORM_POSIX)
140
141
// Can control this at a higher level if required.
142
# define ANGLE_HAS_ADDR2LINE
143
144
# if defined(ANGLE_HAS_ADDR2LINE)
145
namespace
146
{
147
// The following code was adapted from Chromium's "stack_trace_posix.cc".
148
// Describes a region of mapped memory and the path of the file mapped.
149
struct MappedMemoryRegion
150
{
151
enum Permission
152
{
153
READ = 1 << 0,
154
WRITE = 1 << 1,
155
EXECUTE = 1 << 2,
156
PRIVATE = 1 << 3, // If set, region is private, otherwise it is shared.
157
};
158
159
// The address range [start,end) of mapped memory.
160
uintptr_t start;
161
uintptr_t end;
162
163
// Byte offset into |path| of the range mapped into memory.
164
unsigned long long offset;
165
166
// Image base, if this mapping corresponds to an ELF image.
167
uintptr_t base;
168
169
// Bitmask of read/write/execute/private/shared permissions.
170
uint8_t permissions;
171
172
// Name of the file mapped into memory.
173
//
174
// NOTE: path names aren't guaranteed to point at valid files. For example,
175
// "[heap]" and "[stack]" are used to represent the location of the process'
176
// heap and stack, respectively.
177
std::string path;
178
};
179
180
using MemoryRegionArray = std::vector<MappedMemoryRegion>;
181
182
bool ReadProcMaps(std::string *proc_maps)
183
{
184
// seq_file only writes out a page-sized amount on each call. Refer to header
185
// file for details.
186
const long kReadSize = sysconf(_SC_PAGESIZE);
187
188
int fd(HANDLE_EINTR(open("/proc/self/maps", O_RDONLY)));
189
if (fd == -1)
190
{
191
fprintf(stderr, "Couldn't open /proc/self/maps\n");
192
return false;
193
}
194
proc_maps->clear();
195
196
while (true)
197
{
198
// To avoid a copy, resize |proc_maps| so read() can write directly into it.
199
// Compute |buffer| afterwards since resize() may reallocate.
200
size_t pos = proc_maps->size();
201
proc_maps->resize(pos + kReadSize);
202
void *buffer = &(*proc_maps)[pos];
203
204
ssize_t bytes_read = HANDLE_EINTR(read(fd, buffer, kReadSize));
205
if (bytes_read < 0)
206
{
207
fprintf(stderr, "Couldn't read /proc/self/maps\n");
208
proc_maps->clear();
209
close(fd);
210
return false;
211
}
212
213
// ... and don't forget to trim off excess bytes.
214
proc_maps->resize(pos + bytes_read);
215
216
if (bytes_read == 0)
217
break;
218
}
219
220
close(fd);
221
return true;
222
}
223
224
bool ParseProcMaps(const std::string &input, MemoryRegionArray *regions_out)
225
{
226
ASSERT(regions_out);
227
MemoryRegionArray regions;
228
229
// This isn't async safe nor terribly efficient, but it doesn't need to be at
230
// this point in time.
231
std::vector<std::string> lines = SplitString(input, "\n", TRIM_WHITESPACE, SPLIT_WANT_ALL);
232
233
for (size_t i = 0; i < lines.size(); ++i)
234
{
235
// Due to splitting on '\n' the last line should be empty.
236
if (i == lines.size() - 1)
237
{
238
if (!lines[i].empty())
239
{
240
fprintf(stderr, "ParseProcMaps: Last line not empty");
241
return false;
242
}
243
break;
244
}
245
246
MappedMemoryRegion region;
247
const char *line = lines[i].c_str();
248
char permissions[5] = {'\0'}; // Ensure NUL-terminated string.
249
uint8_t dev_major = 0;
250
uint8_t dev_minor = 0;
251
long inode = 0;
252
int path_index = 0;
253
254
// Sample format from man 5 proc:
255
//
256
// address perms offset dev inode pathname
257
// 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
258
//
259
// The final %n term captures the offset in the input string, which is used
260
// to determine the path name. It *does not* increment the return value.
261
// Refer to man 3 sscanf for details.
262
if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4c %llx %hhx:%hhx %ld %n", &region.start,
263
&region.end, permissions, &region.offset, &dev_major, &dev_minor, &inode,
264
&path_index) < 7)
265
{
266
fprintf(stderr, "ParseProcMaps: sscanf failed for line: %s\n", line);
267
return false;
268
}
269
270
region.permissions = 0;
271
272
if (permissions[0] == 'r')
273
region.permissions |= MappedMemoryRegion::READ;
274
else if (permissions[0] != '-')
275
return false;
276
277
if (permissions[1] == 'w')
278
region.permissions |= MappedMemoryRegion::WRITE;
279
else if (permissions[1] != '-')
280
return false;
281
282
if (permissions[2] == 'x')
283
region.permissions |= MappedMemoryRegion::EXECUTE;
284
else if (permissions[2] != '-')
285
return false;
286
287
if (permissions[3] == 'p')
288
region.permissions |= MappedMemoryRegion::PRIVATE;
289
else if (permissions[3] != 's' && permissions[3] != 'S') // Shared memory.
290
return false;
291
292
// Pushing then assigning saves us a string copy.
293
regions.push_back(region);
294
regions.back().path.assign(line + path_index);
295
}
296
297
regions_out->swap(regions);
298
return true;
299
}
300
301
// Set the base address for each memory region by reading ELF headers in
302
// process memory.
303
void SetBaseAddressesForMemoryRegions(MemoryRegionArray &regions)
304
{
305
int mem_fd(HANDLE_EINTR(open("/proc/self/mem", O_RDONLY | O_CLOEXEC)));
306
if (mem_fd == -1)
307
return;
308
309
auto safe_memcpy = [&mem_fd](void *dst, uintptr_t src, size_t size) {
310
return HANDLE_EINTR(pread(mem_fd, dst, size, src)) == ssize_t(size);
311
};
312
313
uintptr_t cur_base = 0;
314
for (MappedMemoryRegion &r : regions)
315
{
316
ElfW(Ehdr) ehdr;
317
static_assert(SELFMAG <= sizeof(ElfW(Ehdr)), "SELFMAG too large");
318
if ((r.permissions & MappedMemoryRegion::READ) &&
319
safe_memcpy(&ehdr, r.start, sizeof(ElfW(Ehdr))) &&
320
memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0)
321
{
322
switch (ehdr.e_type)
323
{
324
case ET_EXEC:
325
cur_base = 0;
326
break;
327
case ET_DYN:
328
// Find the segment containing file offset 0. This will correspond
329
// to the ELF header that we just read. Normally this will have
330
// virtual address 0, but this is not guaranteed. We must subtract
331
// the virtual address from the address where the ELF header was
332
// mapped to get the base address.
333
//
334
// If we fail to find a segment for file offset 0, use the address
335
// of the ELF header as the base address.
336
cur_base = r.start;
337
for (unsigned i = 0; i != ehdr.e_phnum; ++i)
338
{
339
ElfW(Phdr) phdr;
340
if (safe_memcpy(&phdr, r.start + ehdr.e_phoff + i * sizeof(phdr),
341
sizeof(phdr)) &&
342
phdr.p_type == PT_LOAD && phdr.p_offset == 0)
343
{
344
cur_base = r.start - phdr.p_vaddr;
345
break;
346
}
347
}
348
break;
349
default:
350
// ET_REL or ET_CORE. These aren't directly executable, so they
351
// don't affect the base address.
352
break;
353
}
354
}
355
356
r.base = cur_base;
357
}
358
359
close(mem_fd);
360
}
361
362
// Parses /proc/self/maps in order to compile a list of all object file names
363
// for the modules that are loaded in the current process.
364
// Returns true on success.
365
bool CacheMemoryRegions(MemoryRegionArray &regions)
366
{
367
// Reads /proc/self/maps.
368
std::string contents;
369
if (!ReadProcMaps(&contents))
370
{
371
fprintf(stderr, "CacheMemoryRegions: Failed to read /proc/self/maps\n");
372
return false;
373
}
374
375
// Parses /proc/self/maps.
376
if (!ParseProcMaps(contents, &regions))
377
{
378
fprintf(stderr, "CacheMemoryRegions: Failed to parse the contents of /proc/self/maps\n");
379
return false;
380
}
381
382
SetBaseAddressesForMemoryRegions(regions);
383
return true;
384
}
385
386
constexpr size_t kAddr2LineMaxParameters = 50;
387
using Addr2LineCommandLine = angle::FixedVector<const char *, kAddr2LineMaxParameters>;
388
389
void CallAddr2Line(const Addr2LineCommandLine &commandLine)
390
{
391
pid_t pid = fork();
392
if (pid < 0)
393
{
394
std::cerr << "Error: Failed to fork()" << std::endl;
395
}
396
else if (pid > 0)
397
{
398
int status;
399
waitpid(pid, &status, 0);
400
// Ignore the status, since we aren't going to handle it anyway.
401
}
402
else
403
{
404
// Child process executes addr2line
405
//
406
// See comment in test_utils_posix.cpp::PosixProcess regarding const_cast.
407
execv(commandLine[0], const_cast<char *const *>(commandLine.data()));
408
std::cerr << "Error: Child process returned from exevc()" << std::endl;
409
_exit(EXIT_FAILURE); // exec never returns
410
}
411
}
412
413
constexpr size_t kMaxAddressLen = 1024;
414
using AddressBuffer = angle::FixedVector<char, kMaxAddressLen>;
415
416
const char *ResolveAddress(const MemoryRegionArray &regions,
417
const std::string &resolvedModule,
418
const char *address,
419
AddressBuffer &buffer)
420
{
421
size_t lastModuleSlash = resolvedModule.rfind('/');
422
ASSERT(lastModuleSlash != std::string::npos);
423
std::string baseModule = resolvedModule.substr(lastModuleSlash);
424
425
for (const MappedMemoryRegion &region : regions)
426
{
427
size_t pathSlashPos = region.path.rfind('/');
428
if (pathSlashPos != std::string::npos && region.path.substr(pathSlashPos) == baseModule)
429
{
430
uintptr_t scannedAddress;
431
int scanReturn = sscanf(address, "%lX", &scannedAddress);
432
ASSERT(scanReturn == 1);
433
scannedAddress -= region.base;
434
char printBuffer[255] = {};
435
size_t scannedSize = sprintf(printBuffer, "0x%lX", scannedAddress);
436
size_t bufferSize = buffer.size();
437
buffer.resize(bufferSize + scannedSize + 1, 0);
438
memcpy(&buffer[bufferSize], printBuffer, scannedSize);
439
return &buffer[bufferSize];
440
}
441
}
442
443
return address;
444
}
445
} // anonymous namespace
446
# endif // defined(ANGLE_HAS_ADDR2LINE)
447
448
void PrintStackBacktrace()
449
{
450
printf("Backtrace:\n");
451
452
void *stack[64];
453
const int count = backtrace(stack, ArraySize(stack));
454
char **symbols = backtrace_symbols(stack, count);
455
456
# if defined(ANGLE_HAS_ADDR2LINE)
457
458
MemoryRegionArray regions;
459
CacheMemoryRegions(regions);
460
461
// Child process executes addr2line
462
constexpr size_t kAddr2LineFixedParametersCount = 6;
463
Addr2LineCommandLine commandLineArgs = {
464
"/usr/bin/addr2line", // execv requires an absolute path to find addr2line
465
"-s",
466
"-p",
467
"-f",
468
"-C",
469
"-e",
470
};
471
const char *currentModule = "";
472
std::string resolvedModule;
473
AddressBuffer addressBuffer;
474
475
for (int i = 0; i < count; i++)
476
{
477
char *symbol = symbols[i];
478
479
// symbol looks like the following:
480
//
481
// path/to/module(+localAddress) [address]
482
//
483
// If module is not an absolute path, it needs to be resolved.
484
485
char *module = symbol;
486
char *address = strchr(symbol, '[') + 1;
487
488
*strchr(module, '(') = 0;
489
*strchr(address, ']') = 0;
490
491
// If module is the same as last, continue batching addresses. If commandLineArgs has
492
// reached its capacity however, make the call to addr2line already. Note that there should
493
// be one entry left for the terminating nullptr at the end of the command line args.
494
if (strcmp(module, currentModule) == 0 &&
495
commandLineArgs.size() + 1 < commandLineArgs.max_size())
496
{
497
commandLineArgs.push_back(
498
ResolveAddress(regions, resolvedModule, address, addressBuffer));
499
continue;
500
}
501
502
// If there's a command batched, execute it before modifying currentModule (a pointer to
503
// which is stored in the command line args).
504
if (currentModule[0] != 0)
505
{
506
commandLineArgs.push_back(nullptr);
507
CallAddr2Line(commandLineArgs);
508
addressBuffer.clear();
509
}
510
511
// Reset the command line and remember this module as the current.
512
resolvedModule = currentModule = module;
513
commandLineArgs.resize(kAddr2LineFixedParametersCount);
514
515
// We need an absolute path to get to the executable and all of the various shared objects,
516
// but the caller may have used a relative path to launch the executable, so build one up if
517
// we don't see a leading '/'.
518
if (resolvedModule.at(0) != GetPathSeparator())
519
{
520
const Optional<std::string> &cwd = angle::GetCWD();
521
if (!cwd.valid())
522
{
523
std::cerr << "Error getting CWD to print the backtrace." << std::endl;
524
}
525
else
526
{
527
std::string absolutePath = cwd.value();
528
size_t lastPathSepLoc = resolvedModule.find_last_of(GetPathSeparator());
529
std::string relativePath = resolvedModule.substr(0, lastPathSepLoc);
530
531
// Remove "." from the relativePath path
532
// For example: ./out/LinuxDebug/angle_perftests
533
size_t pos = relativePath.find('.');
534
if (pos != std::string::npos)
535
{
536
// If found then erase it from string
537
relativePath.erase(pos, 1);
538
}
539
540
// Remove the overlapping relative path from the CWD so we can build the full
541
// absolute path.
542
// For example:
543
// absolutePath = /home/timvp/code/angle/out/LinuxDebug
544
// relativePath = /out/LinuxDebug
545
pos = absolutePath.find(relativePath);
546
if (pos != std::string::npos)
547
{
548
// If found then erase it from string
549
absolutePath.erase(pos, relativePath.length());
550
}
551
resolvedModule = absolutePath + GetPathSeparator() + resolvedModule;
552
}
553
}
554
555
// Check if this is a symlink. We assume the symlinks are relative to the target.
556
constexpr size_t kBufSize = 1000;
557
char linkBuf[kBufSize] = {};
558
ssize_t readLinkRet = readlink(resolvedModule.c_str(), linkBuf, kBufSize);
559
if (readLinkRet != -1)
560
{
561
ASSERT(strchr(linkBuf, '/') == nullptr);
562
size_t lastSlash = resolvedModule.rfind('/');
563
ASSERT(lastSlash != std::string::npos);
564
resolvedModule = resolvedModule.substr(0, lastSlash + 1) + linkBuf;
565
}
566
567
const char *resolvedAddress =
568
ResolveAddress(regions, resolvedModule, address, addressBuffer);
569
570
commandLineArgs.push_back(resolvedModule.c_str());
571
commandLineArgs.push_back(resolvedAddress);
572
}
573
574
// Call addr2line for the last batch of addresses.
575
if (currentModule[0] != 0)
576
{
577
commandLineArgs.push_back(nullptr);
578
CallAddr2Line(commandLineArgs);
579
}
580
# else
581
for (int i = 0; i < count; i++)
582
{
583
Dl_info info;
584
if (dladdr(stack[i], &info) && info.dli_sname)
585
{
586
// Make sure this is large enough to hold the fully demangled names, otherwise we could
587
// segault/hang here. For example, Vulkan validation layer errors can be deep enough
588
// into the stack that very large symbol names are generated.
589
char demangled[4096];
590
size_t len = ArraySize(demangled);
591
int ok;
592
593
abi::__cxa_demangle(info.dli_sname, demangled, &len, &ok);
594
if (ok == 0)
595
{
596
printf(" %s\n", demangled);
597
continue;
598
}
599
}
600
printf(" %s\n", symbols[i]);
601
}
602
# endif // defined(ANGLE_HAS_ADDR2LINE)
603
}
604
605
static void Handler(int sig)
606
{
607
if (gCrashHandlerCallback)
608
{
609
(*gCrashHandlerCallback)();
610
}
611
612
printf("\nSignal %d [%s]:\n", sig, strsignal(sig));
613
PrintStackBacktrace();
614
615
// Exit NOW. Don't notify other threads, don't call anything registered with atexit().
616
_Exit(sig);
617
}
618
619
# endif // defined(ANGLE_PLATFORM_APPLE)
620
621
static constexpr int kSignals[] = {
622
SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP,
623
};
624
625
void InitCrashHandler(CrashCallback *callback)
626
{
627
gCrashHandlerCallback = callback;
628
for (int sig : kSignals)
629
{
630
// Register our signal handler unless something's already done so (e.g. catchsegv).
631
void (*prev)(int) = signal(sig, Handler);
632
if (prev != SIG_DFL)
633
{
634
signal(sig, prev);
635
}
636
}
637
}
638
639
void TerminateCrashHandler()
640
{
641
gCrashHandlerCallback = nullptr;
642
for (int sig : kSignals)
643
{
644
void (*prev)(int) = signal(sig, SIG_DFL);
645
if (prev != Handler && prev != SIG_DFL)
646
{
647
signal(sig, prev);
648
}
649
}
650
}
651
652
#endif // defined(ANGLE_PLATFORM_ANDROID) || defined(ANGLE_PLATFORM_FUCHSIA)
653
654
} // namespace angle
655
656