diff options
author | Adrian Szyndela <adrian.s@samsung.com> | 2016-09-07 12:26:48 +0200 |
---|---|---|
committer | Ćukasz Stelmach <l.stelmach@samsung.com> | 2016-12-08 12:27:11 +0100 |
commit | 5816ad273656552adb9a35b862a1db3f9dba1072 (patch) | |
tree | 336a8844bc418f5a9b7e219f122621c89a2dfd3a /src/crash-stack/crash-stack.c | |
parent | ecb3a416f6d0ac41e6611bdd8f62b20197b3b672 (diff) | |
download | crash-worker-5816ad273656552adb9a35b862a1db3f9dba1072.tar.gz crash-worker-5816ad273656552adb9a35b862a1db3f9dba1072.tar.bz2 crash-worker-5816ad273656552adb9a35b862a1db3f9dba1072.zip |
Fixed coding style & added doxygen docs
This fixes and adds again commit 11c142a267540ee070db0c8c1dc47c022edd02a6.
Thus, this reverts commit 7ec36fc1820b0b3622f0c1d941439e906a4603ff
which reverted commit 11c142a267540ee070db0c8c1dc47c022edd02a6.
Change-Id: I944d1e9a664cbc38e84853f9a67b093ecb68754b
Diffstat (limited to 'src/crash-stack/crash-stack.c')
-rw-r--r-- | src/crash-stack/crash-stack.c | 244 |
1 files changed, 204 insertions, 40 deletions
diff --git a/src/crash-stack/crash-stack.c b/src/crash-stack/crash-stack.c index ef733e0..71c3114 100644 --- a/src/crash-stack/crash-stack.c +++ b/src/crash-stack/crash-stack.c @@ -1,7 +1,35 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Author: Adrian Szyndela <adrian.s@samsung.com> + */ +/** + * @file crash-stack.c + * @brief This file contains Main module of call stack unwinding program + * + * 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. + */ #include <stdlib.h> #include <stdio.h> #include <libelf.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 @@ -20,15 +48,21 @@ #include <sys/types.h> #include <sys/wait.h> -static FILE *outputfile = NULL; -static FILE *errfile = NULL; +static FILE *outputfile = NULL; ///< global output stream +static FILE *errfile = NULL; ///< global error stream +/** + * @brief definitions for getopt options: identifiers + */ enum { OPT_PID, OPT_OUTPUTFILE, OPT_ERRFILE }; +/** + * @brief definitions for getopt options: full specifications + */ const struct option opts[] = { { "pid", required_argument, 0, OPT_PID }, { "output", required_argument, 0, OPT_OUTPUTFILE }, @@ -36,10 +70,27 @@ const struct option opts[] = { { 0, 0, 0, 0 } }; +/* + * __cxa_demangle() is taken from libstdc++, however there is no header that we + * can take a declaration from. Importing through 'extern' allows using it. + */ +/// @cond false extern char *__cxa_demangle(const char *mangled_name, char *output_buffer, size_t *length, int *status); - -static int module_callback(Dwfl_Module *module, void **userdata, +///@endcond + +/** + * @brief A callback for dwfl_getmodules(). + * + * This callback is called once for every module discovered by dwfl_getmodules(). + * + * @param module the dwfl module + * @param userdata unused, required by dwfl_getmodules() + * @param name name of the module + * @param address address of the module + * @param arg 4th argument to dwfl_getmodules is passed here + */ +static int __module_callback(Dwfl_Module *module, void **userdata, const char *name, Dwarf_Addr address, void *arg) { @@ -60,7 +111,15 @@ static int module_callback(Dwfl_Module *module, void **userdata, return DWARF_CB_OK; } -static void getvalue(Elf *core, const void *from, size_t size, void *to) +/** + * @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) { @@ -69,7 +128,7 @@ static void getvalue(Elf *core, const void *from, size_t size, void *to) case 32: type = ELF_T_WORD; break; case 64: type = ELF_T_XWORD; break; default: - fprintf(stderr, "getvalue for strange size: %llu\n", (unsigned long long)size); + fprintf(stderr, "__get_value for strange size: %llu\n", (unsigned long long)size); break; } Elf_Data out = { @@ -97,12 +156,25 @@ static void getvalue(Elf *core, const void *from, size_t size, void *to) fprintf(errfile, "failed to get value from core file\n"); } -static void parse_note_file(Elf *elf, const char *desc, uint64_t *values_cnt, uint64_t *page_size, +/** + * @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); - getvalue(elf, desc, *addr_size*8, values_cnt); - getvalue(elf, desc + *addr_size, *addr_size*8, page_size); + __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. @@ -111,15 +183,40 @@ static void parse_note_file(Elf *elf, const char *desc, uint64_t *values_cnt, ui *filenames = *values + 3 * *addr_size * *values_cnt; } -static void get_mapping_item(Elf *elf, size_t addr_size, const void *item, +/** + * @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) { - getvalue(elf, item, addr_size*8, mapping_start); - getvalue(elf, item + addr_size, addr_size*8, mapping_end); - getvalue(elf, item + 2 * addr_size, addr_size*8, 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); } -static char *try_symbol_from_elfs(Elf *core, Elf_Data *notes, uintptr_t address, +/** + * @brief Tries to get symbol name from ELF files + * + * @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 core ELF handler for the core dump file + * @param notes notes descriptor from core file + * @param address address to get symbol name for + * @param[out] module_name name of the module of the found symbol. Not touched when + * symbol is not found + * @return name of the symbol or NULL if not found + */ +static char *__try_symbol_from_elfs(Elf *core, Elf_Data *notes, uintptr_t address, const char **module_name) { GElf_Nhdr nhdr; @@ -136,14 +233,16 @@ static char *try_symbol_from_elfs(Elf *core, Elf_Data *notes, uintptr_t address, 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); + __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); + __get_mapping_item(core, addr_size, item, &mapping_start, &mapping_end, + &offset_in_pages); if (mapping_start <= address && address < mapping_end) { Elf *elf; @@ -202,7 +301,13 @@ static char *try_symbol_from_elfs(Elf *core, Elf_Data *notes, uintptr_t address, return NULL; } -static Dwfl *open_dwfl_with_pid(pid_t pid) +/** + * @brief Opens libdwfl for using with live process + * + * @param pid pid of the process to attach to + * @return Dwfl handle + */ +static Dwfl *__open_dwfl_with_pid(pid_t pid) { int status; pid_t stopped_pid; @@ -249,11 +354,18 @@ static Dwfl *open_dwfl_with_pid(pid_t pid) return dwfl; } -/* - * 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. +/** + * @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 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, @@ -289,10 +401,16 @@ static Dwfl *open_dwfl_with_core(Elf *core, const char *core_file_name) return dwfl; } -static int get_registers_ptrace(pid_t pid) +/** + * @brief Gets registers information for live process + * + * @param pid pid of the live process + * @return 0 on success, -1 otherwise + */ +static int __get_registers_ptrace(pid_t pid) { struct iovec data; - data.iov_base = crash_stack_get_memory_for_ptrace_registers( &data.iov_len ); + data.iov_base = _crash_stack_get_memory_for_ptrace_registers(&data.iov_len); if (NULL == data.iov_base) { fprintf(errfile, "Cannot get memory for registers for ptrace (not implemented for this architecture\n"); @@ -304,13 +422,27 @@ static int get_registers_ptrace(pid_t pid) return -1; } - crash_stack_set_ptrace_registers(data.iov_base); + _crash_stack_set_ptrace_registers(data.iov_base); return 0; } #ifdef WITH_CORE_DUMP -static void updateMapping(Mappings *mappings, uint64_t mapping_start, uint64_t mapping_end, +/** + * @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; @@ -327,9 +459,20 @@ static void updateMapping(Mappings *mappings, uint64_t mapping_start, uint64_t m } #endif -static Elf_Data *get_registers_core(Elf *core, const char *core_file_name, Mappings *mappings) +/** + * @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); @@ -399,10 +542,10 @@ static Elf_Data *get_registers_core(Elf *core, const char *core_file_name, Mappi 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); + void *place_for_reg_value = _get_place_for_register_value(regname, regnum); if (place_for_reg_value != NULL) - getvalue(core, register_location, bits, place_for_reg_value); + __get_value(core, register_location, bits, place_for_reg_value); register_location += bits / 8 + reglocs[i].pad; } @@ -413,7 +556,7 @@ static Elf_Data *get_registers_core(Elf *core, const char *core_file_name, Mappi const char *filenames; size_t addr_size = 0; - parse_note_file(core, notes->d_buf + desc_pos, &values_cnt, &page_size, + __parse_note_file(core, notes->d_buf + desc_pos, &values_cnt, &page_size, &addr_size, &values, &filenames); int ii; @@ -425,9 +568,9 @@ static Elf_Data *get_registers_core(Elf *core, const char *core_file_name, Mappi 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, + __get_mapping_item(core, addr_size, item, &mapping_start, &mapping_end, &offset_in_pages); - updateMapping(mappings, mapping_start, mapping_end, + __updateMapping(mappings, mapping_start, mapping_end, offset_in_pages*page_size, filenames); filenames += strlen(filenames)+1; } @@ -441,7 +584,16 @@ static Elf_Data *get_registers_core(Elf *core, const char *core_file_name, Mappi return notes; } -static void printCallstack(Callstack *callstack, Dwfl *dwfl, Elf *core, pid_t pid, +/** + * @brief Prints call stack to the screen. + * + * @param callstack gathered call stack database + * @param dwfl dwfl handler + * @param core ELF handler for the core dump file, NULL if live process analyzed + * @param pid PID of the live process, 0 if core dump file analyzed + * @param notes notes handler, NULL if live process analyzed + */ +static void __print_callstack(Callstack *callstack, Dwfl *dwfl, Elf *core, pid_t pid, Elf_Data *notes) { fprintf(outputfile, "Call stack"); @@ -463,7 +615,7 @@ static void printCallstack(Callstack *callstack, Dwfl *dwfl, Elf *core, pid_t pi const char *module_name = dwfl_module_info(module, NULL, NULL, NULL, NULL, NULL, &fname, NULL); char *symbol_from_elf = 0; if (symbol == NULL) - symbol = symbol_from_elf = try_symbol_from_elfs(core, notes, callstack->tab[it], &fname); + symbol = symbol_from_elf = __try_symbol_from_elfs(core, notes, callstack->tab[it], &fname); if (symbol != 0 && symbol[0] == '_' && symbol[1] == 'Z') { int status = -1; @@ -489,6 +641,18 @@ static void printCallstack(Callstack *callstack, Dwfl *dwfl, Elf *core, pid_t pi } } +/** + * @brief Main function. + * + * Main module accepts two forms of launching: + * + * crash-stack core-dump-file + * crash-stack --pid pid + * + * 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 might be also used for connecting to a process from system's core dump handler. + */ int main(int argc, char **argv) { int c; @@ -526,7 +690,7 @@ int main(int argc, char **argv) Dwfl *dwfl = NULL; if (pid > 1) - dwfl = open_dwfl_with_pid(pid); + dwfl = __open_dwfl_with_pid(pid); else { if (argc != 1) { fprintf(errfile, @@ -547,7 +711,7 @@ int main(int argc, char **argv) return 3; } - dwfl = open_dwfl_with_core(core, core_file_name); + dwfl = __open_dwfl_with_core(core, core_file_name); } if (NULL == dwfl) @@ -556,15 +720,15 @@ int main(int argc, char **argv) Mappings mappings; mappings.elems = 0; - dwfl_getmodules(dwfl, module_callback, &mappings, 0); + dwfl_getmodules(dwfl, __module_callback, &mappings, 0); Elf_Data *notes = 0; /* Now, get registers */ if (pid > 1) { - if (-1 == get_registers_ptrace(pid)) + if (-1 == __get_registers_ptrace(pid)) return 3333; } else { - notes = get_registers_core(core, core_file_name, &mappings); + notes = __get_registers_core(core, core_file_name, &mappings); if (NULL == notes) return 2222; } @@ -572,10 +736,10 @@ int main(int argc, char **argv) /* Unwind call stack */ Callstack callstack; - create_crash_stack(dwfl, core, pid, &mappings, &callstack); + _create_crash_stack(dwfl, core, pid, &mappings, &callstack); /* Print the results */ - printCallstack(&callstack, dwfl, core, pid, notes); + __print_callstack(&callstack, dwfl, core, pid, notes); /* Clean up */ dwfl_report_end(dwfl, NULL, NULL); |