*
* Authors: Adrian Szyndela <adrian.s@samsung.com>
* Łukasz Stelmach <l.stelmach@samsung.com>
+ * Rafał Pietruch <r.pietruch@samsung.com>
*/
#define _GNU_SOURCE 1
*
* Crash-stack is a single purpose program. Its duty is to show call stack
* of a crashed program. Crash-stack must be called with proper arguments:
- * either core dump file name or PID of a crashed program.
+ * PID of a crashed program.
*/
#include "crash-stack.h"
#include <dirent.h>
#include <elfutils/version.h>
#include <elfutils/libdwfl.h>
-/*
- * In case the program is compiled with core dumps, the license may switch to GPL2.
- */
-#ifdef WITH_CORE_DUMP
-#include <elfutils/libebl.h>
-#endif
#define BUF_SIZE (BUFSIZ)
#define HEXA 16
#define STR_ANONY "[anony]"
#define STR_ANONY_LEN 8
-static siginfo_t __siginfo;
static FILE *outputfile = NULL; ///< global output stream
static FILE *errfile = NULL; ///< global error stream
+static FILE *bufferfile = NULL; ///< buffer file for ordering
/**
* @brief definitions for getopt options: identifiers
enum {
OPT_PID,
OPT_TID,
+ OPT_SIGNUM,
OPT_OUTPUTFILE,
OPT_ERRFILE
};
const struct option opts[] = {
{ "pid", required_argument, 0, OPT_PID },
{ "tid", required_argument, 0, OPT_TID },
+ { "sig", required_argument, 0, OPT_SIGNUM },
{ "output", required_argument, 0, OPT_OUTPUTFILE },
{ "erroutput", required_argument, 0, OPT_ERRFILE },
{ 0, 0, 0, 0 }
const char *name, Dwarf_Addr address,
void *arg)
{
- if (name != NULL && name[0] == '[') {
- /* libdwfl couldn't get the module file - we will get it later from notes */
- Mappings *mappings = arg;
- if (mappings->elems < MAX_MAPPINGS_NUM) {
- size_t elems = mappings->elems;
- mappings->tab[elems].m_start = address;
- mappings->tab[elems].m_end = 0;
- mappings->tab[elems].m_offset = 0;
- mappings->tab[elems].m_name = NULL;
- mappings->tab[elems].m_fd = -1;
- mappings->tab[elems].m_elf = 0;
- mappings->elems++;
- }
- }
return DWARF_CB_OK;
}
-/**
- * @brief Reads a value of specified size from core dump file.
- *
- * @param core ELF handler for the core dump file
- * @param from the source address in the ELF file
- * @param size size of the value in bits
- * @param to the address of allocated memory, where the value will be copied
- */
-static void __get_value(Elf *core, const void *from, size_t size, void *to)
-{
- Elf_Type type = ELF_T_BYTE;
- switch (size) {
- case 8: type = ELF_T_BYTE; break;
- case 16: type = ELF_T_HALF; break;
- case 32: type = ELF_T_WORD; break;
- case 64: type = ELF_T_XWORD; break;
- default:
- fprintf(errfile, "__get_value for strange size: %llu\n", (unsigned long long)size);
- break;
- }
- Elf_Data out = {
- .d_buf = to,
- .d_type = type,
- .d_version = EV_CURRENT,
- .d_size = size/8,
- .d_off = 0,
- .d_align = 0
- };
- Elf_Data in = {
- .d_buf = (void*)(from),
- .d_type = out.d_type,
- .d_version = out.d_version,
- .d_size = out.d_size,
- .d_off = 0,
- .d_align = 0
- };
- Elf_Data *data;
- if (gelf_getclass(core) == ELFCLASS32)
- data = elf32_xlatetom(&out, &in, elf_getident(core, NULL)[EI_DATA]);
- else
- data = elf64_xlatetom(&out, &in, elf_getident(core, NULL)[EI_DATA]);
- if (data == NULL)
- fprintf(errfile, "failed to get value from core file\n");
-}
-
-/**
- * @brief gets number of values, page size, address size, values and names from ELF notes
- *
- * @remarks This is very specific for organization of notes part in ELF files
- *
- * @param elf ELF handler - may be core file
- * @param desc address of ELF notes descriptor
- * @param[out] values_cnt a place for number of values
- * @param[out] page_size a place for page size
- * @param[out] addr_size a place for address size
- * @param[out] values a place for address of values
- * @param[out] filenames a place for address of filenames
- */
-static void __parse_note_file(Elf *elf, const char *desc, uint64_t *values_cnt, uint64_t *page_size,
- size_t *addr_size, const char **values, const char **filenames)
-{
- *addr_size = gelf_fsize(elf, ELF_T_ADDR, 1, EV_CURRENT);
- __get_value(elf, desc, *addr_size*8, values_cnt);
- __get_value(elf, desc + *addr_size, *addr_size*8, page_size);
- /* First: triplets of <mapping-start> <mapping-end> <offset-in-pages>
- * count = values_cnt
- * Then the names of files.
- */
- *values = desc + 2 * *addr_size;
- *filenames = *values + 3 * *addr_size * *values_cnt;
-}
-
-/**
- * @brief Simple accessor for mapping items in ELF files
- *
- * @remarks This is very specific for organization of notes part in ELF files
- *
- * @param elf ELF handler - may be core file
- * @param addr_size size of addresses in this ELF
- * @param item address of mapping item to get data from
- * @param[out] mapping_start value of the start of the mapping
- * @param[out] mapping_end value of the end of the mapping
- * @param[out] offset_in_pages number of pages of offset in the file
- */
-static void __get_mapping_item(Elf *elf, size_t addr_size, const void *item,
- uint64_t *mapping_start, uint64_t *mapping_end, uint64_t *offset_in_pages)
-{
- __get_value(elf, item, addr_size*8, mapping_start);
- __get_value(elf, item + addr_size, addr_size*8, mapping_end);
- __get_value(elf, item + 2 * addr_size, addr_size*8, offset_in_pages);
-}
-
-void __find_symbol_in_elf(ProcInfo *proc_info, Dwarf_Addr mapping_start)
+static void __find_symbol_in_elf(ProcInfo *proc_info, Dwarf_Addr mapping_start)
{
Elf *elf;
int fd;
return -1;
/* check if status is D */
- if (fscanf(f, "%*d %*s %c", &status) != 1)
+ if (fscanf(f, "%*d %*s %c", &status) != 1) {
+ fclose(f);
return -1;
+ }
fclose(f);
return NULL;
}
- if (ptrace(PTRACE_GETSIGINFO, tid, NULL, &__siginfo) != 0) {
- fprintf(errfile, "ptrace GETSIGINFO failed: %m, pid=%d, tid=%d\n", pid, tid);
- return NULL;
- }
-
static const Dwfl_Callbacks proc_callbacks = {
.find_elf = dwfl_linux_proc_find_elf,
.find_debuginfo = dwfl_standard_find_debuginfo,
}
/**
- * @brief Opens libdwfl for using with core dump file
- *
- * @remarks This function will open core file regardless of WITH_CORE_DUMP setting.
- * It may help with detecting issues with using dwfl even without support
- * for dumps.
- *
- * @param core elf handler for the core dump file
- * @param core_file_name name of the core file; needed only for diagnostics
- * @return Dwfl handle
- */
-static Dwfl *__open_dwfl_with_core(Elf *core, const char *core_file_name)
-{
- static const Dwfl_Callbacks core_callbacks = {
- .find_elf = dwfl_build_id_find_elf,
- .find_debuginfo = dwfl_standard_find_debuginfo,
- .section_address = NULL,
- .debuginfo_path = NULL
- };
-
- Dwfl *dwfl = dwfl_begin(&core_callbacks);
- if (dwfl == NULL) {
- fprintf(errfile, "%s : Can't start dwfl (%s)\n", core_file_name, dwfl_errmsg(-1));
- return NULL;
- }
-
-#if _ELFUTILS_PREREQ(0,158)
- if (dwfl_core_file_report(dwfl, core, NULL) < 0)
-#else
- if (dwfl_core_file_report(dwfl, core) < 0)
-#endif
- {
- fprintf(errfile, "%s : dwfl report failed (%s)\n", core_file_name, dwfl_errmsg(-1));
- dwfl_end(dwfl);
- return NULL;
- }
-
-#if _ELFUTILS_PREREQ(0,158)
- if (dwfl_core_file_attach(dwfl, core) < 0) {
- fprintf(errfile, "%s : dwfl attach failed (%s)\n", core_file_name, dwfl_errmsg(-1));
- dwfl_end(dwfl);
- return NULL;
- }
-#endif
- return dwfl;
-}
-
-/**
* @brief Gets registers information for live process
*
* @param pid pid of the live process
}
/**
- * @brief Get information about the signal that stopped the process
- *
- * @param pid pid of the live process
- * @return 0 on success, -1 otherwise
+ * @brief Print signal number causing dump
*/
-static int __get_signal_ptrace(pid_t pid)
+static void __crash_stack_print_signal(int signo)
{
const char* const signal_table[] = {
[SIGHUP]="SIGHUP", [SIGINT]="SIGINT", [SIGQUIT]="SIGQUIT",
[SIGILL]="SIGILL", [SIGTRAP]="SIGTRAP", [SIGABRT]="SIGABRT",
- [SIGIOT]="SIGIOT", [SIGBUS]="SIGBUS", [SIGFPE]="SIGFPE",
+ /* [SIGIOT]="SIGIOT", */ [SIGBUS]="SIGBUS", [SIGFPE]="SIGFPE",
[SIGKILL]="SIGKILL", [SIGUSR1]="SIGUSR1", [SIGSEGV]="SIGSEGV",
[SIGUSR2]="SIGUSR2", [SIGPIPE]="SIGPIPE", [SIGALRM]="SIGALRM",
[SIGTERM]="SIGTERM", [SIGSTKFLT]="SIGSTKFLT", [SIGCHLD]="SIGCHLD",
[SIGTTIN]="SIGTTIN", [SIGTTOU]="SIGTTOU", [SIGURG]="SIGURG",
[SIGXCPU]="SIGXCPU", [SIGXFSZ]="SIGXFSZ", [SIGVTALRM]="SIGVTALRM",
[SIGPROF]="SIGPROF", [SIGWINCH]="SIGWINCH", [SIGIO]="SIGIO",
- [SIGPWR]="SIGPWR", [SIGSYS]="SIGSYS", [SIGUNUSED]="SIGUNUSED",
+ [SIGPWR]="SIGPWR", [SIGSYS]="SIGSYS", /* [SIGUNUSED]="SIGUNUSED", */
};
- printf("Signal: %d\n"
- "\t(%s)\n"
- "\tsi_code: %d\n",
- __siginfo.si_signo,
- signal_table[__siginfo.si_signo],
- __siginfo.si_code);
- switch (__siginfo.si_code) {
- case SI_TKILL:
- case SI_USER:
- printf("\tsignal sent by %s (sent by pid %d, uid %d)",
- __siginfo.si_code == SI_TKILL ? "tkill" : "kill",
- __siginfo.si_pid, __siginfo.si_uid);
- break;
- case SI_KERNEL:
- printf("\tsignal sent by the kernel\n");
- break;
- }
- return 0;
-}
-
-#ifdef WITH_CORE_DUMP
-/**
- * @brief Helper function for updating mappings.
- *
- * @remarks Old versions of libelf not always extract full information about modules.
- * For such cases we maintain mappings for every module. Those mappings
- * may be updated while reading notes from core file.
- *
- * @param mappings mappings database
- * @param mapping_start address of the mapped start of the module; module is identified
- * by mapping_start
- * @param mapping_end address of the end of the module; needed to compute module boundaries
- * @param offset offset within core file - unused
- * @param name file name of the module
- */
-static void __updateMapping(Mappings *mappings, uint64_t mapping_start, uint64_t mapping_end,
- uint64_t offset, const char *name)
-{
- int i;
- for (i = 0; i < mappings->elems; i++) {
- if (mappings->tab[i].m_start == mapping_start) {
- mappings->tab[i].m_end = mapping_end;
- mappings->tab[i].m_name = name;
- mappings->tab[i].m_offset = offset;
- mappings->tab[i].m_fd = open(name, O_RDONLY);
- mappings->tab[i].m_elf = elf_begin(mappings->tab[i].m_fd, ELF_C_READ_MMAP, NULL);
- return;
- }
- }
-}
-#endif
-
-/**
- * @brief Gets registers from core dump
- *
- * @param core ELF handler for the core dump file
- * @param core_file_name name of the core file; needed only for diagnostics
- * @param mappings mappings database
- * @return notes handler, NULL on error
- */
-static Elf_Data *__get_registers_core(Elf *core, const char *core_file_name, Mappings *mappings)
-{
- Elf_Data *notes = NULL;
- /* The part below uses libebl. In case WITH_CORE_DUMP is enabled, the license
- * may switch to GPL2.
- */
-#ifdef WITH_CORE_DUMP
- GElf_Phdr mem;
- GElf_Phdr *phdr = gelf_getphdr(core, 0, &mem);
-
- if (phdr == NULL || phdr->p_type != PT_NOTE) {
- fprintf(errfile, "%s : Missing note section at the first position in core file\n",
- core_file_name);
- return NULL;
- }
-
- notes = elf_getdata_rawchunk(core, phdr->p_offset, phdr->p_filesz, ELF_T_NHDR);
- if (notes == NULL) {
- fprintf(errfile, "%s : error getting notes (%s)\n", core_file_name, dwfl_errmsg(-1));
- return NULL;
- }
-
- Ebl *ebl = ebl_openbackend(core);
- if (ebl == NULL) {
- fprintf(errfile, "%s : Can't initialize ebl\n", core_file_name);
- return NULL;
+ if (SIGHUP > signo || signo > SIGSYS) {
+ fprintf(errfile, "Invalid signal number: %d\n", signo);
+ return;
}
- GElf_Nhdr nhdr;
- size_t name_pos;
- size_t desc_pos;
- size_t pos = 0;
- size_t new_pos = 0;
- int got_regs = 0;
- /* registers should be in the first note! */
- while ((new_pos = gelf_getnote(notes, pos, &nhdr, &name_pos, &desc_pos)) > 0) {
- if (nhdr.n_type == NT_PRSTATUS && !got_regs) {
- GElf_Word regs_offset;
- size_t nregloc;
- const Ebl_Register_Location *reglocs;
- size_t nitems;
- const Ebl_Core_Item *items;
-
- got_regs = 1;
-
- if (0 == ebl_core_note(ebl, &nhdr, "CORE", ®s_offset, &nregloc,
- ®locs, &nitems, &items)) {
- fprintf(errfile,
- "%s : error parsing notes (built with different build of libebl?)\n",
- core_file_name);
- return NULL;
- }
-
- const char *regs_location = (const char *)(notes->d_buf) + pos + desc_pos
- + regs_offset;
- unsigned i;
-
- for (i = 0; i < nregloc; i++) {
- const char *register_location = regs_location + reglocs[i].offset;
- int regnum;
- for (regnum = reglocs[i].regno;
- regnum < reglocs[i].regno + reglocs[i].count;
- regnum++) {
- char regname[5];
- int bits, type;
- const char *prefix = 0;
- const char *setname = 0;
-
- ssize_t ret = ebl_register_info(ebl, regnum, regname,
- sizeof(regname), &prefix, &setname,
- &bits, &type);
- if (ret < 0) {
- fprintf(errfile, "%s : can't get register info\n", core_file_name);
- return NULL;
- }
- void *place_for_reg_value = _get_place_for_register_value(regname, regnum);
-
- if (place_for_reg_value != NULL)
- __get_value(core, register_location, bits, place_for_reg_value);
-
- register_location += bits / 8 + reglocs[i].pad;
- }
- }
- } else if (nhdr.n_type == NT_FILE) {
- uint64_t values_cnt = 0, page_size = 0;
- const char *values;
- const char *filenames;
- size_t addr_size = 0;
-
- __parse_note_file(core, notes->d_buf + desc_pos, &values_cnt, &page_size,
- &addr_size, &values, &filenames);
-
- int ii;
- /* First: triplets of <mapping-start> <mapping-end> <offset-in-pages>
- * count = values_cnt
- * Then the names of files.
- */
- for (ii = 0; ii < values_cnt; ii++) {
- uint64_t mapping_start = 0, mapping_end = 0, offset_in_pages = 0;
- const char *item = values + 3 * addr_size * ii;
-
- __get_mapping_item(core, addr_size, item, &mapping_start, &mapping_end,
- &offset_in_pages);
- __updateMapping(mappings, mapping_start, mapping_end,
- offset_in_pages*page_size, filenames);
- filenames += strlen(filenames)+1;
- }
- }
- pos = new_pos;
- }
- ebl_closebackend(ebl);
-#else
- fprintf(errfile, "Configured without support for core dump files\n");
-#endif
- return notes;
+ printf("Signal: %d\n"
+ " (%s)\n",
+ signo,
+ signal_table[signo]);
}
/**
Dwarf_Addr mapping_start = 0;
const char *fname = 0;
const char *module_name = dwfl_module_info(module, NULL, &mapping_start, NULL, NULL, NULL, &fname, NULL);
+
+ proc_info->module_offset = address - mapping_start;
+
if (!proc_info->module_name) {
if (fname)
proc_info->module_name = strdup(fname);
}
/**
- * @brief Resolves procedure and module names using elfutils
- *
- * @remarks This function is used in case that symbol name is not available by libelf,
- * e.g. when old libelf version does not take into account some modules.
- *
- * @param proc_info gathered call stack element
- * @param core ELF handler for the core dump file, NULL if live process analyzed
- * @param notes notes handler, NULL if live process analyzed
- */
-static void __resolve_symbols_from_elf(ProcInfo *proc_info, Elf *core, Elf_Data *notes)
-{
- GElf_Nhdr nhdr;
- size_t pos = 0;
- size_t new_pos = 0;
- size_t name_pos;
- size_t desc_pos;
-
- while ((new_pos = gelf_getnote(notes, pos, &nhdr, &name_pos, &desc_pos)) > 0) {
- if (nhdr.n_type == NT_FILE) {
- uint64_t values_cnt = 0, page_size = 0;
- const char *values;
- const char *filenames;
- size_t addr_size = 0;
-
- __parse_note_file(core, notes->d_buf + desc_pos, &values_cnt,
- &page_size, &addr_size, &values, &filenames);
-
- int ii;
- for (ii = 0; ii < values_cnt; ii++) {
- uint64_t mapping_start = 0, mapping_end = 0, offset_in_pages = 0;
- const char *item = values + 3 * addr_size * ii;
-
- __get_mapping_item(core, addr_size, item, &mapping_start, &mapping_end,
- &offset_in_pages);
-
- if (mapping_start <= proc_info->addr && proc_info->addr < mapping_end) {
- free(proc_info->module_name);
- proc_info->module_name = strdup(filenames);
- __find_symbol_in_elf(proc_info, mapping_start);
- return;
- }
-
- filenames += strlen(filenames)+1;
- }
- }
- pos = new_pos;
- }
-}
-
-/**
* @brief Checks if symbol starts with '_Z' prefix
*
* @param symbol string to compare
*
* @param proc_info gathered call stack element
* @param dwfl dwfl handler
- * @param core ELF handler for the core dump file, NULL if live process analyzed
* @param notes notes handler, NULL if live process analyzed
*/
-static void __resolve_symbols(ProcInfo *proc_info, Dwfl *dwfl, Elf *core, Elf_Data *notes)
+static void __resolve_symbols(ProcInfo *proc_info, Dwfl *dwfl)
{
__resolve_symbols_from_dwfl(proc_info, dwfl);
- if (core != NULL && (!proc_info->module_name || !proc_info->name))
- __resolve_symbols_from_elf(proc_info, core, notes);
-
if (is_symbol_demanglable(proc_info->name))
__demangle_symbols(proc_info);
}
fprintf(outputfile, "(0x%08x)", (int32_t)proc_info->addr);
if (proc_info->module_name != 0)
- fprintf(outputfile, " [%s]", proc_info->module_name);
+ fprintf(outputfile, " [%s] + 0x%x", proc_info->module_name, proc_info->module_offset);
fprintf(outputfile, "\n");
}
* @brief Prints call stack to the global outputfile.
*
* @param callstack gathered call stack database
- * @param pid PID of the live process, 0 if core dump file analyzed
+ * @param pid PID of the live process
*/
static void __print_callstack(Callstack *callstack, pid_t pid)
{
fprintf(outputfile, "\nCallstack Information");
- if (pid > 1)
- fprintf(outputfile, " (PID:%d)", pid);
+ fprintf(outputfile, " (PID:%d)", pid);
fprintf(outputfile, "\nCall Stack Count: %zu\n", callstack->elems);
size_t it;
*/
static void __crash_stack_print_exe(FILE* outputfile, pid_t pid)
{
+ int fd, ret;
char file_path[PATH_MAX];
- char link_path[PATH_MAX];
+ char cmd_path[PATH_MAX];
+
+ snprintf(cmd_path, PATH_MAX, "/proc/%d/cmdline", pid);
+ if ((fd = open(cmd_path, O_RDONLY)) < 0)
+ return;
- snprintf(link_path, PATH_MAX, "/proc/%d/exe", pid);
- if (readlink(link_path, file_path, PATH_MAX) == -1) {
+ if ((ret = read(fd, file_path, sizeof(file_path))) <= 0) {
+ close(fd);
return;
}
+ file_path[ret] = '\0';
+
fprintf(outputfile, "Executable File Path: %s\n", file_path);
+ close(fd);
}
/**
if (!strncmp(STR_ANONY, t_node->fpath, STR_ANONY_LEN)) {
t_node = t_node->next;
} else {
- printf( "%16lx %16lx %s %s\n",
+ fprintf(outputfile, "%16lx %16lx %s %s\n",
(unsigned long)t_node->startaddr,
(unsigned long)t_node->endaddr,
t_node->perm, t_node->fpath);
char file_path[PATH_MAX];
int fd;
- printf("\nMemory information\n");
+ fprintf(outputfile, "\nMemory information\n");
if ((fd = open("/proc/meminfo", O_RDONLY)) < 0) {
fprintf(errfile, "[crash-stack] cannot open /proc/meminfo\n");
while (fgets_fd(linebuf, BUF_SIZE, fd) != NULL) {
sscanf(linebuf, "%s %s %*s", infoname, memsize);
if (strcmp("MemTotal:", infoname) == 0) {
- printf("%s %8s KB\n", infoname, memsize);
+ fprintf(outputfile, "%s %8s KB\n", infoname, memsize);
} else if (strcmp("MemFree:", infoname) == 0) {
- printf("%s %8s KB\n", infoname, memsize);
+ fprintf(outputfile, "%s %8s KB\n", infoname, memsize);
} else if (strcmp("Buffers:", infoname) == 0) {
- printf("%s %8s KB\n", infoname, memsize);
+ fprintf(outputfile, "%s %8s KB\n", infoname, memsize);
} else if (strcmp("Cached:", infoname) == 0) {
- printf("%s %8s KB\n", infoname, memsize);
+ fprintf(outputfile, "%s %8s KB\n", infoname, memsize);
break;
}
}
while (fgets_fd(linebuf, BUF_SIZE, fd) != NULL) {
sscanf(linebuf, "%s %s %*s", infoname, memsize);
if (strcmp("VmPeak:", infoname) == 0) {
- printf("%s %8s KB\n", infoname,
+ fprintf(outputfile, "%s %8s KB\n", infoname,
memsize);
} else if (strcmp("VmSize:", infoname) == 0) {
- printf("%s %8s KB\n", infoname,
+ fprintf(outputfile, "%s %8s KB\n", infoname,
memsize);
} else if (strcmp("VmLck:", infoname) == 0) {
- printf("%s %8s KB\n", infoname,
+ fprintf(outputfile, "%s %8s KB\n", infoname,
memsize);
} else if (strcmp("VmPin:", infoname) == 0) {
- printf("%s %8s KB\n", infoname,
+ fprintf(outputfile, "%s %8s KB\n", infoname,
memsize);
} else if (strcmp("VmHWM:", infoname) == 0) {
- printf("%s %8s KB\n",
+ fprintf(outputfile, "%s %8s KB\n",
infoname, memsize);
} else if (strcmp("VmRSS:", infoname) == 0) {
- printf("%s %8s KB\n",
+ fprintf(outputfile, "%s %8s KB\n",
infoname, memsize);
} else if (strcmp("VmData:", infoname) == 0) {
- printf("%s %8s KB\n",
+ fprintf(outputfile, "%s %8s KB\n",
infoname, memsize);
} else if (strcmp("VmStk:", infoname) == 0) {
- printf("%s %8s KB\n",
+ fprintf(outputfile, "%s %8s KB\n",
infoname, memsize);
} else if (strcmp("VmExe:", infoname) == 0) {
- printf("%s %8s KB\n",
+ fprintf(outputfile, "%s %8s KB\n",
infoname, memsize);
} else if (strcmp("VmLib:", infoname) == 0) {
- printf("%s %8s KB\n",
+ fprintf(outputfile, "%s %8s KB\n",
infoname, memsize);
} else if (strcmp("VmPTE:", infoname) == 0) {
- printf("%s %8s KB\n",
+ fprintf(outputfile, "%s %8s KB\n",
infoname, memsize);
} else if (strcmp("VmSwap:", infoname) == 0) {
- printf("%s %8s KB\n",
+ fprintf(outputfile, "%s %8s KB\n",
infoname, memsize);
break;
}
}
/**
+ * @brief Print information saved in buffer (bufferfile)
+ *
+ * @param bufferfile File handle for reading saved info.
+ * @param outputfile File handle for printing report.
+ */
+static void __print_buffer_info(FILE* bufferfile, FILE *outputfile)
+{
+ int cnt;
+ char buf[1024];
+
+ if (fseek(bufferfile, 0, SEEK_SET) < 0) {
+ fprintf(errfile, "Failed to fseek\n");
+ return;
+ }
+ while ((cnt = fread(buf, sizeof(char), sizeof(buf), bufferfile)) != 0) {
+ if (cnt != fwrite(buf, sizeof(char), cnt, outputfile))
+ break;
+ }
+}
+
+/**
+ * @brief Check wchan of thread
+ *
+ * @param pid PID of the inspected process
+ * @param tid TID of the thread to check
+ */
+static int check_thread_wchan(int pid, int tid)
+{
+ int fd, cnt;
+ char path[PATH_MAX], buf[100];
+
+ snprintf(path, PATH_MAX, "/proc/%d/task/%d/wchan", pid, tid);
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ fprintf(errfile, "[crash-stack] cannot open %s\n", path);
+ return -errno;
+ }
+ cnt = read(fd, buf, sizeof(buf));
+ if (cnt == -1 || cnt == sizeof(buf)) {
+ fprintf(errfile, "[crash-stack] read %s error\n", path);
+ close(fd);
+ return -errno;
+ }
+ buf[cnt] = 0;
+ close(fd);
+
+ if (strncmp("do_coredump", buf, sizeof(buf)) == 0)
+ return tid;
+ else
+ return 0;
+}
+
+/**
+ * @brief Find crashed tid if tid was not offered
+ *
+ * @param pid PID of the inspected process
+ */
+static int find_crash_tid(int pid)
+{
+ int threadnum = 1;
+ int crash_tid = -1;
+ DIR *dir;
+ struct dirent entry;
+ struct dirent *dentry = NULL;
+ char task_path[PATH_MAX];
+ struct stat sb;
+
+ snprintf(task_path, PATH_MAX, "/proc/%d/task", pid);
+ if (stat(task_path, &sb) == -1) {
+ return -1;
+ }
+
+ threadnum = sb.st_nlink - 2;
+
+ if (threadnum > 1) {
+ dir = opendir(task_path);
+ if (!dir) {
+ fprintf(errfile, "[crash-stack] cannot open %s\n", task_path);
+ return -1;
+ } else {
+ while (readdir_r(dir, &entry, &dentry) == 0 && dentry) {
+ if (strcmp(dentry->d_name, ".") == 0 ||
+ strcmp(dentry->d_name, "..") == 0)
+ continue;
+ crash_tid = check_thread_wchan(pid,
+ atoi(dentry->d_name));
+ if (crash_tid > 0)
+ break;
+ }
+ closedir(dir);
+ return crash_tid;
+ }
+ }
+ return -1;
+}
+
+/**
* @brief Main function.
*
- * Main module accepts two forms of launching:
+ * Main module accepts should be launched with:
*
- * crash-stack core-dump-file
- * crash-stack --pid pid
+ * crash-stack --pid pid [--tid tid] [--sig sig]
*
- * The first form allows user to print call stack of a generated core dump file.
- * The second form allows connecting to a live process and displaying its call stack.
+ * It allows connecting to a live process and displaying its call stack.
* It might be also used for connecting to a process from system's core dump handler.
*/
int main(int argc, char **argv)
{
int c;
+ int signo = 0;
pid_t pid = 0;
pid_t tid = 0;
-
- const char *core_file_name;
+ char bufferfile_path[20] = "/tmp/crash.XXXXXX";
prctl(PR_SET_DUMPABLE, 0);
case OPT_TID:
tid = atoi(optarg);
break;
+ case OPT_SIGNUM:
+ signo = atoi(optarg);
+ break;
case OPT_OUTPUTFILE:
outputfile = fopen(optarg, "w");
break;
if (NULL == errfile) errfile = stderr;
if (NULL == outputfile) outputfile = stdout;
- if (tid == 0) tid = pid;
+ if (tid == 0) {
+ if ((tid = find_crash_tid(pid)) < 0)
+ tid = pid;
+ }
+
+ if (mkstemp(bufferfile_path) < 0) {
+ fprintf(errfile, "Failed to create buffer file.\n");
+ return errno;
+ }
+ bufferfile = fopen(bufferfile_path, "w+");
+ unlink(bufferfile_path);
- core_file_name = argv[optind];
argc -= optind;
elf_version(EV_CURRENT);
/* First, prepare dwfl and modules */
- Elf *core = NULL;
- int core_fd = -1;
Dwfl *dwfl = NULL;
- if (pid > 1)
- dwfl = __open_dwfl_with_pid(pid, tid);
- else {
- if (argc != 1) {
- fprintf(errfile,
- "Usage: %s [--output file] [--erroutput file] [--pid <pid> [--tid <tid>] | <core-file>]\n",
- argv[0]);
- return 1;
- }
+ /* Executable File Path */
+ __crash_stack_print_exe(outputfile, pid);
- core_fd = open(core_file_name, O_RDONLY);
- if (core_fd < 0) {
- perror(core_file_name);
- return 2;
- }
+ /* Signal information */
+ __crash_stack_print_signal(signo);
- core = elf_begin(core_fd, ELF_C_READ_MMAP, NULL);
- if (core == NULL) {
- fprintf(errfile, "%s : Can't open ELF (%s)\n", core_file_name, elf_errmsg(-1));
- return 3;
- }
+ /*
+ * Pre-ptrace info
+ * Memory, thread, map info should be print in advance
+ * because some of them will be lost after ptrace analysis
+ */
+
+ /* Memory information */
+ __crash_stack_print_meminfo(bufferfile, pid);
+
+ /* Threads */
+ __crash_stack_print_threads(bufferfile, pid, tid);
- dwfl = __open_dwfl_with_core(core, core_file_name);
+ /* Maps information */
+ __crash_stack_print_maps(bufferfile, pid);
+
+ if (pid > 1)
+ dwfl = __open_dwfl_with_pid(pid, tid);
+ else {
+ fprintf(errfile,
+ "Usage: %s [--output file] [--erroutput file] [--pid <pid> [--tid <tid>]]\n",
+ argv[0]);
+ return 1;
}
if (NULL == dwfl)
return 1111;
- Mappings mappings;
- mappings.elems = 0;
+ dwfl_getmodules(dwfl, __module_callback, NULL, 0);
- dwfl_getmodules(dwfl, __module_callback, &mappings, 0);
- Elf_Data *notes = 0;
-
- /* Executable File Path */
- __crash_stack_print_exe(outputfile, pid);
-
- /* Now, get registers */
- if (pid > 1) {
- if (-1 == __get_signal_ptrace(pid))
- return 4444;
- if (-1 == __get_registers_ptrace(tid))
- return 3333;
- } else {
- notes = __get_registers_core(core, core_file_name, &mappings);
- if (NULL == notes)
- return 2222;
- }
+ if (-1 == __get_registers_ptrace(tid))
+ return 3333;
/* Unwind call stack */
Callstack callstack;
callstack_constructor(&callstack);
- _create_crash_stack(dwfl, core, tid, &mappings, &callstack);
+ _create_crash_stack(dwfl, tid, &callstack);
size_t it;
for (it = 0; it != callstack.elems; ++it)
- __resolve_symbols(&callstack.proc[it], dwfl, core, notes);
+ __resolve_symbols(&callstack.proc[it], dwfl);
/* Print registers */
_crash_stack_print_regs(outputfile);
- /* Memory information */
- __crash_stack_print_meminfo(outputfile, pid);
-
- /* Threads */
- __crash_stack_print_threads(outputfile, pid, tid);
-
- /* Maps Information */
- __crash_stack_print_maps(outputfile, pid);
+ /* Print pre-ptrace info */
+ __print_buffer_info(bufferfile, outputfile);
/* Print the results */
__print_callstack(&callstack, tid);
callstack_destructor(&callstack);
dwfl_report_end(dwfl, NULL, NULL);
dwfl_end(dwfl);
- if (NULL != core) elf_end(core);
- if (-1 != core_fd) close(core_fd);
return 0;
}