summaryrefslogtreecommitdiff
path: root/src/crash-stack/crash-stack.c
diff options
context:
space:
mode:
authorAdrian 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
commit5816ad273656552adb9a35b862a1db3f9dba1072 (patch)
tree336a8844bc418f5a9b7e219f122621c89a2dfd3a /src/crash-stack/crash-stack.c
parentecb3a416f6d0ac41e6611bdd8f62b20197b3b672 (diff)
downloadcrash-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.c244
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);