Path: blob/main_old/util/posix/crash_handler_posix.cpp
1693 views
//1// Copyright 2019 The ANGLE Project Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4//5// crash_handler_posix:6// ANGLE's crash handling and stack walking code. Modified from Skia's:7// https://github.com/google/skia/blob/master/tools/CrashHandler.cpp8//910#include "util/test_utils.h"1112#include "common/FixedVector.h"13#include "common/angleutils.h"14#include "common/string_utils.h"15#include "common/system_utils.h"1617#include <fcntl.h>18#include <inttypes.h>19#include <stdio.h>20#include <stdlib.h>21#include <sys/types.h>22#include <sys/wait.h>23#include <unistd.h>24#include <iostream>2526#if !defined(ANGLE_PLATFORM_ANDROID) && !defined(ANGLE_PLATFORM_FUCHSIA)27# if defined(ANGLE_PLATFORM_APPLE)28// We only use local unwinding, so we can define this to select a faster implementation.29# define UNW_LOCAL_ONLY30# include <cxxabi.h>31# include <libunwind.h>32# include <signal.h>33# elif defined(ANGLE_PLATFORM_POSIX)34// We'd use libunwind here too, but it's a pain to get installed for35// both 32 and 64 bit on bots. Doesn't matter much: catchsegv is best anyway.36# include <cxxabi.h>37# include <dlfcn.h>38# include <execinfo.h>39# include <libgen.h>40# include <link.h>41# include <signal.h>42# include <string.h>43# endif // defined(ANGLE_PLATFORM_APPLE)44#endif // !defined(ANGLE_PLATFORM_ANDROID) && !defined(ANGLE_PLATFORM_FUCHSIA)4546// This code snippet is coped from Chromium's base/posix/eintr_wrapper.h.47#if defined(NDEBUG)48# define HANDLE_EINTR(x) \49({ \50decltype(x) eintr_wrapper_result; \51do \52{ \53eintr_wrapper_result = (x); \54} while (eintr_wrapper_result == -1 && errno == EINTR); \55eintr_wrapper_result; \56})57#else58# define HANDLE_EINTR(x) \59({ \60int eintr_wrapper_counter = 0; \61decltype(x) eintr_wrapper_result; \62do \63{ \64eintr_wrapper_result = (x); \65} while (eintr_wrapper_result == -1 && errno == EINTR && \66eintr_wrapper_counter++ < 100); \67eintr_wrapper_result; \68})69#endif // NDEBUG7071namespace angle72{73#if defined(ANGLE_PLATFORM_ANDROID) || defined(ANGLE_PLATFORM_FUCHSIA)7475void PrintStackBacktrace()76{77// No implementations yet.78}7980void InitCrashHandler(CrashCallback *callback)81{82// No implementations yet.83}8485void TerminateCrashHandler()86{87// No implementations yet.88}8990#else91namespace92{93CrashCallback *gCrashHandlerCallback;94} // namespace9596# if defined(ANGLE_PLATFORM_APPLE)9798void PrintStackBacktrace()99{100printf("Backtrace:\n");101102unw_context_t context;103unw_getcontext(&context);104105unw_cursor_t cursor;106unw_init_local(&cursor, &context);107108while (unw_step(&cursor) > 0)109{110static const size_t kMax = 256;111char mangled[kMax], demangled[kMax];112unw_word_t offset;113unw_get_proc_name(&cursor, mangled, kMax, &offset);114115int ok;116size_t len = kMax;117abi::__cxa_demangle(mangled, demangled, &len, &ok);118119printf(" %s (+0x%zx)\n", ok == 0 ? demangled : mangled, (size_t)offset);120}121printf("\n");122}123124static void Handler(int sig)125{126if (gCrashHandlerCallback)127{128(*gCrashHandlerCallback)();129}130131printf("\nSignal %d:\n", sig);132PrintStackBacktrace();133134// Exit NOW. Don't notify other threads, don't call anything registered with atexit().135_Exit(sig);136}137138# elif defined(ANGLE_PLATFORM_POSIX)139140// Can control this at a higher level if required.141# define ANGLE_HAS_ADDR2LINE142143# if defined(ANGLE_HAS_ADDR2LINE)144namespace145{146// The following code was adapted from Chromium's "stack_trace_posix.cc".147// Describes a region of mapped memory and the path of the file mapped.148struct MappedMemoryRegion149{150enum Permission151{152READ = 1 << 0,153WRITE = 1 << 1,154EXECUTE = 1 << 2,155PRIVATE = 1 << 3, // If set, region is private, otherwise it is shared.156};157158// The address range [start,end) of mapped memory.159uintptr_t start;160uintptr_t end;161162// Byte offset into |path| of the range mapped into memory.163unsigned long long offset;164165// Image base, if this mapping corresponds to an ELF image.166uintptr_t base;167168// Bitmask of read/write/execute/private/shared permissions.169uint8_t permissions;170171// Name of the file mapped into memory.172//173// NOTE: path names aren't guaranteed to point at valid files. For example,174// "[heap]" and "[stack]" are used to represent the location of the process'175// heap and stack, respectively.176std::string path;177};178179using MemoryRegionArray = std::vector<MappedMemoryRegion>;180181bool ReadProcMaps(std::string *proc_maps)182{183// seq_file only writes out a page-sized amount on each call. Refer to header184// file for details.185const long kReadSize = sysconf(_SC_PAGESIZE);186187int fd(HANDLE_EINTR(open("/proc/self/maps", O_RDONLY)));188if (fd == -1)189{190fprintf(stderr, "Couldn't open /proc/self/maps\n");191return false;192}193proc_maps->clear();194195while (true)196{197// To avoid a copy, resize |proc_maps| so read() can write directly into it.198// Compute |buffer| afterwards since resize() may reallocate.199size_t pos = proc_maps->size();200proc_maps->resize(pos + kReadSize);201void *buffer = &(*proc_maps)[pos];202203ssize_t bytes_read = HANDLE_EINTR(read(fd, buffer, kReadSize));204if (bytes_read < 0)205{206fprintf(stderr, "Couldn't read /proc/self/maps\n");207proc_maps->clear();208close(fd);209return false;210}211212// ... and don't forget to trim off excess bytes.213proc_maps->resize(pos + bytes_read);214215if (bytes_read == 0)216break;217}218219close(fd);220return true;221}222223bool ParseProcMaps(const std::string &input, MemoryRegionArray *regions_out)224{225ASSERT(regions_out);226MemoryRegionArray regions;227228// This isn't async safe nor terribly efficient, but it doesn't need to be at229// this point in time.230std::vector<std::string> lines = SplitString(input, "\n", TRIM_WHITESPACE, SPLIT_WANT_ALL);231232for (size_t i = 0; i < lines.size(); ++i)233{234// Due to splitting on '\n' the last line should be empty.235if (i == lines.size() - 1)236{237if (!lines[i].empty())238{239fprintf(stderr, "ParseProcMaps: Last line not empty");240return false;241}242break;243}244245MappedMemoryRegion region;246const char *line = lines[i].c_str();247char permissions[5] = {'\0'}; // Ensure NUL-terminated string.248uint8_t dev_major = 0;249uint8_t dev_minor = 0;250long inode = 0;251int path_index = 0;252253// Sample format from man 5 proc:254//255// address perms offset dev inode pathname256// 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm257//258// The final %n term captures the offset in the input string, which is used259// to determine the path name. It *does not* increment the return value.260// Refer to man 3 sscanf for details.261if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4c %llx %hhx:%hhx %ld %n", ®ion.start,262®ion.end, permissions, ®ion.offset, &dev_major, &dev_minor, &inode,263&path_index) < 7)264{265fprintf(stderr, "ParseProcMaps: sscanf failed for line: %s\n", line);266return false;267}268269region.permissions = 0;270271if (permissions[0] == 'r')272region.permissions |= MappedMemoryRegion::READ;273else if (permissions[0] != '-')274return false;275276if (permissions[1] == 'w')277region.permissions |= MappedMemoryRegion::WRITE;278else if (permissions[1] != '-')279return false;280281if (permissions[2] == 'x')282region.permissions |= MappedMemoryRegion::EXECUTE;283else if (permissions[2] != '-')284return false;285286if (permissions[3] == 'p')287region.permissions |= MappedMemoryRegion::PRIVATE;288else if (permissions[3] != 's' && permissions[3] != 'S') // Shared memory.289return false;290291// Pushing then assigning saves us a string copy.292regions.push_back(region);293regions.back().path.assign(line + path_index);294}295296regions_out->swap(regions);297return true;298}299300// Set the base address for each memory region by reading ELF headers in301// process memory.302void SetBaseAddressesForMemoryRegions(MemoryRegionArray ®ions)303{304int mem_fd(HANDLE_EINTR(open("/proc/self/mem", O_RDONLY | O_CLOEXEC)));305if (mem_fd == -1)306return;307308auto safe_memcpy = [&mem_fd](void *dst, uintptr_t src, size_t size) {309return HANDLE_EINTR(pread(mem_fd, dst, size, src)) == ssize_t(size);310};311312uintptr_t cur_base = 0;313for (MappedMemoryRegion &r : regions)314{315ElfW(Ehdr) ehdr;316static_assert(SELFMAG <= sizeof(ElfW(Ehdr)), "SELFMAG too large");317if ((r.permissions & MappedMemoryRegion::READ) &&318safe_memcpy(&ehdr, r.start, sizeof(ElfW(Ehdr))) &&319memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0)320{321switch (ehdr.e_type)322{323case ET_EXEC:324cur_base = 0;325break;326case ET_DYN:327// Find the segment containing file offset 0. This will correspond328// to the ELF header that we just read. Normally this will have329// virtual address 0, but this is not guaranteed. We must subtract330// the virtual address from the address where the ELF header was331// mapped to get the base address.332//333// If we fail to find a segment for file offset 0, use the address334// of the ELF header as the base address.335cur_base = r.start;336for (unsigned i = 0; i != ehdr.e_phnum; ++i)337{338ElfW(Phdr) phdr;339if (safe_memcpy(&phdr, r.start + ehdr.e_phoff + i * sizeof(phdr),340sizeof(phdr)) &&341phdr.p_type == PT_LOAD && phdr.p_offset == 0)342{343cur_base = r.start - phdr.p_vaddr;344break;345}346}347break;348default:349// ET_REL or ET_CORE. These aren't directly executable, so they350// don't affect the base address.351break;352}353}354355r.base = cur_base;356}357358close(mem_fd);359}360361// Parses /proc/self/maps in order to compile a list of all object file names362// for the modules that are loaded in the current process.363// Returns true on success.364bool CacheMemoryRegions(MemoryRegionArray ®ions)365{366// Reads /proc/self/maps.367std::string contents;368if (!ReadProcMaps(&contents))369{370fprintf(stderr, "CacheMemoryRegions: Failed to read /proc/self/maps\n");371return false;372}373374// Parses /proc/self/maps.375if (!ParseProcMaps(contents, ®ions))376{377fprintf(stderr, "CacheMemoryRegions: Failed to parse the contents of /proc/self/maps\n");378return false;379}380381SetBaseAddressesForMemoryRegions(regions);382return true;383}384385constexpr size_t kAddr2LineMaxParameters = 50;386using Addr2LineCommandLine = angle::FixedVector<const char *, kAddr2LineMaxParameters>;387388void CallAddr2Line(const Addr2LineCommandLine &commandLine)389{390pid_t pid = fork();391if (pid < 0)392{393std::cerr << "Error: Failed to fork()" << std::endl;394}395else if (pid > 0)396{397int status;398waitpid(pid, &status, 0);399// Ignore the status, since we aren't going to handle it anyway.400}401else402{403// Child process executes addr2line404//405// See comment in test_utils_posix.cpp::PosixProcess regarding const_cast.406execv(commandLine[0], const_cast<char *const *>(commandLine.data()));407std::cerr << "Error: Child process returned from exevc()" << std::endl;408_exit(EXIT_FAILURE); // exec never returns409}410}411412constexpr size_t kMaxAddressLen = 1024;413using AddressBuffer = angle::FixedVector<char, kMaxAddressLen>;414415const char *ResolveAddress(const MemoryRegionArray ®ions,416const std::string &resolvedModule,417const char *address,418AddressBuffer &buffer)419{420size_t lastModuleSlash = resolvedModule.rfind('/');421ASSERT(lastModuleSlash != std::string::npos);422std::string baseModule = resolvedModule.substr(lastModuleSlash);423424for (const MappedMemoryRegion ®ion : regions)425{426size_t pathSlashPos = region.path.rfind('/');427if (pathSlashPos != std::string::npos && region.path.substr(pathSlashPos) == baseModule)428{429uintptr_t scannedAddress;430int scanReturn = sscanf(address, "%lX", &scannedAddress);431ASSERT(scanReturn == 1);432scannedAddress -= region.base;433char printBuffer[255] = {};434size_t scannedSize = sprintf(printBuffer, "0x%lX", scannedAddress);435size_t bufferSize = buffer.size();436buffer.resize(bufferSize + scannedSize + 1, 0);437memcpy(&buffer[bufferSize], printBuffer, scannedSize);438return &buffer[bufferSize];439}440}441442return address;443}444} // anonymous namespace445# endif // defined(ANGLE_HAS_ADDR2LINE)446447void PrintStackBacktrace()448{449printf("Backtrace:\n");450451void *stack[64];452const int count = backtrace(stack, ArraySize(stack));453char **symbols = backtrace_symbols(stack, count);454455# if defined(ANGLE_HAS_ADDR2LINE)456457MemoryRegionArray regions;458CacheMemoryRegions(regions);459460// Child process executes addr2line461constexpr size_t kAddr2LineFixedParametersCount = 6;462Addr2LineCommandLine commandLineArgs = {463"/usr/bin/addr2line", // execv requires an absolute path to find addr2line464"-s",465"-p",466"-f",467"-C",468"-e",469};470const char *currentModule = "";471std::string resolvedModule;472AddressBuffer addressBuffer;473474for (int i = 0; i < count; i++)475{476char *symbol = symbols[i];477478// symbol looks like the following:479//480// path/to/module(+localAddress) [address]481//482// If module is not an absolute path, it needs to be resolved.483484char *module = symbol;485char *address = strchr(symbol, '[') + 1;486487*strchr(module, '(') = 0;488*strchr(address, ']') = 0;489490// If module is the same as last, continue batching addresses. If commandLineArgs has491// reached its capacity however, make the call to addr2line already. Note that there should492// be one entry left for the terminating nullptr at the end of the command line args.493if (strcmp(module, currentModule) == 0 &&494commandLineArgs.size() + 1 < commandLineArgs.max_size())495{496commandLineArgs.push_back(497ResolveAddress(regions, resolvedModule, address, addressBuffer));498continue;499}500501// If there's a command batched, execute it before modifying currentModule (a pointer to502// which is stored in the command line args).503if (currentModule[0] != 0)504{505commandLineArgs.push_back(nullptr);506CallAddr2Line(commandLineArgs);507addressBuffer.clear();508}509510// Reset the command line and remember this module as the current.511resolvedModule = currentModule = module;512commandLineArgs.resize(kAddr2LineFixedParametersCount);513514// We need an absolute path to get to the executable and all of the various shared objects,515// but the caller may have used a relative path to launch the executable, so build one up if516// we don't see a leading '/'.517if (resolvedModule.at(0) != GetPathSeparator())518{519const Optional<std::string> &cwd = angle::GetCWD();520if (!cwd.valid())521{522std::cerr << "Error getting CWD to print the backtrace." << std::endl;523}524else525{526std::string absolutePath = cwd.value();527size_t lastPathSepLoc = resolvedModule.find_last_of(GetPathSeparator());528std::string relativePath = resolvedModule.substr(0, lastPathSepLoc);529530// Remove "." from the relativePath path531// For example: ./out/LinuxDebug/angle_perftests532size_t pos = relativePath.find('.');533if (pos != std::string::npos)534{535// If found then erase it from string536relativePath.erase(pos, 1);537}538539// Remove the overlapping relative path from the CWD so we can build the full540// absolute path.541// For example:542// absolutePath = /home/timvp/code/angle/out/LinuxDebug543// relativePath = /out/LinuxDebug544pos = absolutePath.find(relativePath);545if (pos != std::string::npos)546{547// If found then erase it from string548absolutePath.erase(pos, relativePath.length());549}550resolvedModule = absolutePath + GetPathSeparator() + resolvedModule;551}552}553554// Check if this is a symlink. We assume the symlinks are relative to the target.555constexpr size_t kBufSize = 1000;556char linkBuf[kBufSize] = {};557ssize_t readLinkRet = readlink(resolvedModule.c_str(), linkBuf, kBufSize);558if (readLinkRet != -1)559{560ASSERT(strchr(linkBuf, '/') == nullptr);561size_t lastSlash = resolvedModule.rfind('/');562ASSERT(lastSlash != std::string::npos);563resolvedModule = resolvedModule.substr(0, lastSlash + 1) + linkBuf;564}565566const char *resolvedAddress =567ResolveAddress(regions, resolvedModule, address, addressBuffer);568569commandLineArgs.push_back(resolvedModule.c_str());570commandLineArgs.push_back(resolvedAddress);571}572573// Call addr2line for the last batch of addresses.574if (currentModule[0] != 0)575{576commandLineArgs.push_back(nullptr);577CallAddr2Line(commandLineArgs);578}579# else580for (int i = 0; i < count; i++)581{582Dl_info info;583if (dladdr(stack[i], &info) && info.dli_sname)584{585// Make sure this is large enough to hold the fully demangled names, otherwise we could586// segault/hang here. For example, Vulkan validation layer errors can be deep enough587// into the stack that very large symbol names are generated.588char demangled[4096];589size_t len = ArraySize(demangled);590int ok;591592abi::__cxa_demangle(info.dli_sname, demangled, &len, &ok);593if (ok == 0)594{595printf(" %s\n", demangled);596continue;597}598}599printf(" %s\n", symbols[i]);600}601# endif // defined(ANGLE_HAS_ADDR2LINE)602}603604static void Handler(int sig)605{606if (gCrashHandlerCallback)607{608(*gCrashHandlerCallback)();609}610611printf("\nSignal %d [%s]:\n", sig, strsignal(sig));612PrintStackBacktrace();613614// Exit NOW. Don't notify other threads, don't call anything registered with atexit().615_Exit(sig);616}617618# endif // defined(ANGLE_PLATFORM_APPLE)619620static constexpr int kSignals[] = {621SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP,622};623624void InitCrashHandler(CrashCallback *callback)625{626gCrashHandlerCallback = callback;627for (int sig : kSignals)628{629// Register our signal handler unless something's already done so (e.g. catchsegv).630void (*prev)(int) = signal(sig, Handler);631if (prev != SIG_DFL)632{633signal(sig, prev);634}635}636}637638void TerminateCrashHandler()639{640gCrashHandlerCallback = nullptr;641for (int sig : kSignals)642{643void (*prev)(int) = signal(sig, SIG_DFL);644if (prev != Handler && prev != SIG_DFL)645{646signal(sig, prev);647}648}649}650651#endif // defined(ANGLE_PLATFORM_ANDROID) || defined(ANGLE_PLATFORM_FUCHSIA)652653} // namespace angle654655656