├── .clang-format ├── .gitignore ├── AUTHORS ├── CMakeLists.txt ├── COPYING ├── ChangeLog ├── README.md ├── compile ├── examples ├── Makefile └── tftpd.c ├── include └── coredumper │ └── coredumper.h ├── man ├── CoreDumpParameters.man ├── GetCoreDump.man └── WriteCoreDump.man ├── src ├── CMakeLists.txt ├── coredumper.c ├── elfcore.cc ├── elfcore.h ├── libcoredumper.sym ├── linux_syscall_support.h ├── linuxthreads.cc ├── linuxthreads.h ├── thread_lister.c └── thread_lister.h ├── test-driver └── test ├── CMakeLists.txt ├── coredumper_unittest.c └── linux_syscall_support_unittest.cc /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: google 3 | ColumnLimit: 120 4 | --- 5 | Language: Cpp 6 | Standard: Cpp11 7 | BreakConstructorInitializersBeforeComma: true 8 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 9 | Cpp11BracedListStyle: true 10 | ForEachMacros: [] 11 | --- 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Editor/IDE/debugger artifacts 2 | *~ 3 | \#*\# 4 | *.diff 5 | *.patch 6 | *.log 7 | *.out 8 | *.swp 9 | .gdb_history 10 | GPATH 11 | GRTAGS 12 | GSYMS 13 | GTAGS 14 | ID 15 | TAGS 16 | 17 | # Build / autotools artifacts 18 | /autom4te.cache/ 19 | Makefile 20 | config.h 21 | config.status 22 | stamp-* 23 | .dirstamp 24 | .deps/ 25 | .libs/ 26 | *.o 27 | *.lo 28 | *.la 29 | core.* 30 | coredumper_unittest 31 | linux_syscall_support_unittest 32 | *.trs 33 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | opensource@google.com 2 | 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | if (POLICY CMP0048) 3 | cmake_policy(SET CMP0048 NEW) 4 | endif(POLICY CMP0048) 5 | project(coredumper C CXX) 6 | # We do not wish to work without threading support 7 | # TODO: remove related ifdefs 8 | set(THREADS_PREFER_PTHREAD_FLAG ON) 9 | find_package(Threads REQUIRED) 10 | 11 | set(COREDUMPER_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 12 | 13 | add_subdirectory(src) 14 | 15 | include(CTest) 16 | if(BUILD_TESTING) 17 | enable_testing() 18 | add_subdirectory(test) 19 | endif() 20 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2007, Google Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following disclaimer 12 | in the documentation and/or other materials provided with the 13 | distribution. 14 | * Neither the name of Google Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | Fri Feb 11 12:51:37 2005 Google Inc. 2 | 3 | * coredumper: initial release: 4 | The coredumper utility allows a running program to generate a 5 | core file without actually crashing. This serves to allow the 6 | programmer to generate a snapshot of a running program's state 7 | at any time. 8 | 9 | Mon May 2 07:04:46 2005 Google Inc. 10 | 11 | * coredumper: version 0.2 release 12 | * fixed compilation on icc (simonb) 13 | * added a new WriteCoreDumpLimited() function that allows the 14 | caller to specify the maximum core file size. This is needed to 15 | emulate "ulimit -c". (markus) 16 | 17 | Thu Aug 10 12:44:40 2006 Google Inc. 18 | 19 | * coredumper: version 0.3 release 20 | * reorganized the source to make porting and reuse easier. 21 | * ported to ARM. 22 | * core files can now be compressed on-the-fly. 23 | * added support for recent 2.6.x kernels, which subtly changed the 24 | ptrace() API. It used to be possible to PTRACE_ATTACH to threads 25 | in the same process, but newer kernels require making this call 26 | from a separate process. This change is backwards compatible. 27 | * improved compatibility with more (historic) versions of both 28 | libc and of the Linux kernel. 29 | * the convenience methods now enforce more restrictive file 30 | permissions when writing to disk. The caller no longer needs to 31 | adjust the umask() to make file writing secure. 32 | * the coredumper fails gracefully if called when the stack is almost 33 | about to overflow. 34 | * fixed bugs that prevented thread listing on 64bit machines. 35 | * switched to different syscall() functions in order to better 36 | preserve the call stack. Added work-around for broken libc 37 | system headers. 38 | * all assembly code is position-independent. 39 | * the unittest can now be run even if the systems locale is not 40 | English. 41 | 42 | Wed Feb 14 14:44:40 2007 Google Inc. 43 | 44 | * fixed some packaging problems with newer versions of RPM 45 | * added assembly version of sys_clone() in order to work around 46 | buggy implementations in glibc. 47 | 48 | Thu Feb 15 16:24:32 2007 Google Inc. 49 | 50 | * coredumper: version 1.0 release 51 | * made devel RPMs depend on the runtime-only package. 52 | * fixed signal related system calls on x86_64. 53 | * wrote manual pages. 54 | 55 | Tue Feb 20 15:07:03 2007 Google Inc. 56 | 57 | * changed from NT_PRFPXREG to NT_PRXFPREG, as this appears to be 58 | what the kernel and the binary tool chain finally agrees on. 59 | 60 | * dump anonymous executable mappins such as the [vdso] segment. 61 | This helps "gdb" in following call chains past the invocation of 62 | signal handlers. 63 | 64 | Fri Jan 25 18:36:01 2008 Google Inc. 65 | 66 | * Added kernel data structures to linux_syscall_support.h 67 | * Added some more system calls 68 | * General clean ups and minor bug fixes to linux_syscall_support.h 69 | * Updated MIPS support 70 | * Added PPC support to linux_syscall_support.h (based on the patch at 71 | http://google-perftools.googlegroups.com/web/google-perftools-0.94.1.ppc.patch?gda=k9bft1IAAACubrlYz6X6f2r_QSIc5WKIP3FyvVAXIQ9N70rJj7w7MWG1qiJ7UbTIup-M2XPURDR0OdCHCKqS2f7o1Lzcc8Kg4jvJEVA5r4WYNVZfjSxuln7gCK2zepjMSjyreBgvHJk) 72 | This does not mean that the coredumper works on PPC, yet. That still 73 | requires additional work 74 | * Added linux_syscall_support_unittest.cc 75 | * Updated other files as needed so that they would work with the 76 | changes made to linux_syscall_support.h 77 | 78 | Thu Apr 3 14:16:01 2008 Google Inc. 79 | 80 | * coredumper: version 1.2 release 81 | * Added the option to prioritize smaller memory segments when limiting 82 | core dumps. 83 | * Added the option to add user defined notes into the core dump. 84 | * Added a generalized system to be able to combine parameters easily. 85 | * Increased the library version number to 1.0.0 since the interface has 86 | been extended. 87 | * Added vdso segments to the core dump. 88 | * Made the failing thread's registers the first in the core file as 89 | expected by gdb. 90 | 91 | Fri Apr 4 09:38:05 2008 Google Inc. 92 | 93 | * coredumper: version 1.2.1 release 94 | * Fixed a problem which occurs if auxv vectors aren't supported. 95 | 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The coredumper library can be compiled into applications to create 2 | core dumps of the running program, without having to terminate 3 | them. It supports both single- and multi-threaded core dumps, even if 4 | the kernel does not have native support for multi-threaded core files. 5 | 6 | This library is primarily intended to simplify debugging of 7 | long-running services. It is often inacceptable to suspend production 8 | services by attaching a debugger, nor is it possible to crash the 9 | service in order to generate a core file. 10 | 11 | By modifying an existing service to take advantage of the coredumper 12 | library, it is possible to expose an interface for obtaining snapshots 13 | of the running application. The library supports writing of core files 14 | to disk (e.g. triggered upon reception of a signal) but it can also 15 | generate in-memory core files. This makes it possible for web 16 | services to expose remote access to core files. 17 | 18 | The "examples" directory shows how to add a core file feature to an 19 | existing TFTP server. For an example of how to use on-disk core files, 20 | take a look at "src/coredump_unittest.c". 21 | 22 | The code has been tested on Linux x86/64. It is 23 | available as a git source archive, and can be build using CMake. 24 | 25 | To configure the library, execute CMake first by running: 26 | 27 | ``` 28 | cmake -DCMAKE_BUILD_TYPE=(Debug|RelWithDebInfo|Release) \ 29 | -DBUILD_SHARED_LIBS=(ON|OFF) \ 30 | -DCMAKE_INSTALL_PREFIX= \ 31 | -DBUILD_TESTING=(ON|OFF) 32 | -G 33 | ``` 34 | 35 | All options are optional, and the boolean options default to `ON`. 36 | The library can be used both in the build tree and at the install location, 37 | both using `add_subdirectory` and using the exported cmake configurations. 38 | 39 | After configuring, run Make/Ninja/anythnig: 40 | 41 | ``` 42 | ninja 43 | ``` 44 | 45 | If the tests were built, they can be executed using CTest: 46 | 47 | ``` 48 | ctest . 49 | ``` 50 | 51 | The library can be installed using the install target: 52 | 53 | ``` 54 | ninja install 55 | ``` 56 | The preferred method of using the library is by using the provided 57 | cmake configuration files. These configuration files can be found at: 58 | 59 | * CMAKE_BUILD_DIR/src/libcoredumper.cmake 60 | * CMAKE_INSTALL_DIR/cmake/libcoredumper.cmake 61 | 62 | For more information on how to use the library, read the manual pages 63 | for "GetCoreDump" and "WriteCoreDump". 64 | 65 | 14 May 2020 66 | -------------------------------------------------------------------------------- /compile: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # Wrapper for compilers which do not understand `-c -o'. 4 | 5 | # Copyright 1999, 2000 Free Software Foundation, Inc. 6 | # Written by Tom Tromey . 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2, or (at your option) 11 | # any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program; if not, write to the Free Software 20 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21 | 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | 27 | # Usage: 28 | # compile PROGRAM [ARGS]... 29 | # `-o FOO.o' is removed from the args passed to the actual compile. 30 | 31 | prog=$1 32 | shift 33 | 34 | ofile= 35 | cfile= 36 | args= 37 | while test $# -gt 0; do 38 | case "$1" in 39 | -o) 40 | # configure might choose to run compile as `compile cc -o foo foo.c'. 41 | # So we do something ugly here. 42 | ofile=$2 43 | shift 44 | case "$ofile" in 45 | *.o | *.obj) 46 | ;; 47 | *) 48 | args="$args -o $ofile" 49 | ofile= 50 | ;; 51 | esac 52 | ;; 53 | *.c) 54 | cfile=$1 55 | args="$args $1" 56 | ;; 57 | *) 58 | args="$args $1" 59 | ;; 60 | esac 61 | shift 62 | done 63 | 64 | if test -z "$ofile" || test -z "$cfile"; then 65 | # If no `-o' option was seen then we might have been invoked from a 66 | # pattern rule where we don't need one. That is ok -- this is a 67 | # normal compilation that the losing compiler can handle. If no 68 | # `.c' file was seen then we are probably linking. That is also 69 | # ok. 70 | exec "$prog" $args 71 | fi 72 | 73 | # Name of file we expect compiler to create. 74 | cofile=`echo $cfile | sed -e 's|^.*/||' -e 's/\.c$/.o/'` 75 | 76 | # Create the lock directory. 77 | # Note: use `[/.-]' here to ensure that we don't use the same name 78 | # that we are using for the .o file. Also, base the name on the expected 79 | # object file name, since that is what matters with a parallel build. 80 | lockdir=`echo $cofile | sed -e 's|[/.-]|_|g'`.d 81 | while true; do 82 | if mkdir $lockdir > /dev/null 2>&1; then 83 | break 84 | fi 85 | sleep 1 86 | done 87 | # FIXME: race condition here if user kills between mkdir and trap. 88 | trap "rmdir $lockdir; exit 1" 1 2 15 89 | 90 | # Run the compile. 91 | "$prog" $args 92 | status=$? 93 | 94 | if test -f "$cofile"; then 95 | mv "$cofile" "$ofile" 96 | fi 97 | 98 | rmdir $lockdir 99 | exit $status 100 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | # This makefile will probably work out of the box for you, but if not, try 2 | # changing PTHREAD to be -pthread (possibly needed for FreeBSD), and/or CC 3 | # to be cc (possibly needed for Solaris). 4 | 5 | PORT = 9900 6 | LIBTOOL = ../libtool 7 | CC = gcc 8 | PTHREAD = -lpthread 9 | 10 | all: tftpd 11 | 12 | # This rule works both in the source tree, and stand-alone. In the latter case 13 | # it assumes that the libraries and headers are installed system-wide. 14 | tftpd: tftpd.c 15 | @if [ -r ../src/coredumper.c ]; then \ 16 | ( cd .. && { [ -r Makefile ] || ./configure; } && $(MAKE) ) &&\ 17 | echo $(CC) -o $@ -g -Wall -O2 -lcoredumper $(PTHREAD) \ 18 | tftpd.c && \ 19 | $(LIBTOOL) --mode=link $(CC) -o $@ -g -Wall -O2 -I../src -L.. \ 20 | -lcoredumper $(PTHREAD) $^ ../libcoredumper.la; \ 21 | else \ 22 | echo $(CC) -o $@ -g -Wall -O2 -lcoredumper $(PTHREAD) tftpd.c;\ 23 | $(CC) -o $@ -g -Wall -O2 -lcoredumper $(PTHREAD) tftpd.c; \ 24 | fi 25 | 26 | distclean: clean 27 | clean: 28 | @echo $(RM) -f tftpd core 29 | @if [ -r ../src/coredumper.c ]; then \ 30 | $(LIBTOOL) --mode=clean $(RM) -f tftpd core; \ 31 | else \ 32 | $(RM) -f tftpd core; \ 33 | fi 34 | 35 | check: tftpd 36 | @echo "Starting TFTP server on port $(PORT)" && \ 37 | rm -f core && set -m && \ 38 | { ./tftpd --port $(PORT) /dev/null 2>&1" EXIT && \ 40 | sleep 1 && \ 41 | echo "Downloading tftp://localhost:$(PORT)/core" && \ 42 | { echo; \ 43 | echo "connect localhost $(PORT)"; \ 44 | echo "binary"; \ 45 | echo "get core"; \ 46 | echo "quit"; } | tftp >/dev/null && \ 47 | kill -15 -$$pid >/dev/null 2>&1 && \ 48 | { wait $$pid; [ $$? -eq 143 ]; } && \ 49 | echo "Validating core file contents" && \ 50 | readelf -a core && \ 51 | echo "PASS" || \ 52 | echo "FAILED" 53 | -------------------------------------------------------------------------------- /examples/tftpd.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005, Google Inc. 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * --- 31 | * Author: Markus Gutschke 32 | * 33 | * This file demonstrates how to add a core dump interface to an existing 34 | * service. Typically, you would want to add the call to GetCoreDump() 35 | * to an existing interface exposed by your server. But if no such interface 36 | * exists, you could also adapt the TFTP code in this module to run as a 37 | * thread in your server. 38 | * 39 | * The code in this module does not perform any type of access control. 40 | * As corefiles can expose security sensitive data (e.g. passwords), you 41 | * would need to add appropriate access controls when using this (or similar) 42 | * code in a production environment. 43 | */ 44 | 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | 62 | #undef NO_THREADS /* Support only one connection at a time. */ 63 | #undef CAN_READ_FILES /* Supports reading files from "/tftpboot/..." */ 64 | #undef CAN_WRITE_FILES /* Supports updating files in "/tftpboot/..." */ 65 | #define CAN_READ_CORES /* Supports serving "core" snapshot files. */ 66 | 67 | #ifndef TFTPHDRSIZE 68 | #define TFTPHDRSIZE 4 69 | #endif 70 | 71 | /* The "Request" structure contains all the parameters that get passed 72 | * from the main server loop to the thread that is processing a given 73 | * TFTP connection. 74 | */ 75 | typedef struct Request { 76 | int debug; 77 | int id; 78 | int fd; 79 | struct tftphdr *tftp; 80 | char buf[TFTPHDRSIZE + SEGSIZE]; 81 | size_t count; 82 | struct sockaddr_in addr; 83 | socklen_t len; 84 | const char *core_name; 85 | char **dirs; 86 | int no_ack; 87 | int sanitize; 88 | } Request; 89 | 90 | #define DBG(...) \ 91 | do { \ 92 | if (debug) fprintf(stderr, __VA_ARGS__); \ 93 | } while (0) 94 | 95 | /* tftp_thread() runs in its own thread (unless NO_THREADS is defined). This 96 | * function processes a single TFTP connection. 97 | */ 98 | static void *tftp_thread(void *arg) { 99 | #define debug (request->debug) 100 | Request *request = (Request *)arg; 101 | struct tftphdr *tftp = request->tftp; 102 | int fd = -1; 103 | int src = -1; 104 | int err = EBADOP; 105 | int ioerr = 0; 106 | struct sockaddr_in addr; 107 | socklen_t len; 108 | char *raw_file_name, *mode, *msg, msg_buf[80]; 109 | char *file_name = NULL; 110 | 111 | /* Create a new socket for this connection. */ 112 | if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { 113 | perror("socket"); 114 | exit(1); 115 | } 116 | 117 | /* Connect this socket to the outgoing interface. */ 118 | len = sizeof(addr); 119 | if (getsockname(request->fd, (struct sockaddr *)&addr, &len) >= 0 && len >= sizeof(struct sockaddr_in)) { 120 | DBG("Responding to %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); 121 | addr.sin_port = 0; 122 | bind(fd, (struct sockaddr *)&addr, len); 123 | } 124 | 125 | /* Get file name and transfer mode from the incoming TFTP packet. */ 126 | if (!(raw_file_name = request->buf + 2, 127 | mode = memchr(raw_file_name, '\000', request->count - 1 - (raw_file_name - request->buf))) || 128 | !(++mode, memchr(mode, '\000', request->count - (mode - request->buf)))) { 129 | char *ptr; 130 | msg = "Truncated request"; 131 | 132 | error: 133 | /* Send error message back to client. */ 134 | DBG("%s\n", msg); 135 | ptr = strrchr(strcpy(tftp->th_msg, msg), '\000') + 1; 136 | tftp->th_opcode = htons(ERROR); 137 | tftp->th_code = htons(err); 138 | sendto(fd, tftp, ptr - request->buf, 0, (struct sockaddr *)&request->addr, request->len); 139 | goto done; 140 | } 141 | 142 | /* Only text and binary transfer modes are supported. */ 143 | if (strcasecmp(mode, "netascii") && strcasecmp(mode, "octet")) { 144 | msg = "Unsupported transfer mode"; 145 | goto error; 146 | } 147 | 148 | /* Check whether client requested a "core" snapshot of the running process.*/ 149 | if (!strcmp(raw_file_name, request->core_name)) { 150 | #ifdef CAN_READ_CORES 151 | /* Core files must be transferred in binary. */ 152 | if (strcasecmp(mode, "octet")) { 153 | err = EBADOP; 154 | msg = "Core files must be transferred in binary"; 155 | goto error; 156 | } 157 | 158 | /* Writing core files is not a supported operation. */ 159 | if (ntohs(tftp->th_opcode) == WRQ) { 160 | err = EBADOP; 161 | msg = "Core files cannot be written"; 162 | goto error; 163 | } 164 | 165 | /* Here we go. Create a snapshot of this process. */ 166 | src = GetCoreDump(); 167 | 168 | /* If we failed to created a core file, report error to the client. */ 169 | if (src < 0) { 170 | err = ENOTFOUND; 171 | *msg_buf = '\000'; 172 | msg = strerror_r(errno, msg_buf, sizeof(msg_buf)); 173 | goto error; 174 | } 175 | #else 176 | err = ENOTFOUND; 177 | msg = "Core file support is not enabled"; 178 | goto error; 179 | #endif 180 | } else { 181 | #if defined(CAN_READ_FILES) || defined(CAN_WRITE_FILES) 182 | /* TFTP is a very simple protocol, which does not support any user 183 | * authentication/authorization. So, we have to be very conservative 184 | * when accessing files. Unless overridden on the command line, this 185 | * server will only access files underneath the "/tftpboot" directory. 186 | * It only serves world-readable files, and it only allow writing to 187 | * world-writable files. 188 | */ 189 | static char *tftpdirs[] = {"/tftpboot", NULL}; 190 | char **dirs = tftpdirs; 191 | struct stat sb; 192 | 193 | /* Unless the user requested otherwise, restrict to "/tftpboot/..." */ 194 | if (*request->dirs) { 195 | dirs = request->dirs; 196 | } 197 | 198 | /* If "sanitize" option is set, prepend "/tftpboot" (or name of the first 199 | * source directory listed on the command line) to any absolute file 200 | * names. 201 | */ 202 | if (*raw_file_name == '/' && request->sanitize) { 203 | char *path = dirs[0]; 204 | strcat(strcat(strcpy(malloc(strlen(path) + strlen(raw_file_name) + 2), path), "/"), raw_file_name); 205 | 206 | } else { 207 | file_name = strdup(raw_file_name); 208 | } 209 | 210 | /* Check file attributes. */ 211 | memset(&sb, 0, sizeof(sb)); 212 | if (*file_name == '/') { 213 | int ok = 0; 214 | char **ptr; 215 | 216 | /* Search for file in all source directories (normally just 217 | * "/tftpboot") 218 | */ 219 | for (ptr = &dirs[0]; *ptr; ptr++) { 220 | if (!strncmp(*ptr, file_name, strlen(*ptr)) && stat(file_name, &sb) >= 0 && S_ISREG(sb.st_mode)) { 221 | ok++; 222 | break; 223 | } 224 | } 225 | if (!ok) { 226 | file_not_found: 227 | /* Only pre-existing files can be accessed. */ 228 | if (request->no_ack) 229 | goto done; 230 | else { 231 | err = ENOTFOUND; 232 | msg = "File not found"; 233 | goto error; 234 | } 235 | } 236 | } else { 237 | char **ptr, *absolute_file_name = NULL; 238 | 239 | /* Search for file in all source directories (normally just 240 | * "/tftpboot") 241 | */ 242 | for (ptr = &dirs[0]; *ptr; ptr++) { 243 | absolute_file_name = strcat(strcat(strcpy(malloc(strlen(*ptr) + strlen(file_name) + 2), *ptr), "/"), file_name); 244 | if (stat(absolute_file_name, &sb) >= 0 && S_ISREG(sb.st_mode)) break; 245 | free(absolute_file_name); 246 | absolute_file_name = NULL; 247 | } 248 | if (!absolute_file_name) goto file_not_found; 249 | free(file_name); 250 | file_name = absolute_file_name; 251 | } 252 | 253 | /* Check whether the necessary support for reading/writing is compiled 254 | * into this server, and whether the file is world-readable/writable. 255 | */ 256 | if (ntohs(tftp->th_opcode) == WRQ) { 257 | #ifdef CAN_WRITE_FILES 258 | if (!(sb.st_mode & S_IWOTH) || (src = open(file_name, O_WRONLY)) < 0) 259 | #endif 260 | { 261 | access_denied: 262 | err = EACCESS; 263 | msg = "Access denied"; 264 | goto error; 265 | } 266 | } else { 267 | #ifdef CAN_READ_FILES 268 | if (!(sb.st_mode & S_IROTH) || (src = open(file_name, O_RDONLY)) < 0) 269 | #endif 270 | goto access_denied; 271 | } 272 | #else 273 | err = ENOTFOUND; 274 | msg = "File operations are not enabled"; 275 | goto error; 276 | #endif 277 | } 278 | 279 | if (ntohs(tftp->th_opcode) == RRQ) { 280 | DBG("received RRQ <%s, %s>\n", raw_file_name, mode); 281 | #if defined(CAN_READ_FILES) || defined(CAN_READ_CORES) 282 | unsigned short block = 0; 283 | int count; 284 | 285 | /* Mainloop for serving files to clients. */ 286 | do { 287 | char buf[TFTPHDRSIZE + SEGSIZE]; 288 | struct tftphdr *send_tftp = (struct tftphdr *)buf; 289 | char *ptr = send_tftp->th_msg; 290 | int retry; 291 | 292 | /* Deal with partial reads, and reblock in units of 512 bytes. */ 293 | count = 0; 294 | while (!ioerr && count < SEGSIZE) { 295 | int rc = read(src, ptr + count, SEGSIZE - count); 296 | if (rc < 0) { 297 | if (errno == EINTR) continue; 298 | ioerr = errno; 299 | break; 300 | } else if (rc == 0) { 301 | break; 302 | } 303 | count += rc; 304 | } 305 | 306 | /* Report any read errors back to the client. */ 307 | if (count == 0 && ioerr) { 308 | err = ENOTFOUND; 309 | *msg_buf = '\000'; 310 | msg = strerror_r(ioerr, msg_buf, sizeof(msg_buf)); 311 | goto error; 312 | } 313 | send_tftp->th_opcode = htons(DATA); 314 | send_tftp->th_block = htons(++block); 315 | 316 | /* Transmit a single packet. Retry if necessary. */ 317 | retry = 10; 318 | for (;;) { 319 | int rc; 320 | 321 | /* Terminate entire transfers after too many retries. */ 322 | if (--retry < 0) goto done; 323 | 324 | /* Send one 512 byte packet. */ 325 | DBG("send DATA \n", block); 326 | if (sendto(fd, send_tftp, TFTPHDRSIZE + count, 0, (struct sockaddr *)&request->addr, request->len) < 0) { 327 | if (errno == EINTR) continue; 328 | goto done; 329 | } 330 | 331 | /* Wait for response from client. */ 332 | do { 333 | fd_set in_fds; 334 | struct timeval timeout; 335 | FD_ZERO(&in_fds); 336 | FD_SET(fd, &in_fds); 337 | timeout.tv_sec = 5; 338 | timeout.tv_usec = 0; 339 | rc = select(fd + 1, &in_fds, NULL, NULL, &timeout); 340 | } while (rc < 0 && errno == EINTR); 341 | 342 | /* If no response received, try sending payload again. */ 343 | if (rc == 0) continue; 344 | 345 | /* Receive actual response. */ 346 | rc = recv(fd, tftp, TFTPHDRSIZE + SEGSIZE, MSG_TRUNC); 347 | 348 | /* If operation failed, terminate entire transfer. */ 349 | if (rc < 0) { 350 | if (errno == EINTR) continue; 351 | goto done; 352 | } 353 | 354 | /* Done transmitting this block, after receiving matching ACK */ 355 | if (rc >= TFTPHDRSIZE) { 356 | switch (ntohs(tftp->th_opcode)) { 357 | case ACK: 358 | DBG("received ACK \n", ntohs(tftp->th_block)); 359 | break; 360 | case RRQ: 361 | DBG("received RRQ\n"); 362 | break; 363 | case WRQ: 364 | DBG("received WRQ\n"); 365 | break; 366 | case DATA: 367 | DBG("received DATA\n"); 368 | break; 369 | case ERROR: 370 | DBG("received ERROR\n"); 371 | break; 372 | default: 373 | DBG("unexpected data, op=%d\n", ntohs(tftp->th_opcode)); 374 | break; 375 | } 376 | } 377 | if (rc >= TFTPHDRSIZE && ntohs(tftp->th_opcode) == ACK && tftp->th_block == send_tftp->th_block) break; 378 | } 379 | } while (count); 380 | #endif 381 | } else { 382 | #ifdef CAN_WRITE_FILES 383 | /* TODO: Add support for writing files */ 384 | #endif 385 | } 386 | 387 | done: 388 | /* Clean up, close all file handles, and release memory */ 389 | if (fd >= 0) close(fd); 390 | if (src >= 0) close(src); 391 | if (file_name) free(file_name); 392 | free(request); 393 | return 0; 394 | #undef debug 395 | } 396 | 397 | /* This is a very basic TFTP server implementing RFC 1350, but none of the 398 | * optional protocol extensions (e.g. no block size negotiation, and no 399 | * multicasting). 400 | */ 401 | int main(int argc, char *argv[]) { 402 | static const struct option long_opts[] = {/* Set file name for "core" snapshot file of running process. */ 403 | {"core", 1, NULL, 'c'}, 404 | 405 | /* Enable debugging output. */ 406 | {"debug", 0, NULL, 'd'}, 407 | 408 | /* Print usage information for this server. */ 409 | {"help", 0, NULL, 'h'}, 410 | 411 | /* Suppress negative acknowledge for non-existant files. */ 412 | {"noack", 0, NULL, 'n'}, 413 | 414 | /* Set port number to listen on. */ 415 | {"port", 1, NULL, 'p'}, 416 | 417 | /* Sanitize requests for absolute filenames by prepending the name of the 418 | * first directory specified on the command line. If no directory is 419 | * given, prepend "/tftpboot/". 420 | */ 421 | {"sanitize", 0, NULL, 's'}, 422 | {NULL, 0, NULL, 0}}; 423 | static const char *opt_string = "c:dhnp:s"; 424 | const char *core_name = "core"; 425 | int debug = 0; 426 | int no_ack = 0; 427 | int port = -1; 428 | int sanitize = 0; 429 | char **dirs = NULL; 430 | int server_fd = 0; 431 | int id = 0; 432 | 433 | /* Parse command line options. */ 434 | for (;;) { 435 | int idx = 0; 436 | int c = getopt_long(argc, argv, opt_string, long_opts, &idx); 437 | if (c == -1) break; 438 | switch (c) { 439 | case 'c': 440 | core_name = optarg; 441 | break; 442 | case 'd': 443 | debug = 1; 444 | break; 445 | case 'n': 446 | no_ack = 1; 447 | break; 448 | case 'p': 449 | port = atoi(optarg); 450 | if (port <= 0 || port > 65535) { 451 | fprintf(stderr, "Port out of range: %d\n", port); 452 | exit(1); 453 | } 454 | break; 455 | case 's': 456 | sanitize = 1; 457 | break; 458 | case 'h': 459 | default: 460 | fprintf(stderr, 461 | "Usage: %s --core --debug --help --port --noack " 462 | "--sanitize\n", 463 | argv[0]); 464 | exit(c != 'h'); 465 | } 466 | } 467 | 468 | /* All remaining command line arguments (if any) list the directories of 469 | * files that this server can access. If no directories are given, then 470 | * only files in "/tftpboot/..." are accessible. 471 | */ 472 | dirs = argv + optind; 473 | 474 | /* If a port is given on the command line, then listen on. Otherwise, 475 | * assume that stdin is already connected to a server port. This allows 476 | * us to run the server from inetd. 477 | */ 478 | if (port >= 0) { 479 | struct sockaddr_in addr; 480 | 481 | server_fd = socket(PF_INET, SOCK_DGRAM, 0); 482 | if (server_fd < 0) { 483 | perror("socket"); 484 | exit(1); 485 | } 486 | memset(&addr, 0, sizeof(addr)); 487 | addr.sin_family = AF_INET; 488 | addr.sin_port = htons(port); 489 | addr.sin_addr.s_addr = INADDR_ANY; 490 | if (bind(server_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) < 0) { 491 | perror("bind"); 492 | exit(1); 493 | } 494 | } 495 | 496 | /* Server mainloop. Accept incoming connections and spawn threads to 497 | * serve the requests. 498 | */ 499 | for (;;) { 500 | const char *msg; 501 | char buf[TFTPHDRSIZE + SEGSIZE]; 502 | struct tftphdr *tftp = (struct tftphdr *)buf; 503 | struct sockaddr_in addr; 504 | socklen_t len; 505 | int count, type; 506 | Request *request; 507 | #ifndef NO_THREADS 508 | pthread_t thread; 509 | #endif 510 | 511 | /* Receive next request. */ 512 | len = sizeof(addr); 513 | count = recvfrom(server_fd, tftp, sizeof(buf), MSG_TRUNC, (struct sockaddr *)&addr, &len); 514 | if (count < 0) { 515 | if (errno == EINTR) continue; 516 | perror("recvfrom"); 517 | exit(1); 518 | } 519 | 520 | /* If request arrived from unsupported address, just ignore it. */ 521 | if (len < sizeof(struct sockaddr_in)) continue; 522 | 523 | /* If request was truncated, report error back to client. */ 524 | if (count < sizeof(tftp)) { 525 | char *ptr; 526 | msg = "Truncated request"; 527 | send_error: 528 | /* Send error message to client. */ 529 | DBG("%s\n", msg); 530 | ptr = strrchr(strcpy(tftp->th_msg, msg), '\000') + 1; 531 | tftp->th_opcode = htons(ERROR); 532 | tftp->th_code = htons(EBADOP); 533 | sendto(server_fd, tftp, ptr - buf, 0, (struct sockaddr *)&addr, len); 534 | continue; 535 | } 536 | 537 | /* Determine whether this was a read or write request. */ 538 | type = ntohs(tftp->th_opcode); 539 | if (type != RRQ && type != WRQ) { 540 | msg = "Request must be RRQ or WRQ"; 541 | goto send_error; 542 | } 543 | 544 | /* Build "Request" data structure with parameters describing connection. */ 545 | request = calloc(sizeof(Request), 1); 546 | request->debug = debug; 547 | request->id = id++; 548 | request->fd = server_fd; 549 | request->tftp = (struct tftphdr *)&request->buf; 550 | request->count = count; 551 | request->len = len; 552 | request->core_name = core_name; 553 | request->dirs = dirs; 554 | request->no_ack = no_ack; 555 | request->sanitize = sanitize; 556 | memcpy(&request->buf, buf, count); 557 | memcpy(&request->addr, &addr, len); 558 | 559 | /* Hand request off to its own thread. */ 560 | #ifdef NO_THREADS 561 | tftp_thread(request); 562 | #else 563 | pthread_create(&thread, NULL, tftp_thread, request); 564 | #endif 565 | } 566 | } 567 | -------------------------------------------------------------------------------- /include/coredumper/coredumper.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005-2008, Google Inc. 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * --- 31 | * Author: Markus Gutschke, Carl Crous 32 | * 33 | * Code to extract a core dump snapshot of the current process. 34 | */ 35 | #ifndef _COREDUMP_H 36 | #define _COREDUMP_H 37 | 38 | #include 39 | 40 | #ifdef __cplusplus 41 | extern "C" { 42 | #endif 43 | 44 | /* Description of external compressor programs to use for creating compressed 45 | * coredumps. There are a few predefined compressor descriptions; callers 46 | * can also define their own compressors. 47 | * All functions expect an array of compressors. Array entries will be tried 48 | * in sequence until an executable compressor has been found. An empty 49 | * c-string in place of the compressor name signals that no compression should 50 | * be performed. 51 | * The end of the array is signalled by an entry that is completely zero'd out. 52 | */ 53 | struct CoredumperCompressor { 54 | const char *compressor; /* File name of compressor; e.g. gzip */ 55 | const char *const *args; /* execv()-style command line arguments */ 56 | const char *suffix; /* Suffix that should be appended; e.g. .gz */ 57 | }; 58 | 59 | /* Description of a elf note for use in the PT_NOTES section of the core file. 60 | */ 61 | struct CoredumperNote { 62 | const char *name; /* The vendor name */ 63 | unsigned int type; /* A vendor specific type */ 64 | unsigned int description_size; /* The size of the description field */ 65 | const void *description; /* The note data */ 66 | }; 67 | 68 | /* Parameters used to control the core dumper. Future versions of this 69 | * structure must be backwards compatible so any new fields must be appended to 70 | * the end. 71 | */ 72 | struct CoreDumpParameters { 73 | /* The size of this structure. This is used to make sure future versions are 74 | * backwards compatible. 75 | */ 76 | size_t size; 77 | /* Specific settings for the core dumper. See COREDUMPER_FLAG_* */ 78 | int flags; 79 | /* The maximum file size for the core dump. */ 80 | size_t max_length; 81 | /* The set of compressors to choose from. */ 82 | const struct CoredumperCompressor *compressors; 83 | /* After dumping a compressed core, this will be set to the compressor which 84 | * was used to compress the core file. 85 | */ 86 | struct CoredumperCompressor **selected_compressor; 87 | /* Extra notes to write to the core file notes section. */ 88 | const struct CoredumperNote *notes; 89 | /* The amount of notes in the notes array. */ 90 | int note_count; 91 | /* Callback function */ 92 | int (*callback_fn)(void *); 93 | /* Callback argument */ 94 | void *callback_arg; 95 | }; 96 | 97 | /* The core file is limited in size and max_length denotes the maximum size. If 98 | * the core file exceeds this maximum, the file will be truncated. 99 | */ 100 | #define COREDUMPER_FLAG_LIMITED 1 101 | 102 | /* The core file is limited in size and max_length denotes the maximum size. If 103 | * the core file exceeds this maximum, the largest memory segments will be 104 | * reduced or removed first in order to preserve the smaller ones. 105 | */ 106 | #define COREDUMPER_FLAG_LIMITED_BY_PRIORITY 2 107 | 108 | /* Try compressing with either bzip2, gzip, or compress. If all of those fail, 109 | * fall back on generating an uncompressed file. 110 | */ 111 | extern const struct CoredumperCompressor COREDUMPER_COMPRESSED[]; 112 | 113 | /* Try compressing with a specific compressor. Fail if no compressor could 114 | * be found. 115 | */ 116 | extern const struct CoredumperCompressor COREDUMPER_BZIP2_COMPRESSED[]; 117 | extern const struct CoredumperCompressor COREDUMPER_GZIP_COMPRESSED[]; 118 | extern const struct CoredumperCompressor COREDUMPER_COMPRESS_COMPRESSED[]; 119 | 120 | /* Try compressing with a specific compressor. Fall back on generating an 121 | * uncompressed file, if the specified compressor is unavailable. 122 | */ 123 | extern const struct CoredumperCompressor COREDUMPER_TRY_BZIP2_COMPRESSED[]; 124 | extern const struct CoredumperCompressor COREDUMPER_TRY_GZIP_COMPRESSED[]; 125 | extern const struct CoredumperCompressor COREDUMPER_TRY_COMPRESS_COMPRESSED[]; 126 | 127 | /* Always create an uncompressed core file. 128 | */ 129 | extern const struct CoredumperCompressor COREDUMPER_UNCOMPRESSED[]; 130 | 131 | /* Returns a file handle that can be read to obtain a snapshot of the 132 | * current state of this process. If a core file could not be 133 | * generated for any reason, -1 is returned and "errno" will be set 134 | * appropriately. 135 | * 136 | * This function momentarily suspends all threads, while creating a 137 | * COW (copy-on-write) copy of the process's address space. 138 | * 139 | * This function is neither reentrant nor async signal safe. Callers 140 | * should wrap a mutex around the invocation of this function, if necessary. 141 | * 142 | * The current implementation tries very hard to behave reasonably when 143 | * called from a signal handler, but no guarantees are made that this will 144 | * always work. Most importantly, it is the caller's responsibility to 145 | * make sure that there are never more than one instance of GetCoreDump() 146 | * or WriteCoreDump() executing concurrently. 147 | */ 148 | int GetCoreDump(void); 149 | 150 | /* Gets a core dump with the given parameters. This is not compatible with any 151 | * core size limiting parameters. 152 | */ 153 | int GetCoreDumpWith(const struct CoreDumpParameters *params); 154 | 155 | /* Attempts to compress the core file on the fly, if a suitable compressor 156 | * could be located. Sets "selected_compressor" to the compressor that 157 | * was picked. 158 | */ 159 | int GetCompressedCoreDump(const struct CoredumperCompressor compressors[], 160 | struct CoredumperCompressor **selected_compressor); 161 | 162 | /* Writes the core file to disk. This is a convenience method wrapping 163 | * GetCoreDump(). If a core file could not be generated for any reason, 164 | * -1 is returned and errno is set appropriately. On success, zero is 165 | * returned. 166 | */ 167 | int WriteCoreDump(const char *file_name); 168 | 169 | /* Writes a core dump to the given file with the given parameters. */ 170 | int WriteCoreDumpWith(const struct CoreDumpParameters *params, const char *file_name); 171 | 172 | /* Callers might need to restrict the maximum size of the core file. This 173 | * convenience method provides the necessary support to emulate "ulimit -c". 174 | */ 175 | int WriteCoreDumpLimited(const char *file_name, size_t max_length); 176 | 177 | /* Writes a limited size core file, however instead of truncating the file at 178 | * the limit, the core dumper will prioritize smaller memory segments. This 179 | * means that a large heap will most likely either be only partially included 180 | * or not included at all. If the max_length is set too small, this could cause 181 | * performance issues. 182 | */ 183 | int WriteCoreDumpLimitedByPriority(const char *file_name, size_t max_length); 184 | 185 | /* Attempts to compress the core file on the fly, if a suitable compressor 186 | * could be located. Sets "selected_compressor" to the compressor that 187 | * was picked. The filename automatically has a suitable suffix appended 188 | * to it. Normally this would be ".bz2" for bzip2 compression ".gz" for 189 | * gzip compression, or ".Z" for compress compression. This behavior can 190 | * be changed by defining custom CoredumperCompressor descriptions. 191 | */ 192 | int WriteCompressedCoreDump(const char *file_name, size_t max_length, const struct CoredumperCompressor compressors[], 193 | struct CoredumperCompressor **selected_compressor); 194 | 195 | /* A convenience definition to clear core dump parameters. 196 | */ 197 | #define ClearCoreDumpParameters(p) ClearCoreDumpParametersInternal((p), sizeof(struct CoreDumpParameters)) 198 | 199 | /* Checks if the current version of the coredumper has a specific parameter. 200 | */ 201 | #define CoreDumpParametersHas(p, f) ((p)->size >= offsetof(struct CoreDumpParameters, f) + sizeof((p)->f)) 202 | 203 | /* Sets a coredumper parameter to a given value. This will abort the program if 204 | * the given parameter doesn't exist in the parameters. 205 | */ 206 | #define SetCoreDumpParameter(p, f, v) \ 207 | do { \ 208 | if (!CoreDumpParametersHas(p, f)) { \ 209 | abort(); \ 210 | } \ 211 | (p)->f = (v); \ 212 | } while (0) 213 | 214 | /* Gets a coredumper parameter. If the parameter doesn't exist, 0 is returned. 215 | */ 216 | #define GetCoreDumpParameter(p, f) (CoreDumpParametersHas(p, f) ? (p)->f : 0) 217 | 218 | /* Clears the given coredumper parameters to zero, sets the size parameter and 219 | * the max_length parameter to SIZE_MAX. 220 | */ 221 | void ClearCoreDumpParametersInternal(struct CoreDumpParameters *params, size_t size); 222 | 223 | /* Sets the coredumper parameters to provide a limited core dump. Returns 224 | * zero on success otherwise -1 will be returned and errno will be set. 225 | */ 226 | int SetCoreDumpLimited(struct CoreDumpParameters *params, size_t max_length); 227 | 228 | /* Sets the coredumper parameters to provide a compressed core dump. Returns 229 | * zero on success otherwise -1 will be returned and errno will be set. 230 | */ 231 | int SetCoreDumpCompressed(struct CoreDumpParameters *params, const struct CoredumperCompressor *compressors, 232 | struct CoredumperCompressor **selected_compressor); 233 | 234 | /* Sets the coredumper parameters to provide a prioritized limited core file. 235 | * Returns zero on success otherwise -1 will be returned and errno will be set. 236 | */ 237 | int SetCoreDumpLimitedByPriority(struct CoreDumpParameters *params, size_t max_length); 238 | 239 | /* Sets the coredumper parameters to add extra notes to the core file. 240 | * Returns zero on success otherwise -1 will be returned and errno will be set. 241 | */ 242 | int SetCoreDumpNotes(struct CoreDumpParameters *params, struct CoredumperNote *notes, int note_count); 243 | 244 | /* Sets the coredumper parameters to provide a callback function. 245 | * The callback will be invoked after all threads have been suspended but 246 | * before any coredump operation is invoked. The callback argument will be 247 | * provided. If the return value is 0 then the coredump will continue; if the 248 | * value is something other than 0 then the coredump will not be generated. 249 | */ 250 | int SetCoreDumpCallback(struct CoreDumpParameters *params, int (*fn)(void *), void *arg); 251 | 252 | #ifdef __cplusplus 253 | } 254 | #endif 255 | #endif /* _COREDUMP_H */ 256 | -------------------------------------------------------------------------------- /man/CoreDumpParameters.man: -------------------------------------------------------------------------------- 1 | '\" t 2 | .TH COREDUMPERPARAMETERS 3 "Mar 11, 2008" 3 | .SH NAME 4 | ClearCoreDumpParameters, SetCoreDumpLimited, SetCoreDumpCompressed, SetCoreDumpLimitedByPriority, SetCoreDumpNotes, SetCoreDumpCallback \- functions to initialize and set fields in a CoreDumpParameters structure. 5 | .SH SYNOPSIS 6 | .ad l 7 | .PP 8 | .B "#include \*(lqgoogle/coredumper.h\*(rq" 9 | .HP 30 10 | .BI void\~Clear\%Core\%Dump\%Parameters(struct\~CoreDump\%Parameters\ \:* params ); 11 | .HP 23 12 | .BI int\~Set\%Core\%Dump\%Limited(\:struct\~Core\%Dump\%Parameters\ \:* params ,\ size_t\~ \:max_length ); 13 | .HP 33 14 | .BI int\~Set\%Core\%Dump\%Limited\%By\%Priority(\:struct\~Core\%Dump\%Parameters\ \:* params ,\ size_t\ \: max_length ); 15 | .HP 26 16 | .BI int\~Set\%Core\%Dump\%Compressed(\:struct\~Core\%Dump\%Parameters\ * \:params ,\ const\~struct\~Coredump\%Compressor\ \:* compressors\ 17 | ,\ \:struct\~Coredumper\%Compressor\ \:** \ 18 | selected_compressor ); 19 | .HP 21 20 | .BI int\~Set\%Core\%Dump\%Notes(\:struct\~Core\%Dump\%Parameters\ \:* params ,\ \:struct\~Coredumper\%Note\~\:* notes ,\ \:int\~ \:note_count ); 21 | .HP 24 22 | .BI int\~Set\%Core\%Dump\%Callback(\:struct\~Core\%Dump\%Parameters\ \:* params ,\ \:int\~(* callback_fn )(void*),\ \:void*\~ \:callback_arg ); 23 | .ad b 24 | .SH DESCRIPTION 25 | These functions must be used to set the attributes in a CoreDumpParameters 26 | structure. Combinations of these functions may be used together. For instance 27 | to produce a compressed core dump with additional notes you should call 28 | \fBClearCoreDumpParameters\fP(), \fBSetCoreDumpCompressed\fP(), and 29 | \fBSetCoreDumpNotes\fP(). 30 | .PP 31 | The \fBClearCoreDumpParameters\fP() function clears the given 32 | core dumper parameters structure to its default values. 33 | .PP 34 | The \fBSetCoreDumpLimited\fP() function sets the attributes in 35 | .IR params 36 | to produce core files of at most 37 | .IR max_length 38 | bytes when called with \fBWriteCoreDumpWith\fP(). This must not be used with 39 | \fBSetCoreDumpLimitedByPriority\fP(). 40 | .PP 41 | The \fBSetCoreDumpLimitedByPriority\fP() function sets the attributes in 42 | .IR params 43 | to produce core files of at most 44 | .IR max_length 45 | bytes when called with \fBWriteCoreDumpWith\fP(). This differs from normal 46 | limiting by truncating the largest memory segments first as opposed to 47 | truncating the whole core file. This must not be used with 48 | \fBSetCoreDumpLimited\fP() or \fBSetCoreDumpCompressed\fP(). 49 | .PP 50 | The \fBSetCoreDumpCompressed\fP() function sets the attributes in 51 | .IR params 52 | to produce a compressed core dump when called with \fBGetCoreDumpWith\fP() or 53 | \fBWriteCoreDumpWith\fP(). Its arguments match the ones passed to 54 | \fBGetCompressedCoreDump\fP(). 55 | .PP 56 | The \fBSetCoreDumpNotes\fP() function sets the attributes in 57 | .IR params 58 | to add additional notes to core dumps when called with \fBGetCoreDumpWith\fP() 59 | or \fBWriteCoreDumpWith\fP(). The notes are specified as an array of the 60 | CoredumperNote structure: 61 | .sp 62 | .RS 63 | .nf 64 | struct CoredumperNote { 65 | const char *name; // The vendor name 66 | unsigned int type; // A vendor specific type 67 | unsigned int description_size; // The size of the description field 68 | const void *description; // The note data 69 | }; 70 | .fi 71 | .RE 72 | .PP 73 | The name, including a terminating null character, will be 4 byte aligned. The 74 | type is a user chosen value. The description_size specifies the amount of bytes 75 | to write from the description pointer in memory to the core dump note. The 76 | description will be padded to be 4 byte aligned. 77 | .PP 78 | The \fBSetCoreDumpCallback\fP() function sets the attributes in 79 | .IR params 80 | to add a callback function that will be invoked after all threads in the 81 | process have been suspended but before any actual core is dumped. The 82 | argument provided will be passed to the callback function and is otherwise 83 | unused by the coredumper library. 84 | .PP 85 | If the callback function returns 0, then the core will continue to be dumped. 86 | If the callback function returns non-0, then the core dump will be failed. 87 | .PP 88 | .SH RETURN VALUE 89 | .PP 90 | On success 0 will be returned. On error \-1 will be returned and 91 | .I errno 92 | will be set appropriately. 93 | .SH ERRORS 94 | The most common reason for an error is that incompatable parameters are combined. 95 | .SH "SEE ALSO" 96 | .BR GetCoreDump (3), 97 | .BR GetCoreDumpWith (3), 98 | .BR GetCompressedCoreDump(3), 99 | .BR WriteCoreDump (3), 100 | .BR WriteCoreDumpWith (3), 101 | .BR WriteCoreDumpLimited (3), 102 | .BR WriteCoreDumpLimitedByPriority (3), 103 | and 104 | .BR WriteCompressedCoreDump(3). 105 | -------------------------------------------------------------------------------- /man/GetCoreDump.man: -------------------------------------------------------------------------------- 1 | '\" t 2 | .TH GETCOREDUMP 3 "Feb 15, 2007" 3 | .SH NAME 4 | GetCoreDump, GetCompressedCoreDump, GetCoreDumpWith \- creates a copy-on-write snapshot 5 | of the current process 6 | .SH SYNOPSIS 7 | .ad l 8 | .PP 9 | .B "#include \*(lqgoogle/coredumper.h\*(rq" 10 | .PP 11 | .B int GetCoreDump(void); 12 | .PP 13 | .BI int\~Get\%Core\%Dump\%With(const\~struct\~Core\%Dump\%Parameters\~* params ); 14 | .HP 26 15 | .BI int\~Get\%Compressed\%Core\%Dump(const\~struct\~Coredumper\%Compressor\ \:\ 16 | compressors[] ,\ \:struct\~Coredumper\%Compressor\ \:** selected_compressor ); 17 | .ad b 18 | .SH DESCRIPTION 19 | The \fBGetCoreDump\fP() function returns a file handle that can be 20 | read to obtain a snapshot of the current state of the calling process. This 21 | function is a convenience wrapper for \fBGetCoreDumpWith\fP() using the default 22 | parameters. 23 | .PP 24 | The \fBGetCoreDumpWith\fP() function returns a file handle that can be read 25 | to obtain a snapshot of the current state of the calling process using the 26 | parameters specified in the \fBCoreDumpParameters\fP structure. The 27 | parameters can specify any behaviour of the core dumper however the limiting 28 | values in the parameter will be ignored in this type of core dump. 29 | .PP 30 | The \fBGetCompressedCoreDump\fP() function returns a file handle to a 31 | core file that has been compressed on the fly. This function is a convenience 32 | wrapper for \fBGetCoreDumpWith\fP(). In 33 | .IR compressor , 34 | the caller passes a pointer to an array of possible compressors: 35 | .sp 36 | .RS 37 | .nf 38 | struct CoredumperCompressor { 39 | const char *compressor; // File name of compressor; e.g. \*(lqgzip\*(rq 40 | const char *const *args; // execv()-style command line arguments 41 | const char *suffix; // File name suffix; e.g. \*(lq.gz\*(rq 42 | }; 43 | .fi 44 | .RE 45 | .PP 46 | The 47 | .I suffix 48 | will be ignored by the \fBGetCoreDump\fP() and 49 | \fBGetCompressedCoreDump\fP() functions, and is only needed for the 50 | \fBWriteCoreDump\fP() family of functions. 51 | .PP 52 | Array entries will be tried in sequence until an executable compressor 53 | has been found or the end of the array has been reached. The end is 54 | signalled by an entry that has been zero'd out completely. An empty 55 | string in place of the 56 | .I compressor 57 | name signals that no compression should be performed. 58 | .PP 59 | There are several pre-defined compressor descriptions available: 60 | .TP \w'COREDUMPER_'u 61 | .B COREDUMPER_COMPRESSED 62 | Try compressing with either 63 | .BR bzip2 (1), 64 | .BR gzip (1), 65 | or 66 | .BR compress (1). 67 | If all of those fail, fall back on generating an uncompressed image. 68 | .TP 69 | .B COREDUMPER_BZIP2_COMPRESSED 70 | .TP 71 | .B COREDUMPER_GZIP_COMPRESSED 72 | .TP 73 | .B COREDUMPER_COMPRESS_COMPRESSED 74 | Try compressing with a specific compressor. Fail if no compressor could 75 | be found. 76 | .TP 77 | .B COREDUMPER_TRY_BZIP2_COMPRESSED 78 | .TP 79 | .B COREDUMPER_TRY_GZIP_COMPRESSED 80 | .TP 81 | .B COREDUMPER_TRY_COMPRESS_COMPRESSED 82 | Try compressing with a specific compressor. Fall back on generating an 83 | uncompressed image, if the specified compressor is unavailable. 84 | .TP 85 | .B COREDUMPER_UNCOMPRESSED 86 | Always create an uncompressed core file. 87 | .PP 88 | If 89 | .I selected_compressor 90 | is non-NULL, it will be set to the actual 91 | .I CoredumperCompressor 92 | object used. 93 | .SH RETURN VALUE 94 | \fBGetCoreDump\fP(), \fBGetCoreDumpWith\fP(), and \fBGetCompressedCoreDump\fP() 95 | all return a non-seekable file handle on success. The copy-on-write snapshot 96 | will automatically be released, when the caller \fBclose\fP()s this file 97 | handle. 98 | .PP 99 | On error \-1 will be returned and 100 | .I errno 101 | will be set appropriately. 102 | .SH ERRORS 103 | The most common reason for failure is for another process to already 104 | use the debugging API that is needed to generate the core 105 | files. This could, for instance, be 106 | .BR gdb (1), 107 | or 108 | .BR strace(1). 109 | .SH NOTES 110 | The coredumper functions momentarily suspend all threads, while 111 | creating a COW (copy-on-write) copy of the process's address 112 | space. The snapshot shows up as a new child process of the current 113 | process, but memory requirements are relatively small, as most pages 114 | are shared between parent and child. 115 | .PP 116 | The functions are neither reentrant nor async signal safe. Callers 117 | should wrap a mutex around their invocation, if necessary. 118 | .PP 119 | The current implementation tries very hard to behave reasonably when 120 | called from a signal handler, but no guarantees are made that this 121 | will always work. Most importantly, it is the caller's responsibility 122 | to make sure that there are never more than one instance of functions 123 | from the \fBGetCoreDump\fP() or \fBWriteCoreDump\fP() family executing 124 | concurrently. 125 | .SH "SEE ALSO" 126 | .BR WriteCoreDump (3), 127 | .BR WriteCoreDumpWith (3), 128 | .BR WriteCoreDumpLimited (3), 129 | .BR WriteCoreDumpLimitedByPriority (3), 130 | .BR WriteCompressedCoreDump(3), 131 | and 132 | .BR CoreDumpParameters (3). 133 | -------------------------------------------------------------------------------- /man/WriteCoreDump.man: -------------------------------------------------------------------------------- 1 | '\" t 2 | .TH WRITECOREDUMP 3 "Feb 15, 2007" 3 | .SH NAME 4 | WriteCoreDump, WriteCoreDumpWith, WriteCoreDumpLimited, 5 | WriteCoreDumpLimitedByPriority, WriteCompressedCoreDump \- writes a snapshot of the 6 | current process 7 | .SH SYNOPSIS 8 | .ad l 9 | .PP 10 | .B "#include \*(lqgoogle/coredumper.h\*(rq" 11 | .PP 12 | .BI int\~WriteCoreDump(const\~char\~* file_name ); 13 | .PP 14 | .BI int\~WriteCoreDumpWith(const\~char\ \:* file_name ,\ \:const\~struct\~Core\%Dump\%Parameters\ \:* params ); 15 | .PP 16 | .BI int\~WriteCoreDumpLimited(const\~char\ \:* file_name ,\ \:size_t\~ \ 17 | \:max_length ); 18 | .PP 19 | .BI int\~WriteCoreDumpLimitedByPriority(const\~char\ \:* file_name ,\ \:size_t\~ \ 20 | \:max_length ); 21 | .HP 28 22 | .BI int\~Write\%Compressed\%Core\%Dump(const\~char\ \:* file_name \ 23 | ,\ \:size_t\ \: max_length ,\ \:const\~struct\~Coredumper\%Compressor\ \ 24 | \:compressors [],\ \:struct\~Coredumper\%Compressor\ \:** \ 25 | selected_compressor ); 26 | .PP 27 | .ad b 28 | .SH DESCRIPTION 29 | The \fBWriteCoreDump\fP() function writes a core file to 30 | .IR file_name . 31 | The calling process continues running after the file has been 32 | written. This is a convenience wrapper for \fBWriteCoreDumpWith\fP() using the 33 | default parameters. 34 | .PP 35 | The \fBWriteCoreDumpWith\fP() function writes a core file to 36 | .IR file_name 37 | using the parameters specified in the 38 | \fBCoreDumpParameters\fP() structure. This allows for various combinations of 39 | configurations. 40 | .PP 41 | The \fBWriteCoreDumpLimited\fP() function restricts the maximum length 42 | of the written file. This is similar to what setting calling 43 | .BR setrlimit (2) 44 | does for system-generated core files. This is a convenience wrapper for 45 | \fBWriteCoreDumpWith\fP(). 46 | .PP 47 | The \fBWriteCoreDumpLimitedByPriority\fP() function restricts the maximum length 48 | of the written file, however it does so by prioritizing smaller memory 49 | segments. This means that the core file is not truncated at the end of the 50 | file, as in 51 | \fBWriteCoreDumpLimited\fP(), but individual memory segments in the core file 52 | are truncated with the largest segments truncated first. This is a convenience 53 | wrapper for \fBWriteCoreDumpWith\fP(). 54 | .PP 55 | The \fBWriteCompressedCoreDump\fP() function attempts to compress the 56 | core file on the fly, if a suitable 57 | .I compressor 58 | could be located. 59 | .PP 60 | If 61 | .IR selected_compressor 62 | is non-NULL, it will be set to the actual 63 | .I CoredumperCompressor 64 | object used. 65 | .PP 66 | The 67 | .I filename 68 | automatically has a suitable suffix appended to it. Normally this 69 | would be \*(lq.bz2\*(rq for 70 | .BR bzip2 (1) 71 | compression, \*(lq.gz\*(rq for 72 | .BR gzip (1) 73 | compression, or \*(lq.Z\*(rq for 74 | .BR compress (1) 75 | compression. This behavior can be changed by defining custom 76 | .I CoredumperCompressor 77 | descriptions. This is a convenience wrapper for \fBWriteCoreDumpWith\fP(). See 78 | the manual page for 79 | .BR GetCoreDump (3) 80 | for more details. 81 | .SH RETURN VALUE 82 | \fBWriteCoreDump\fP(), \fBWriteCoreDumpWith\fP(), \fBWriteCoreDumpLimited\fP(), 83 | \fBWriteCoreDumpLimitedByPriority\fP(), and 84 | \fBWriteCompressedCoreDump\fP() return zero on success and \-1 on 85 | failure. In case of failure, 86 | .I errno 87 | will be set appropriately. 88 | .SH ERRORS 89 | The most common reason for failure is for another process to already 90 | use the debugging API that is needed to generate the core 91 | files. This could, for instance, be 92 | .BR gdb (1), 93 | or 94 | .BR strace(1). 95 | .SH NOTES 96 | The coredumper functions momentarily suspend all threads, while 97 | creating a COW (copy-on-write) copy of the process's address space. 98 | .PP 99 | The functions are neither reentrant nor async signal safe. Callers 100 | should wrap a mutex around their invocation, if necessary. 101 | .PP 102 | The current implementation tries very hard to behave reasonably when 103 | called from a signal handler, but no guarantees are made that this 104 | will always work. Most importantly, it is the caller's responsibility 105 | to make sure that there are never more than one instance of functions 106 | from the \fBGetCoreDump\fP() or \fBWriteCoreDump\fP() family executing 107 | concurrently. 108 | .SH "SEE ALSO" 109 | .BR GetCoreDump (3), 110 | .BR GetCoreDumpWith (3), 111 | .BR GetCompressedCoreDump(3), 112 | and 113 | .BR CoreDumpParameters (3). 114 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library( 3 | coredumper 4 | STATIC 5 | coredumper.c 6 | thread_lister.c 7 | elfcore.cc 8 | linuxthreads.cc 9 | ) 10 | 11 | target_include_directories( 12 | coredumper PUBLIC 13 | $ 14 | $ 15 | ) 16 | set_target_properties( 17 | coredumper PROPERTIES PUBLIC_HEADER 18 | "../include/coredumper/coredumper.h" 19 | ) 20 | 21 | target_link_libraries(coredumper ${CMAKE_THREAD_LIBS_INIT}) 22 | 23 | export(TARGETS coredumper FILE "coredumper.cmake") 24 | install( 25 | TARGETS coredumper 26 | EXPORT coredumper 27 | ARCHIVE DESTINATION lib 28 | PUBLIC_HEADER DESTINATION include/coredumper 29 | LIBRARY DESTINATION lib) 30 | install(EXPORT coredumper DESTINATION cmake) 31 | -------------------------------------------------------------------------------- /src/coredumper.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005-2008, Google Inc. 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * --- 31 | * Author: Markus Gutschke, Carl Crous 32 | * 33 | * Code to extract a core dump snapshot of the current process. 34 | */ 35 | #include "coredumper/coredumper.h" 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include "elfcore.h" 47 | #include "linux_syscall_support.h" 48 | #include "linuxthreads.h" 49 | #include "thread_lister.h" 50 | 51 | static const char *const no_args_bzip2[] = {"bzip2", NULL}; 52 | static const char *const no_args_gzip[] = {"gzip", NULL}; 53 | static const char *const no_args_compress[] = {"compress", NULL}; 54 | const struct CoredumperCompressor COREDUMPER_COMPRESSED[] = {{"/bin/bzip2", no_args_bzip2, ".bz2"}, 55 | {"/usr/bin/bzip2", no_args_bzip2, ".bz2"}, 56 | {"bzip2", no_args_bzip2, ".bz2"}, 57 | {"/bin/gzip", no_args_gzip, ".gz"}, 58 | {"/usr/bin/gzip", no_args_gzip, ".gz"}, 59 | {"gzip", no_args_gzip, ".gz"}, 60 | {"/bin/compress", no_args_compress, ".Z"}, 61 | {"/usr/bin/compress", no_args_compress, ".Z"}, 62 | {"compress", no_args_compress, ".Z"}, 63 | {"", 0, ""}, 64 | {0, 0, 0}}; 65 | const struct CoredumperCompressor COREDUMPER_BZIP2_COMPRESSED[] = {{"/bin/bzip2", no_args_bzip2, ".bz2"}, 66 | {"/usr/bin/bzip2", no_args_bzip2, ".bz2"}, 67 | {"bzip2", no_args_bzip2, ".bz2"}, 68 | {0, 0, 0}}; 69 | const struct CoredumperCompressor COREDUMPER_GZIP_COMPRESSED[] = {{"/bin/gzip", no_args_gzip, ".gz"}, 70 | {"/usr/bin/gzip", no_args_gzip, ".gz"}, 71 | {"gzip", no_args_gzip, ".gz"}, 72 | {0, 0, 0}}; 73 | const struct CoredumperCompressor COREDUMPER_COMPRESS_COMPRESSED[] = {{"/bin/compress", no_args_compress, ".Z"}, 74 | {"/usr/bin/compress", no_args_compress, ".Z"}, 75 | {"compress", no_args_compress, ".Z"}, 76 | {0, 0, 0}}; 77 | const struct CoredumperCompressor COREDUMPER_TRY_BZIP2_COMPRESSED[] = {{"/bin/bzip2", no_args_bzip2, ".bz2"}, 78 | {"/usr/bin/bzip2", no_args_bzip2, ".bz2"}, 79 | {"bzip2", no_args_bzip2, ".bz2"}, 80 | {"", 0, ""}, 81 | {0, 0, 0}}; 82 | const struct CoredumperCompressor COREDUMPER_TRY_GZIP_COMPRESSED[] = {{"/bin/gzip", no_args_gzip, ".gz"}, 83 | {"/usr/bin/gzip", no_args_gzip, ".gz"}, 84 | {"gzip", no_args_gzip, ".gz"}, 85 | {"", 0, ""}, 86 | {0, 0, 0}}; 87 | const struct CoredumperCompressor COREDUMPER_TRY_COMPRESS_COMPRESSED[] = {{"/bin/compress", no_args_compress, ".Z"}, 88 | {"/usr/bin/compress", no_args_compress, ".Z"}, 89 | {"compress", no_args_compress, ".Z"}, 90 | {"", 0, ""}, 91 | {0, 0, 0}}; 92 | const struct CoredumperCompressor COREDUMPER_UNCOMPRESSED[] = {{"", 0, ""}, {0, 0, 0}}; 93 | 94 | #ifndef DUMPER 95 | /* If the target platform lacks the necessary support for generating core dumps 96 | * on the fly, or if nobody has ported the code, then return an error. 97 | */ 98 | typedef void *Frame; 99 | #define FRAME(f) \ 100 | void *f = &&label; \ 101 | label: 102 | 103 | int InternalGetCoreDump(void *frame, int num_threads, pid_t *thread_pids, va_list ap) { 104 | errno = EINVAL; 105 | return -1; 106 | } 107 | #endif 108 | 109 | /* Internal helper method used by GetCoreDump(). 110 | */ 111 | static int GetCoreDumpFunction(void *frame, const struct CoreDumpParameters *params) { 112 | return ListAllProcessThreads(frame, InternalGetCoreDump, params, NULL, getenv("PATH")); 113 | } 114 | 115 | /* Returns a file handle that can be read to obtain a snapshot of the 116 | * current state of this process. If a core file could not be 117 | * generated for any reason, -1 is returned. 118 | * 119 | * This function momentarily suspends all threads, while creating a 120 | * COW copy of the process's address space. 121 | * 122 | * This function is neither reentrant nor async signal safe. Callers 123 | * should wrap a mutex around the invocation of this function, if necessary. 124 | * 125 | * The current implementation tries very hard to do behave reasonably when 126 | * called from a signal handler, but no guarantees are made that this will 127 | * always work. 128 | */ 129 | int GetCoreDump(void) { 130 | FRAME(frame); 131 | struct CoreDumpParameters params; 132 | ClearCoreDumpParameters(¶ms); 133 | return GetCoreDumpFunction(&frame, ¶ms); 134 | } 135 | 136 | int GetCoreDumpWith(const struct CoreDumpParameters *params) { 137 | FRAME(frame); 138 | if ((params->flags & COREDUMPER_FLAG_LIMITED) || (params->flags & COREDUMPER_FLAG_LIMITED_BY_PRIORITY)) { 139 | errno = EINVAL; 140 | return -1; 141 | } 142 | return GetCoreDumpFunction(&frame, params); 143 | } 144 | 145 | /* Attempts to compress the core file on the fly, if a suitable compressor 146 | * could be located. Sets "selected_compressor" to the compressor that 147 | * was picked. 148 | */ 149 | int GetCompressedCoreDump(const struct CoredumperCompressor compressors[], 150 | struct CoredumperCompressor **selected_compressor) { 151 | FRAME(frame); 152 | struct CoreDumpParameters params; 153 | ClearCoreDumpParameters(¶ms); 154 | SetCoreDumpCompressed(¶ms, compressors, selected_compressor); 155 | return GetCoreDumpFunction(&frame, ¶ms); 156 | } 157 | 158 | /* Re-runs fn until it doesn't cause EINTR. 159 | */ 160 | #define NO_INTR(fn) \ 161 | do { \ 162 | } while ((fn) < 0 && errno == EINTR) 163 | 164 | /* Internal helper method used by WriteCoreDump(). 165 | */ 166 | static int WriteCoreDumpFunction(void *frame, const struct CoreDumpParameters *params, const char *file_name) { 167 | return ListAllProcessThreads(frame, InternalGetCoreDump, params, file_name, getenv("PATH")); 168 | } 169 | 170 | /* Writes the core file to disk. This is a convenience method wrapping 171 | * GetCoreDump(). If a core file could not be generated for any reason, 172 | * -1 is returned. On success, zero is returned. 173 | */ 174 | int WriteCoreDump(const char *file_name) { 175 | FRAME(frame); 176 | struct CoreDumpParameters params; 177 | ClearCoreDumpParameters(¶ms); 178 | return WriteCoreDumpFunction(&frame, ¶ms, file_name); 179 | } 180 | 181 | int WriteCoreDumpWith(const struct CoreDumpParameters *params, const char *file_name) { 182 | FRAME(frame); 183 | return WriteCoreDumpFunction(&frame, params, file_name); 184 | } 185 | 186 | /* Callers might need to restrict the maximum size of the core file. This 187 | * convenience method provides the necessary support to emulate "ulimit -c". 188 | */ 189 | int WriteCoreDumpLimited(const char *file_name, size_t max_length) { 190 | FRAME(frame); 191 | struct CoreDumpParameters params; 192 | ClearCoreDumpParameters(¶ms); 193 | SetCoreDumpLimited(¶ms, max_length); 194 | return WriteCoreDumpFunction(&frame, ¶ms, file_name); 195 | } 196 | 197 | /* This will limit the size of the core file by reducing or removing the 198 | * largest memory segments first, effectively prioritizing the smaller memory 199 | * segments. This behavior is preferred when the process has a large heap and 200 | * you would like to preserve the relatively small stack. 201 | */ 202 | int WriteCoreDumpLimitedByPriority(const char *file_name, size_t max_length) { 203 | FRAME(frame); 204 | struct CoreDumpParameters params; 205 | ClearCoreDumpParameters(¶ms); 206 | SetCoreDumpLimitedByPriority(¶ms, max_length); 207 | return WriteCoreDumpFunction(&frame, ¶ms, file_name); 208 | } 209 | 210 | /* Attempts to compress the core file on the fly, if a suitable compressor 211 | * could be located. Sets "selected_compressor" to the compressor that 212 | * was picked. The filename automatically has a suitable suffix appended 213 | * to it. Normally this would be ".bz2" for bzip2 compression ".gz" for 214 | * gzip compression, or ".Z" for compress compression. This behavior can 215 | * be changed by defining custom CoredumperCompressor descriptions. 216 | */ 217 | int WriteCompressedCoreDump(const char *file_name, size_t max_length, const struct CoredumperCompressor compressors[], 218 | struct CoredumperCompressor **selected_compressor) { 219 | FRAME(frame); 220 | struct CoreDumpParameters params; 221 | ClearCoreDumpParameters(¶ms); 222 | SetCoreDumpCompressed(¶ms, compressors, selected_compressor); 223 | SetCoreDumpLimited(¶ms, max_length); 224 | return WriteCoreDumpFunction(&frame, ¶ms, file_name); 225 | } 226 | 227 | void ClearCoreDumpParametersInternal(struct CoreDumpParameters *params, size_t size) { 228 | memset(params, 0, size); 229 | params->size = size; 230 | SetCoreDumpParameter(params, max_length, SIZE_MAX); 231 | } 232 | 233 | int SetCoreDumpLimited(struct CoreDumpParameters *params, size_t max_length) { 234 | if (params->flags & COREDUMPER_FLAG_LIMITED_BY_PRIORITY) { 235 | errno = EINVAL; 236 | return -1; 237 | } 238 | params->flags |= COREDUMPER_FLAG_LIMITED; 239 | SetCoreDumpParameter(params, max_length, max_length); 240 | return 0; 241 | } 242 | 243 | int SetCoreDumpCompressed(struct CoreDumpParameters *params, const struct CoredumperCompressor *compressors, 244 | struct CoredumperCompressor **selected_compressor) { 245 | if (params->flags & COREDUMPER_FLAG_LIMITED_BY_PRIORITY) { 246 | errno = EINVAL; 247 | return -1; 248 | } 249 | SetCoreDumpParameter(params, compressors, compressors); 250 | SetCoreDumpParameter(params, selected_compressor, selected_compressor); 251 | return 0; 252 | } 253 | 254 | int SetCoreDumpLimitedByPriority(struct CoreDumpParameters *params, size_t max_length) { 255 | if (((params->flags & COREDUMPER_FLAG_LIMITED) && !(params->flags & COREDUMPER_FLAG_LIMITED_BY_PRIORITY)) || 256 | params->compressors != NULL) { 257 | errno = EINVAL; 258 | return -1; 259 | } 260 | SetCoreDumpParameter(params, flags, params->flags | COREDUMPER_FLAG_LIMITED | COREDUMPER_FLAG_LIMITED_BY_PRIORITY); 261 | SetCoreDumpParameter(params, max_length, max_length); 262 | return 0; 263 | } 264 | 265 | int SetCoreDumpNotes(struct CoreDumpParameters *params, struct CoredumperNote *notes, int note_count) { 266 | SetCoreDumpParameter(params, notes, notes); 267 | SetCoreDumpParameter(params, note_count, note_count); 268 | return 0; 269 | } 270 | 271 | int SetCoreDumpCallback(struct CoreDumpParameters *params, int (*fn)(void *), void *arg) { 272 | SetCoreDumpParameter(params, callback_fn, fn); 273 | SetCoreDumpParameter(params, callback_arg, arg); 274 | return 0; 275 | } 276 | -------------------------------------------------------------------------------- /src/elfcore.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005-2008, Google Inc. 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * --- 31 | * Author: Markus Gutschke, Carl Crous 32 | */ 33 | 34 | #ifndef _ELFCORE_H 35 | #define _ELFCORE_H 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | /* We currently only support x86-32, x86-64, ARM, and MIPS on Linux. 41 | * Porting to other related platforms should not be difficult. 42 | */ 43 | #if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__)) && defined(__linux) 44 | 45 | #include 46 | #include 47 | #include 48 | 49 | /* Define the DUMPER symbol to make sure that there is exactly one 50 | * core dumper built into the library. 51 | */ 52 | #define DUMPER "ELF" 53 | 54 | /* By the time that we get a chance to read CPU registers in the 55 | * calling thread, they are already in a not particularly useful 56 | * state. Besides, there will be multiple frames on the stack that are 57 | * just making the core file confusing. To fix this problem, we take a 58 | * snapshot of the frame pointer, stack pointer, and instruction 59 | * pointer at an earlier time, and then insert these values into the 60 | * core file. 61 | */ 62 | 63 | #if defined(__i386__) || defined(__x86_64__) 64 | typedef struct i386_regs { /* Normal (non-FPU) CPU registers */ 65 | #ifdef __x86_64__ 66 | #define BP rbp 67 | #define SP rsp 68 | #define IP rip 69 | uint64_t r15, r14, r13, r12, rbp, rbx, r11, r10; 70 | uint64_t r9, r8, rax, rcx, rdx, rsi, rdi, orig_rax; 71 | uint64_t rip, cs, eflags; 72 | uint64_t rsp, ss; 73 | uint64_t fs_base, gs_base; 74 | uint64_t ds, es, fs, gs; 75 | #else 76 | #define BP ebp 77 | #define SP esp 78 | #define IP eip 79 | uint32_t ebx, ecx, edx, esi, edi, ebp, eax; 80 | uint16_t ds, __ds, es, __es; 81 | uint16_t fs, __fs, gs, __gs; 82 | uint32_t orig_eax, eip; 83 | uint16_t cs, __cs; 84 | uint32_t eflags, esp; 85 | uint16_t ss, __ss; 86 | #endif 87 | } i386_regs; 88 | #elif defined(__ARM_ARCH_3__) 89 | typedef struct arm_regs { /* General purpose registers */ 90 | #define BP uregs[11] /* Frame pointer */ 91 | #define SP uregs[13] /* Stack pointer */ 92 | #define IP uregs[15] /* Program counter */ 93 | #define LR uregs[14] /* Link register */ 94 | long uregs[18]; 95 | } arm_regs; 96 | #elif defined(__mips__) 97 | typedef struct mips_regs { 98 | unsigned long pad[6]; /* Unused padding to match kernel structures */ 99 | unsigned long uregs[32]; /* General purpose registers. */ 100 | unsigned long hi; /* Used for multiplication and division. */ 101 | unsigned long lo; 102 | unsigned long cp0_epc; /* Program counter. */ 103 | unsigned long cp0_badvaddr; 104 | unsigned long cp0_status; 105 | unsigned long cp0_cause; 106 | unsigned long unused; 107 | } mips_regs; 108 | #endif 109 | 110 | #if defined(__i386__) && defined(__GNUC__) 111 | /* On x86 we provide an optimized version of the FRAME() macro, if the 112 | * compiler supports a GCC-style asm() directive. This results in somewhat 113 | * more accurate values for CPU registers. 114 | */ 115 | typedef struct Frame { 116 | struct i386_regs uregs; 117 | int errno_; 118 | pid_t tid; 119 | } Frame; 120 | #define FRAME(f) \ 121 | Frame f; \ 122 | do { \ 123 | f.errno_ = errno; \ 124 | f.tid = sys_gettid(); \ 125 | __asm__ volatile( \ 126 | "push %%ebp\n" \ 127 | "push %%ebx\n" \ 128 | "mov %%ebx,0(%%eax)\n" \ 129 | "mov %%ecx,4(%%eax)\n" \ 130 | "mov %%edx,8(%%eax)\n" \ 131 | "mov %%esi,12(%%eax)\n" \ 132 | "mov %%edi,16(%%eax)\n" \ 133 | "mov %%ebp,20(%%eax)\n" \ 134 | "mov %%eax,24(%%eax)\n" \ 135 | "mov %%ds,%%ebx\n" \ 136 | "mov %%ebx,28(%%eax)\n" \ 137 | "mov %%es,%%ebx\n" \ 138 | "mov %%ebx,32(%%eax)\n" \ 139 | "mov %%fs,%%ebx\n" \ 140 | "mov %%ebx,36(%%eax)\n" \ 141 | "mov %%gs,%%ebx\n" \ 142 | "mov %%ebx, 40(%%eax)\n" \ 143 | "call 0f\n" \ 144 | "0:pop %%ebx\n" \ 145 | "add $1f-0b,%%ebx\n" \ 146 | "mov %%ebx,48(%%eax)\n" \ 147 | "mov %%cs,%%ebx\n" \ 148 | "mov %%ebx,52(%%eax)\n" \ 149 | "pushf\n" \ 150 | "pop %%ebx\n" \ 151 | "mov %%ebx,56(%%eax)\n" \ 152 | "mov %%esp,%%ebx\n" \ 153 | "add $8,%%ebx\n" \ 154 | "mov %%ebx,60(%%eax)\n" \ 155 | "mov %%ss,%%ebx\n" \ 156 | "mov %%ebx,64(%%eax)\n" \ 157 | "pop %%ebx\n" \ 158 | "pop %%ebp\n" \ 159 | "1:" \ 160 | : \ 161 | : "a"(&f) \ 162 | : "memory"); \ 163 | } while (0) 164 | #define SET_FRAME(f, r) \ 165 | do { \ 166 | errno = (f).errno_; \ 167 | (r) = (f).uregs; \ 168 | } while (0) 169 | #elif defined(__x86_64__) && defined(__GNUC__) 170 | /* The FRAME and SET_FRAME macros for x86_64. */ 171 | typedef struct Frame { 172 | struct i386_regs uregs; 173 | int errno_; 174 | pid_t tid; 175 | } Frame; 176 | #define FRAME(f) \ 177 | Frame f; \ 178 | do { \ 179 | f.errno_ = errno; \ 180 | f.tid = sys_gettid(); \ 181 | __asm__ volatile( \ 182 | "push %%rbp\n" \ 183 | "push %%rbx\n" \ 184 | "mov %%r15,0(%%rax)\n" \ 185 | "mov %%r14,8(%%rax)\n" \ 186 | "mov %%r13,16(%%rax)\n" \ 187 | "mov %%r12,24(%%rax)\n" \ 188 | "mov %%rbp,32(%%rax)\n" \ 189 | "mov %%rbx,40(%%rax)\n" \ 190 | "mov %%r11,48(%%rax)\n" \ 191 | "mov %%r10,56(%%rax)\n" \ 192 | "mov %%r9,64(%%rax)\n" \ 193 | "mov %%r8,72(%%rax)\n" \ 194 | "mov %%rax,80(%%rax)\n" \ 195 | "mov %%rcx,88(%%rax)\n" \ 196 | "mov %%rdx,96(%%rax)\n" \ 197 | "mov %%rsi,104(%%rax)\n" \ 198 | "mov %%rdi,112(%%rax)\n" \ 199 | "mov %%ds,%%rbx\n" \ 200 | "mov %%rbx,184(%%rax)\n" \ 201 | "mov %%es,%%rbx\n" \ 202 | "mov %%rbx,192(%%rax)\n" \ 203 | "mov %%fs,%%rbx\n" \ 204 | "mov %%rbx,200(%%rax)\n" \ 205 | "mov %%gs,%%rbx\n" \ 206 | "mov %%rbx,208(%%rax)\n" \ 207 | "call 0f\n" \ 208 | "0:pop %%rbx\n" \ 209 | "add $1f-0b,%%rbx\n" \ 210 | "mov %%rbx,128(%%rax)\n" \ 211 | "mov %%cs,%%rbx\n" \ 212 | "mov %%rbx,136(%%rax)\n" \ 213 | "pushf\n" \ 214 | "pop %%rbx\n" \ 215 | "mov %%rbx,144(%%rax)\n" \ 216 | "mov %%rsp,%%rbx\n" \ 217 | "add $16,%%ebx\n" \ 218 | "mov %%rbx,152(%%rax)\n" \ 219 | "mov %%ss,%%rbx\n" \ 220 | "mov %%rbx,160(%%rax)\n" \ 221 | "pop %%rbx\n" \ 222 | "pop %%rbp\n" \ 223 | "1:" \ 224 | : \ 225 | : "a"(&f) \ 226 | : "memory"); \ 227 | } while (0) 228 | #define SET_FRAME(f, r) \ 229 | do { \ 230 | errno = (f).errno_; \ 231 | (f).uregs.fs_base = (r).fs_base; \ 232 | (f).uregs.gs_base = (r).gs_base; \ 233 | (r) = (f).uregs; \ 234 | } while (0) 235 | #elif defined(__ARM_ARCH_3__) && defined(__GNUC__) 236 | /* ARM calling conventions are a little more tricky. A little assembly 237 | * helps in obtaining an accurate snapshot of all registers. 238 | */ 239 | typedef struct Frame { 240 | struct arm_regs arm; 241 | int errno_; 242 | pid_t tid; 243 | } Frame; 244 | #define FRAME(f) \ 245 | Frame f; \ 246 | do { \ 247 | long cpsr; \ 248 | f.errno_ = errno; \ 249 | f.tid = sys_gettid(); \ 250 | __asm__ volatile("stmia %0, {r0-r15}\n" /* All integer regs */ \ 251 | : \ 252 | : "r"(&f.arm) \ 253 | : "memory"); \ 254 | f.arm.uregs[16] = 0; \ 255 | __asm__ volatile("mrs %0, cpsr\n" /* Condition code reg */ \ 256 | : "=r"(cpsr)); \ 257 | f.arm.uregs[17] = cpsr; \ 258 | } while (0) 259 | #define SET_FRAME(f, r) \ 260 | do { \ 261 | /* Don't override the FPU status register. */ \ 262 | /* Use the value obtained from ptrace(). This*/ \ 263 | /* works, because our code does not perform */ \ 264 | /* any FPU operations, itself. */ \ 265 | long fps = (f).arm.uregs[16]; \ 266 | errno = (f).errno_; \ 267 | (r) = (f).arm; \ 268 | (r).uregs[16] = fps; \ 269 | } while (0) 270 | #elif defined(__mips__) && defined(__GNUC__) 271 | typedef struct Frame { 272 | struct mips_regs mips_regs; 273 | int errno_; 274 | pid_t tid; 275 | } Frame; 276 | #define MIPSREG(n) \ 277 | ({ \ 278 | register unsigned long r __asm__("$" #n); \ 279 | r; \ 280 | }) 281 | #define FRAME(f) \ 282 | Frame f = {0}; \ 283 | do { \ 284 | unsigned long hi, lo; \ 285 | register unsigned long pc __asm__("$31"); \ 286 | f.mips_regs.uregs[0] = MIPSREG(0); \ 287 | f.mips_regs.uregs[1] = MIPSREG(1); \ 288 | f.mips_regs.uregs[2] = MIPSREG(2); \ 289 | f.mips_regs.uregs[3] = MIPSREG(3); \ 290 | f.mips_regs.uregs[4] = MIPSREG(4); \ 291 | f.mips_regs.uregs[5] = MIPSREG(5); \ 292 | f.mips_regs.uregs[6] = MIPSREG(6); \ 293 | f.mips_regs.uregs[7] = MIPSREG(7); \ 294 | f.mips_regs.uregs[8] = MIPSREG(8); \ 295 | f.mips_regs.uregs[9] = MIPSREG(9); \ 296 | f.mips_regs.uregs[10] = MIPSREG(10); \ 297 | f.mips_regs.uregs[11] = MIPSREG(11); \ 298 | f.mips_regs.uregs[12] = MIPSREG(12); \ 299 | f.mips_regs.uregs[13] = MIPSREG(13); \ 300 | f.mips_regs.uregs[14] = MIPSREG(14); \ 301 | f.mips_regs.uregs[15] = MIPSREG(15); \ 302 | f.mips_regs.uregs[16] = MIPSREG(16); \ 303 | f.mips_regs.uregs[17] = MIPSREG(17); \ 304 | f.mips_regs.uregs[18] = MIPSREG(18); \ 305 | f.mips_regs.uregs[19] = MIPSREG(19); \ 306 | f.mips_regs.uregs[20] = MIPSREG(20); \ 307 | f.mips_regs.uregs[21] = MIPSREG(21); \ 308 | f.mips_regs.uregs[22] = MIPSREG(22); \ 309 | f.mips_regs.uregs[23] = MIPSREG(23); \ 310 | f.mips_regs.uregs[24] = MIPSREG(24); \ 311 | f.mips_regs.uregs[25] = MIPSREG(25); \ 312 | f.mips_regs.uregs[26] = MIPSREG(26); \ 313 | f.mips_regs.uregs[27] = MIPSREG(27); \ 314 | f.mips_regs.uregs[28] = MIPSREG(28); \ 315 | f.mips_regs.uregs[29] = MIPSREG(29); \ 316 | f.mips_regs.uregs[30] = MIPSREG(30); \ 317 | f.mips_regs.uregs[31] = MIPSREG(31); \ 318 | __asm__ volatile("mfhi %0" : "=r"(hi)); \ 319 | __asm__ volatile("mflo %0" : "=r"(lo)); \ 320 | __asm__ volatile("jal 1f; 1:nop" : "=r"(pc)); \ 321 | f.mips_regs.hi = hi; \ 322 | f.mips_regs.lo = lo; \ 323 | f.mips_regs.cp0_epc = pc; \ 324 | f.errno_ = errno; \ 325 | f.tid = sys_gettid(); \ 326 | } while (0) 327 | #define SET_FRAME(f, r) \ 328 | do { \ 329 | errno = (f).errno_; \ 330 | memcpy((r).uregs, (f).mips_regs.uregs, 32 * sizeof(unsigned long)); \ 331 | (r).hi = (f).mips_regs.hi; \ 332 | (r).lo = (f).mips_regs.lo; \ 333 | (r).cp0_epc = (f).mips_regs.cp0_epc; \ 334 | } while (0) 335 | #else 336 | /* If we do not have a hand-optimized assembly version of the FRAME() 337 | * macro, we cannot reliably unroll the stack. So, we show a few additional 338 | * stack frames for the coredumper. 339 | */ 340 | typedef struct Frame { 341 | pid_t tid; 342 | } Frame; 343 | #define FRAME(f) \ 344 | Frame f; \ 345 | do { \ 346 | f.tid = sys_gettid(); \ 347 | } while (0) 348 | #define SET_FRAME(f, r) \ 349 | do { \ 350 | } while (0) 351 | #endif 352 | 353 | /* Internal function for generating a core file. This API can change without 354 | * notice and is only supposed to be used internally by the core dumper. 355 | * 356 | * This function works for both single- and multi-threaded core 357 | * dumps. If called as 358 | * 359 | * FRAME(frame); 360 | * InternalGetCoreDump(&frame, 0, NULL, ap); 361 | * 362 | * it creates a core file that only contains information about the 363 | * calling thread. 364 | * 365 | * Optionally, the caller can provide information about other threads 366 | * by passing their process ids in "thread_pids". The process id of 367 | * the caller should not be included in this array. All of the threads 368 | * must have been attached to with ptrace(), prior to calling this 369 | * function. They will be detached when "InternalGetCoreDump()" returns. 370 | * 371 | * This function either returns a file handle that can be read for obtaining 372 | * a core dump, or "-1" in case of an error. In the latter case, "errno" 373 | * will be set appropriately. 374 | * 375 | * While "InternalGetCoreDump()" is not technically async signal safe, you 376 | * might be tempted to invoke it from a signal handler. The code goes to 377 | * great lengths to make a best effort that this will actually work. But in 378 | * any case, you must make sure that you preserve the value of "errno" 379 | * yourself. It is guaranteed to be clobbered otherwise. 380 | * 381 | * Also, "InternalGetCoreDump" is not strictly speaking re-entrant. Again, 382 | * it makes a best effort to behave reasonably when called in a multi- 383 | * threaded environment, but it is ultimately the caller's responsibility 384 | * to provide locking. 385 | */ 386 | int InternalGetCoreDump(void *frame, int num_threads, pid_t *thread_pids, 387 | va_list ap 388 | /* const struct CoreDumpParameters *params, 389 | const char *file_name, 390 | const char *PATH 391 | */); 392 | 393 | #endif 394 | 395 | #ifdef __cplusplus 396 | } 397 | #endif 398 | #endif /* _ELFCORE_H */ 399 | -------------------------------------------------------------------------------- /src/libcoredumper.sym: -------------------------------------------------------------------------------- 1 | COREDUMPER_COMPRESSED 2 | COREDUMPER_BZIP2_COMPRESSED 3 | COREDUMPER_GZIP_COMPRESSED 4 | COREDUMPER_COMPRESS_COMPRESSED 5 | COREDUMPER_TRY_BZIP2_COMPRESSED 6 | COREDUMPER_TRY_GZIP_COMPRESSED 7 | COREDUMPER_TRY_COMPRESS_COMPRESSED 8 | COREDUMPER_UNCOMPRESSED 9 | GetCoreDump 10 | GetCompressedCoreDump 11 | GetCoreDumpWith 12 | WriteCoreDump 13 | WriteCoreDumpWith 14 | WriteCoreDumpLimited 15 | WriteCoreDumpLimitedByPriority 16 | WriteCompressedCoreDump 17 | ClearCoreDumpParametersInternal 18 | SetCoreDumpCompressed 19 | SetCoreDumpLimited 20 | SetCoreDumpLimitedByPriority 21 | SetCoreDumpNotes 22 | SetCoreDumpCallback 23 | -------------------------------------------------------------------------------- /src/linuxthreads.cc: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005-2007, Google Inc. 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * --- 31 | * Author: Markus Gutschke 32 | */ 33 | 34 | #include "linuxthreads.h" 35 | 36 | #ifdef THREADS 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | #include "linux_syscall_support.h" 53 | #include "thread_lister.h" 54 | 55 | #ifndef CLONE_UNTRACED 56 | #define CLONE_UNTRACED 0x00800000 57 | #endif 58 | 59 | /* Synchronous signals that should not be blocked while in the lister thread. 60 | */ 61 | static const int sync_signals[] = {SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGXCPU, SIGXFSZ}; 62 | 63 | /* itoa() is not a standard function, and we cannot safely call printf() 64 | * after suspending threads. So, we just implement our own copy. A 65 | * recursive approach is the easiest here. 66 | */ 67 | static char *local_itoa(char *buf, int i) { 68 | if (i < 0) { 69 | *buf++ = '-'; 70 | return local_itoa(buf, -i); 71 | } else { 72 | if (i >= 10) buf = local_itoa(buf, i / 10); 73 | *buf++ = (i % 10) + '0'; 74 | *buf = '\000'; 75 | return buf; 76 | } 77 | } 78 | 79 | /* Wrapper around clone() that runs "fn" on the same stack as the 80 | * caller! Unlike fork(), the cloned thread shares the same address space. 81 | * The caller must be careful to use only minimal amounts of stack until 82 | * the cloned thread has returned. 83 | * There is a good chance that the cloned thread and the caller will share 84 | * the same copy of errno! 85 | */ 86 | #ifdef __GNUC__ 87 | #if __GNUC__ == 3 && __GNUC_MINOR__ >= 1 || __GNUC__ > 3 88 | /* Try to force this function into a separate stack frame, and make sure 89 | * that arguments are passed on the stack. 90 | */ 91 | static int local_clone(int (*fn)(void *), void *arg, ...) __attribute__((noinline)); 92 | #endif 93 | #endif 94 | 95 | static int local_clone(int (*fn)(void *), void *arg, ...) { 96 | /* Leave 4kB of gap between the callers stack and the new clone. This 97 | * should be more than sufficient for the caller to call waitpid() until 98 | * the cloned thread terminates. 99 | * 100 | * It is important that we set the CLONE_UNTRACED flag, because newer 101 | * versions of "gdb" otherwise attempt to attach to our thread, and will 102 | * attempt to reap its status codes. This subsequently results in the 103 | * caller hanging indefinitely in waitpid(), waiting for a change in 104 | * status that will never happen. By setting the CLONE_UNTRACED flag, we 105 | * prevent "gdb" from stealing events, but we still expect the thread 106 | * lister to fail, because it cannot PTRACE_ATTACH to the process that 107 | * is being debugged. This is OK and the error code will be reported 108 | * correctly. 109 | */ 110 | return sys_clone(fn, (char *)&arg - 4096, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED, arg, 0, 0, 0); 111 | } 112 | 113 | /* Local substitute for the atoi() function, which is not necessarily safe 114 | * to call once threads are suspended (depending on whether libc looks up 115 | * locale information, when executing atoi()). 116 | */ 117 | static int local_atoi(const char *s) { 118 | int n = 0; 119 | int neg = *s == '-'; 120 | if (neg) s++; 121 | while (*s >= '0' && *s <= '9') n = 10 * n + (*s++ - '0'); 122 | return neg ? -n : n; 123 | } 124 | 125 | /* Re-runs fn until it doesn't cause EINTR 126 | */ 127 | #define NO_INTR(fn) \ 128 | do { \ 129 | } while ((fn) < 0 && errno == EINTR) 130 | 131 | /* Wrap a class around system calls, in order to give us access to 132 | * a private copy of errno. This only works in C++, but it has the 133 | * advantage of not needing nested functions, which are a non-standard 134 | * language extension. 135 | */ 136 | #ifdef __cplusplus 137 | namespace { 138 | class SysCalls { 139 | public: 140 | #define SYS_CPLUSPLUS 141 | #define SYS_ERRNO my_errno 142 | #define SYS_INLINE inline 143 | #define SYS_PREFIX -1 144 | #undef SYS_LINUX_SYSCALL_SUPPORT_H 145 | #include "linux_syscall_support.h" 146 | SysCalls() : my_errno(0) {} 147 | int my_errno; 148 | }; 149 | } // namespace 150 | #define ERRNO sys.my_errno 151 | #else 152 | #define ERRNO my_errno 153 | #endif 154 | 155 | /* Wrapper for open() which is guaranteed to never return EINTR. 156 | */ 157 | static int c_open(const char *fname, int flags, int mode) { 158 | ssize_t rc; 159 | NO_INTR(rc = sys_open(fname, flags, mode)); 160 | return rc; 161 | } 162 | 163 | /* abort() is not safely reentrant, and changes it's behavior each time 164 | * it is called. This means, if the main application ever called abort() 165 | * we cannot safely call it again. This would happen if we were called 166 | * from a SIGABRT signal handler in the main application. So, document 167 | * that calling SIGABRT from the thread lister makes it not signal safe 168 | * (and vice-versa). 169 | * Also, since we share address space with the main application, we 170 | * cannot call abort() from the callback and expect the main application 171 | * to behave correctly afterwards. In fact, the only thing we can do, is 172 | * to terminate the main application with extreme prejudice (aka 173 | * PTRACE_KILL). 174 | * We set up our own SIGABRT handler to do this. 175 | * In order to find the main application from the signal handler, we 176 | * need to store information about it in global variables. This is 177 | * safe, because the main application should be suspended at this 178 | * time. If the callback ever called ResumeAllProcessThreads(), then 179 | * we are running a higher risk, though. So, try to avoid calling 180 | * abort() after calling ResumeAllProcessThreads. 181 | */ 182 | static volatile int *sig_pids, sig_num_threads, sig_proc, sig_marker; 183 | 184 | /* Signal handler to help us recover from dying while we are attached to 185 | * other threads. 186 | */ 187 | static void SignalHandler(int signum, siginfo_t *si, void *data) { 188 | if (sig_pids != NULL) { 189 | if (signum == SIGABRT) { 190 | while (sig_num_threads-- > 0) { 191 | /* Not sure if sched_yield is really necessary here, but it does not */ 192 | /* hurt, and it might be necessary for the same reasons that we have */ 193 | /* to do so in sys_ptrace_detach(). */ 194 | sys_sched_yield(); 195 | sys_ptrace(PTRACE_KILL, sig_pids[sig_num_threads], 0, 0); 196 | } 197 | } else if (sig_num_threads > 0) { 198 | ResumeAllProcessThreads(sig_num_threads, (int *)sig_pids); 199 | } 200 | } 201 | sig_pids = NULL; 202 | if (sig_marker >= 0) NO_INTR(sys_close(sig_marker)); 203 | sig_marker = -1; 204 | if (sig_proc >= 0) NO_INTR(sys_close(sig_proc)); 205 | sig_proc = -1; 206 | 207 | sys__exit(signum == SIGABRT ? 1 : 2); 208 | } 209 | 210 | /* Try to dirty the stack, and hope that the compiler is not smart enough 211 | * to optimize this function away. Or worse, the compiler could inline the 212 | * function and permanently allocate the data on the stack. 213 | */ 214 | static void DirtyStack(size_t amount) { 215 | char buf[amount]; 216 | memset(buf, 0, amount); 217 | sys_read(-1, buf, amount); 218 | } 219 | 220 | /* Data structure for passing arguments to the lister thread. 221 | */ 222 | #define ALT_STACKSIZE (MINSIGSTKSZ + 4096) 223 | 224 | struct ListerParams { 225 | int result, err; 226 | char *altstack_mem; 227 | ListAllProcessThreadsCallBack callback; 228 | void *parameter; 229 | va_list ap; 230 | }; 231 | 232 | static void ListerThread(struct ListerParams *args) { 233 | int found_parent = 0; 234 | pid_t clone_pid = sys_gettid(), ppid = sys_getppid(); 235 | char proc_self_task[80], marker_name[48], *marker_path; 236 | const char *proc_paths[3]; 237 | const char *const *proc_path = proc_paths; 238 | int proc = -1, marker = -1, num_threads = 0; 239 | int max_threads = 0, sig; 240 | struct kernel_stat marker_sb, proc_sb; 241 | stack_t altstack; 242 | 243 | /* Create "marker" that we can use to detect threads sharing the same 244 | * address space and the same file handles. By setting the FD_CLOEXEC flag 245 | * we minimize the risk of misidentifying child processes as threads; 246 | * and since there is still a race condition, we will filter those out 247 | * later, anyway. 248 | */ 249 | if ((marker = sys_socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0 || sys_fcntl(marker, F_SETFD, FD_CLOEXEC) < 0) { 250 | failure: 251 | args->result = -1; 252 | args->err = errno; 253 | if (marker >= 0) NO_INTR(sys_close(marker)); 254 | sig_marker = marker = -1; 255 | if (proc >= 0) NO_INTR(sys_close(proc)); 256 | sig_proc = proc = -1; 257 | sys__exit(1); 258 | } 259 | 260 | /* Compute search paths for finding thread directories in /proc */ 261 | local_itoa(strrchr(strcpy(proc_self_task, "/proc/"), '\000'), ppid); 262 | strcpy(marker_name, proc_self_task); 263 | marker_path = marker_name + strlen(marker_name); 264 | strcat(proc_self_task, "/task/"); 265 | proc_paths[0] = proc_self_task; /* /proc/$$/task/ */ 266 | proc_paths[1] = "/proc/"; /* /proc/ */ 267 | proc_paths[2] = NULL; 268 | 269 | /* Compute path for marker socket in /proc */ 270 | local_itoa(strcpy(marker_path, "/fd/") + 4, marker); 271 | if (sys_stat(marker_name, &marker_sb) < 0) { 272 | goto failure; 273 | } 274 | 275 | /* Catch signals on an alternate pre-allocated stack. This way, we can 276 | * safely execute the signal handler even if we ran out of memory. 277 | */ 278 | memset(&altstack, 0, sizeof(altstack)); 279 | altstack.ss_sp = args->altstack_mem; 280 | altstack.ss_flags = 0; 281 | altstack.ss_size = ALT_STACKSIZE; 282 | sys_sigaltstack(&altstack, (const stack_t *)NULL); 283 | 284 | /* Some kernels forget to wake up traced processes, when the 285 | * tracer dies. So, intercept synchronous signals and make sure 286 | * that we wake up our tracees before dying. It is the caller's 287 | * responsibility to ensure that asynchronous signals do not 288 | * interfere with this function. 289 | */ 290 | sig_marker = marker; 291 | sig_proc = -1; 292 | for (sig = 0; sig < sizeof(sync_signals) / sizeof(*sync_signals); sig++) { 293 | struct kernel_sigaction sa; 294 | memset(&sa, 0, sizeof(sa)); 295 | sa.sa_sigaction_ = SignalHandler; 296 | sys_sigfillset(&sa.sa_mask); 297 | sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESETHAND; 298 | sys_sigaction(sync_signals[sig], &sa, (struct kernel_sigaction *)NULL); 299 | } 300 | 301 | /* Read process directories in /proc/... */ 302 | for (;;) { 303 | /* Some kernels know about threads, and hide them in "/proc" 304 | * (although they are still there, if you know the process 305 | * id). Threads are moved into a separate "task" directory. We 306 | * check there first, and then fall back on the older naming 307 | * convention if necessary. 308 | */ 309 | if ((sig_proc = proc = c_open(*proc_path, O_RDONLY | O_DIRECTORY, 0)) < 0) { 310 | if (*++proc_path != NULL) continue; 311 | goto failure; 312 | } 313 | if (sys_fstat(proc, &proc_sb) < 0) goto failure; 314 | 315 | /* Since we are suspending threads, we cannot call any libc 316 | * functions that might acquire locks. Most notably, we cannot 317 | * call malloc(). So, we have to allocate memory on the stack, 318 | * instead. Since we do not know how much memory we need, we 319 | * make a best guess. And if we guessed incorrectly we retry on 320 | * a second iteration (by jumping to "detach_threads"). 321 | * 322 | * Unless the number of threads is increasing very rapidly, we 323 | * should never need to do so, though, as our guestimate is very 324 | * conservative. 325 | */ 326 | if (max_threads < proc_sb.st_nlink + 100) max_threads = proc_sb.st_nlink + 100; 327 | 328 | /* scope */ { 329 | pid_t pids[max_threads]; 330 | int added_entries = 0; 331 | sig_num_threads = num_threads; 332 | sig_pids = pids; 333 | for (;;) { 334 | struct kernel_dirent *entry; 335 | char buf[4096]; 336 | ssize_t nbytes = sys_getdents(proc, (struct kernel_dirent *)buf, sizeof(buf)); 337 | if (nbytes < 0) 338 | goto failure; 339 | else if (nbytes == 0) { 340 | if (added_entries) { 341 | /* Need to keep iterating over "/proc" in multiple 342 | * passes until we no longer find any more threads. This 343 | * algorithm eventually completes, when all threads have 344 | * been suspended. 345 | */ 346 | added_entries = 0; 347 | sys_lseek(proc, 0, SEEK_SET); 348 | continue; 349 | } 350 | break; 351 | } 352 | for (entry = (struct kernel_dirent *)buf; entry < (struct kernel_dirent *)&buf[nbytes]; 353 | entry = (struct kernel_dirent *)((char *)entry + entry->d_reclen)) { 354 | if (entry->d_ino != 0) { 355 | const char *ptr = entry->d_name; 356 | pid_t pid; 357 | 358 | /* Some kernels hide threads by preceding the pid with a '.' */ 359 | if (*ptr == '.') ptr++; 360 | 361 | /* If the directory is not numeric, it cannot be a 362 | * process/thread 363 | */ 364 | if (*ptr < '0' || *ptr > '9') continue; 365 | pid = local_atoi(ptr); 366 | 367 | /* Attach (and suspend) all threads */ 368 | if (pid && pid != clone_pid) { 369 | struct kernel_stat tmp_sb; 370 | char fname[entry->d_reclen + 48]; 371 | strcat(strcat(strcpy(fname, "/proc/"), entry->d_name), marker_path); 372 | 373 | /* Check if the marker is identical to the one we created */ 374 | if (sys_stat(fname, &tmp_sb) >= 0 && marker_sb.st_ino == tmp_sb.st_ino) { 375 | long i, j; 376 | 377 | /* Found one of our threads, make sure it is no duplicate */ 378 | for (i = 0; i < num_threads; i++) { 379 | /* Linear search is slow, but should not matter much for 380 | * the typically small number of threads. 381 | */ 382 | if (pids[i] == pid) { 383 | /* Found a duplicate; most likely on second pass */ 384 | goto next_entry; 385 | } 386 | } 387 | 388 | /* Check whether data structure needs growing */ 389 | if (num_threads >= max_threads) { 390 | /* Back to square one, this time with more memory */ 391 | NO_INTR(sys_close(proc)); 392 | goto detach_threads; 393 | } 394 | 395 | /* Attaching to thread suspends it */ 396 | pids[num_threads++] = pid; 397 | sig_num_threads = num_threads; 398 | if (sys_ptrace(PTRACE_ATTACH, pid, (void *)0, (void *)0) < 0) { 399 | /* If operation failed, ignore thread. Maybe it 400 | * just died? There might also be a race 401 | * condition with a concurrent core dumper or 402 | * with a debugger. In that case, we will just 403 | * make a best effort, rather than failing 404 | * entirely. 405 | */ 406 | num_threads--; 407 | sig_num_threads = num_threads; 408 | goto next_entry; 409 | } 410 | while (sys_waitpid(pid, (int *)0, __WALL) < 0) { 411 | if (errno != EINTR) { 412 | sys_ptrace_detach(pid); 413 | num_threads--; 414 | sig_num_threads = num_threads; 415 | goto next_entry; 416 | } 417 | } 418 | 419 | if (sys_ptrace(PTRACE_PEEKDATA, pid, &i, &j) || i++ != j || sys_ptrace(PTRACE_PEEKDATA, pid, &i, &j) || 420 | i != j) { 421 | /* Address spaces are distinct, even though both 422 | * processes show the "marker". This is probably 423 | * a forked child process rather than a thread. 424 | */ 425 | sys_ptrace_detach(pid); 426 | num_threads--; 427 | sig_num_threads = num_threads; 428 | } else { 429 | found_parent |= pid == ppid; 430 | added_entries++; 431 | } 432 | } 433 | } 434 | } 435 | next_entry:; 436 | } 437 | } 438 | NO_INTR(sys_close(proc)); 439 | sig_proc = proc = -1; 440 | 441 | /* If we failed to find any threads, try looking somewhere else in 442 | * /proc. Maybe, threads are reported differently on this system. 443 | */ 444 | if (num_threads > 1 || !*++proc_path) { 445 | NO_INTR(sys_close(marker)); 446 | sig_marker = marker = -1; 447 | 448 | /* If we never found the parent process, something is very wrong. 449 | * Most likely, we are running in debugger. Any attempt to operate 450 | * on the threads would be very incomplete. Let's just report an 451 | * error to the caller. 452 | */ 453 | if (!found_parent) { 454 | ResumeAllProcessThreads(num_threads, pids); 455 | sys__exit(3); 456 | } 457 | 458 | /* Now we are ready to call the callback, 459 | * which takes care of resuming the threads for us. 460 | */ 461 | args->result = args->callback(args->parameter, num_threads, pids, args->ap); 462 | args->err = errno; 463 | 464 | /* Callback should have resumed threads, but better safe than sorry */ 465 | if (ResumeAllProcessThreads(num_threads, pids)) { 466 | /* Callback forgot to resume at least one thread, report error */ 467 | args->err = EINVAL; 468 | args->result = -1; 469 | } 470 | 471 | sys__exit(0); 472 | } 473 | detach_threads: 474 | /* Resume all threads prior to retrying the operation */ 475 | ResumeAllProcessThreads(num_threads, pids); 476 | sig_pids = NULL; 477 | num_threads = 0; 478 | sig_num_threads = num_threads; 479 | max_threads += 100; 480 | } 481 | } 482 | } 483 | 484 | /* This function gets the list of all linux threads of the current process 485 | * passes them to the 'callback' along with the 'parameter' pointer; at the 486 | * call back call time all the threads are paused via 487 | * PTRACE_ATTACH. 488 | * The callback is executed from a separate thread which shares only the 489 | * address space, the filesystem, and the filehandles with the caller. Most 490 | * notably, it does not share the same pid and ppid; and if it terminates, 491 | * the rest of the application is still there. 'callback' is supposed to do 492 | * or arrange for ResumeAllProcessThreads. This happens automatically, if 493 | * the thread raises a synchronous signal (e.g. SIGSEGV); asynchronous 494 | * signals are blocked. If the 'callback' decides to unblock them, it must 495 | * ensure that they cannot terminate the application, or that 496 | * ResumeAllProcessThreads will get called. 497 | * It is an error for the 'callback' to make any library calls that could 498 | * acquire locks. Most notably, this means that most system calls have to 499 | * avoid going through libc. Also, this means that it is not legal to call 500 | * exit() or abort(). 501 | * We return -1 on error and the return value of 'callback' on success. 502 | */ 503 | int ListAllProcessThreads(void *parameter, ListAllProcessThreadsCallBack callback, ...) { 504 | char altstack_mem[ALT_STACKSIZE]; 505 | struct ListerParams args; 506 | pid_t clone_pid; 507 | int dumpable = 1, sig; 508 | struct kernel_sigset_t sig_blocked, sig_old; 509 | 510 | va_start(args.ap, callback); 511 | 512 | /* If we are short on virtual memory, initializing the alternate stack 513 | * might trigger a SIGSEGV. Let's do this early, before it could get us 514 | * into more trouble (i.e. before signal handlers try to use the alternate 515 | * stack, and before we attach to other threads). 516 | */ 517 | memset(altstack_mem, 0, sizeof(altstack_mem)); 518 | 519 | /* Some of our cleanup functions could conceivable use more stack space. 520 | * Try to touch the stack right now. This could be defeated by the compiler 521 | * being too smart for it's own good, so try really hard. 522 | */ 523 | DirtyStack(32768); 524 | 525 | /* Make this process "dumpable". This is necessary in order to ptrace() 526 | * after having called setuid(). 527 | */ 528 | dumpable = sys_prctl(PR_GET_DUMPABLE, 0); 529 | if (!dumpable) sys_prctl(PR_SET_DUMPABLE, 1); 530 | 531 | /* Fill in argument block for dumper thread */ 532 | args.result = -1; 533 | args.err = 0; 534 | args.altstack_mem = altstack_mem; 535 | args.parameter = parameter; 536 | args.callback = callback; 537 | 538 | /* Before cloning the thread lister, block all asynchronous signals, as we */ 539 | /* are not prepared to handle them. */ 540 | sys_sigfillset(&sig_blocked); 541 | for (sig = 0; sig < sizeof(sync_signals) / sizeof(*sync_signals); sig++) { 542 | sys_sigdelset(&sig_blocked, sync_signals[sig]); 543 | } 544 | if (sys_sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old)) { 545 | args.err = errno; 546 | args.result = -1; 547 | goto failed; 548 | } 549 | 550 | /* scope */ { 551 | /* After cloning, both the parent and the child share the same instance 552 | * of errno. We must make sure that at least one of these processes 553 | * (in our case, the parent) uses modified syscall macros that update 554 | * a local copy of errno, instead. 555 | */ 556 | #ifdef __cplusplus 557 | #define sys0_sigprocmask sys.sigprocmask 558 | #define sys0_waitpid sys.waitpid 559 | SysCalls sys; 560 | #else 561 | int my_errno; 562 | #define SYS_ERRNO my_errno 563 | #define SYS_INLINE inline 564 | #define SYS_PREFIX 0 565 | #undef SYS_LINUX_SYSCALL_SUPPORT_H 566 | #include "linux_syscall_support.h" 567 | #endif 568 | 569 | int clone_errno; 570 | clone_pid = local_clone((int (*)(void *))ListerThread, &args); 571 | clone_errno = errno; 572 | 573 | /* Allow the child to ptrace us, if YAMA ptrace protection is enabled. */ 574 | #ifndef PR_SET_PTRACER 575 | #define PR_SET_PTRACER 0x59616d61 576 | #endif 577 | sys_prctl(PR_SET_PTRACER, clone_pid); 578 | 579 | sys_sigprocmask(SIG_SETMASK, &sig_old, &sig_old); 580 | 581 | if (clone_pid >= 0) { 582 | int status, rc; 583 | while ((rc = sys0_waitpid(clone_pid, &status, __WALL)) < 0 && ERRNO == EINTR) { 584 | /* Keep waiting */ 585 | } 586 | if (rc < 0) { 587 | args.err = ERRNO; 588 | args.result = -1; 589 | } else if (WIFEXITED(status)) { 590 | switch (WEXITSTATUS(status)) { 591 | case 0: 592 | break; /* Normal process termination */ 593 | case 2: 594 | args.err = EFAULT; /* Some fault (e.g. SIGSEGV) detected */ 595 | args.result = -1; 596 | break; 597 | case 3: 598 | args.err = EPERM; /* Process is already being traced */ 599 | args.result = -1; 600 | break; 601 | default: 602 | args.err = ECHILD; /* Child died unexpectedly */ 603 | args.result = -1; 604 | break; 605 | } 606 | } else if (!WIFEXITED(status)) { 607 | args.err = EFAULT; /* Terminated due to an unhandled signal*/ 608 | args.result = -1; 609 | } 610 | } else { 611 | args.result = -1; 612 | args.err = clone_errno; 613 | } 614 | } 615 | 616 | /* Restore the "dumpable" state of the process */ 617 | failed: 618 | if (!dumpable) sys_prctl(PR_SET_DUMPABLE, dumpable); 619 | 620 | va_end(args.ap); 621 | 622 | errno = args.err; 623 | return args.result; 624 | } 625 | 626 | /* This function resumes the list of all linux threads that 627 | * ListAllProcessThreads pauses before giving to its callback. 628 | * The function returns non-zero if at least one thread was 629 | * suspended and has now been resumed. 630 | */ 631 | int ResumeAllProcessThreads(int num_threads, pid_t *thread_pids) { 632 | int detached_at_least_one = 0; 633 | while (num_threads-- > 0) { 634 | detached_at_least_one |= sys_ptrace_detach(thread_pids[num_threads]) >= 0; 635 | } 636 | return detached_at_least_one; 637 | } 638 | 639 | #ifdef __cplusplus 640 | } 641 | #endif 642 | #endif 643 | -------------------------------------------------------------------------------- /src/linuxthreads.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005-2007, Google Inc. 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * --- 31 | * Author: Markus Gutschke 32 | */ 33 | 34 | #ifndef _LINUXTHREADS_H 35 | #define _LINUXTHREADS_H 36 | 37 | /* Include thread_lister.h to get the interface that we implement for linux. 38 | */ 39 | 40 | /* We currently only support x86-32 and x86-64 on Linux. Porting to other 41 | * related platforms should not be difficult. 42 | */ 43 | #if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__) || defined(__PPC__)) && \ 44 | defined(__linux) 45 | 46 | /* Define the THREADS symbol to make sure that there is exactly one core dumper 47 | * built into the library. 48 | */ 49 | #define THREADS "Linux /proc" 50 | 51 | #endif 52 | 53 | #endif /* _LINUXTHREADS_H */ 54 | -------------------------------------------------------------------------------- /src/thread_lister.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005-2007, Google Inc. 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * --- 31 | * Author: Markus Gutschke 32 | */ 33 | 34 | #include "thread_lister.h" 35 | 36 | #include /* needed for NULL on some powerpc platforms (?!) */ 37 | #include /* getpid() */ 38 | #include 39 | 40 | #include "linuxthreads.h" 41 | /* Include other thread listers here that define THREADS macro 42 | * only when they can provide a good implementation. 43 | */ 44 | 45 | #ifndef THREADS 46 | 47 | /* Default trivial thread lister for single-threaded applications, 48 | * or if the multi-threading code has not been ported, yet. 49 | */ 50 | 51 | int ListAllProcessThreads(void *parameter, ListAllProcessThreadsCallBack callback, ...) { 52 | int rc; 53 | va_list ap; 54 | 55 | int dumpable = prctl(PR_GET_DUMPABLE, 0); 56 | if (!dumpable) prctl(PR_SET_DUMPABLE, 1); 57 | va_start(ap, callback); 58 | pid_t pid = getpid(); 59 | rc = callback(parameter, 1, &pid, ap); 60 | va_end(ap); 61 | if (!dumpable) prctl(PR_SET_DUMPABLE, 0); 62 | return rc; 63 | } 64 | 65 | int ResumeAllProcessThreads(int num_threads, pid_t *thread_pids) { return 1; } 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /src/thread_lister.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005-2007, Google Inc. 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * --- 31 | * Author: Markus Gutschke 32 | */ 33 | 34 | #ifndef _THREAD_LISTER_H 35 | #define _THREAD_LISTER_H 36 | 37 | #include 38 | #include 39 | 40 | #ifdef __cplusplus 41 | extern "C" { 42 | #endif 43 | 44 | typedef int (*ListAllProcessThreadsCallBack)(void *parameter, int num_threads, pid_t *thread_pids, va_list ap); 45 | 46 | /* This function gets the list of all linux threads of the current process 47 | * passes them to the 'callback' along with the 'parameter' pointer; at the 48 | * call back call time all the threads are paused via 49 | * PTRACE_ATTACH. 50 | * The callback is executed from a separate thread which shares only the 51 | * address space, the filesystem, and the filehandles with the caller. Most 52 | * notably, it does not share the same pid and ppid; and if it terminates, 53 | * the rest of the application is still there. 'callback' is supposed to do 54 | * or arrange for ResumeAllProcessThreads. This happens automatically, if 55 | * the thread raises a synchronous signal (e.g. SIGSEGV); asynchronous 56 | * signals are blocked. If the 'callback' decides to unblock them, it must 57 | * ensure that they cannot terminate the application, or that 58 | * ResumeAllProcessThreads will get called. 59 | * It is an error for the 'callback' to make any library calls that could 60 | * acquire locks. Most notably, this means that most system calls have to 61 | * avoid going through libc. Also, this means that it is not legal to call 62 | * exit() or abort(). 63 | * We return -1 on error and the return value of 'callback' on success. 64 | */ 65 | int ListAllProcessThreads(void *parameter, ListAllProcessThreadsCallBack callback, ...); 66 | 67 | /* This function resumes the list of all linux threads that 68 | * ListAllProcessThreads pauses before giving to its callback. 69 | * The function returns non-zero if at least one thread was 70 | * suspended and has now been resumed. 71 | */ 72 | int ResumeAllProcessThreads(int num_threads, pid_t *thread_pids); 73 | 74 | #ifdef __cplusplus 75 | } 76 | #endif 77 | 78 | #endif /* _THREAD_LISTER_H */ 79 | -------------------------------------------------------------------------------- /test-driver: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # test-driver - basic testsuite driver script. 3 | 4 | scriptversion=2013-07-13.22; # UTC 5 | 6 | # Copyright (C) 2011-2014 Free Software Foundation, Inc. 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2, or (at your option) 11 | # any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | 21 | # As a special exception to the GNU General Public License, if you 22 | # distribute this file as part of a program that contains a 23 | # configuration script generated by Autoconf, you may include it under 24 | # the same distribution terms that you use for the rest of that program. 25 | 26 | # This file is maintained in Automake, please report 27 | # bugs to or send patches to 28 | # . 29 | 30 | # Make unconditional expansion of undefined variables an error. This 31 | # helps a lot in preventing typo-related bugs. 32 | set -u 33 | 34 | usage_error () 35 | { 36 | echo "$0: $*" >&2 37 | print_usage >&2 38 | exit 2 39 | } 40 | 41 | print_usage () 42 | { 43 | cat <$log_file 2>&1 108 | estatus=$? 109 | 110 | if test $enable_hard_errors = no && test $estatus -eq 99; then 111 | tweaked_estatus=1 112 | else 113 | tweaked_estatus=$estatus 114 | fi 115 | 116 | case $tweaked_estatus:$expect_failure in 117 | 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; 118 | 0:*) col=$grn res=PASS recheck=no gcopy=no;; 119 | 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; 120 | 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; 121 | *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; 122 | *:*) col=$red res=FAIL recheck=yes gcopy=yes;; 123 | esac 124 | 125 | # Report the test outcome and exit status in the logs, so that one can 126 | # know whether the test passed or failed simply by looking at the '.log' 127 | # file, without the need of also peaking into the corresponding '.trs' 128 | # file (automake bug#11814). 129 | echo "$res $test_name (exit status: $estatus)" >>$log_file 130 | 131 | # Report outcome to console. 132 | echo "${col}${res}${std}: $test_name" 133 | 134 | # Register the test result, and other relevant metadata. 135 | echo ":test-result: $res" > $trs_file 136 | echo ":global-test-result: $res" >> $trs_file 137 | echo ":recheck: $recheck" >> $trs_file 138 | echo ":copy-in-global-log: $gcopy" >> $trs_file 139 | 140 | # Local Variables: 141 | # mode: shell-script 142 | # sh-indentation: 2 143 | # eval: (add-hook 'write-file-hooks 'time-stamp) 144 | # time-stamp-start: "scriptversion=" 145 | # time-stamp-format: "%:y-%02m-%02d.%02H" 146 | # time-stamp-time-zone: "UTC" 147 | # time-stamp-end: "; # UTC" 148 | # End: 149 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(coredumper_unittest coredumper_unittest.c) 3 | add_test(NAME coredumper_unittest COMMAND coredumper_unittest) 4 | 5 | target_link_libraries(coredumper_unittest coredumper) 6 | # Needs access to internal headers 7 | target_include_directories(coredumper_unittest PRIVATE "${COREDUMPER_SRC_DIR}/src/") 8 | 9 | add_executable(linux_syscall_support_unittest linux_syscall_support_unittest.cc) 10 | # Needs access to internal headers 11 | target_include_directories(linux_syscall_support_unittest PRIVATE "${COREDUMPER_SRC_DIR}/src/") 12 | add_test(NAME linux_syscall_support_unittest COMMAND linux_syscall_support_unittest) 13 | -------------------------------------------------------------------------------- /test/coredumper_unittest.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005-2008, Google Inc. 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are 6 | * met: 7 | * 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following disclaimer 12 | * in the documentation and/or other materials provided with the 13 | * distribution. 14 | * * Neither the name of Google Inc. nor the names of its 15 | * contributors may be used to endorse or promote products derived from 16 | * this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * --- 31 | * Author: Markus Gutschke, Carl Crous 32 | */ 33 | 34 | #define __STDC_LIMIT_MACROS 35 | #include "coredumper/coredumper.h" 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | #include "linuxthreads.h" 55 | 56 | /* The gdb binary to use on different architectures. Change these if you have 57 | * specific debuggers for different architectures. 58 | */ 59 | #if __WORDSIZE == 64 60 | #define GDB "gdb" 61 | #else 62 | #define GDB "gdb" 63 | #endif 64 | 65 | /* A simple structure to store memory segment information. */ 66 | struct MemorySegment { 67 | size_t filesz; 68 | size_t memsz; 69 | }; 70 | 71 | /* A comparator function to compare to memory segments based on their memsz in 72 | * descending order. If two segments have the same memsz, their filesz values 73 | * are compared in ascending order. 74 | */ 75 | static int MemorySegmentCmp(const struct MemorySegment *seg1, const struct MemorySegment *seg2) { 76 | if (seg1->memsz == seg2->memsz) { 77 | return seg2->filesz - seg1->filesz; 78 | } 79 | return seg1->memsz - seg2->memsz; 80 | } 81 | 82 | /* A large extra note. */ 83 | unsigned int large_extra_note[256]; 84 | 85 | /* Extra core notes. */ 86 | static struct CoredumperNote extra_notes[] = { 87 | {"GOOGLE", 0xdeadbeef, 16, (const void *)("abcdefghijklmnop")}, 88 | {"FOOO", 0xf00ba7, 9, (const void *)("qwertyuio")}, 89 | {"BAR", 0x0, 0, NULL}, 90 | {"LARGE", 0x1234, sizeof(large_extra_note), (const void *)(large_extra_note)}}; 91 | static const int kExtraNotesCount = 4; 92 | 93 | static const char *getReadelf() { 94 | static const char *readelf = NULL; 95 | if (!readelf) { 96 | readelf = getenv("READELF"); 97 | if (!readelf) readelf = "readelf"; 98 | } 99 | return readelf; 100 | } 101 | 102 | /* Make assertion failures print more readable messages */ 103 | #undef strcmp 104 | #undef strncmp 105 | #undef strstr 106 | 107 | /* Simple signal handler for dealing with timeouts. */ 108 | static jmp_buf jmpenv; 109 | static void TimeOutHandler(int sig, siginfo_t *info, void *p) { siglongjmp(jmpenv, sig); } 110 | 111 | /* This is a really silly CPU hog, but we want to avoid creating a 112 | * core dump while we are executing code in libc. Depending on the 113 | * system environment, gdb might or might not make stack traces 114 | * available within libc, and that would make this unittest 115 | * non-deterministic. 116 | */ 117 | static volatile enum State { IDLE, RUNNING, DEAD } state1, state2; 118 | static volatile unsigned int counter; 119 | static void *Busy(void *arg) { 120 | volatile enum State *state = (volatile enum State *)arg; 121 | *state = RUNNING; 122 | while (*state == RUNNING) { 123 | counter++; 124 | } 125 | return 0; 126 | } 127 | 128 | /* Open the core file with "readelf", and check that all the expected 129 | * entries can be found. We are not checking exact numeric values, as these 130 | * might differ between runs, and it seems overkill recomputing them here. 131 | */ 132 | static void CheckWithReadElf(FILE *input, FILE *output, const char *filename, const char *suffix, 133 | const char *decompress, const char *args) { 134 | static const char *msg[] = { 135 | " ELF", 136 | #if __BYTE_ORDER == __LITTLE_ENDIAN 137 | "little" 138 | #else 139 | "big" 140 | #endif 141 | " endian", 142 | "UNIX - System V", 143 | "Core file", 144 | "no sections in this file", 145 | "NOTE", 146 | "no relocations in this file", 147 | "The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.", 148 | "No version information found in this file", 149 | "NT_PRPSINFO", 150 | #ifndef __mips__ 151 | "NT_TASKSTRUCT", 152 | #endif 153 | "NT_PRSTATUS", 154 | "NT_FPREGSET", 155 | #ifdef THREADS 156 | "NT_PRSTATUS", 157 | "NT_FPREGSET", 158 | "NT_PRSTATUS", 159 | "NT_FPREGSET", 160 | #endif 161 | "DONE", 162 | 0 163 | }; 164 | const char **ptr; 165 | char buffer[4096]; 166 | /* Some of the tests explicitly limit core file size, resulting in a 167 | * truncated core, and causing readelf to print errors on its stderr. 168 | * These errors can then intermix with expected readelf output, causing 169 | * the test to fail. To prevent this, ignore readelf stderr (using '2>&1' 170 | * does not suffice when stdout is fully buffered). 171 | */ 172 | int rc = fprintf(input, 173 | "cat /proc/%d/maps &&" 174 | "%s %s <\"%s%s\" >core.%d &&" 175 | "%s -a core.%d 2>/dev/null; " 176 | "rm -f core.%d; " 177 | "(set +x; echo DONE)\n", 178 | getpid(), decompress, args, filename, suffix, getpid(), getReadelf(), getpid(), getpid()); 179 | assert(rc > 0); 180 | 181 | *buffer = '\000'; 182 | for (ptr = msg; *ptr; ptr++) { 183 | do { 184 | char *line; 185 | assert(strncmp(buffer, "DONE", 4)); 186 | line = fgets(buffer, sizeof(buffer), output); 187 | assert(line); 188 | fputs(buffer, stdout); 189 | } while (!strstr(buffer, *ptr)); 190 | printf("Found: %s\n", *ptr); 191 | } 192 | return; 193 | } 194 | 195 | /* Skips leading white space within a string 196 | */ 197 | static char *SkipLeadingWhiteSpace(char *line) { 198 | while (isspace(*line) && *line != '\0') { 199 | line++; 200 | } 201 | return line; 202 | } 203 | 204 | /* Converts a hex string to a size_t number. pos is set to the character after 205 | * the number in the string. 206 | */ 207 | static size_t hextosizet(char *str, char **pos) { 208 | size_t num = 0; 209 | size_t digit; 210 | if (str[0] == '0' && toupper(str[1]) == 'X') { 211 | str += 2; 212 | } 213 | while (isxdigit(*str)) { 214 | if (isdigit(*str)) { 215 | digit = *str - '0'; 216 | } else { 217 | digit = 10 + toupper(*str) - 'A'; 218 | } 219 | num = num * 16 + digit; 220 | str++; 221 | } 222 | *pos = str; 223 | return num; 224 | } 225 | 226 | /* Open a prioritized core file with "readelf", and check that the 227 | * prioritization was performed correctly. 228 | */ 229 | static void CheckPrioritizationWithReadElf(FILE *input, FILE *output, const char *filename) { 230 | char *line; 231 | char buffer[4096]; 232 | int last_line_was_load; 233 | int rc = fprintf(input, "%s -a %s; echo DONE\n", getReadelf(), filename); 234 | const int kMaxMemorySegments = 256; 235 | struct MemorySegment memory_segments[kMaxMemorySegments]; 236 | int memory_segment_count = 0; 237 | assert(rc > 0); 238 | 239 | *buffer = '\000'; 240 | 241 | /* Read the output from readelf and remember each writable memory segment's 242 | * size in memory and in the file. 243 | */ 244 | last_line_was_load = 0; 245 | do { 246 | line = fgets(buffer, sizeof(buffer), output); 247 | if (line) { 248 | fputs(buffer, stdout); 249 | if (!strncmp(line, "DONE", 4)) { 250 | break; 251 | } 252 | line = SkipLeadingWhiteSpace(line); 253 | if (!strncmp(line, "LOAD", 4)) { 254 | last_line_was_load = 1; 255 | } else if (last_line_was_load) { 256 | last_line_was_load = 0; 257 | line = SkipLeadingWhiteSpace(line); 258 | assert(memory_segment_count < kMaxMemorySegments); 259 | memory_segments[memory_segment_count].filesz = hextosizet(line, &line); 260 | memory_segments[memory_segment_count].memsz = hextosizet(line, &line); 261 | line = SkipLeadingWhiteSpace(line); 262 | /* Line is now at the flags with the second character being 'W' or 263 | * ' '. We only want to add writable memory segments. 264 | */ 265 | if (line[1] == 'W') { 266 | memory_segment_count++; 267 | } 268 | } 269 | } 270 | } while (line); 271 | qsort(memory_segments, sizeof(struct MemorySegment), memory_segment_count, 272 | (int (*)(const void *, const void *))MemorySegmentCmp); 273 | 274 | /* Once the memory segments are sorted according to their size in memory, the 275 | * difference between the memory size and the file size must be decreasing. 276 | * If it is not, this means a memory segment which wasn't the largest was 277 | * decreased in size before the largest one. 278 | */ 279 | size_t last_size_difference = 0; 280 | int i; 281 | for (i = 0; i < memory_segment_count; ++i) { 282 | size_t current_size_difference = memory_segments[i].memsz - memory_segments[i].filesz; 283 | if (i > 0) { 284 | assert(last_size_difference >= current_size_difference); 285 | } 286 | last_size_difference = current_size_difference; 287 | } 288 | 289 | return; 290 | } 291 | 292 | /* Open a core file which has extra notes with "readelf", and check that the 293 | * notes were written correctly. 294 | */ 295 | static void CheckExtraNotesWithReadElf(FILE *input, FILE *output, const char *filename) { 296 | const int kBufferSize = 4096; 297 | char *line; 298 | char buffer[kBufferSize]; 299 | int note_index = 0; 300 | /* The sizes of the notes and their offset are relatively small, definitely 301 | * less than 2GB therefore the int data type is more than enough. 302 | */ 303 | int offset = 0; 304 | int note_sizes[kExtraNotesCount]; 305 | int note_sizes_to_description[kExtraNotesCount]; 306 | int rc = fprintf(input, "%s -n %s; echo DONE\n", getReadelf(), filename); 307 | 308 | assert(rc > 0); 309 | 310 | *buffer = '\000'; 311 | /* Read the output from readelf and check the values are correct. */ 312 | do { 313 | line = fgets(buffer, sizeof(buffer), output); 314 | if (!strncmp(line, "DONE", 4)) { 315 | line = 0; 316 | } 317 | if (line) { 318 | fputs(buffer, stdout); 319 | if (!offset) { 320 | int l = 0; 321 | if (!strncmp(line, "Displaying notes found at file offset ", 38)) { 322 | l = 38; 323 | } else if (!strncmp(line, "Notes at offset ", 16)) { 324 | l = 16; 325 | } 326 | if (l) { 327 | line += l; 328 | offset = hextosizet(line, &line); 329 | /* Skip the line which contains the table headings. */ 330 | char *o = fgets(buffer, sizeof(buffer), output); 331 | assert(o != NULL); 332 | } 333 | } else if (line[0] != '+') { 334 | /* Get the name, its size and the description size. */ 335 | line = SkipLeadingWhiteSpace(line); 336 | char *name = line; 337 | /* Ignore "description data" lines */ 338 | if (!strncmp(name, "description data:", 17)) { 339 | continue; 340 | } 341 | /* Our test names do not include spaces so this will work. */ 342 | while (!isspace(*line) && *line != '\0') { 343 | line++; 344 | } 345 | *line = '\0'; 346 | line++; 347 | int name_size = strlen(name) + 1; 348 | 349 | line = SkipLeadingWhiteSpace(line); 350 | int description_size = hextosizet(line, &line); 351 | 352 | int note_size = 12; 353 | note_size += name_size; 354 | if (name_size % 4 != 0) { 355 | note_size += 4 - name_size % 4; 356 | } 357 | int note_size_to_description = note_size; 358 | note_size += description_size; 359 | if (description_size % 4 != 0) { 360 | note_size += 4 - description_size % 4; 361 | } 362 | 363 | assert(note_index < kExtraNotesCount); 364 | struct CoredumperNote *note = &extra_notes[note_index]; 365 | if (!strcmp(name, note->name)) { 366 | assert(description_size == note->description_size); 367 | line = SkipLeadingWhiteSpace(line); 368 | /* Expect readelf to not recognize our note types. */ 369 | assert(!strncmp(line, "Unknown note type: (", 20)); 370 | line += 20; 371 | 372 | unsigned int type = hextosizet(line, &line); 373 | assert(type == note->type); 374 | 375 | note_sizes[note_index] = note_size; 376 | note_sizes_to_description[note_index] = note_size_to_description; 377 | note_index++; 378 | } else if (note_index == 0) { 379 | /* The custom notes must follow the core notes. */ 380 | assert(!strcmp(name, "CORE") || !strcmp(name, "LINUX")); 381 | offset += note_size; 382 | } 383 | } 384 | } 385 | } while (line); 386 | assert(note_index == kExtraNotesCount); 387 | 388 | /* Check the note descriptions. */ 389 | FILE *fp = fopen(filename, "rb"); 390 | assert(fp); 391 | 392 | assert(!fseek(fp, offset, SEEK_SET)); 393 | int i; 394 | for (i = 0; i < kExtraNotesCount; ++i) { 395 | struct CoredumperNote *note = &extra_notes[i]; 396 | assert(fread(buffer, 1, note_sizes[i], fp) == note_sizes[i]); 397 | if (note->description_size > 0) { 398 | line = &buffer[note_sizes_to_description[i]]; 399 | assert(!strncmp(line, (const char *)note->description, note->description_size)); 400 | } 401 | } 402 | 403 | assert(!fclose(fp)); 404 | 405 | return; 406 | } 407 | 408 | /* Open the core dump with gdb, and check that the stack traces look 409 | * correct. Again, we are not checking for exact numeric values. 410 | * 411 | * We also extract the value of the "dummy" environment variable, and check 412 | * that it is correct. 413 | */ 414 | static void CheckWithGDB(FILE *input, FILE *output, const char *filename, int *dummy, int cmp_parm) { 415 | volatile int cmp = cmp_parm; 416 | char out[4096], buffer[4096]; 417 | char *volatile out_ptr = out; 418 | const char **ptr, *arg = ""; 419 | struct sigaction sa; 420 | 421 | #if defined(__i386__) || defined(__x86_64) || defined(__ARM_ARCH_3__) || defined(__mips__) 422 | /* If we have a platform-specific FRAME() macro, we expect the stack trace 423 | * to be unrolled all the way to WriteCoreDump(). 424 | */ 425 | #define DUMPFUNCTION "CoreDump" 426 | #else 427 | /* Otherwise, we the stack trace will start in ListAllProcessThreads. 428 | */ 429 | #define DUMPFUNCTION "ListAllProcessThreads" 430 | #endif 431 | 432 | /* Messages that we are looking for. "@" is a special character that 433 | * matches a pattern in the output, which can later be used as input 434 | * to gdb. "*" is a glob wildcard character. 435 | */ 436 | static const char *msg[] = {"Core was generated by", 437 | " @ Thread * *" DUMPFUNCTION, 438 | "[Current thread is *", 439 | "#* *CoreDump", 440 | "#@ * TestCoreDump", 441 | " TestCoreDump", 442 | "$1 = ", 443 | #ifdef THREADS 444 | " Busy", 445 | " @ Thread * Busy", 446 | "#* *CoreDump", 447 | #endif 448 | "DONE", 449 | 0}; 450 | 451 | /* Commands that we are sending to gdb. All occurrences of "@" will be 452 | * substituted with the pattern matching the corresponding "@" character 453 | * in the stream of messages received. 454 | */ 455 | sprintf(out, 456 | "%s /proc/%d/exe \"%s\"; (set +x; echo DONE)\n" 457 | "info threads\n" 458 | "thread @\n" 459 | "bt 10\n" 460 | "up @\n" 461 | "print *(unsigned int *)0x%lx\n" 462 | "print %dU\n" 463 | "print %dU\n" 464 | #ifdef THREADS 465 | "info threads\n" 466 | "thread @\n" 467 | "thread apply all bt 10\n" 468 | #endif 469 | "quit\n", 470 | GDB, getpid(), filename, (unsigned long)dummy, *dummy, cmp); 471 | 472 | /* Since we are interactively driving gdb, it is possible that we would 473 | * indefinitely have to wait for a matching message to appear (this is 474 | * different from the "readelf" case, which can just read until EOF). 475 | * So, we have to set up a time out handler. 476 | */ 477 | memset(&sa, 0, sizeof(sa)); 478 | sa.sa_sigaction = TimeOutHandler; 479 | sa.sa_flags = SA_RESTART | SA_SIGINFO; 480 | sigaction(SIGALRM, &sa, 0); 481 | 482 | if (setjmp(jmpenv)) { 483 | puts("Time out!"); 484 | abort(); 485 | } else { 486 | *buffer = '\000'; 487 | for (ptr = msg; *ptr; ptr++) { 488 | /* If there is any command that does not require a pattern read from 489 | * the message stream, output it now. 490 | */ 491 | while (*out_ptr && *out_ptr != '@') { 492 | int rc = putc(*out_ptr++, input); 493 | assert(rc >= 0); 494 | } 495 | fflush(input); 496 | for (;;) { 497 | char *line, *templ, scratch[256], isarg = 0; 498 | 499 | /* We should never try to read any more messages, after we have 500 | * already seen the final "DONE" message. 501 | */ 502 | assert(strncmp(buffer, "DONE", 4)); 503 | 504 | /* Read the next message from gdb. */ 505 | alarm(20); 506 | line = fgets(buffer, sizeof(buffer), output); 507 | alarm(0); 508 | assert(line); 509 | fputs(buffer, stdout); 510 | 511 | /* Extract the "$1 =" string, which we will compare later. */ 512 | if ((arg = strstr(buffer, "$1 = ")) != NULL) { 513 | cmp = atoi(arg + 5); 514 | arg = 0; 515 | } 516 | 517 | /* Try to match the current line against our templates. */ 518 | templ = strcpy(scratch, *ptr); 519 | for (;;) { 520 | /* Split the template in substring separated by "@" and "*" chars. */ 521 | int l = strcspn(templ, "*@"); 522 | char c = templ[l]; 523 | templ[l] = '\000'; 524 | 525 | /* If we just passed a "@", remember pattern for later use. */ 526 | if (isarg) { 527 | arg = line; 528 | isarg = 0; 529 | } 530 | if (c == '@') isarg++; 531 | 532 | /* Check if substring of template matches somewhere in current line*/ 533 | if ((line = strstr(line, templ)) != NULL) { 534 | /* Found a match. Remember arg, if any. */ 535 | if (c != '@') *line = '\000'; 536 | 537 | /* Advance to next pattern that needs matching. */ 538 | line += strlen(templ); 539 | } else { 540 | /* No match. Break out of this loop, and read next line. */ 541 | templ[l] = c; 542 | arg = 0; 543 | break; 544 | } 545 | /* No more patterns. We have a successful match. */ 546 | if (!c) goto found; 547 | templ[l] = c; 548 | templ += l + 1; 549 | } 550 | } 551 | found: 552 | /* Print matched pattern. Enter arg into command stream. Then loop. */ 553 | printf("Found: %s", *ptr); 554 | if (arg && *out_ptr == '@') { 555 | /* We only want to match the very last word; drop leading tokens. */ 556 | int rc; 557 | char *last = strrchr(arg, ' '); 558 | if (last != NULL) arg = last + 1; 559 | 560 | /* Enter matched data into the command stream. */ 561 | rc = fputs(arg, input); 562 | assert(rc > 0); 563 | printf(" (arg = \"%s\")", arg); 564 | arg = 0; 565 | out_ptr++; 566 | } 567 | puts(""); 568 | } 569 | 570 | assert(*dummy == cmp); 571 | printf("Magic marker matches %d\n", *dummy); 572 | } 573 | } 574 | 575 | /* We can test both the WriteCoreDump() and the GetCoreDump() functions 576 | * with the same test cases. We just need to wrap the GetCoreDump() 577 | * family of functions with some code that emulates the WriteCoreDump() 578 | * functions. 579 | */ 580 | static int MyWriteCoreDumpWith(const struct CoreDumpParameters *params, const char *file_name) { 581 | int rc = 0; 582 | int coreFd; 583 | int flags; 584 | size_t max_length = params->max_length; 585 | struct CoredumperCompressor *comp = NULL; 586 | struct CoreDumpParameters new_params; 587 | 588 | if (!max_length) return 0; 589 | /* Remove limiting parameters. */ 590 | memcpy(&new_params, params, sizeof(struct CoreDumpParameters)); 591 | SetCoreDumpParameter(&new_params, max_length, SIZE_MAX); 592 | flags = new_params.flags; 593 | flags &= ~(COREDUMPER_FLAG_LIMITED | COREDUMPER_FLAG_LIMITED_BY_PRIORITY); 594 | SetCoreDumpParameter(&new_params, flags, flags); 595 | coreFd = GetCoreDumpWith(&new_params); 596 | if (params->selected_compressor) { 597 | comp = *params->selected_compressor; 598 | } 599 | if (coreFd >= 0) { 600 | int writeFd; 601 | const char *suffix = ""; 602 | 603 | if (comp != NULL && comp->compressor != NULL && comp->suffix != NULL) suffix = comp->suffix; 604 | 605 | /* scope */ { 606 | char extended_file_name[strlen(file_name) + strlen(suffix) + 1]; 607 | strcat(strcpy(extended_file_name, file_name), suffix); 608 | writeFd = open(extended_file_name, O_WRONLY | O_CREAT | O_TRUNC, 0600); 609 | } 610 | if (writeFd >= 0) { 611 | char buffer[16384]; 612 | ssize_t len; 613 | while (max_length > 0 && 614 | ((len = read(coreFd, buffer, sizeof(buffer) < max_length ? sizeof(buffer) : max_length)) > 0 || 615 | (len < 0 && errno == EINTR))) { 616 | char *ptr = buffer; 617 | while (len > 0) { 618 | int i; 619 | i = write(writeFd, ptr, len); 620 | if (i <= 0) { 621 | rc = -1; 622 | break; 623 | } 624 | ptr += i; 625 | len -= i; 626 | max_length -= i; 627 | } 628 | } 629 | close(writeFd); 630 | } else { 631 | rc = -1; 632 | } 633 | close(coreFd); 634 | } else { 635 | rc = -1; 636 | } 637 | return rc; 638 | } 639 | 640 | static int MyWriteCoreDump(const char *file_name) { 641 | struct CoreDumpParameters params; 642 | ClearCoreDumpParameters(¶ms); 643 | return MyWriteCoreDumpWith(¶ms, file_name); 644 | } 645 | 646 | static int MyWriteCoreDumpLimited(const char *file_name, size_t max_length) { 647 | struct CoreDumpParameters params; 648 | ClearCoreDumpParameters(¶ms); 649 | assert(!SetCoreDumpLimited(¶ms, max_length)); 650 | return MyWriteCoreDumpWith(¶ms, file_name); 651 | } 652 | 653 | static int MyWriteCompressedCoreDump(const char *file_name, size_t max_length, 654 | const struct CoredumperCompressor compressors[], 655 | struct CoredumperCompressor **selected_compressor) { 656 | struct CoreDumpParameters params; 657 | ClearCoreDumpParameters(¶ms); 658 | assert(!SetCoreDumpLimited(¶ms, max_length)); 659 | assert(!SetCoreDumpCompressed(¶ms, compressors, selected_compressor)); 660 | return MyWriteCoreDumpWith(¶ms, file_name); 661 | } 662 | 663 | static int MyCallback(void *arg) { 664 | int *count = arg; 665 | if (*count < 0) { 666 | return 1; 667 | } 668 | ++(*count); 669 | return 0; 670 | } 671 | 672 | /* Do not declare this function static, so that the compiler does not get 673 | * tempted to inline it. We want to be able to see some stack traces. 674 | */ 675 | void TestCoreDump() { 676 | static struct CoredumperCompressor my_compressor[] = {{"/NOSUCHDIR/NOSUCHFILE", 0, 0}, 677 | {0, 0, 0}, /* Will be overwritten by test */ 678 | {0, 0, 0}}; 679 | 680 | int loop, in[2], out[2], dummy, cmp, rc; 681 | pid_t pid; 682 | FILE *input, *output; 683 | pthread_t thread; 684 | struct stat statBuf; 685 | struct CoredumperCompressor *compressor; 686 | struct CoreDumpParameters note_params; 687 | 688 | /* Make stdout unbuffered. We absolutely want to see all output, even 689 | * if the application aborted with an assertion failure. 690 | */ 691 | setvbuf(stdout, NULL, _IONBF, 0); 692 | 693 | /* It is rather tricky to properly call fork() from within a multi-threaded 694 | * application. To simplify this problem, we fork and exec /bin/bash before 695 | * creating the first thread. 696 | */ 697 | puts("Forking /bin/bash process"); 698 | rc = pipe(in); 699 | assert(!rc); 700 | rc = pipe(out); 701 | assert(!rc); 702 | 703 | if ((pid = fork()) == 0) { 704 | int i, openmax; 705 | dup2(in[0], 0); 706 | dup2(out[1], 1); 707 | dup2(out[1], 2); 708 | openmax = sysconf(_SC_OPEN_MAX); 709 | for (i = 3; i < openmax; i++) close(i); 710 | fcntl(0, F_SETFD, 0); 711 | fcntl(1, F_SETFD, 0); 712 | fcntl(2, F_SETFD, 0); 713 | execl("/bin/bash", "bash", "-ex", NULL); 714 | _exit(1); 715 | } 716 | assert(pid >= 0); 717 | assert(!close(in[0])); 718 | assert(!close(out[1])); 719 | input = fdopen(in[1], "w"); 720 | output = fdopen(out[0], "r"); 721 | setvbuf(input, NULL, _IONBF, 0); 722 | setvbuf(output, NULL, _IONBF, 0); 723 | 724 | /* Create a random value in one of our auto variables; we will later look 725 | * for this value by inspecting the core file with gdb. 726 | */ 727 | srand(time(0)); 728 | dummy = random(); 729 | cmp = ~dummy; 730 | 731 | /* Start some threads that should show up in our core dump; this is 732 | * complicated by the fact that we do not want our threads to perform any 733 | * system calls. So, they are busy looping and checking a volatile 734 | * state variable, instead. 735 | */ 736 | puts("Starting threads"); 737 | pthread_create(&thread, 0, Busy, (void *)&state1); 738 | pthread_create(&thread, 0, Busy, (void *)&state2); 739 | while (state1 != RUNNING || state2 != RUNNING) { 740 | usleep(100 * 1000); 741 | } 742 | 743 | const char *core_test = "core-test"; 744 | const char *core_test_gz = "core-test.gz"; 745 | for (loop = 0; loop < 2; loop++) { 746 | /* Prepare to create a core dump for the current process */ 747 | printf("loop %d: Writing core file to \"%s\"\n", loop, core_test); 748 | unlink(core_test); 749 | 750 | /* Check whether limits work correctly */ 751 | rc = (loop ? MyWriteCoreDumpLimited : WriteCoreDumpLimited)(core_test, 0); 752 | assert(!rc); 753 | assert(stat(core_test, &statBuf) < 0); 754 | rc = (loop ? MyWriteCoreDumpLimited : WriteCoreDumpLimited)(core_test, 256); 755 | assert(!rc); 756 | assert(!stat(core_test, &statBuf)); 757 | assert(statBuf.st_size == 256); 758 | assert(!unlink(core_test)); 759 | 760 | /* Check whether prioritized limiting works correctly */ 761 | if (loop) { 762 | puts("Checking priority limited core files of size 0"); 763 | rc = WriteCoreDumpLimitedByPriority(core_test, 0); 764 | assert(!rc); 765 | assert(stat(core_test, &statBuf) < 0); 766 | puts( 767 | "Checking priority limited core files of size 256. " 768 | "This should truncate the header."); 769 | rc = WriteCoreDumpLimitedByPriority(core_test, 256); 770 | assert(!rc); 771 | assert(!stat(core_test, &statBuf)); 772 | assert(statBuf.st_size == 256); 773 | puts( 774 | "Checking priority limited core files of size 60000. " 775 | "This will include a couple of complete segments as well as " 776 | "an incomplete segment."); 777 | rc = WriteCoreDumpLimitedByPriority(core_test, 60000); 778 | assert(!rc); 779 | assert(!stat(core_test, &statBuf)); 780 | assert(statBuf.st_size == 60000); 781 | CheckWithReadElf(input, output, core_test, "", "cat", ""); 782 | CheckPrioritizationWithReadElf(input, output, core_test); 783 | assert(!unlink(core_test)); 784 | } 785 | 786 | /* Check wether compression works */ 787 | puts("Checking compressed core files"); 788 | rc = (loop ? MyWriteCompressedCoreDump : WriteCompressedCoreDump)(core_test, SIZE_MAX, COREDUMPER_GZIP_COMPRESSED, 789 | &compressor); 790 | assert(!rc); 791 | assert(compressor); 792 | assert(strstr(compressor->compressor, "gzip")); 793 | assert(!strcmp(compressor->suffix, ".gz")); 794 | CheckWithReadElf(input, output, core_test, compressor->suffix, compressor->compressor, "-d"); 795 | assert(!unlink(core_test_gz)); 796 | 797 | /* Check wether fallback to uncompressed core files works */ 798 | puts("Checking fallback to uncompressed core files"); 799 | my_compressor[1].compressor = NULL; /* Disable uncompressed files */ 800 | rc = (loop ? MyWriteCompressedCoreDump : WriteCompressedCoreDump)(core_test, SIZE_MAX, my_compressor, &compressor); 801 | assert(rc); 802 | assert(!compressor->compressor); 803 | my_compressor[1].compressor = ""; /* Enable uncompressed files */ 804 | rc = (loop ? MyWriteCompressedCoreDump : WriteCompressedCoreDump)(core_test, SIZE_MAX, my_compressor, &compressor); 805 | assert(!rc); 806 | assert(compressor->compressor); 807 | assert(!*compressor->compressor); 808 | CheckWithReadElf(input, output, core_test, "", "cat", ""); 809 | assert(!unlink(core_test)); 810 | 811 | /* Check if additional notes are written correctly to the core file */ 812 | puts("Checking extra notes in core files"); 813 | ClearCoreDumpParameters(¬e_params); 814 | assert(!SetCoreDumpNotes(¬e_params, extra_notes, kExtraNotesCount)); 815 | assert(!SetCoreDumpLimited(¬e_params, 0x10000)); 816 | rc = (loop ? MyWriteCoreDumpWith : WriteCoreDumpWith)(¬e_params, core_test); 817 | assert(!rc); 818 | CheckWithReadElf(input, output, core_test, "", "cat", ""); 819 | CheckExtraNotesWithReadElf(input, output, core_test); 820 | assert(!unlink(core_test)); 821 | 822 | /* Check callback function handling */ 823 | puts("Checking callback functions"); 824 | ClearCoreDumpParameters(¬e_params); 825 | int count = 0; 826 | assert(!SetCoreDumpCallback(¬e_params, MyCallback, &count)); 827 | assert(!SetCoreDumpLimited(¬e_params, 0x10000)); 828 | rc = (loop ? MyWriteCoreDumpWith : WriteCoreDumpWith)(¬e_params, core_test); 829 | assert(!rc); 830 | CheckWithReadElf(input, output, core_test, "", "cat", ""); 831 | assert(count == 1); 832 | assert(!unlink(core_test)); 833 | count = -1; 834 | rc = (loop ? MyWriteCoreDumpWith : WriteCoreDumpWith)(¬e_params, core_test); 835 | assert(rc); 836 | assert(count == -1); 837 | assert(unlink(core_test) == -1 && errno == ENOENT); 838 | 839 | /* Create a full-size core file */ 840 | puts("Checking uncompressed core files"); 841 | rc = (loop ? MyWriteCoreDump : WriteCoreDump)(core_test); 842 | assert(!rc); 843 | CheckWithReadElf(input, output, core_test, "", "cat", ""); 844 | CheckWithGDB(input, output, core_test, &dummy, cmp); 845 | 846 | unlink(core_test); 847 | } 848 | 849 | /* Stop our threads */ 850 | puts("Stopping threads"); 851 | state1 = DEAD; 852 | state2 = DEAD; 853 | 854 | /* Kill bash process */ 855 | kill(SIGTERM, pid); 856 | fclose(input); 857 | fclose(output); 858 | 859 | return; 860 | } 861 | 862 | int main(int argc, char *argv[]) { 863 | static int bloat[1024 * 1024]; 864 | int i; 865 | 866 | /* This unittest parses the output from "readelf" and "gdb" in order to 867 | * verify that the core files look correct. And unfortunately, some of 868 | * the messages for these programs have been localized, so the unittest 869 | * cannot always find the text that it is looking for. 870 | * Let's just force everything back to English: 871 | */ 872 | putenv(strdup("LANGUAGE=C")); 873 | putenv(strdup("LC_ALL=C")); 874 | 875 | /* Make our RSS a little bigger, so that we can test codepaths that do not 876 | * trigger for very small core files. Also, make sure that this data is 877 | * not easily compressible nor in a read-only memory segment. 878 | */ 879 | for (i = 0; i < sizeof(bloat) / sizeof(int); i++) { 880 | bloat[i] = rand(); 881 | } 882 | 883 | TestCoreDump(); 884 | puts("PASS"); 885 | return 0; 886 | } 887 | -------------------------------------------------------------------------------- /test/linux_syscall_support_unittest.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2007-2008, Google Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of Google Inc. nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | // --- 31 | // Author: Markus Gutschke 32 | 33 | // Include linux_syscall_support.h as the first file, so we will compilation 34 | // errors if it has unexpected dependencies on other header files. 35 | #include "linux_syscall_support.h" 36 | 37 | #include 38 | 39 | // Used to count the number of check failures 40 | static int check_failures = 0; 41 | 42 | #define CHECK(cond) \ 43 | do { \ 44 | if (!(cond)) { \ 45 | puts("Check failed: " #cond); \ 46 | ++check_failures; \ 47 | } \ 48 | } while (0) 49 | 50 | // We need to do some "namespace" magic to be able to include both 51 | // and , which normally are mutually exclusive. 52 | // This is currently the only reason why this test has to be compiled as 53 | // C++ code. 54 | namespace linux_syscall_support { 55 | #include 56 | } 57 | #include 58 | 59 | namespace linux_syscall_support { 60 | // Define kernel data structures as known to glibc 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | 73 | // Set by the signal handler to show that we received a signal 74 | static int signaled; 75 | 76 | static void CheckStructures() { 77 | puts("CheckStructures..."); 78 | // Compare sizes of the kernel structures. This will allow us to 79 | // catch cases where linux_syscall_support.h defined structures that 80 | // are obviously different from the ones the kernel expects. This is 81 | // a little complicated, because glibc often deliberately defines 82 | // incompatible versions. We address this issue on a case-by-case 83 | // basis by including the appropriate linux-specific header files 84 | // within our own namespace, instead of within the global 85 | // namespace. Occasionally, this requires careful sorting of header 86 | // files, too (e.g. in the case of "stat.h"). And we provide cleanup 87 | // where necessary (e.g. in the case of "struct statfs"). This is 88 | // far from perfect, but in the worst case, it'll lead to false 89 | // error messages that need to be fixed manually. Unfortunately, 90 | // there are a small number of data structures (e.g "struct 91 | // kernel_old_sigaction") that we cannot test at all, as glibc does 92 | // not have any definitions for them. 93 | CHECK(sizeof(struct iovec) == sizeof(struct kernel_iovec)); 94 | CHECK(sizeof(struct msghdr) == sizeof(struct kernel_msghdr)); 95 | CHECK(sizeof(struct pollfd) == sizeof(struct kernel_pollfd)); 96 | CHECK(sizeof(struct rlimit) == sizeof(struct kernel_rlimit)); 97 | CHECK(sizeof(struct rusage) == sizeof(struct kernel_rusage)); 98 | CHECK(sizeof(struct sigaction) == sizeof(struct kernel_sigaction) 99 | // glibc defines an excessively large sigset_t. Compensate for it: 100 | + sizeof(((struct sigaction *)0)->sa_mask) - KERNEL_NSIG / 8 101 | #ifdef __mips__ 102 | + 2 * sizeof(int) 103 | #endif 104 | ); 105 | CHECK(sizeof(struct sockaddr) == sizeof(struct kernel_sockaddr)); 106 | CHECK(sizeof(struct stat) == sizeof(struct kernel_stat)); 107 | CHECK(sizeof(struct statfs) == sizeof(struct kernel_statfs) 108 | #ifdef __USE_FILE_OFFSET64 109 | // glibc sometimes defines 64-bit wide fields in "struct statfs" 110 | // even though this is just the 32-bit version of the structure. 111 | + 5 * (sizeof(((struct statfs *)0)->f_blocks) - sizeof(unsigned)) 112 | #endif 113 | ); 114 | CHECK(sizeof(struct timespec) == sizeof(struct kernel_timespec)); 115 | #ifndef __x86_64__ 116 | CHECK(sizeof(struct stat64) == sizeof(struct kernel_stat64)); 117 | CHECK(sizeof(struct statfs64) == sizeof(struct kernel_statfs64)); 118 | #endif 119 | } 120 | 121 | #ifdef __mips__ 122 | #define ZERO_SIGACT \ 123 | { 0 } 124 | #else 125 | #define ZERO_SIGACT \ 126 | { \ 127 | { 0 } \ 128 | } 129 | #endif 130 | 131 | static void SigHandler(int signum) { 132 | if (signaled) { 133 | // Caller will report an error, as we cannot do so from a signal handler 134 | signaled = -1; 135 | } else { 136 | signaled = signum; 137 | } 138 | return; 139 | } 140 | 141 | static void SigAction(int signum, siginfo_t *si, void *arg) { SigHandler(signum); } 142 | 143 | static void Sigaction() { 144 | puts("Sigaction..."); 145 | int signum = SIGPWR; 146 | for (int info = 0; info < 2; info++) { 147 | signaled = 0; 148 | struct kernel_sigaction sa = ZERO_SIGACT, old, orig; 149 | CHECK(!sys_sigaction(signum, NULL, &orig)); 150 | if (info) { 151 | sa.sa_sigaction_ = SigAction; 152 | } else { 153 | sa.sa_handler_ = SigHandler; 154 | } 155 | sa.sa_flags = SA_RESETHAND | SA_RESTART | (info ? SA_SIGINFO : 0); 156 | CHECK(!sys_sigemptyset(&sa.sa_mask)); 157 | CHECK(!sys_sigaction(signum, &sa, &old)); 158 | CHECK(!memcmp(&old, &orig, sizeof(struct kernel_sigaction))); 159 | CHECK(!sys_sigaction(signum, NULL, &old)); 160 | #if defined(__i386__) || defined(__x86_64__) 161 | old.sa_restorer = sa.sa_restorer; 162 | old.sa_flags &= ~SA_RESTORER; 163 | #endif 164 | CHECK(!memcmp(&old, &sa, sizeof(struct kernel_sigaction))); 165 | struct kernel_sigset_t pending; 166 | CHECK(!sys_sigpending(&pending)); 167 | CHECK(!sys_sigismember(&pending, signum)); 168 | struct kernel_sigset_t mask, oldmask; 169 | CHECK(!sys_sigemptyset(&mask)); 170 | CHECK(!sys_sigaddset(&mask, signum)); 171 | CHECK(!sys_sigprocmask(SIG_BLOCK, &mask, &oldmask)); 172 | CHECK(!sys_kill(sys_getpid(), signum)); 173 | CHECK(!sys_sigpending(&pending)); 174 | CHECK(sys_sigismember(&pending, signum)); 175 | CHECK(!signaled); 176 | CHECK(!sys_sigfillset(&mask)); 177 | CHECK(!sys_sigdelset(&mask, signum)); 178 | CHECK(sys_sigsuspend(&mask) == -1); 179 | CHECK(signaled == signum); 180 | CHECK(!sys_sigaction(signum, &orig, NULL)); 181 | CHECK(!sys_sigprocmask(SIG_SETMASK, &oldmask, NULL)); 182 | } 183 | } 184 | 185 | static void OldSigaction() { 186 | #if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__PPC__) || \ 187 | (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) 188 | puts("OldSigaction..."); 189 | int signum = SIGPWR; 190 | for (int info = 0; info < 2; info++) { 191 | signaled = 0; 192 | struct kernel_old_sigaction sa = ZERO_SIGACT, old, orig; 193 | CHECK(!sys__sigaction(signum, NULL, &orig)); 194 | if (info) { 195 | sa.sa_sigaction_ = SigAction; 196 | } else { 197 | sa.sa_handler_ = SigHandler; 198 | } 199 | sa.sa_flags = SA_RESETHAND | SA_RESTART | (info ? SA_SIGINFO : 0); 200 | memset(&sa.sa_mask, 0, sizeof(sa.sa_mask)); 201 | CHECK(!sys__sigaction(signum, &sa, &old)); 202 | CHECK(!memcmp(&old, &orig, sizeof(struct kernel_old_sigaction))); 203 | CHECK(!sys__sigaction(signum, NULL, &old)); 204 | #ifndef __mips__ 205 | old.sa_restorer = sa.sa_restorer; 206 | #endif 207 | CHECK(!memcmp(&old, &sa, sizeof(struct kernel_old_sigaction))); 208 | unsigned long pending; 209 | CHECK(!sys__sigpending(&pending)); 210 | CHECK(!(pending & (1UL << (signum - 1)))); 211 | unsigned long mask, oldmask; 212 | mask = 1 << (signum - 1); 213 | CHECK(!sys__sigprocmask(SIG_BLOCK, &mask, &oldmask)); 214 | CHECK(!sys_kill(sys_getpid(), signum)); 215 | CHECK(!sys__sigpending(&pending)); 216 | CHECK(pending & (1UL << (signum - 1))); 217 | CHECK(!signaled); 218 | mask = ~mask; 219 | CHECK(sys__sigsuspend( 220 | #ifndef __PPC__ 221 | &mask, 0, 222 | #endif 223 | mask) == -1); 224 | CHECK(signaled == signum); 225 | CHECK(!sys__sigaction(signum, &orig, NULL)); 226 | CHECK(!sys__sigprocmask(SIG_SETMASK, &oldmask, NULL)); 227 | } 228 | #endif 229 | } 230 | 231 | template 232 | static void AlmostEquals(A a, B b) { 233 | double d = 0.0 + a - b; 234 | if (d < 0) { 235 | d = -d; 236 | } 237 | double avg = a / 2.0 + b / 2.0; 238 | if (avg < 4096) { 239 | // Round up to a minimum size. Otherwise, even minute changes could 240 | // trigger a false positive. 241 | avg = 4096; 242 | } 243 | // Check that a and b are within one percent of each other. 244 | CHECK(d / avg < 0.01); 245 | } 246 | 247 | static void StatFs() { 248 | puts("StatFs..."); 249 | struct statfs64 libc_statfs; 250 | struct kernel_statfs kernel_statfs; 251 | CHECK(!statfs64("/", &libc_statfs)); 252 | CHECK(!sys_statfs("/", &kernel_statfs)); 253 | CHECK(libc_statfs.f_type == kernel_statfs.f_type); 254 | CHECK(libc_statfs.f_bsize == kernel_statfs.f_bsize); 255 | CHECK(libc_statfs.f_blocks == kernel_statfs.f_blocks); 256 | AlmostEquals(libc_statfs.f_bfree, kernel_statfs.f_bfree); 257 | AlmostEquals(libc_statfs.f_bavail, kernel_statfs.f_bavail); 258 | CHECK(libc_statfs.f_files == kernel_statfs.f_files); 259 | AlmostEquals(libc_statfs.f_ffree, kernel_statfs.f_ffree); 260 | CHECK(libc_statfs.f_fsid.__val[0] == kernel_statfs.f_fsid.val[0]); 261 | CHECK(libc_statfs.f_fsid.__val[1] == kernel_statfs.f_fsid.val[1]); 262 | CHECK(libc_statfs.f_namelen == kernel_statfs.f_namelen); 263 | } 264 | 265 | static void StatFs64() { 266 | #if defined(__i386__) || defined(__ARM_ARCH_3__) || (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) 267 | puts("StatFs64..."); 268 | struct statfs64 libc_statfs; 269 | struct kernel_statfs64 kernel_statfs; 270 | CHECK(!statfs64("/", &libc_statfs)); 271 | CHECK(!sys_statfs64("/", &kernel_statfs)); 272 | CHECK(libc_statfs.f_type == kernel_statfs.f_type); 273 | CHECK(libc_statfs.f_bsize == kernel_statfs.f_bsize); 274 | CHECK(libc_statfs.f_blocks == kernel_statfs.f_blocks); 275 | AlmostEquals(libc_statfs.f_bfree, kernel_statfs.f_bfree); 276 | AlmostEquals(libc_statfs.f_bavail, kernel_statfs.f_bavail); 277 | CHECK(libc_statfs.f_files == kernel_statfs.f_files); 278 | AlmostEquals(libc_statfs.f_ffree, kernel_statfs.f_ffree); 279 | CHECK(libc_statfs.f_fsid.__val[0] == kernel_statfs.f_fsid.val[0]); 280 | CHECK(libc_statfs.f_fsid.__val[1] == kernel_statfs.f_fsid.val[1]); 281 | CHECK(libc_statfs.f_namelen == kernel_statfs.f_namelen); 282 | #endif 283 | } 284 | 285 | static void Stat() { 286 | static const char *const entries[] = {"/dev/null", "/bin/sh", "/", NULL}; 287 | puts("Stat..."); 288 | for (int i = 0; entries[i]; i++) { 289 | struct ::stat64 libc_stat; 290 | struct kernel_stat kernel_stat; 291 | CHECK(!::stat64(entries[i], &libc_stat)); 292 | CHECK(!sys_stat(entries[i], &kernel_stat)); 293 | // CHECK(libc_stat.st_dev == kernel_stat.st_dev); 294 | CHECK(libc_stat.st_ino == kernel_stat.st_ino); 295 | CHECK(libc_stat.st_mode == kernel_stat.st_mode); 296 | CHECK(libc_stat.st_nlink == kernel_stat.st_nlink); 297 | CHECK(libc_stat.st_uid == kernel_stat.st_uid); 298 | CHECK(libc_stat.st_gid == kernel_stat.st_gid); 299 | CHECK(libc_stat.st_rdev == kernel_stat.st_rdev); 300 | CHECK(libc_stat.st_size == kernel_stat.st_size); 301 | #if !defined(__i386__) && !defined(__ARM_ARCH_3__) && !defined(__PPC__) && \ 302 | !(defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) 303 | CHECK(libc_stat.st_blksize == kernel_stat.st_blksize); 304 | CHECK(libc_stat.st_blocks == kernel_stat.st_blocks); 305 | #endif 306 | CHECK(libc_stat.st_atime == kernel_stat.st_atime_); 307 | CHECK(libc_stat.st_mtime == kernel_stat.st_mtime_); 308 | CHECK(libc_stat.st_ctime == kernel_stat.st_ctime_); 309 | } 310 | } 311 | 312 | static void Stat64() { 313 | #if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__PPC__) || \ 314 | (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) 315 | puts("Stat64..."); 316 | static const char *const entries[] = {"/dev/null", "/bin/sh", "/", NULL}; 317 | for (int i = 0; entries[i]; i++) { 318 | struct ::stat64 libc_stat; 319 | struct kernel_stat64 kernel_stat; 320 | CHECK(!::stat64(entries[i], &libc_stat)); 321 | CHECK(!sys_stat64(entries[i], &kernel_stat)); 322 | CHECK(libc_stat.st_dev == kernel_stat.st_dev); 323 | CHECK(libc_stat.st_ino == kernel_stat.st_ino); 324 | CHECK(libc_stat.st_mode == kernel_stat.st_mode); 325 | CHECK(libc_stat.st_nlink == kernel_stat.st_nlink); 326 | CHECK(libc_stat.st_uid == kernel_stat.st_uid); 327 | CHECK(libc_stat.st_gid == kernel_stat.st_gid); 328 | CHECK(libc_stat.st_rdev == kernel_stat.st_rdev); 329 | CHECK(libc_stat.st_size == kernel_stat.st_size); 330 | CHECK(libc_stat.st_blksize == kernel_stat.st_blksize); 331 | CHECK(libc_stat.st_blocks == kernel_stat.st_blocks); 332 | CHECK(libc_stat.st_atime == kernel_stat.st_atime_); 333 | CHECK(libc_stat.st_mtime == kernel_stat.st_mtime_); 334 | CHECK(libc_stat.st_ctime == kernel_stat.st_ctime_); 335 | } 336 | #endif 337 | } 338 | 339 | } // namespace linux_syscall_support 340 | 341 | int main(int argc, char *argv[]) { 342 | linux_syscall_support::CheckStructures(); 343 | linux_syscall_support::Sigaction(); 344 | linux_syscall_support::OldSigaction(); 345 | linux_syscall_support::StatFs(); 346 | linux_syscall_support::StatFs64(); 347 | linux_syscall_support::Stat(); 348 | linux_syscall_support::Stat64(); 349 | 350 | if (check_failures == 0) 351 | puts("PASS"); 352 | else 353 | puts("FAIL"); 354 | return check_failures; 355 | } 356 | --------------------------------------------------------------------------------