diff --git a/Makefile b/Makefile index a092b5e..fd2f4d9 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ NC= COLOR= endif -CXX = g++ +CXX = gcc CC = gcc RM = rm -f AR = ar rvs @@ -49,14 +49,14 @@ SRC = $(shell find . -name '*.cpp') #OBJ = $(patsubst %.cpp,%.o,$(addprefix $(BUILD_DIR)/,$(notdir $(SRC)))) OBJ = $(BUILD_DIR)/main.o -LIBS = -L./ -lpthread -luuid -ldl -ldw -lunwind -lunwind-x86_64 +LIBS = -L./ -lpthread -luuid -ldl -ldw -lunwind -lunwind-x86_64 -lstdc++ ATR_LIBS = ./build/libframework.a INCS = -I./ \ -I/usr/include/postgresql -I /usr/include/libxml2 -I./thirdparty -I/opt/swifttest/include -I/usr/include/GraphicsMagick \ -I./framework -I./framework/swi -I./framework/utils -I./framework/rmq -I./framework/logger -I$(BUILD_DIR) #FLAGS = -Wall -gdwarf-4 -fPIC -O3 -rdynamic -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -std=c++11 -FLAGS = -Wall -ggdb -fPIC -O3 -rdynamic -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -std=c++11 +FLAGS = -Wall -ggdb -fPIC -O3 -rdynamic -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS .PHONY: all clean $(BIN) @@ -68,6 +68,7 @@ $(ATR_LIBS): clean: ${RM} $(BUILD_DIR)/*.o $(BUILD_DIR)/*.dep $(BIN) $(BUILD_DIR)/prepare.bld $(RESULT_DIR)/prepare.res $(BUILD_DIR)/version.h + @make clean -C ./framework @if [ -z "$$(ls -A $(BUILD_DIR) 2>&1)" ]; then ${RM} -r $(BUILD_DIR); fi @if [ -z "$$(ls -A $(RESULT_DIR) 2>&1)" ]; then ${RM} -r $(RESULT_DIR); fi diff --git a/framework/Makefile b/framework/Makefile index b3eb1a1..bfeb573 100644 --- a/framework/Makefile +++ b/framework/Makefile @@ -16,7 +16,7 @@ NC= COLOR= endif -CXX = g++ +CXX = gcc CC = gcc RM = rm -f AR = ar rvs @@ -37,7 +37,7 @@ SRC = $(wildcard $(addsuffix /*.cpp,${VPATH})) OBJ = $(patsubst %.cpp,%.o,$(addprefix $(BUILD_DIR)/,$(notdir $(SRC)))) INCS = -I"../thirdparty" -I"./" -I"./utils" -I"./rmq" -I"/usr/include/json-c" -I"/usr/include/postgresql" -I /usr/include/libxml2 -I"/opt/swifttest/include" -I"../thirdparty/libswiftclient/src/lib" -FLAGS = -Wall -ggdb -fPIC -O3 -rdynamic -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -std=c++11 -DSOFT_BUILD=$(SOFT_BUILD) -DUSER_BUILD=$(USER) -DDATE_BUILD="$(DATE_BUILD)" +FLAGS = -Wall -ggdb -fPIC -O3 -rdynamic -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DSOFT_BUILD=$(SOFT_BUILD) -DUSER_BUILD=$(USER) -DDATE_BUILD="$(DATE_BUILD)" .PHONY: all clean diff --git a/framework/allocator.cpp b/framework/allocator.cpp index 15d498a..f7b6c9f 100644 --- a/framework/allocator.cpp +++ b/framework/allocator.cpp @@ -7,55 +7,69 @@ #include #include +#include +#include "common.h" #include "allocator.h" -void heap_free(pst_allocator& alloc, void* buff) +void heap_free(pst_allocator* alloc, void* buff) { - alloc.size -= malloc_usable_size(buff); + pthread_mutex_lock(&alloc->lock); + alloc->size -= malloc_usable_size(buff); + pthread_mutex_unlock(&alloc->lock); free(buff); } -void* heap_alloc(pst_allocator& alloc, uint32_t size) +void* heap_alloc(pst_allocator* alloc, uint32_t size) { void* buff = malloc(size); - alloc.size += malloc_usable_size(buff); + pthread_mutex_lock(&alloc->lock); + alloc->size += malloc_usable_size(buff); + pthread_mutex_unlock(&alloc->lock); return buff; } -void* heap_realloc(pst_allocator& alloc, void* buff, uint32_t new_size) +void* heap_realloc(pst_allocator* alloc, void* buff, uint32_t new_size) { - alloc.size -= malloc_usable_size(buff); + pthread_mutex_lock(&alloc->lock); + alloc->size -= malloc_usable_size(buff); void* new_buff = realloc(buff, new_size); - alloc.size += malloc_usable_size(new_buff); + alloc->size += malloc_usable_size(new_buff); + pthread_mutex_unlock(&alloc->lock); return new_buff; } -void pst_alloc_init(pst_allocator& alloc) +void pst_alloc_init(pst_allocator* alloc) { - alloc.type = ALLOC_HEAP; - alloc.base = NULL; - alloc.size = 0; + alloc->type = ALLOC_HEAP; + alloc->base = NULL; + alloc->size = 0; - alloc.alloc = heap_alloc; - alloc.free = heap_free; - alloc.realloc = heap_realloc; + alloc->alloc = heap_alloc; + alloc->free = heap_free; + alloc->realloc = heap_realloc; + + pthread_mutex_init(&alloc->lock, NULL); return; } -void pst_alloc_init_custom(pst_allocator& alloc, void* buff, uint32_t size) +void pst_alloc_init_custom(pst_allocator* alloc, void* buff, uint32_t size) { // TBD to implement custom allocator pst_alloc_init(alloc); return; } -void pst_alloc_fini(pst_allocator& alloc) +void pst_alloc_fini(pst_allocator* alloc) { - alloc.type = ALLOC_NONE; - alloc.base = NULL; - alloc.size = 0; + if(alloc->type == ALLOC_HEAP || alloc->type == ALLOC_CUSTOM) { + alloc->type = ALLOC_NONE; + alloc->base = NULL; + alloc->size = 0; + } + + pthread_mutex_destroy(&alloc->lock); } diff --git a/framework/allocator.h b/framework/allocator.h index afa4157..cda0e1d 100644 --- a/framework/allocator.h +++ b/framework/allocator.h @@ -5,17 +5,32 @@ * Author: nnosov */ -#ifndef FRAMEWORK_ALLOCATOR_H_ -#define FRAMEWORK_ALLOCATOR_H_ +#ifndef PST_ALLOCATOR_H_ +#define PST_ALLOCATOR_H_ -#include #include +#include +#include -typedef struct __pst_allocator pst_allocator; +// concatenation +#define CAT(a, ...) CAT2(a, __VA_ARGS__) +#define CAT2(a, ...) a ## __VA_ARGS__ -typedef void* (*pst_alloc_def)(pst_allocator& alloc, uint32_t size); -typedef void (*pst_free_def)(pst_allocator& alloc, void* buff); -typedef void* (*pst_realloc_def)(pst_allocator& alloc, void* buff, uint32_t new_size); +// declaration with initialization and parameters of initialization +#define pst_decl(TYPE, NAME, ...) \ + TYPE NAME; CAT2(TYPE, _init) (&NAME, __VA_ARGS__); + +// declaration with initialization +#define pst_decl0(TYPE, NAME) \ + TYPE NAME; CAT2(TYPE, _init) (&NAME); + +// allocation with initialization +#define pst_new(TYPE, NAME, ...) \ + TYPE* NAME; NAME = CAT2(TYPE, _new) (__VA_ARGS__); + +// de-initialization and deletion if was previously allocated +#define pst_fini(TYPE, NAME) \ + CAT2(TYPE, _fini) (NAME); typedef enum { ALLOC_NONE = 0, // not initialized @@ -23,18 +38,22 @@ typedef enum { ALLOC_CUSTOM = 2 // use custom allocator in predefined range of memory } pst_alloc_type; -typedef struct __pst_allocator { +typedef struct pst_allocator { + // methods + void* (*alloc)(pst_allocator* alloc, uint32_t size); + void (*free)(pst_allocator* alloc, void* buff); + void* (*realloc)(pst_allocator* alloc, void* buff, uint32_t new_size); + + // fields int type; void* base; uint32_t size; - // routines - pst_alloc_def alloc; - pst_free_def free; - pst_realloc_def realloc; + pthread_mutex_t lock; + } pst_allocator; -void pst_alloc_init(pst_allocator& alloc); -void pst_alloc_init_custom(pst_allocator& alloc, void* buff, uint32_t size); -void pst_alloc_fini(pst_allocator& alloc); +void pst_alloc_init(pst_allocator* alloc); +void pst_alloc_init_custom(pst_allocator* alloc, void* buff, uint32_t size); +void pst_alloc_fini(pst_allocator* alloc); -#endif /* FRAMEWORK_ALLOCATOR_H_ */ +#endif /* PST_ALLOCATOR_H_ */ diff --git a/framework/common.cpp b/framework/common.cpp index 1707246..4a9970a 100644 --- a/framework/common.cpp +++ b/framework/common.cpp @@ -19,67 +19,6 @@ #include "dwarf_operations.h" #include "registers.h" -bool dwarf_value::get_int(int64_t& v) -{ - switch (size) { - case 1: - v = *((int8_t*)value); - break; - case 2: - v = *((int16_t*)value); - break; - case 4: - v = *((int32_t*)value); - break; - case 8: - v = *((int64_t*)value); - break; - default: - return false; - break; - } - - return true; -} - -bool dwarf_value::get_uint(uint64_t& v) -{ - if(type & DWARF_TYPE_SIGNED) { - int64_t sig; - if(!get_int(sig)) { - return false; - } - v = llabs(sig); - } else { - return get_generic(v); - } - - return true; -} - -bool dwarf_value::get_generic(uint64_t& v) -{ - switch (size) { - case 1: - v = *((uint8_t*)value); - break; - case 2: - v = *((uint16_t*)value); - break; - case 4: - v = *((uint32_t*)value); - break; - case 8: - v = *((uint64_t*)value); - break; - default: - return false; - break; - } - - return true; -} - bool is_location_form(int form) { if (form == DW_FORM_block1 || form == DW_FORM_block2 || form == DW_FORM_block4 || form == DW_FORM_block || diff --git a/framework/common.h b/framework/common.h index e4aa50d..97073b3 100644 --- a/framework/common.h +++ b/framework/common.h @@ -1,8 +1,18 @@ -#pragma once +#ifndef __PST_COMMON_H__ +#define __PST_COMMON_H__ + +#include #define USE_LIBUNWIND +// TBD custom assertion if needed +#define pst_assert(expr) assert(expr) + +// platform-dependent address size +#define PST_GENERIC_SIZE (8) // for x86_64 architecture int32_t decode_sleb128(uint8_t *sleb128); uint32_t decode_uleb128(uint8_t *uleb128); bool is_location_form(int form); + +#endif // __PST_COMMON_H__ diff --git a/framework/context.cpp b/framework/context.cpp index 57ddd20..4b76d1f 100644 --- a/framework/context.cpp +++ b/framework/context.cpp @@ -13,140 +13,191 @@ #include "dwarf_operations.h" -extern SC_LogBase* logger; extern dwarf_reg_map reg_map[]; extern int regnum; -void __pst_context::print_stack(int max, uint64_t next_cfa) +pst_log logger; // logger for library +pst_allocator allocator; // custom allocator for PST library + +void clean_print(pst_context* ctx) { + ctx->buff[0] = 0; + ctx->offset = 0; +} + +void print_stack(pst_context* ctx, int max, uint64_t next_cfa) { - log(SEVERITY_DEBUG, "CFA = %#lX, NEXT_CFA = %#lX, SP = %#lX", cfa, next_cfa, sp); - clean_print(); + pst_log(SEVERITY_DEBUG, "CFA = %#lX, NEXT_CFA = %#lX, SP = %#lX", ctx->cfa, next_cfa, ctx->sp); + ctx->clean_print(ctx); int i = 0; - if(cfa > sp) { - print("Args: "); - for(; i < max && (cfa - i) > sp; ++i) { - print("#%d 0x%lX ", i, *(uint64_t*)(cfa - i)); + if(ctx->cfa > ctx->sp) { + ctx->print(ctx, "Args: "); + for(; i < max && (ctx->cfa - i) > ctx->sp; ++i) { + ctx->print(ctx, "#%d 0x%lX ", i, *(uint64_t*)(ctx->cfa - i)); } } - if((cfa - i) > next_cfa) { - print("Vars: "); - for(; i < max && (cfa - i) > next_cfa; ++i) { - print("#%d 0x%lX ", i, *(uint64_t*)(cfa - i)); + if((ctx->cfa - i) > next_cfa) { + ctx->print(ctx, "Vars: "); + for(; i < max && (ctx->cfa - i) > next_cfa; ++i) { + ctx->print(ctx, "#%d 0x%lX ", i, *(uint64_t*)(ctx->cfa - i)); } } } -void __pst_context::print_registers(int from, int to) +void print_registers(pst_context* ctx, int from, int to) { - clean_print(); + ctx->clean_print(ctx); for(int i = from; i < regnum && i <= to; ++i) { unw_word_t regval; - if(!unw_get_reg(curr_frame, reg_map[i].regno, ®val)) { - print("%s: %#lX ", reg_map[i].regname, regval); + if(!unw_get_reg(ctx->curr_frame, reg_map[i].regno, ®val)) { + ctx->print(ctx, "%s: %#lX ", reg_map[i].regname, regval); } else { - print("%s: ", reg_map[i].regname); + ctx->print(ctx, "%s: ", reg_map[i].regname); } } } -bool __pst_context::print(const char* fmt, ...) +bool print(pst_context* ctx, const char* fmt, ...) { bool nret = true; va_list args; va_start(args, fmt); - int size = sizeof(buff) - offset; - int ret = vsnprintf(buff + offset, size, fmt, args); + int size = sizeof(ctx->buff) - ctx->offset; + int ret = vsnprintf(ctx->buff + ctx->offset, size, fmt, args); if(ret >= size || ret < 0) { nret = false; } - offset += ret; + ctx->offset += ret; va_end(args); return nret; } -void __pst_context::log(SC_LogSeverity severity, const char* fmt, ...) +bool print_expr_block (pst_context* ctx, Dwarf_Op *exprs, int exprlen, Dwarf_Attribute* attr) { - uint32_t str_len = 0; - char str[PATH_MAX]; str[0] = 0; - va_list args; - va_start(args, fmt); - str_len += vsnprintf(str + str_len, sizeof(str) - str_len, fmt, args); - va_end(args); - - logger->Log(severity, "%s", str); -} - -uint32_t __pst_context::print_expr_block (Dwarf_Op *exprs, int len, char* buff, uint32_t buff_size, Dwarf_Attribute* attr) -{ - uint32_t offset = 0; - for (int i = 0; i < len; i++) { - //printf ("%s", (i + 1 < len ? ", " : "")); + ctx->clean_print(ctx); + for (int i = 0; i < exprlen; i++) { const dwarf_op_map* map = find_op_map(exprs[i].atom); if(map) { if(map->op_num >= DW_OP_breg0 && map->op_num <= DW_OP_breg16) { int32_t off = decode_sleb128((unsigned char*)&exprs[i].number); int regno = map->op_num - DW_OP_breg0; unw_word_t ptr = 0; - unw_get_reg(curr_frame, regno, &ptr); - //ptr += off; + unw_get_reg(ctx->curr_frame, regno, &ptr); - offset += snprintf(buff + offset, buff_size - offset, "%s(*%s%s%d) reg_value: 0x%lX", map->op_name, unw_regname(regno), off >=0 ? "+" : "", off, ptr); + ctx->print(ctx, "%s(*%s%s%d) reg_value: 0x%lX", map->op_name, unw_regname(regno), off >=0 ? "+" : "", off, ptr); } else if(map->op_num >= DW_OP_reg0 && map->op_num <= DW_OP_reg16) { unw_word_t value = 0; int regno = map->op_num - DW_OP_reg0; - unw_get_reg(curr_frame, regno, &value); - offset += snprintf(buff + offset, buff_size - offset, "%s(*%s) value: 0x%lX", map->op_name, unw_regname(regno), value); + unw_get_reg(ctx->curr_frame, regno, &value); + ctx->print(ctx, "%s(*%s) value: 0x%lX", map->op_name, unw_regname(regno), value); } else if(map->op_num == DW_OP_GNU_entry_value) { + if(!attr) { + pst_log(SEVERITY_ERROR, "No attribute of DW_OP_GNU_entry_value provided"); + return false; + } uint32_t value = decode_uleb128((unsigned char*)&exprs[i].number); - offset += snprintf(buff + offset, buff_size - offset, "%s(%u, ", map->op_name, value); + ctx->print(ctx, "%s(%u, ", map->op_name, value); Dwarf_Attribute attr_mem; if(!dwarf_getlocation_attr(attr, exprs, &attr_mem)) { Dwarf_Op *expr; size_t exprlen; if (dwarf_getlocation(&attr_mem, &expr, &exprlen) == 0) { - offset += print_expr_block (expr, exprlen, buff + offset, buff_size - offset, &attr_mem); - offset += snprintf(buff + offset, buff_size - offset, ") "); + ctx->print_expr(ctx, expr, exprlen, &attr_mem); + ctx->print(ctx, ") "); } else { - log(SEVERITY_ERROR, "Failed to get DW_OP_GNU_entry_value attr location"); + pst_log(SEVERITY_ERROR, "Failed to get DW_OP_GNU_entry_value attr location"); + return false; } } else { - log(SEVERITY_ERROR, "Failed to get DW_OP_GNU_entry_value attr expression"); + pst_log(SEVERITY_ERROR, "Failed to get DW_OP_GNU_entry_value attr expression"); + return false; } } else if(map->op_num == DW_OP_stack_value) { - offset += snprintf(buff + offset, buff_size - offset, "%s", map->op_name); + ctx->print(ctx, "%s", map->op_name); } else if(map->op_num == DW_OP_plus_uconst) { uint32_t value = decode_uleb128((unsigned char*)&exprs[i].number); - offset += snprintf(buff + offset, buff_size - offset, "%s(+%u) ", map->op_name, value); + ctx->print(ctx, "%s(+%u) ", map->op_name, value); } else if(map->op_num == DW_OP_bregx) { uint32_t regno = decode_uleb128((unsigned char*)&exprs[i].number); int32_t off = decode_sleb128((unsigned char*)&exprs[i].number2); unw_word_t ptr = 0; - unw_get_reg(curr_frame, regno, &ptr); + unw_get_reg(ctx->curr_frame, regno, &ptr); //ptr += off; - offset += snprintf(buff + offset, buff_size - offset, "%s(%s%s%d) reg_value = 0x%lX", map->op_name, unw_regname(regno), off >= 0 ? "+" : "", off, ptr); + ctx->print(ctx, "%s(%s%s%d) reg_value = 0x%lX", map->op_name, unw_regname(regno), off >= 0 ? "+" : "", off, ptr); } else if(map->op_num == DW_OP_regx) { int32_t reg = decode_sleb128((unsigned char*)&exprs[i].number); unw_word_t value = 0; - unw_get_reg(curr_frame, reg, &value); + unw_get_reg(ctx->curr_frame, reg, &value); - offset += snprintf(buff + offset, buff_size - offset, "%s(%s) value = 0x%lX", map->op_name, unw_regname(reg), value); + ctx->print(ctx, "%s(%s) value = 0x%lX", map->op_name, unw_regname(reg), value); } else if(map->op_num == DW_OP_addr) { - offset += snprintf(buff + offset, buff_size - offset, "%s value = %p", map->op_name, (void*)exprs[i].number); + ctx->print(ctx, "%s value = %p", map->op_name, (void*)exprs[i].number); } else if(map->op_num == DW_OP_fbreg) { int32_t off = decode_sleb128((unsigned char*)&exprs[i].number); - offset += snprintf(buff + offset, buff_size - offset, "%s(SP%s%d) ", map->op_name, off >=0 ? "+" : "", off); + ctx->print(ctx, "%s(SP%s%d) ", map->op_name, off >=0 ? "+" : "", off); } else { - offset += snprintf(buff + offset, buff_size - offset, "%s(0x%lX, 0x%lx) ", map->op_name, exprs[i].number, exprs[i].number2); + ctx->print(ctx, "%s(0x%lX, 0x%lx) ", map->op_name, exprs[i].number, exprs[i].number2); } } else { - offset += snprintf(buff + offset, buff_size - offset, "0x%hhX(0x%lX, 0x%lx)", exprs[i].atom, exprs[i].number, exprs[i].number2); + ctx->print(ctx, "0x%hhX(0x%lX, 0x%lx)", exprs[i].atom, exprs[i].number, exprs[i].number2); } } - return offset; + return true; +} + +void pst_context_init(pst_context* ctx, ucontext_t* hctx) +{ + // global + pst_alloc_init(&allocator); + pst_log_init_console(&logger); + + // methods + ctx->clean_print = clean_print; + ctx->print = print; + ctx->print_expr = print_expr_block; + ctx->print_registers = print_registers; + ctx->print_stack = print_stack; + + // fields + ctx->hcontext = hctx; + ctx->base_addr = 0; + ctx->sp = 0; + ctx->cfa = 0; + ctx->curr_frame = NULL; + ctx->frame = NULL; + ctx->dwfl = NULL; + ctx->module = NULL; +} + +void pst_context_fini(pst_context* ctx) +{ + ctx->hcontext = NULL; + ctx->clean_print(ctx); + ctx->base_addr = 0; + ctx->sp = 0; + ctx->cfa = 0; + ctx->curr_frame = NULL; + ctx->frame = NULL; + ctx->dwfl = NULL; + ctx->module = NULL; + + // global + pst_log_fini(&logger); + pst_alloc_fini(&allocator); } +char* pst_strdup(const char* str) +{ + pst_assert(str); + + uint32_t len = strlen(str); + char* dst = (char*)allocator.alloc(&allocator, len + 1); + memcpy(dst, str, len); + dst[len] = 0; + + return dst; +} diff --git a/framework/context.h b/framework/context.h index e07211f..c564b34 100644 --- a/framework/context.h +++ b/framework/context.h @@ -15,51 +15,46 @@ #include #include "logger/log.h" +#include "allocator.h" -typedef struct __pst_context { - __pst_context(ucontext_t* hctx) : hcontext(hctx) - { - clean_print(); - base_addr = 0; - sp = 0; - cfa = 0; - curr_frame = NULL; - frame = NULL; - dwfl = NULL; - module = NULL; - } +extern pst_log logger; // logger for whole PST library +extern pst_allocator allocator; // custom allocator for PST library - bool print(const char* fmt, ...); - void log(SC_LogSeverity severity, const char*fmt, ...); - uint32_t print_expr_block(Dwarf_Op *exprs, int len, char* buff, uint32_t buff_size, Dwarf_Attribute* attr = 0); - void print_registers(int from, int to); - void print_stack(int max, uint64_t next_cfa); - void clean_print() { - buff[0] = 0; - offset = 0; - } +#define pst_alloc(TYPE) (TYPE*)allocator.alloc(&allocator, sizeof(TYPE)) +#define pst_free(NAME) allocator.free(&allocator, NAME) - const char* get_print() { - return buff; - } +#define pst_log(SEVERITY, FORMAT, ...) logger.log(&logger, SEVERITY, FORMAT, ##__VA_ARGS__) +char* pst_strdup(const char* str); + +typedef struct pst_context { + // methods + void (*clean_print) (pst_context* ctx); + bool (*print) (pst_context* ctx, const char* fmt, ...); + bool (*print_expr) (pst_context* ctx, Dwarf_Op *exprs, int exprlen, Dwarf_Attribute* attr); + void (*print_registers) (pst_context* ctx, int from, int to); + void (*print_stack) (pst_context* ctx, int max, uint64_t next_cfa); + + // fields ucontext_t* hcontext; // context of signal handler unw_context_t context; // context of stack trace - unw_cursor_t cursor; + unw_cursor_t cursor; // libunwind stack frame storage unw_cursor_t* curr_frame; // callee libunwind frame Dwarf_Addr base_addr; // base address where process loaded Dwarf_Addr sp; // stack pointer of currently processed stack frame - Dwarf_Addr cfa; // CFA (Canonical Frame Address) of currently processed stack + Dwarf_Addr cfa; // CFA (Canonical Frame Address) of currently processed stack frame Dwarf_Frame* frame; // currently examined libdwfl frame Dwfl* dwfl; // DWARF context Dwfl_Module* module; // currently processed CU -protected: + // print buffer char buff[8192]; // stack trace buffer uint32_t offset; // offset in the 'buff' } pst_context; +void pst_context_init(pst_context* ctx, ucontext_t* hctx); +void pst_context_fini(pst_context* ctx); #endif /* FRAMEWORK_CONTEXT_H_ */ diff --git a/framework/data_entry.h b/framework/data_entry.h deleted file mode 100644 index eb443d3..0000000 --- a/framework/data_entry.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * data_entry.h - * - * Created on: Oct 30, 2013 - * Author: nnosov - * - * Simple representation of data. Pointer to data and size of data. - */ - -#ifndef DATA_ENTRY_H_ -#define DATA_ENTRY_H_ - - -//system -#include - -typedef struct _SC_Data -{ - _SC_Data() - { - data = 0; - size =0; - } - - _SC_Data(const char* _data, uint32_t _size) - { - data = (char*)_data; - size = _size; - } - - char* data; - uint32_t size; -} SC_Data; - - -#endif /* DATA_ENTRY_H_ */ diff --git a/framework/dictionary.cpp b/framework/dictionary.cpp deleted file mode 100644 index 738efa5..0000000 --- a/framework/dictionary.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * dictionary.cpp - * - * Created on: Oct 28, 2014 - * Author: nnosov - */ -#include "dictionary.h" - -//framework -#include "logger/log.h" - -extern SC_LogBase* logger; - -typedef struct __key_value { - const char* key; - uint32_t keylen; - int value; -} key_value; - -#define KEY_VALUE(key, value) {key, sizeof(key), value} - -key_value utable[] = { - KEY_VALUE("key1", 1), - KEY_VALUE("key2", 2), - KEY_VALUE("key1", 3), -}; - - -bool SC_Dict::UnitTest() -{ - logger->Log(SEVERITY_DEBUG, "%s: starting ...", __PRETTY_FUNCTION__); - logger->Log(SEVERITY_DEBUG, "\t\tshift = %d, hash function = %p, number of buckets = %d, mask = 0x%X. count = %d", mShift, pHashFn, mSize, mMask, mCount); - logger->Log(SEVERITY_DEBUG, "... inserting values into the dictionary ..."); - for(uint32_t i = 0; i < sizeof(utable) / sizeof(key_value); i++) - { - logger->Log(SEVERITY_DEBUG, "\t\tKey = %s, Key size = %d, Key value = %d", utable[i].key, utable[i].keylen, utable[i].value); - Insert(utable[i].key, utable[i].keylen, &(utable[i].value)); - } - logger->Log(SEVERITY_DEBUG, "\t\tdictionary size = %d",mCount); - logger->Log(SEVERITY_DEBUG, "... went through the dictionary sequentially ..."); - for(void* data = First(); data; data = Next()) - { - logger->Log(SEVERITY_DEBUG, "\t\tvalue = %d", *((int*)data)); - } - - logger->Log(SEVERITY_DEBUG, "... went through the dictionary in key order ..."); - for(uint32_t i = 0; i < sizeof(utable) / sizeof(key_value); i++) - { - void* data = Lookup(utable[i].key, utable[i].keylen); - if(data) - logger->Log(SEVERITY_DEBUG, "\t\tKey = %s, Key size = %d, Key value = %d", utable[i].key, utable[i].keylen, *((int*)data)); - } - - void* data = Lookup(utable[0].key, utable[0].keylen); - if(data) - { - logger->Log(SEVERITY_DEBUG, "... removing first key[%s] ...", utable[0].key); - Remove(); - } - - logger->Log(SEVERITY_DEBUG, "... went through the dictionary in key order 2 ..."); - for(uint32_t i = 0; i < sizeof(utable) / sizeof(key_value); i++) - { - void* data = Lookup(utable[i].key, utable[i].keylen); - if(data) - logger->Log(SEVERITY_DEBUG, "\t\tKey = %s, Key size = %d, Key value = %d", utable[i].key, utable[i].keylen, *((int*)data)); - } - - logger->Log(SEVERITY_DEBUG, "%s: finished.", __PRETTY_FUNCTION__); - return true; -} diff --git a/framework/dictionary.h b/framework/dictionary.h deleted file mode 100644 index bbda9be..0000000 --- a/framework/dictionary.h +++ /dev/null @@ -1,275 +0,0 @@ -/* - * dictionary.h - * - * Created on: Aug 5, 2014 - * Author: nnosov - */ - -#ifndef DICTIONARY_H_ -#define DICTIONARY_H_ - -//system -#include -#include - -//framework -#include "data_entry.h" -#include "linkedlist.h" - -#define HASH_MIN_SHIFT (8) -#define HASH_MAX_SHIFT (15) - -typedef uint32_t (* SC_HashFn)(const void *key, uint32_t key_size); - -inline uint32_t hash_bernstein(const void* key, uint32_t key_size) -{ - uint32_t hash = 0; - register unsigned char* p = (unsigned char*)key; - - for (unsigned int i = 0; i < key_size; i++) - { - hash = 33 * hash + p[i]; - } - - return hash; -} - -inline uint32_t hash_sdbm(const void *key, uint32_t key_size) -{ - uint32_t hash = 0; - register unsigned char* p = (unsigned char*)key; - - for (uint32_t i = 0; i < key_size; i++) - { - hash = p[i] + (hash << 6) + (hash << 16) - hash; - } - - return hash; -} - -inline uint32_t hash_fnv(const void *key, uint32_t key_size ) -{ - register unsigned char *p = (unsigned char*)key; - uint32_t hash = 2166136261U; - - for (uint32_t i = 0; i < key_size; i++ ) - { - hash = ( hash * 16777619 ) ^ p[i]; - } - - return hash; -} - -typedef struct __SC_DictItem : public SC_ListNode -{ - __SC_DictItem() - { - data = 0; - hash = 0; - } - - ~__SC_DictItem() - { - if(key.data) { - delete[] key.data; - } - key.data = 0; - key.size = 0; - } - - void* data; - SC_Data key; - uint32_t hash; -} SC_DictItem; - -class SC_Dict -{ -private: - SC_Dict(SC_Dict& other) : pHashFn(0), mCurIdx(0), pCurItem(0), mCount(0), mShift(0), mMask(0), mBuckets(0), mSize(0){(void) other;} - -public: - SC_Dict(uint16_t shift = 8, SC_HashFn fn = hash_bernstein) : pHashFn(fn), mCurIdx(0), pCurItem(0), mCount(0) - { - if(shift <= HASH_MIN_SHIFT) { - mShift = HASH_MIN_SHIFT; - } else if(shift >= HASH_MAX_SHIFT) { - mShift = HASH_MAX_SHIFT; - } else { - mShift = shift; - } - - mSize = 1UL << shift; - mMask = mSize -1; - - mBuckets = new SC_ListHead[mSize]; - } - - virtual ~SC_Dict() - { - Clean(); - delete[] mBuckets; - } - - void Clean() - { - First(); - while(mCount) Remove(); - mCurIdx = 0; - pCurItem = 0; - } - - - inline void Insert(const void* key, uint32_t size, void* data) - { - SC_DictItem* item = new SC_DictItem; - item->hash = pHashFn(key, size); - item->data = data; - - // save key - item->key.data = new char[size]; - memcpy(item->key.data, key, size); - item->key.size = size; - - mCurIdx = item->hash & mMask; - - for(pCurItem = (SC_DictItem*)mBuckets[mCurIdx].First(); pCurItem ; pCurItem = (SC_DictItem*)mBuckets[mCurIdx].Next(pCurItem)) - { - if(pCurItem->key.size == item->key.size) - { - if(!memcmp(pCurItem->key.data, item->key.data, size)) - { - pCurItem->data = data; - delete item; - return; - } - } - } - mBuckets[mCurIdx].InsertLast(item); - pCurItem = item; - mCount++; - } - - void Remove() - { - SC_DictItem* item = pCurItem; - if(item) - { - MoveNext(); - mBuckets[item->hash & mMask].Remove(item); - if(pCurItem == item) - pCurItem = 0; - delete item; - mCount--; - } - } - - //returns first data in dictionary - inline void* First() - { - if(!mCount) - { - return 0; - } - - mCurIdx = 0; - pCurItem = 0; - MoveNext(); - - return pCurItem ? pCurItem->data : 0; - } - - //returns next data in dictionary - inline void* Next() - { - MoveNext(); - return pCurItem ? pCurItem->data : 0; - } - - //looking up for the next data - inline void* LookupNext(const void* key, uint32_t size) - { - uint32_t hash = pHashFn(key, size); - - for(SC_DictItem* curr = (SC_DictItem*)mBuckets[mCurIdx].Next(pCurItem); curr; curr = (SC_DictItem*)mBuckets[mCurIdx].Next(curr)) - { - if(curr->hash == hash && curr->key.size == size) - { - if(!memcmp(key, curr->key.data, size)) - { - pCurItem = curr; - return curr->data; - } - } - } - - return 0; - } - - //looking up for the first data corresponds to the key value - inline void* Lookup(const void* key, uint32_t size) - { - uint32_t hash = pHashFn(key, size); - uint32_t idx = hash & mMask; - for(SC_DictItem* curr = (SC_DictItem*)mBuckets[idx].First(); curr; curr = (SC_DictItem*)mBuckets[idx].Next(curr)) - { - if(curr->hash == hash && curr->key.size == size) - { - if(!memcmp(key, curr->key.data, size)) - { - mCurIdx = idx; - pCurItem = curr; - return curr->data; - } - } - } - - return 0; - } - - uint32_t Size() - { - return mCount; - } - -protected: - inline SC_DictItem* MoveNext() - { - if(mCurIdx >= mSize) - { - return 0; - } - - if(pCurItem) - { - pCurItem = (SC_DictItem*)mBuckets[mCurIdx].Next(pCurItem); - if(!pCurItem) - mCurIdx++; - } - - while(!pCurItem && mCurIdx < mSize) - { - pCurItem = (SC_DictItem*)mBuckets[mCurIdx].First(); - if(pCurItem) - break; - mCurIdx++; - } - - return pCurItem; - } -public: - virtual bool UnitTest(); - -protected: - - SC_HashFn pHashFn; //hash function - uint32_t mCurIdx; //index of current bucket - SC_DictItem* pCurItem; //current item - uint32_t mCount; //number of elements in dictionary - uint32_t mShift; //shift of the hash - uint32_t mMask; //mask to be applied to the hash - SC_ListHead* mBuckets; //Buckets of hash table - uint32_t mSize; //size of array in buckets -}; - - -#endif /* DICTIONARY_H_ */ diff --git a/framework/dwarf_call_site.cpp b/framework/dwarf_call_site.cpp new file mode 100644 index 0000000..caa0836 --- /dev/null +++ b/framework/dwarf_call_site.cpp @@ -0,0 +1,351 @@ +/* + * dwarf_call_site.cpp + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#include +#include + +#include "dwarf_call_site.h" +#include "dwarf_utils.h" +#include "hash_multimap.h" +#include "dwarf_utils.h" +#include "dwarf_function.h" + +// ----------------------------------------------------------------------------------- +// pst_call_site_param +// ----------------------------------------------------------------------------------- +void pst_call_site_param_init(pst_call_site_param* param) +{ + // fields + param->param = NULL; + param->name = NULL; + param->value = 0; + pst_dwarf_expr_init(¶m->location); + param->allocated = false; +} + +pst_call_site_param* pst_call_site_param_new() +{ + pst_call_site_param* param = pst_alloc(pst_call_site_param); + if(param) { + pst_call_site_param_init(param); + param->allocated = true; + } + + return param; +} + +void pst_call_site_param_fini(pst_call_site_param* param) +{ + if(param->name) { + pst_free(param->name); + param->name = NULL; + } + + if(param->allocated) { + pst_free(param); + } +} + +// ----------------------------------------------------------------------------------- +// pst_call_site +// ----------------------------------------------------------------------------------- +pst_call_site_param* add_param(pst_call_site* site) +{ + pst_call_site_param* p = pst_call_site_param_new(); + pst_call_site_param_init(p); + list_add_bottom(&site->params, &p->node); + + return p; +} + +void del_param(pst_call_site*, pst_call_site_param* p) +{ + list_del(&p->node); + pst_call_site_param_fini(p); +} + +pst_call_site_param* next_param(pst_call_site* site, pst_call_site_param* p) +{ + list_node* n = (p == NULL) ? list_first(&site->params) : list_next(&p->node); + pst_call_site_param* ret = NULL; + if(n) { + ret = list_entry(n, pst_call_site_param, node); + } + + return ret; +} + +pst_call_site_param* pst_call_site_find(pst_call_site* site, pst_dwarf_expr* expr) +{ + for(pst_call_site_param* param = next_param(site, NULL); param; param = next_param(site, param)) { + if(pst_dwarf_expr_equal(¶m->location, expr)) { + return param; + } + } + + return NULL; +} + +bool call_site_handle_dwarf(pst_call_site* site, Dwarf_Die* child) +{ + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; + + do { + switch(dwarf_tag(child)) { + case DW_TAG_GNU_call_site_parameter: { + Dwarf_Addr pc; + unw_get_reg(site->ctx->curr_frame, UNW_REG_IP, &pc); + + // expression represent where callee parameter will be stored + pst_call_site_param* param = add_param(site); + if(dwarf_hasattr(child, DW_AT_location)) { + // handle location expression here + // determine location of parameter in stack/heap or CPU registers + attr = dwarf_attr(child, DW_AT_location, &attr_mem); + if(!handle_location(site->ctx, attr, ¶m->location, pc, NULL)) { + pst_log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", site->ctx->buff); + del_param(site, param); + return false; + } + pst_log(SEVERITY_DEBUG, " DW_AT_location: %s", site->ctx->buff); + } + + // expression represents call parameter's value + if(dwarf_hasattr(child, DW_AT_GNU_call_site_value)) { + // handle value expression here + attr = dwarf_attr(child, DW_AT_GNU_call_site_value, &attr_mem); + pst_decl0(pst_dwarf_expr, loc); + if(handle_location(site->ctx, attr, &loc, pc, NULL)) { + param->value = loc.value; + pst_dwarf_expr_fini(&loc); + pst_log(SEVERITY_DEBUG, " DW_AT_GNU_call_site_value:\"%s\" ==> 0x%lX", site->ctx->buff, param->value); + } else { + pst_log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", site->ctx->buff); + del_param(site, param); + pst_dwarf_expr_fini(&loc); + return false; + } + } + break; + } + default: + break; + } + } while (dwarf_siblingof (child, child) == 0); + + return true; +} + +void pst_call_site_init(pst_call_site* site, pst_context* c, uint64_t tgt, const char* orn) +{ + list_node_init(&site->node); + + site->target = tgt; + if(orn) { + site->origin = strdup(orn); + } else { + site->origin = NULL; + } + site->die = NULL; + list_head_init(&site->params); + site->ctx = c; + site->allocated = false; +} + +pst_call_site* pst_call_site_new(pst_context* c, uint64_t tgt, const char* orn) +{ + pst_call_site* nc = pst_alloc(pst_call_site); + if(nc) { + pst_call_site_init(nc, c, tgt, orn); + nc->allocated = true; + } + + return nc; +} + +void pst_call_site_fini(pst_call_site* site) +{ + if(site->origin) { + pst_free(site->origin); + site->origin = NULL; + } + + if(site->allocated) { + pst_free(site); + } +} + + +// ----------------------------------------------------------------------------------- +// pst_call_site_storage +// ----------------------------------------------------------------------------------- + +// DW_AT_low_pc should point to the offset from process base address which is actually PC of current function, usually. +// further handle DW_AT_abstract_origin attribute of DW_TAG_GNU_call_site DIE to determine what DIE is referenced by it. +// probably by invoke by: +// Dwarf_Die *scopes; +// int n = dwarf_getscopes_die (funcdie, &scopes); // where 'n' is the number of scopes +// if (n <= 0) -> FAILURE +// see handle_function() in elfutils/tests/funcscopes.c -> handle_function() -> print_vars() +// DW_TAG_GNU_call_site_parameter is defined under child DIE of DW_TAG_GNU_call_site and defines value of subroutine before calling it +// relates to DW_OP_GNU_entry_value() handling in callee function to determine the value of an argument/variable of the callee +// get DIE of return type +bool pst_call_site_storage_handle_dwarf(pst_call_site_storage* storage, Dwarf_Die* result, pst_function* info) +{ + Dwarf_Die origin; + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; + + pst_log(SEVERITY_DEBUG, "***** DW_TAG_GNU_call_site contents:"); + // reference to DIE which represents callee's parameter if compiler knows where it is at compile time + const char* oname = NULL; + if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) { + oname = dwarf_diename(&origin); + pst_log(SEVERITY_DEBUG, "DW_AT_abstract_origin: '%s'", oname); + } + + // The call site may have a DW_AT_call_site_target attribute which is a DWARF expression. For indirect calls or jumps where it is unknown at + // compile time which subprogram will be called the expression computes the address of the subprogram that will be called. + uint64_t target = 0; + if(dwarf_hasattr (result, DW_AT_GNU_call_site_target)) { + attr = dwarf_attr(result, DW_AT_GNU_call_site_target, &attr_mem); + if(attr) { + pst_decl0(pst_dwarf_expr, expr); + if(handle_location(storage->ctx, &attr_mem, &expr, info->pc, info)) { + target = expr.value; + pst_log(SEVERITY_DEBUG, "DW_AT_GNU_call_site_target: %#lX", target); + } + pst_dwarf_expr_fini(&expr); + } + } + + if(target == 0 && oname == NULL) { + pst_log(SEVERITY_ERROR, "Cannot determine both call-site target and origin"); + return false; + } + + Dwarf_Die child; + if(dwarf_child (result, &child) == 0) { + pst_call_site* st = pst_call_site_storage_add(storage, target, oname); + + st->tail_call = (dwarf_hasattr(result, DW_AT_call_tail_call) != 0); + + // if DW_AT_low_pc attribute is specified, then it's value is actually PC in caller's frame (address of invocation of callee) + if(dwarf_hasattr (result, DW_AT_low_pc) && dwarf_attr(result, DW_AT_low_pc, &attr_mem)) { + if(!dwarf_formaddr(&attr_mem, &st->call_pc)) { + pst_log(SEVERITY_DEBUG, "DW_AT_low_pc: %#lX", st->call_pc); + } + } + + if(!call_site_handle_dwarf(st, &child)) { + pst_call_site_storage_del(storage, st); + return false; + } + } + + return true; +} + +pst_call_site* storage_call_site_by_origin(pst_call_site_storage* storage, const char* origin) +{ + pst_call_site* ret = NULL; + hash_node* node = hash_find(&storage->cs_to_origin, origin, strlen(origin)); + if(node) { + ret = hash_entry(node, pst_call_site, org_node); + } + + return ret; +} + +pst_call_site* storage_call_site_by_target(pst_call_site_storage* storage, uint64_t target) +{ + pst_call_site* ret = NULL; + hash_node* node = hash_find(&storage->cs_to_target, (char*)&target, sizeof(target)); + if(node) { + ret = hash_entry(node, pst_call_site, tgt_node); + } + + return ret; +} + +pst_call_site* pst_call_site_storage_add(pst_call_site_storage* storage, uint64_t target, const char* origin) +{ + pst_new(pst_call_site, st, storage->ctx, target, origin); + list_add_bottom(&storage->call_sites, &st->node); + + if(target) { + hash_add(&storage->cs_to_target, &st->tgt_node, &target, sizeof(target)); + } else if(origin) { + hash_add(&storage->cs_to_origin, &st->org_node, origin, strlen(origin)); + } + + return st; +} + +void pst_call_site_storage_del(pst_call_site_storage* storage, pst_call_site* st) +{ + hash_node* node = NULL; + list_del(&st->node); + + if(st->target) { + node = hash_find(&storage->cs_to_target, &st->target, sizeof(st->target)); + } else if(st->origin) { + node = hash_find(&storage->cs_to_origin, st->origin, strlen(st->origin)); + } + + if(node) { + hash_del(node); + } + + pst_free(st); +} + +pst_call_site* pst_call_site_storage_find(pst_call_site_storage* storage, pst_function* callee) +{ + uint64_t start_pc = storage->ctx->base_addr + callee->lowpc; + pst_call_site* cs = storage_call_site_by_target(storage, start_pc); + if(!cs) { + cs = storage_call_site_by_origin(storage, callee->name); + } + + return cs; +} + +void pst_call_site_storage_init(pst_call_site_storage* storage, pst_context* ctx) +{ + storage->ctx = ctx; + list_head_init(&storage->call_sites); + hash_head_init(&storage->cs_to_target); + hash_head_init(&storage->cs_to_origin); + storage->allocated = false; +} + +pst_call_site_storage* pst_call_site_storage_new(pst_context* ctx) +{ + pst_call_site_storage* ns = pst_alloc(pst_call_site_storage); + if(ns) { + pst_call_site_storage_init(ns, ctx); + ns->allocated = true; + } + + return ns; +} + +void pst_call_site_storage_fini(pst_call_site_storage* storage) +{ + pst_call_site* site = NULL; + struct list_node *pos, *tn; + list_for_each_entry_safe(site, pos, tn, &storage->call_sites, node) { + list_del(&site->node); + pst_call_site_fini(site); + } + + if(storage->allocated) { + pst_free(storage); + } +} diff --git a/framework/dwarf_call_site.h b/framework/dwarf_call_site.h new file mode 100644 index 0000000..cc8079e --- /dev/null +++ b/framework/dwarf_call_site.h @@ -0,0 +1,90 @@ +/* + * dwarf_call_site.h + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#ifndef FRAMEWORK_DWARF_CALL_SITE_H_ +#define FRAMEWORK_DWARF_CALL_SITE_H_ + +#include +#include + +#include "list_head.h" +#include "context.h" +#include "dwarf_expression.h" +#include "hash_multimap.h" + +typedef struct pst_callee_info { + Dwarf_Addr target; + char* origin; +} pst_callee_info; + + +// ----------------------------------------------------------------------------------- +// DW_TAG_call_site_parameter +// ----------------------------------------------------------------------------------- +typedef struct pst_call_site_param { + list_node node; // uplink. !!! must be first !!! + + Dwarf_Die* param; // reference to parameter DIE in callee (DW_AT_call_parameter) + char* name; // name of parameter if present (DW_AT_name) + pst_dwarf_expr location; // DWARF stack containing location expression + uint64_t value; // parameter's value + bool allocated; // whether this object was allocated or not +} pst_call_site_param; + +void pst_call_site_param_init(pst_call_site_param* param); +pst_call_site_param* pst_call_site_param_new(); +void pst_call_site_param_fini(pst_call_site_param* param); + + +// ----------------------------------------------------------------------------------- +// DW_TAG_call_site +// ----------------------------------------------------------------------------------- +typedef struct pst_call_site { + list_node node; // uplink to list of call-sites + hash_node tgt_node; // uplink to call-site by node dictionary + hash_node org_node; // uplink to call-site by origin dictionary + + uint64_t target; // pointer to callee function (it's Low PC + base address) + char* origin; // name of callee function + Dwarf_Addr call_pc; // address of 'call' instruction to callee inside of caller + bool tail_call; // whether this a tail-call (jump) or normal call 'call' + Dwarf_Die* die; // DIE of function for which this call site has parameters + list_head params; // list of parameters and their values + pst_context* ctx; // execution context + bool allocated; // whether this object was allocated or not +} pst_call_site; +void pst_call_site_init(pst_call_site* site, pst_context* context, uint64_t target, const char* origin); +pst_call_site* pst_call_site_new(pst_context* context, uint64_t target, const char* origin); +void pst_call_site_fini(pst_call_site* site); + +pst_call_site_param* pst_call_site_find(pst_call_site* site, pst_dwarf_expr* expr); +bool pst_call_site_handle_dwarf(pst_call_site* site, Dwarf_Die* child); + +typedef struct __pst_function pst_function; +// ----------------------------------------------------------------------------------- +// storage for all of function's call sites +// ----------------------------------------------------------------------------------- +typedef struct pst_call_site_storage { + pst_context* ctx; + list_head call_sites; // Call-Site definitions + hash_head cs_to_target; // map pointer to caller to call-site + hash_head cs_to_origin; // map caller name to call-site + bool allocated; // whether this object was allocated or not +} pst_call_site_storage; + +void pst_call_site_storage_init(pst_call_site_storage* storage, pst_context* ctx); +pst_call_site_storage* pst_call_site_storage_new(pst_context* ctx); +void pst_call_site_storage_fini(pst_call_site_storage* storage); + +bool pst_call_site_storage_handle_dwarf(pst_call_site_storage* storage, Dwarf_Die* result, pst_function* info); +pst_call_site* pst_call_site_storage_find(pst_call_site_storage* storage, pst_function* callee); +pst_call_site* pst_call_site_storage_add(pst_call_site_storage* storage, uint64_t target, const char* origin); +void pst_call_site_storage_del(pst_call_site_storage* storage, pst_call_site* st); +void pst_call_site_storage_del(pst_call_site_storage* storage, pst_call_site* st); + + +#endif /* FRAMEWORK_DWARF_CALL_SITE_H_ */ diff --git a/framework/dwarf_expression.cpp b/framework/dwarf_expression.cpp new file mode 100644 index 0000000..85f531b --- /dev/null +++ b/framework/dwarf_expression.cpp @@ -0,0 +1,164 @@ +/* + * dwarf_expression.cpp + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#include + +#include "dwarf_expression.h" +#include "common.h" +#include "allocator.h" +#include "context.h" + +// +// DWARF operation +// +void pst_dwarf_op_init(pst_dwarf_op* dwop, uint8_t op, uint64_t a1, uint64_t a2) +{ + //pst_assert(dwop); + + list_node_init(&dwop->node); + dwop->operation = op; + dwop->arg1 = a1; + dwop->arg2 = a2; + dwop->allocated = false; +} + +pst_dwarf_op* pst_dwarf_op_new(uint8_t op, uint64_t a1, uint64_t a2) +{ + pst_dwarf_op* nop = pst_alloc(pst_dwarf_op); + if(nop) { + pst_dwarf_op_init(nop, op, a1, a2); + nop->allocated = true; + } + + return nop; +} + +void pst_dwarf_op_fini(pst_dwarf_op* dwop) +{ + assert(dwop); + + if(dwop->allocated) { + pst_free(dwop); + } else { + dwop->operation = 0; + dwop->arg1 = 0; + dwop->arg2 = 0; + } +} + +// +// pst_dwarf_expr +// +pst_dwarf_op* add_op(pst_dwarf_expr* expr, uint8_t operation, uint64_t arg1, uint64_t arg2) +{ + pst_new(pst_dwarf_op, op, operation, arg1, arg2); + list_add_bottom(&expr->operations, &op->node); + + return op; +} + +pst_dwarf_op* next_op(pst_dwarf_expr* expr, pst_dwarf_op* op) +{ + struct list_node* n = (op == NULL) ? list_first(&expr->operations) : list_next(&op->node); + + pst_dwarf_op* ret = NULL; + if(n) { + ret = list_entry(n, pst_dwarf_op, node); + } + + return ret; +} + +void clean(pst_dwarf_expr* expr) +{ + pst_dwarf_op* op = NULL; + struct list_node *pos, *tn; + list_for_each_entry_safe(op, pos, tn, &expr->operations, node) { + list_del(&op->node); + pst_dwarf_op_fini(op); + } +} + +bool pst_dwarf_expr_print_op(pst_dwarf_expr* expr, const char* fmt, ...) +{ + bool nret = true; + + va_list args; + va_start(args, fmt); + int size = sizeof(expr->buff) - expr->offset; + int ret = vsnprintf(expr->buff + expr->offset, size, fmt, args); + if(ret >= size || ret < 0) { + nret = false; + } + expr->offset += ret; + va_end(args); + + return nret; +} + +void pst_dwarf_expr_set_value(pst_dwarf_expr* expr, uint64_t v) +{ + expr->has_value = true; + expr->value = v; +} + +bool pst_dwarf_expr_equal(pst_dwarf_expr* lhs, pst_dwarf_expr* rhs) +{ + if(list_count(&lhs->operations) != list_count(&rhs->operations)) { + return false; + } + + pst_dwarf_op* lop = next_op(lhs, NULL); + pst_dwarf_op* rop = next_op(rhs, NULL); + while(lop && rop) { + if(lop->operation == rop->operation && lop->arg1 == rop->arg1 && lop->arg2 == rop->arg2) { + return true; + } + lop = next_op(lhs, lop); + rop = next_op(rhs, rop); + } + + return false; +} + +void pst_dwarf_expr_setup(pst_dwarf_expr* expr, Dwarf_Op* exprs, size_t exprlen) +{ + clean(expr); + for(size_t i = 0; i < exprlen; ++i) { + add_op(expr, exprs[i].atom, exprs[i].number, exprs[i].number2); + } +} + +void pst_dwarf_expr_init(pst_dwarf_expr* expr) +{ + list_head_init(&expr->operations); + expr->has_value = false; + expr->value = 0; + + expr->buff[0] = 0; + expr->offset = 0; + expr->allocated = false; +} + +pst_dwarf_expr* pst_dwarf_expr_new() +{ + pst_dwarf_expr* ne = pst_alloc(pst_dwarf_expr); + if(ne) { + pst_dwarf_expr_init(ne); + ne->allocated = true; + } + + return ne; +} + +void pst_dwarf_expr_fini(pst_dwarf_expr* expr) +{ + clean(expr); + if(expr->allocated) { + pst_free(expr); + } +} diff --git a/framework/dwarf_expression.h b/framework/dwarf_expression.h new file mode 100644 index 0000000..3ec85a1 --- /dev/null +++ b/framework/dwarf_expression.h @@ -0,0 +1,49 @@ +/* + * dwarf_expression.h + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#ifndef FRAMEWORK_DWARF_EXPRESSION_H_ +#define FRAMEWORK_DWARF_EXPRESSION_H_ + +#include +#include + +#include "list_head.h" +#include "allocator.h" + +// DWARF operation (represents our own DW_OP_XXX) +typedef struct { + list_node node; //uplink + + uint8_t operation; + uint64_t arg1; + uint64_t arg2; + bool allocated; +} pst_dwarf_op; +void pst_dwarf_op_init(pst_dwarf_op* dwop, uint8_t op, uint64_t a1, uint64_t a2); +pst_dwarf_op* pst_dwarf_op_new(uint8_t op, uint64_t a1, uint64_t a2); +void pst_dwarf_op_fini(pst_dwarf_op* dwop); + + +// DWARF expression +typedef struct pst_dwarf_expr { + list_head operations; + bool has_value; + uint64_t value; + char buff[512]; + uint16_t offset; + bool allocated; +} pst_dwarf_expr; +void pst_dwarf_expr_init(pst_dwarf_expr* expr); +pst_dwarf_expr* pst_dwarf_expr_new(); +void pst_dwarf_expr_fini(pst_dwarf_expr* expr); + +void pst_dwarf_expr_setup(pst_dwarf_expr* expr, Dwarf_Op* exprs, size_t exprlen); +bool pst_dwarf_expr_equal(pst_dwarf_expr* lhs, pst_dwarf_expr* rhs); +bool pst_dwarf_expr_print_op(pst_dwarf_expr* expr, const char* fmt, ...); +void pst_dwarf_expr_set_value(pst_dwarf_expr* expr, uint64_t v); + +#endif /* FRAMEWORK_DWARF_EXPRESSION_H_ */ diff --git a/framework/dwarf_function.cpp b/framework/dwarf_function.cpp new file mode 100644 index 0000000..ed1a7e1 --- /dev/null +++ b/framework/dwarf_function.cpp @@ -0,0 +1,481 @@ +/* + * dwarf_function.cpp + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#include +#include +#include + +#include "dwarf_function.h" +#include "dwarf_utils.h" +#include "dwarf_stack.h" + +// ----------------------------------------------------------------------------------- +// pst_function +// ----------------------------------------------------------------------------------- +bool get_frame(pst_function* fn) +{ + // get CFI (Call Frame Information) for current module + // from handle_cfi() + Dwarf_Addr mod_bias = 0; + Dwarf_CFI* cfi = dwfl_module_eh_cfi(fn->ctx->module, &mod_bias); // rty .eh_cfi first + if(!cfi) { // then try .debug_fame second + cfi = dwfl_module_dwarf_cfi(fn->ctx->module, &mod_bias); + } + if(!cfi) { + pst_log(SEVERITY_ERROR, "Cannot find CFI for module"); + return false; + } + + // get frame of CFI for address + int result = dwarf_cfi_addrframe (cfi, fn->pc - mod_bias, &fn->frame); + if (result != 0) { + pst_log(SEVERITY_ERROR, "Failed to find CFI frame for module"); + return false; + } + + // setup context to match frame + fn->ctx->frame = fn->frame; + + // get return register and PC range for function + Dwarf_Addr start = fn->pc; + Dwarf_Addr end = fn->pc; + bool signalp; + int ra_regno = dwarf_frame_info (fn->frame, &start, &end, &signalp); + if(ra_regno >= 0) { + start += mod_bias; + end += mod_bias; + reginfo info; info.regno = ra_regno; + dwfl_module_register_names(fn->ctx->module, regname_callback, &info); + pst_log(SEVERITY_INFO, "Function %s(...): '.eh/debug frame' info: PC range: => [%#" PRIx64 ", %#" PRIx64 "], return register: %s, in_signal = %s", + fn->name, start, end, info.regname, signalp ? "true" : "false"); + } else { + pst_log(SEVERITY_WARNING, "Return address register info unavailable (%s)", dwarf_errmsg(0)); + } + + // finally get CFA (Canonical Frame Address) + // Point cfa_ops to dummy to match print_detail expectations. + // (nops == 0 && cfa_ops != NULL => "undefined") + Dwarf_Op dummy; + Dwarf_Op *cfa_ops = &dummy; + size_t cfa_nops; + if(dwarf_frame_cfa(fn->frame, &cfa_ops, &cfa_nops)) { + pst_log(SEVERITY_ERROR, "Failed to get CFA for frame"); + return false; + } + + fn->ctx->print_expr(fn->ctx, cfa_ops, cfa_nops, NULL); + + pst_decl(pst_dwarf_stack, stack, fn->ctx); + if(pst_dwarf_stack_calc(&stack, cfa_ops, cfa_nops, NULL, NULL) && pst_dwarf_stack_get_value(&stack, &fn->cfa)) { + pst_log(SEVERITY_INFO, "Function %s(...): CFA expression: %s ==> %#lX", fn->name, fn->ctx->buff, fn->cfa); + + // setup context to match CFA for frame + fn->ctx->cfa = fn->cfa; + } else { + pst_log(SEVERITY_ERROR, "Failed to calculate CFA expression"); + } + + pst_dwarf_stack_fini(&stack); + + return true; +} + +pst_parameter* add_param(pst_function* fn) +{ + pst_new(pst_parameter, p, fn->ctx); + list_add_bottom(&fn->params, &p->node); + + return p; +} + +void del_param(pst_parameter* p) +{ + list_del(&p->node); + pst_free(p); +} + +void clear(pst_function* fn) +{ + pst_parameter* param = NULL; + struct list_node *pos, *tn; + list_for_each_entry_safe(param, pos, tn, &fn->params, node) { + list_del(¶m->node); + pst_parameter_fini(param); + } + + pst_call_site_storage_fini(&fn->call_sites); +} + +pst_parameter* next_param(pst_function* fn, pst_parameter* p) +{ + struct list_node* n = (p == NULL) ? list_first(&fn->params) : list_next(&p->node); + + pst_parameter* ret = NULL; + if(n) { + ret = list_entry(n, pst_parameter, node); + } + + return ret; +} + +bool handle_lexical_block(pst_function* fn, Dwarf_Die* result) +{ + uint64_t lowpc = 0, highpc = 0; const char* origin_name = ""; + dwarf_lowpc(result, &lowpc); + dwarf_highpc(result, &lowpc); + + Dwarf_Attribute attr_mem; + Dwarf_Die origin; + if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) { + origin_name = dwarf_diename(&origin); + Dwarf_Die child; + if(dwarf_child (&origin, &child) == 0) { + do { + switch (dwarf_tag (&child)) + { + case DW_TAG_variable: + case DW_TAG_formal_parameter: + pst_log(SEVERITY_DEBUG, "Abstract origin: %s('%s')", dwarf_diename(&origin), dwarf_diename (&child)); + break; + // Also handle DW_TAG_unspecified_parameters (unknown number of arguments i.e. fun(arg1, ...); + default: + break; + } + } while (dwarf_siblingof (&child, &child) == 0); + } + } + const char* die_name = ""; + if(dwarf_diename(result)) { + die_name = dwarf_diename(result); + } + pst_log(SEVERITY_DEBUG, "Lexical block with name '%s', tag 0x%X and origin '%s' found. lowpc = 0x%lX, highpc = 0x%lX", die_name, dwarf_tag (result), origin_name, lowpc, highpc); + Dwarf_Die child; + if(dwarf_child (result, &child) == 0) { + do { + switch (dwarf_tag (&child)) { + case DW_TAG_lexical_block: + handle_lexical_block(fn, &child); + break; + case DW_TAG_variable: { + pst_parameter* param = add_param(fn); + if(!pst_parameter_handle_dwarf(param, &child, fn)) { + del_param(param); + } + break; + } + case DW_TAG_GNU_call_site: + pst_call_site_storage_handle_dwarf(&fn->call_sites, &child, fn); + break; + case DW_TAG_inlined_subroutine: + pst_log(SEVERITY_DEBUG, "Skipping Lexical block tag 'DW_TAG_inlined_subroutine'"); + break; + default: + pst_log(SEVERITY_DEBUG, "Unknown Lexical block tag 0x%X", dwarf_tag(&child)); + break; + } + }while (dwarf_siblingof (&child, &child) == 0); + } + + return true; +} + +bool pst_function_print_dwarf(pst_function* fn) +{ + char* at = NULL; + if(!asprintf(&at, " at %s:%d, %p", fn->file, fn->line, (void*)fn->pc)) { + return false; + } + if(at[4] == ':' && at[5] == '-') { + free(at); + if(!asprintf(&at, " at %p", (void*)fn->pc)) { + return false; + } + } + //ctx->print(ctx, "%s:%d: ", file.c_str(), line); + + // handle return parameter and be safe if function haven't parameters (for example, dwar info for function is absent) + pst_parameter* param = next_param(fn, NULL); + if(param && param->is_return) { + // print return value type, function name and start list of parameters + pst_parameter_print_dwarf(param); + fn->ctx->print(fn->ctx, " %s(", fn->name); + param = next_param(fn, param); + } else { + fn->ctx->print(fn->ctx, "%s(", fn->name); + } + + bool first = true; bool start_variable = false; + for(; param; param = next_param(fn, param)) { + if(param->is_return) { + // print return value type, function name and start list of parameters + pst_parameter_print_dwarf(param); + fn->ctx->print(fn->ctx, " %s(", fn->name); + continue; + } + + if(param->is_variable) { + if(!start_variable) { + fn->ctx->print(fn->ctx, ")%s\n", at); + fn->ctx->print(fn->ctx, "{\n"); + start_variable = true; + } + if(param->line) { + fn->ctx->print(fn->ctx, "%04u: ", param->line); + } else { + fn->ctx->print(fn->ctx, " "); + } + pst_parameter_print_dwarf(param); + fn->ctx->print(fn->ctx, ";\n"); + } else { + if(first) { + first = false; + } else { + fn->ctx->print(fn->ctx, ", "); + } + pst_parameter_print_dwarf(param); + } + } + + if(!start_variable) { + fn->ctx->print(fn->ctx, ");%s\n", at); + } else { + fn->ctx->print(fn->ctx, "}\n"); + } + + free(at); + return true; +} + +bool pst_function_handle_dwarf(pst_function * fn, Dwarf_Die* d) +{ + fn->die = d; + get_frame(fn); + + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; + + // get list of offsets from process base address of continuous memory ranges where function's code resides +// if(dwarf_haspc(d, pc)) { + dwarf_lowpc(d, &fn->lowpc); + dwarf_highpc(d, &fn->highpc); +// } else { +// pst_log(SEVERITY_ERROR, "Function's '%s' DIE hasn't definitions of memory offsets of function's code", dwarf_diename(d)); +// return false; +// } + + unw_proc_info_t info; + unw_get_proc_info(&fn->cursor, &info); + fn->ctx->clean_print(fn->ctx); + + pst_log(SEVERITY_INFO, "Function %s(...): LOW_PC = %#lX, HIGH_PC = %#lX, offset from base address: 0x%lX, START_PC = 0x%lX, offset from start of function: 0x%lX", + dwarf_diename(d), fn->lowpc, fn->highpc, fn->pc - fn->ctx->base_addr, info.start_ip, info.start_ip - fn->ctx->base_addr); + fn->ctx->print_registers(fn->ctx, 0x0, 0x10); + pst_log(SEVERITY_INFO, "Function %s(...): CFA: %#lX %s", dwarf_diename(d), fn->parent ? fn->parent->sp : 0, fn->ctx->buff); + pst_log(SEVERITY_INFO, "Function %s(...): %s", dwarf_diename(d), fn->ctx->buff); + + // determine function's stack frame base + attr = dwarf_attr(fn->die, DW_AT_frame_base, &attr_mem); + if(attr) { + if(dwarf_hasform(attr, DW_FORM_exprloc)) { + Dwarf_Op *expr; + size_t exprlen; + if (dwarf_getlocation (attr, &expr, &exprlen) == 0) { + fn->ctx->print_expr(fn->ctx, expr, exprlen, attr); + pst_decl(pst_dwarf_stack, stack, fn->ctx); + if(pst_dwarf_stack_calc(&stack, expr, exprlen, attr, fn)) { + uint64_t value; + if(pst_dwarf_stack_get_value(&stack, &value)) { + pst_log(SEVERITY_DEBUG, "DW_AT_framebase expression: \"%s\" ==> 0x%lX", fn->ctx->buff, value); + } else { + pst_log(SEVERITY_ERROR, "Failed to get value of calculated DW_AT_framebase expression: %s", fn->ctx->buff); + } + } else { + pst_log(SEVERITY_ERROR, "Failed to calculate DW_AT_framebase expression: %s", fn->ctx->buff); + } + pst_dwarf_stack_fini(&stack); + } else { + pst_log(SEVERITY_WARNING, "Unknown attribute form = 0x%X, code = 0x%X", attr->form, attr->code); + } + } + } + + // Get reference to return attribute type of the function + // may be to use dwfl_module_return_value_location() instead + pst_parameter* ret_p = add_param(fn); ret_p->is_return = true; + attr = dwarf_attr(fn->die, DW_AT_type, &attr_mem); + if(attr) { + if(!pst_parameter_handle_type(ret_p, attr)) { + pst_log(SEVERITY_ERROR, "Failed to handle return parameter type for function %s(...)", fn->name); + del_param(ret_p); + } + } else { + pst_parameter_add_type(ret_p, "void", 0); + } + + // handle and save additionally these attributes: + // 1. string of DW_AT_linkage_name (mangled name of program if any) + // A debugging information entry may have a DW_AT_linkage_name attribute + // whose value is a null-terminated string containing the object file linkage name + // associated with the corresponding entity. + + // 2. flag DW_AT_external attribute + // A DW_AT_external attribute, which is a flag, if the name of a variable is + // visible outside of its enclosing compilation unit. + + // 3. A subroutine entry may contain a DW_AT_main_subprogram attribute which is + // a flag whose presence indicates that the subroutine has been identified as the + // starting function of the program. If more than one subprogram contains this flag, + // any one of them may be the starting subroutine of the program. + + + Dwarf_Die result; + if(dwarf_child(fn->die, &result) != 0) + return false; + + // went through parameters and local variables of the function + do { + switch (dwarf_tag(&result)) { + case DW_TAG_formal_parameter: + case DW_TAG_variable: { + pst_parameter* param = add_param(fn); + if(!pst_parameter_handle_dwarf(param, &result, fn)) { + del_param(param); + } + + break; + } + case DW_TAG_GNU_call_site: + pst_call_site_storage_handle_dwarf(&fn->call_sites, &result, fn); + break; + + // case DW_TAG_inlined_subroutine: + // /* Recurse further down */ + // HandleFunction(&result); + // break; + case DW_TAG_lexical_block: { + handle_lexical_block(fn, &result); + break; + } + // Also handle: + // DW_TAG_unspecified_parameters (unknown number of arguments i.e. fun(arg1, ...); + // DW_AT_inline + default: + pst_log(SEVERITY_DEBUG, "Unknown TAG of function: 0x%X", dwarf_tag(&result)); + break; + } + } while(dwarf_siblingof(&result, &result) == 0); + + return true; +} + +bool pst_function_unwind(pst_function* fn, Dwarf_Addr addr) +{ + fn->pc = addr; + + Dwfl_Line *dwline = dwfl_getsrc(fn->ctx->dwfl, addr); + if(dwline != NULL) { + Dwarf_Addr addr; + const char* filename = dwfl_lineinfo (dwline, &addr, &fn->line, NULL, NULL, NULL); + if(filename) { + const char* str = strrchr(filename, '/'); + if(str && *str != 0) { + str++; + } else { + str = filename; + } + fn->file = pst_strdup(str); + fn->ctx->print(fn->ctx, "%s:%d", str, fn->line); + } else { + fn->ctx->print(fn->ctx, "%p", (void*)addr); + } + } else { + fn->ctx->print(fn->ctx, "%p", (void*)addr); + } + + const char* addrname = dwfl_module_addrname(fn->ctx->module, addr); + char* demangle_name = NULL; + if(addrname) { + int status; + demangle_name = abi::__cxa_demangle(addrname, NULL, NULL, &status); + char* function_name = NULL; + if(asprintf(&function_name, "%s%s", demangle_name ? demangle_name : addrname, demangle_name ? "" : "()") == -1) { + pst_log(SEVERITY_ERROR, "Failed to allocate memory"); + return false; + } + fn->ctx->print(fn->ctx, " --> %s", function_name); + + char* str = strchr(function_name, '('); + if(str) { + *str = 0; + } + fn->name = pst_strdup(function_name); + free(function_name); + } + + if(demangle_name) { + free(demangle_name); + } + + return true; +} + +void pst_function_init(pst_function* fn, pst_context* _ctx, __pst_function* _parent) +{ + list_node_init(&fn->node); + + // fields + fn->lowpc = 0; + fn->highpc = 0; + fn->pc = 0; + fn->die = NULL; + fn->name = NULL; + list_head_init(&fn->params); + pst_call_site_storage_init(&fn->call_sites, _ctx); + + fn->sp = 0; + fn->cfa = 0; + memcpy(&fn->cursor, _ctx->curr_frame, sizeof(fn->cursor)); + fn->line = -1; + fn->file = NULL; + fn->parent = _parent; + fn->frame = NULL; + fn->ctx = _ctx; + fn->allocated = false; +} + +pst_function* pst_function_new(pst_context* _ctx, __pst_function* _parent) +{ + pst_function* fn = pst_alloc(pst_function); + if(fn) { + pst_function_init(fn, _ctx, _parent); + fn->allocated = true; + } + + return fn; +} + +void pst_function_fini(pst_function* fn) +{ + clear(fn); + + if(fn->frame) { + // use free here because it was allocate out of our control by libdw + free(fn->frame); + } + + if(fn->name) { + pst_free(fn->name); + } + + if(fn->file) { + pst_free(fn->file); + } + + if(fn->allocated) { + pst_free(fn); + } +} diff --git a/framework/dwarf_function.h b/framework/dwarf_function.h new file mode 100644 index 0000000..390e3a4 --- /dev/null +++ b/framework/dwarf_function.h @@ -0,0 +1,49 @@ +/* + * dwarf_function.h + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#ifndef FRAMEWORK_DWARF_FUNCTION_H_ +#define FRAMEWORK_DWARF_FUNCTION_H_ + +#include "context.h" +#include "dwarf_expression.h" +#include "list_head.h" +#include "dwarf_call_site.h" +#include "dwarf_parameter.h" + +// ----------------------------------------------------------------------------------- +// pst_function +// ----------------------------------------------------------------------------------- +typedef struct __pst_function { + list_node node; + + Dwarf_Addr lowpc; // offset to start of the function against base address + Dwarf_Addr highpc; // offset to the next address after the end of the function against base address + unw_word_t pc; // address between LowPC & HighPC (plus base address offset). actually, currently executed command + Dwarf_Die* die; // DWARF DIE containing definition of the function + char* name; // function's name + list_head params; // function's parameters + pst_call_site_storage call_sites; + + unw_word_t sp; // SP register in function's frame + unw_word_t cfa; // CFA (Canonical Frame Address) of the function + unw_cursor_t cursor; // copy of stack state of the function + int line; // line in code where function is defined + char* file; // file name (DWARF Compilation Unit) where function is defined + __pst_function* parent; // parent function in call trace (caller) + Dwarf_Frame* frame; // function's stack frame + pst_context* ctx; // context of unwinding + bool allocated; // whether this object was allocated or not +} pst_function; +void pst_function_init(pst_function* fn, pst_context* _ctx, __pst_function* _parent); +pst_function* pst_function_new(pst_context* _ctx, __pst_function* _parent); +void pst_function_fini(pst_function* fn); + +bool pst_function_unwind(pst_function* fn, Dwarf_Addr addr); +bool pst_function_handle_dwarf(pst_function * fn, Dwarf_Die* d); +bool pst_function_print_dwarf(pst_function* fn); + +#endif /* FRAMEWORK_DWARF_FUNCTION_H_ */ diff --git a/framework/dwarf_handler.cpp b/framework/dwarf_handler.cpp new file mode 100644 index 0000000..ef44149 --- /dev/null +++ b/framework/dwarf_handler.cpp @@ -0,0 +1,275 @@ +/* + * sysutils.cpp + * + * Created on: Dec 28, 2019 + * Author: nnosov + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "dwarf_handler.h" +#include "logger/log.h" + +#define USE_LIBUNWIND + +#ifdef USE_LIBUNWIND +#include +#endif + +#include "common.h" +#include "dwarf_operations.h" +#include "allocator.h" +#include "dwarf_stack.h" + +// dwfl_addrsegment() possibly can be used to check address validity +// dwarf_getattrs() allows to enumerate all DIE attributes +// dwarf_getfuncs() allows to enumerate functions within CU + +bool get_dwarf_function(pst_handler* h, pst_function* fun) +{ + Dwarf_Addr mod_cu = 0; + // get CU(Compilation Unit) debug definition + h->ctx.module = dwfl_addrmodule(h->ctx.dwfl, fun->pc); + Dwarf_Die* cdie = dwfl_module_addrdie(h->ctx.module, fun->pc, &mod_cu); + //Dwarf_Die* cdie = dwfl_addrdie(dwfl, addr, &mod_bias); + if(!cdie) { + pst_log(SEVERITY_INFO, "Failed to find DWARF DIE for address %X", fun->pc); + return false; + } + + if(dwarf_tag(cdie) != DW_TAG_compile_unit) { + pst_log(SEVERITY_DEBUG, "Skipping non-cu die. DWARF tag: 0x%X, name = %s", dwarf_tag(cdie), dwarf_diename(cdie)); + return false; + } + + Dwarf_Die result; + if(dwarf_child(cdie, &result)) { + pst_log(SEVERITY_ERROR, "No child DIE found for CU %s", dwarf_diename(cdie)); + return false; + } + + bool nret = false; + + do { + int tag = dwarf_tag(&result); + if(tag == DW_TAG_subprogram || tag == DW_TAG_entry_point || tag == DW_TAG_inlined_subroutine) { + //ctx.log(SEVERITY_DEBUG, "function die name %s", dwarf_diename(&result)); + if(!strcmp(fun->name, dwarf_diename(&result))) { + return pst_function_handle_dwarf(fun, &result); + } + } + } while(dwarf_siblingof(&result, &result) == 0); + + return nret; +} + +pst_function* add_function(pst_handler* h, pst_function* parent) +{ + pst_new(pst_function, fn, &h->ctx, parent); + list_add_bottom(&h->functions, &fn->node); + + return fn; +} + +void del_function(pst_function* fn) +{ + list_del(&fn->node); + pst_function_fini(fn); +} + +void clear(pst_handler* h) +{ + pst_function* fn = NULL; + struct list_node *pos, *tn; + list_for_each_entry_safe(fn, pos, tn, &h->functions, node) { + list_del(&fn->node); + pst_function_fini(fn); + } +} + +pst_function* next_function(pst_handler* h, pst_function* fn) +{ + list_node* n = (fn == NULL) ? list_first(&h->functions) : list_next(&fn->node); + pst_function* ret = NULL; + if(n) { + ret = list_entry(n, pst_function, node); + } + + return ret; +} + +pst_function* prev_function(pst_handler* h, pst_function* fn) +{ + list_node* n = (fn == NULL) ? list_last(&h->functions) : list_prev(&fn->node); + pst_function* ret = NULL; + if(n) { + ret = list_entry(n, pst_function, node); + } + + return ret; +} + +pst_function* last_function(pst_handler* h) +{ + list_node* n = list_last(&h->functions); + if(n) { + return list_entry(n, pst_function, node); + } + + return NULL; +} + +bool pst_handler_handle_dwarf(pst_handler* h) +{ + h->ctx.clean_print(&h->ctx); + Dl_info info; + + //for(pst_function* fun = next_function(NULL); fun; fun = next_function(fun)) { + for(pst_function* fun = last_function(h); fun; fun = prev_function(h, fun)) { + dladdr((void*)(fun->pc), &info); + pst_log(SEVERITY_INFO, "Function %s(...): module name: %s, base address: %p, CFA: %#lX", fun->name, info.dli_fname, info.dli_fbase, fun->parent ? fun->parent->sp : 0); + + // setup context + h->ctx.module = dwfl_addrmodule(h->ctx.dwfl, fun->pc); + h->ctx.base_addr = (uint64_t)info.dli_fbase; + h->ctx.curr_frame = &fun->cursor; + h->ctx.sp = fun->sp; + h->ctx.cfa = fun->cfa; + h->ctx.curr_frame = &fun->cursor; + + get_dwarf_function(h, fun); + } + + return true; +} + +void pst_handler_print_dwarf(pst_handler* h) +{ + h->ctx.clean_print(&h->ctx); + h->ctx.print(&h->ctx, "DWARF-based stack trace information:\n"); + uint32_t idx = 0; + for(pst_function* fn = next_function(h, NULL); fn; fn = next_function(h, fn)) { + h->ctx.print(&h->ctx, "[%-2u] ", idx); idx++; + pst_function_print_dwarf(fn); + h->ctx.print(&h->ctx, "\n"); + } +} + +char *debuginfo_path = NULL; +Dwfl_Callbacks callbacks = { + .find_elf = dwfl_linux_proc_find_elf, + .find_debuginfo = dwfl_standard_find_debuginfo, + .section_address = dwfl_offline_section_address, + .debuginfo_path = &debuginfo_path, +}; + +#include + +bool pst_handler_unwind(pst_handler* h) +{ + h->ctx.clean_print(&h->ctx); + void* caller; // pointer to the function which requested to unwind stack + +#ifdef REG_RIP // x86_64 + caller = (void *) h->ctx.hcontext->uc_mcontext.gregs[REG_RIP]; + h->ctx.sp = h->ctx.hcontext->uc_mcontext.gregs[REG_RSP]; + pst_log(SEVERITY_DEBUG, "Original caller's SP: %#lX", h->ctx.sp); +#elif defined(REG_EIP) // x86_32 + caller_address = (void *) uctx->uc_mcontext.gregs[REG_EIP]); +#elif defined(__arm__) + caller_address = (void *) uctx->uc_mcontext.arm_pc); +#elif defined(__aarch64__) + caller_address = (void *) uctx->uc_mcontext.pc); +#elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) + caller_address = (void *) uctx->uc_mcontext.regs->nip); +#elif defined(__s390x__) + caller_address = (void *) uctx->uc_mcontext.psw.addr); +#elif defined(__APPLE__) && defined(__x86_64__) + caller_address = (void *) uctx->uc_mcontext->__ss.__rip); +#else +# error "unknown architecture!" +#endif + + //handle = dlopen(NULL, RTLD_NOW); + Dl_info info; + dladdr(caller, &info); + h->ctx.base_addr = (uint64_t)info.dli_fbase; + pst_log(SEVERITY_INFO, "Process address information: PC address: %p, base address: %p, object name: %s", caller, info.dli_fbase, info.dli_fname); + + h->ctx.dwfl = dwfl_begin(&callbacks); + if(h->ctx.dwfl == NULL) { + h->ctx.print(&h->ctx, "Failed to initialize libdw session for parse stack frames"); + return false; + } + + if(dwfl_linux_proc_report(h->ctx.dwfl, getpid()) != 0 || dwfl_report_end(h->ctx.dwfl, NULL, NULL) !=0) { + h->ctx.print(&h->ctx, "Failed to parse debug section of executable"); + return false; + } + + h->ctx.print(&h->ctx, "Stack trace: caller = %p\n", caller); + + unw_getcontext(&h->ctx.context); + unw_init_local(&h->ctx.cursor, &h->ctx.context); + h->ctx.curr_frame = &h->ctx.cursor; + + Dwarf_Addr addr; // address of currently processed function + for(int i = 0, skip = 1; unw_step(h->ctx.curr_frame) > 0; ++i) { + if(unw_get_reg(h->ctx.curr_frame, UNW_REG_IP, &addr)) { + pst_log(SEVERITY_DEBUG, "Failed to get IP value"); + continue; + } + unw_word_t sp; + if(unw_get_reg(h->ctx.curr_frame, UNW_REG_SP, &sp)) { + pst_log(SEVERITY_DEBUG, "Failed to get SP value"); + continue; + } + + if(addr == (uint64_t)caller) { + skip = 0; + } else if(skip) { + pst_log(SEVERITY_DEBUG, "Skipping frame #%d: PC = %#lX, SP = %#lX", i, addr, sp); + continue; + } + + h->ctx.print(&h->ctx, "[%-2d] ", i); + h->ctx.module = dwfl_addrmodule(h->ctx.dwfl, addr); + pst_function* last = last_function(h); + pst_function* fn = add_function(h, NULL); + fn->sp = sp; + pst_log(SEVERITY_DEBUG, "Analyze frame #%d: PC = %#lX, SP = %#lX", i, addr, sp); + if(!pst_function_unwind(fn, addr)) { + del_function(fn); + } else { + if(last) { + last->parent = fn; + } + //get_frame(fun); + } + h->ctx.print(&h->ctx, "\n"); + } + + return true; +} + +void pst_handler_init(pst_handler* h, ucontext_t* hctx) +{ + pst_context_init(&h->ctx, hctx); + list_head_init(&h->functions); +} + +void pst_handler_fini(pst_handler* h) +{ + clear(h); + pst_context_fini(&h->ctx); +} + diff --git a/framework/dwarf_handler.h b/framework/dwarf_handler.h new file mode 100644 index 0000000..06638e6 --- /dev/null +++ b/framework/dwarf_handler.h @@ -0,0 +1,45 @@ +/* + * sysutils.h + * + * Created on: Dec 28, 2019 + * Author: nnosov + */ + +#ifndef SC_SYSUTILS_H_ +#define SC_SYSUTILS_H_ + +//system +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "allocator.h" +#include "common.h" +#include "context.h" +#include "allocator.h" +#include "list_head.h" +#include "dwarf_expression.h" +#include "dwarf_parameter.h" +#include "dwarf_utils.h" +#include "dwarf_function.h" + +typedef struct pst_handler { + pst_context ctx; // context of unwinding + list_head functions; // list of functions in stack frame +} pst_handler; +void pst_handler_init(pst_handler* h, ucontext_t* hctx); +void pst_handler_fini(pst_handler* h); + +bool pst_handler_handle_dwarf(pst_handler* h); +void pst_handler_print_dwarf(pst_handler* h); +bool pst_handler_unwind(pst_handler* h); + +#endif /* SC_SYSUTILS_H_ */ diff --git a/framework/dwarf_operations.cpp b/framework/dwarf_operations.cpp index 44737df..9b8200f 100644 --- a/framework/dwarf_operations.cpp +++ b/framework/dwarf_operations.cpp @@ -10,19 +10,19 @@ #include "dwarf_operations.h" #include "common.h" +#include "registers.h" // not implemented operations -bool dw_op_notimpl(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_notimpl(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - stack->ctx->log(SEVERITY_ERROR, "%s(0x%lX, 0x%lX) operation is not implemented", map->op_name, op1, op2); return false; } // The DW_OP_addr operation has a single operand that encodes a machine // address and whose size is the size of an address on the target machine. -bool dw_op_addr(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_addr(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - stack->push(&op1, sizeof(op1), DWARF_TYPE_GENERIC); + pst_dwarf_stack_push(stack, &op1, sizeof(op1), DWARF_TYPE_GENERIC); return true; } @@ -32,30 +32,31 @@ bool dw_op_addr(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwa // retrieved is zero extended to the size of an address on the target machine // before being pushed onto the expression stack. -bool dw_op_deref_size(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_deref_size(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value = stack->pop(); + pst_dwarf_value* value = pst_dwarf_stack_pop(stack); if(value) { - uint64_t addr; - if(value->get_uint(addr)) { - uint64_t res = 0; - switch(op1) { - case 1: - res = *((uint8_t*)addr); - break; - case 2: - res = *((uint16_t*)addr); - break; - case 4: - res = *((uint32_t*)addr); - break; - case 8: - res = *((uint64_t*)addr); - break; - } - stack->push(&res, sizeof(res), DWARF_TYPE_GENERIC); - return true; + uint64_t addr = value->value.uint64; + uint64_t res = 0; + switch(op1) { + case 1: + res = *((uint8_t*)addr); + break; + case 2: + res = *((uint16_t*)addr); + break; + case 4: + res = *((uint32_t*)addr); + break; + case 8: + res = *((uint64_t*)addr); + break; + default: + return false; + break; } + pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_GENERIC); + return true; } return false; @@ -64,17 +65,17 @@ bool dw_op_deref_size(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op // The DW_ OP_deref operation pops the top stack entry and treats it as an address. // The popped value must have an integral type. The value retrieved from that address is pushed, and has the generic type. // The size of the data retrieved from the dereferenced address is the size of an address on the target machine. -bool dw_op_deref(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_deref(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { return dw_op_deref_size(stack, map, 8, op2); } // DW_OP_const1u, DW_OP_const2u, DW_OP_const4u, DW_OP_const8u. The single operand of a DW_OP_constu operation provides a 1, 2, 4, or 8-byte unsigned integer constant, respectively. // These operations push a value with the generic type -bool dw_op_const_x_u(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_const_x_u(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { uint8_t size = 0; - dwarf_value_type type = DWARF_TYPE_UNSIGNED; + pst_dwarf_value_type type = DWARF_TYPE_UNSIGNED; switch (map->op_num) { case DW_OP_const1u: size = 1; @@ -96,17 +97,17 @@ bool dw_op_const_x_u(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1 return false; } - stack->push(&op1, size, type | DWARF_TYPE_CONST | DWARF_TYPE_GENERIC); + pst_dwarf_stack_push(stack, &op1, size, type | DWARF_TYPE_CONST | DWARF_TYPE_GENERIC); return true; } // DW_OP_const1s, DW_OP_const2s, DW_OP_const4s, DW_OP_const8s. The single operand of a DW_OP_consts operation provides a 1, 2, 4, or 8-byte signed integer constant, respectively. // These operations push a value with the generic type -bool dw_op_const_x_s(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_const_x_s(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { uint8_t size; int64_t v; - dwarf_value_type type = DWARF_TYPE_SIGNED; + pst_dwarf_value_type type = DWARF_TYPE_SIGNED; switch (map->op_num) { case DW_OP_const1s: v = (int8_t)op1; @@ -132,62 +133,62 @@ bool dw_op_const_x_s(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1 return false; } - stack->push(&v, size, type | DWARF_TYPE_CONST | DWARF_TYPE_GENERIC); + pst_dwarf_stack_push(stack, &v, size, type | DWARF_TYPE_CONST | DWARF_TYPE_GENERIC); return true; } // The single operand of the DW_OP_constu operation provides an unsigned LEB128 integer constant. -bool dw_op_constu(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_constu(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { uint64_t value = decode_uleb128((unsigned char*)&op1); - stack->push(&value, sizeof(value), DWARF_TYPE_LONG | DWARF_TYPE_UNSIGNED | DWARF_TYPE_CONST | DWARF_TYPE_GENERIC); + pst_dwarf_stack_push(stack, &value, sizeof(value), DWARF_TYPE_LONG | DWARF_TYPE_UNSIGNED | DWARF_TYPE_CONST | DWARF_TYPE_GENERIC); return true; } -bool dw_op_consts(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_consts(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { // The single operand of the DW_OP_consts operation provides a signed LEB128 integer constant. int64_t value = decode_sleb128((unsigned char*)&op1); - stack->push(&value, sizeof(value), DWARF_TYPE_LONG | DWARF_TYPE_SIGNED | DWARF_TYPE_CONST | DWARF_TYPE_GENERIC); + pst_dwarf_stack_push(stack, &value, sizeof(value), DWARF_TYPE_LONG | DWARF_TYPE_SIGNED | DWARF_TYPE_CONST | DWARF_TYPE_GENERIC); return true; } // The DW_OP_dup operation duplicates the value (including its type identifier) at the top of the stack. -bool dw_op_dup(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_dup(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value = stack->get(); - stack->push(value->value, value->size, value->type); + pst_dwarf_value* value = pst_dwarf_stack_get(stack, 0); + pst_dwarf_stack_push(stack, &value->value, sizeof(value->value), value->type); return true; } // The DW_OP_drop operation pops the value (including its type identifier) at the top of the stack. -bool dw_op_drop(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_drop(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value = stack->pop(); - free(value); + pst_dwarf_value* value = pst_dwarf_stack_pop(stack); + pst_dwarf_value_fini(value); return true; } // The DW_OP_over operation duplicates the entry currently second in the stack at the top of the stack. // This is equivalent to a DW_OP_pick operation, with index 1. -bool dw_op_over(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_over(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value = stack->get(1); - stack->push(value->value, value->size, value->type); + pst_dwarf_value* value = pst_dwarf_stack_get(stack, 1); + pst_dwarf_stack_push(stack, &value->value, sizeof(value->value), value->type); return true; } // The single operand of the DW_OP_pick operation provides a 1-byte index. // A copy of the stack entry (including its type identifier) with the specified index (0 through 255, inclusive) is pushed onto the stack. -bool dw_op_pick(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_pick(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value = stack->get(op1); + pst_dwarf_value* value = pst_dwarf_stack_get(stack, op1); if(value) { - stack->push(value->value, value->size, value->type); + pst_dwarf_stack_push(stack, &value->value, sizeof(value->value), value->type); return true; } @@ -196,21 +197,21 @@ bool dw_op_pick(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwa // The DW_OP_swap operation swaps the top two stack entries. The entry at the top of the stack (including its type identifier) becomes the second stack // entry, and the second entry (including its type identifier) becomes the top of the stack. -bool dw_op_swap(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_swap(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value1 = stack->pop(); - dwarf_value* value2 = stack->pop(); + pst_dwarf_value* value1 = pst_dwarf_stack_pop(stack); + pst_dwarf_value* value2 = pst_dwarf_stack_pop(stack); if(value1 && value2) { - stack->push(value1); - stack->push(value2); + pst_dwarf_stack_push_value(stack, value1); + pst_dwarf_stack_push_value(stack, value2); return true; } if(value1) { - delete(value1); + pst_dwarf_value_fini(value1); } if(value2) { - delete(value2); + pst_dwarf_value_fini(value2); } return false; @@ -220,26 +221,26 @@ bool dw_op_swap(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwa // The entry at the top of the stack (including its type identifier) becomes the third stack entry, // the second entry (including its type identifier) becomes the top of the stack, // and the third entry (including its type identifier) becomes the second entry -bool dw_op_rot(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_rot(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value1 = stack->pop(); - dwarf_value* value2 = stack->pop(); - dwarf_value* value3 = stack->pop(); + pst_dwarf_value* value1 = pst_dwarf_stack_pop(stack); + pst_dwarf_value* value2 = pst_dwarf_stack_pop(stack); + pst_dwarf_value* value3 = pst_dwarf_stack_pop(stack); if(value1 && value2 && value3) { - stack->push(value1); - stack->push(value3); - stack->push(value2); + pst_dwarf_stack_push_value(stack, value1); + pst_dwarf_stack_push_value(stack, value3); + pst_dwarf_stack_push_value(stack, value2); return true; } if(value1) { - free(value1); + pst_dwarf_value_fini(value1); } if(value2) { - free(value2); + pst_dwarf_value_fini(value2); } if(value3) { - free(value3); + pst_dwarf_value_fini(value3); } return false; @@ -247,45 +248,33 @@ bool dw_op_rot(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwar // The DW_OP_abs operation pops the top stack entry, interprets it as a signed value and pushes its absolute value. // If the absolute value cannot be represented, the result is undefined. -bool dw_op_abs(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_abs(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value = stack->get(); + pst_dwarf_value* value = pst_dwarf_stack_get(stack, 0); if(value) { - int64_t v; - if(!value->get_int(v)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong %d size of stack value", value->size); - return false; - } - uint64_t res = llabs(v); - value->replace(&res, value->size, DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC | DWARF_TYPE_LONG); + uint64_t res = llabs(value->value.int64); + pst_dwarf_value_set(value, &res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC | DWARF_TYPE_LONG); } return false; } // The DW_OP_and operation pops the top two stack values, performs a bitwise and operation on the two, and pushes the result. -bool dw_op_and(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_and(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value1 = stack->get(0); - dwarf_value* value2 = stack->get(1); + pst_dwarf_value* value1 = pst_dwarf_stack_get(stack, 0); + pst_dwarf_value* value2 = pst_dwarf_stack_get(stack, 1); if(value1 && value2) { if(!(value1->type & value2->type)) { - stack->ctx->log(SEVERITY_ERROR, "Different types of two stack values for operation: %s(%0x%X, %0x%X)", map->op_name, value1->type, value2->type); - return false; - } - uint64_t v1 = 0, v2 = 0; - if(!value1->get_generic(v1)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 1st stack value for operation %s(%d)", map->op_name, value1->size); return false; } - if(!value2->get_generic(v2)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 2nd stack value for operation %s(%d)", map->op_name, value1->size); - return false; - } - uint64_t res = v1 & v2; - stack->pop(); stack->pop(); - stack->push(&res, value1->size, DWARF_TYPE_GENERIC); + uint64_t res = value1->value.uint64 & value2->value.uint64; + pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack); + pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_GENERIC); + + pst_dwarf_value_fini(value1); + pst_dwarf_value_fini(value2); return true; } @@ -293,84 +282,49 @@ bool dw_op_and(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwar } // The DW_OP_div operation pops the top two stack values, divides the former second entry by the former top of the stack using signed division, and pushes the result. -bool dw_op_div(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_div(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value1 = stack->get(0); - dwarf_value* value2 = stack->get(1); + pst_dwarf_value* value1 = pst_dwarf_stack_get(stack, 0); + pst_dwarf_value* value2 = pst_dwarf_stack_get(stack, 1); if(value1 && value2) { if(!(value1->type & value2->type)) { - stack->ctx->log(SEVERITY_ERROR, "Different types of two stack values for operation: %s(%0x%X, %0x%X)", map->op_name, value1->type, value2->type); return false; } if(value2->type & DWARF_TYPE_SIGNED) { - int64_t sig2; - if(!value2->get_int(sig2)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 2nd stack value for operation %s(%d)", map->op_name, value1->size); - return false; - } if(value1->type & DWARF_TYPE_SIGNED) { - int64_t sig1; - if(!value1->get_int(sig1)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 1st stack value for operation %s(%d)", map->op_name, value1->size); + if(value1->value.int64 == 0) { return false; } - if(sig1 == 0) { - stack->ctx->log(SEVERITY_ERROR, "Division by zero for operation %s(0x%lX, 0x%lX)", map->op_name, sig1, sig2); - return false; - } - uint64_t res = sig2 / sig1; - stack->pop(); stack->pop(); - stack->push(&res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC); + uint64_t res = value2->value.int64 / value1->value.int64; + pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack); + pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC); return true; } else { - uint64_t unsig1; - if(!value1->get_uint(unsig1)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 1st stack value for operation %s(%d)", map->op_name, value1->size); + if(value1->value.uint64 == 0) { return false; } - if(unsig1 == 0) { - stack->ctx->log(SEVERITY_ERROR, "Division by zero for operation %s(0x%lX, 0x%lX)", map->op_name, unsig1, sig2); - return false; - } - int64_t res = sig2 / unsig1; - stack->pop(); stack->pop(); - stack->push(&res, sizeof(res), DWARF_TYPE_SIGNED | DWARF_TYPE_GENERIC); + int64_t res = value2->value.int64 / value1->value.uint64; + pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack); + pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_SIGNED | DWARF_TYPE_GENERIC); return true; } } else { - uint64_t unsig2; - if(!value2->get_uint(unsig2)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 2nd stack value for operation %s(%d)", map->op_name, value1->size); - return false; - } if(value1->type & DWARF_TYPE_SIGNED) { - int64_t sig1; - if(!value1->get_int(sig1)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 1st stack value for operation %s(%d)", map->op_name, value1->size); + if(value1->value.int64 == 0) { return false; } - if(sig1 == 0) { - stack->ctx->log(SEVERITY_ERROR, "Division by zero for operation %s(0x%lX, 0x%lX)", map->op_name, sig1, sig1); - return false; - } - int64_t res = unsig2 / sig1; - stack->pop(); stack->pop(); - stack->push(&res, sizeof(res), DWARF_TYPE_SIGNED | DWARF_TYPE_GENERIC); + int64_t res = value2->value.uint64 / value1->value.int64; + pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack); + pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_SIGNED | DWARF_TYPE_GENERIC); return true; } else { - uint64_t unsig1; - if(!value1->get_uint(unsig1)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 1st stack value for operation %s(%d)", map->op_name, value1->size); + if(value1->value.uint64 == 0) { return false; } - if(unsig1 == 0) { - stack->ctx->log(SEVERITY_ERROR, "Division by zero for operation %s(0x%lX, 0x%lX)", map->op_name, unsig1, unsig2); - return false; - } - uint64_t res = unsig2 / unsig1; - stack->pop(); stack->pop(); - stack->push(&res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC); + uint64_t res = value2->value.uint64 / value1->value.uint64; + pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack); + pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC); return true; } } @@ -380,32 +334,23 @@ bool dw_op_div(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwar } // The DW_OP_minus operation pops the top two stack values, subtracts the former top of the stack from the former second entry, and pushes the result. -bool dw_op_minus(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_minus(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value1 = stack->get(0); - dwarf_value* value2 = stack->get(1); + pst_dwarf_value* value1 = pst_dwarf_stack_get(stack, 0); + pst_dwarf_value* value2 = pst_dwarf_stack_get(stack, 1); if(value1 && value2) { if(!(value1->type & value2->type)) { - stack->ctx->log(SEVERITY_ERROR, "Different types of two stack values for operation: %s(%d, %d)", map->op_name, value1->type, value2->type); return false; } - // use arithmetic by modulo 1 plus - uint64_t unsig1; uint64_t unsig2; - if(!value1->get_uint(unsig1)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 1st stack value for operation %s(%d)", map->op_name, value1->size); - return false; - } - if(!value2->get_uint(unsig2)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 2nd stack value for operation %s(%d)", map->op_name, value2->size); - return false; - } + int res_type = DWARF_TYPE_GENERIC; if(value2->type & DWARF_TYPE_MEMORY_LOC) { res_type |= DWARF_TYPE_MEMORY_LOC; } - uint64_t res = unsig2 - unsig1; - stack->pop(); stack->pop(); - stack->push(&res, sizeof(res), res_type); + // use arithmetic by modulo 1 plus + uint64_t res = value2->value.uint64 - value1->value.uint64; + pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack); + pst_dwarf_stack_push(stack, &res, sizeof(res), res_type); return true; } @@ -414,34 +359,23 @@ bool dw_op_minus(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dw } // The DW_OP_mod operation pops the top two stack values and pushes the result of the calculation: former second stack entry modulo the former top of the stack. -bool dw_op_mod(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_mod(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value1 = stack->get(0); - dwarf_value* value2 = stack->get(1); + pst_dwarf_value* value1 = pst_dwarf_stack_get(stack, 0); + pst_dwarf_value* value2 = pst_dwarf_stack_get(stack, 1); if(value1 && value2) { if(!(value1->type & value2->type)) { - stack->ctx->log(SEVERITY_ERROR, "Different types of two stack values for operation: %s(%d, %d)", map->op_name, value1->type, value2->type); - return false; - } - uint64_t v1 = 0, v2 = 0; - if(!value1->get_uint(v1)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 1st stack value for operation %s(%d)", map->op_name, value1->size); return false; } - if(v1 == 0) { - stack->ctx->log(SEVERITY_ERROR, "Division by zero requested, aborting."); + if(value1->value.uint64 == 0) { return false; } - if(!value2->get_uint(v2)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 2nd stack value for operation %s(%d)", map->op_name, value1->size); - return false; - } + uint64_t res = value2->value.uint64 % value1->value.uint64; + pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack); + pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC); - uint64_t res = v2 % v1; - stack->pop(); stack->pop(); - stack->push(&res, value1->size, DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC); return true; } @@ -449,84 +383,42 @@ bool dw_op_mod(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwar } // The DW_OP_mul operation pops the top two stack entries, multiplies them together, and pushes the result. -bool dw_op_mul(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_mul(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value1 = stack->get(0); - dwarf_value* value2 = stack->get(1); + pst_dwarf_value* value1 = pst_dwarf_stack_get(stack, 0); + pst_dwarf_value* value2 = pst_dwarf_stack_get(stack, 1); if(value1 && value2) { if(!(value1->type & value2->type)) { - stack->ctx->log(SEVERITY_ERROR, "Different types of two stack values for operation: %s(%0x%X, %0x%X)", map->op_name, value1->type, value2->type); + pst_log(SEVERITY_ERROR, "Different types of two stack values for operation: %s(%0x%X, %0x%X)", map->op_name, value1->type, value2->type); return false; } if(value2->type & DWARF_TYPE_SIGNED) { - int64_t sig2; - if(!value2->get_int(sig2)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 2nd stack value for operation %s(%d)", map->op_name, value1->size); - return false; - } if(value1->type & DWARF_TYPE_SIGNED) { - int64_t sig1; - if(!value1->get_int(sig1)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 1st stack value for operation %s(%d)", map->op_name, value1->size); - return false; - } - if(sig1 == 0) { - stack->ctx->log(SEVERITY_ERROR, "Division by zero for operation %s(0x%lX, 0x%lX)", map->op_name, sig1, sig2); - return false; - } - uint64_t res = sig2 * sig1; - stack->pop(); stack->pop(); - stack->push(&res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC); + uint64_t res = value2->value.int64 * value1->value.int64; + pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack); + pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC); + return true; } else { - uint64_t unsig1; - if(!value1->get_uint(unsig1)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 1st stack value for operation %s(%d)", map->op_name, value1->size); - return false; - } - if(unsig1 == 0) { - stack->ctx->log(SEVERITY_ERROR, "Division by zero for operation %s(0x%lX, 0x%lX)", map->op_name, unsig1, sig2); - return false; - } - int64_t res = sig2 * unsig1; - stack->pop(); stack->pop(); - stack->push(&res, sizeof(res), DWARF_TYPE_SIGNED | DWARF_TYPE_GENERIC); + int64_t res = value2->value.int64 * value1->value.uint64; + pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack); + pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_SIGNED | DWARF_TYPE_GENERIC); + return true; } } else { - uint64_t unsig2; - if(!value2->get_uint(unsig2)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 2nd stack value for operation %s(%d)", map->op_name, value1->size); - return false; - } if(value1->type & DWARF_TYPE_SIGNED) { - int64_t sig1; - if(!value1->get_int(sig1)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 1st stack value for operation %s(%d)", map->op_name, value1->size); - return false; - } - if(sig1 == 0) { - stack->ctx->log(SEVERITY_ERROR, "Division by zero for operation %s(0x%lX, 0x%lX)", map->op_name, sig1, sig1); - return false; - } - int64_t res = unsig2 * sig1; - stack->pop(); stack->pop(); - stack->push(&res, sizeof(res), DWARF_TYPE_SIGNED | DWARF_TYPE_GENERIC); + int64_t res = value2->value.uint64 * value1->value.int64; + pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack); + pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_SIGNED | DWARF_TYPE_GENERIC); + return true; } else { - uint64_t unsig1; - if(!value1->get_uint(unsig1)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 1st stack value for operation %s(%d)", map->op_name, value1->size); - return false; - } - if(unsig1 == 0) { - stack->ctx->log(SEVERITY_ERROR, "Division by zero for operation %s(0x%lX, 0x%lX)", map->op_name, unsig1, unsig2); - return false; - } - uint64_t res = unsig2 * unsig1; - stack->pop(); stack->pop(); - stack->push(&res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC); + uint64_t res = value2->value.uint64 * value1->value.uint64; + pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack); + pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC); + return true; } } @@ -537,62 +429,31 @@ bool dw_op_mul(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwar // The DW_OP_neg operation pops the top stack entry, interprets it as a signed value and pushes its negation. // If the negation cannot be represented, the result is undefined. -bool dw_op_neg(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_neg(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value = stack->get(); + pst_dwarf_value* value = pst_dwarf_stack_get(stack, 0); if(value) { - int64_t v = 0; - if(!value->get_int(v)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of stack value for operation %s(%d)", map->op_name, value->size); - return false; - } - v *= -1; - - switch (value->size) { - case 1: { - int8_t vv = (int8_t)v; - value->replace(&vv, sizeof(vv), value->type); - break; - } - case 2: { - int16_t vv = (int16_t)v; - value->replace(&vv, sizeof(vv), value->type); - break; - } - case 4: { - int32_t vv = (int32_t)v; - value->replace(&vv, sizeof(vv), value->type); - break; - } - case 8: { - value->replace(&v, sizeof(v), value->type); - break; - } - default: - stack->ctx->log(SEVERITY_ERROR, "Wrong size of stack value for operation %s(%d)", map->op_name, value->size); - return false; - break; - } - - return true; + if(value->type & DWARF_TYPE_CHAR) { + value->value.int8 *= -1; + } else if(value->type & DWARF_TYPE_SHORT) { + value->value.int16 *= -1; + } else if(value->type & DWARF_TYPE_INT) { + value->value.int32 *= -1; + } else { + value->value.int64 *= -1; + } + return true; } return false; } // The DW_OP_not operation pops the top stack entry, and pushes its bitwise complement. -bool dw_op_not(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_not(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value = stack->get(); + pst_dwarf_value* value = pst_dwarf_stack_get(stack, 0); if(value) { - uint64_t v = 0; - if(!value->get_uint(v)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of stack value for operation %s(%d)", map->op_name, value->size); - return false; - } - v = ~v; - - value->replace(&v, value->size, value->type); + value->value.uint64 = ~value->value.uint64; return true; } @@ -601,29 +462,22 @@ bool dw_op_not(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwar } // The DW_OP_or operation pops the top two stack entries, performs a bitwise or operation on the two, and pushes the result. -bool dw_op_or(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_or(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value1 = stack->get(0); - dwarf_value* value2 = stack->get(1); + pst_dwarf_value* value1 = pst_dwarf_stack_get(stack, 0); + pst_dwarf_value* value2 = pst_dwarf_stack_get(stack, 1); if(value1 && value2) { if(!(value1->type & value2->type)) { - stack->ctx->log(SEVERITY_ERROR, "Different types of two stack values for operation: %s(%d, %d)", map->op_name, value1->type, value2->type); - return false; - } - uint64_t v1 = 0, v2 = 0; - if(!value1->get_uint(v1)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 1st stack value for operation %s(%d)", map->op_name, value1->size); return false; } - if(!value2->get_uint(v2)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 2nd stack value for operation %s(%d)", map->op_name, value1->size); - return false; - } + uint64_t res = value2->value.uint64 | value1->value.uint64; + pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack); + pst_dwarf_stack_push(stack, &res, sizeof(res), value1->type); + + pst_dwarf_value_fini(value1); + pst_dwarf_value_fini(value2); - uint64_t res = v2 | v1; - stack->pop(); stack->pop(); - stack->push(&res, value1->size, value1->type); return true; } @@ -631,30 +485,30 @@ bool dw_op_or(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf } // The DW_OP_plus operation pops the top two stack entries, adds them together, and pushes the result -bool dw_op_plus(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_plus(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value1 = stack->get(0); - dwarf_value* value2 = stack->get(1); + pst_dwarf_value* value1 = pst_dwarf_stack_get(stack, 0); + pst_dwarf_value* value2 = pst_dwarf_stack_get(stack, 1); if(value1 && value2) { if(!(value1->type & value2->type)) { - stack->ctx->log(SEVERITY_ERROR, "Different types of two stack values for operation: %s(%d, %d)", map->op_name, value1->type, value2->type); - return false; - } - int64_t v1 = 0, v2 = 0; - if(!value1->get_int(v1)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 1st stack value for operation %s(%d)", map->op_name, value1->size); return false; } - if(!value2->get_int(v2)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of 2nd stack value for operation %s(%d)", map->op_name, value1->size); - return false; + if((value1->type & DWARF_TYPE_SIGNED) && (value2->type & DWARF_TYPE_SIGNED)) { + int64_t res = value2->value.int64 + value1->value.int64; + pst_dwarf_stack_push(stack, &res, sizeof(res), value1->type); + } else { + // if in arithmetic expression even one operand is unsigned then result is unsigned as well + int type = (value1->type & (~DWARF_TYPE_SIGNED)) | DWARF_TYPE_UNSIGNED; + uint64_t res = value2->value.uint64 + value1->value.uint64; + pst_dwarf_stack_push(stack, &res, sizeof(res), type); } - uint64_t res = v2 + v1; - stack->pop(); stack->pop(); - stack->push(&res, value1->size, value1->type); - return true; + pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack); + pst_dwarf_value_fini(value1); + pst_dwarf_value_fini(value2); + + return true; } return false; @@ -664,18 +518,12 @@ bool dw_op_plus(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwa // operand popped from the top of the stack and pushes the result. // This operation is supplied specifically to be able to encode more field offsets in two // bytes than can be done with “DW_OP_lit DW_OP_plus.” -bool dw_op_plus_uconst(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_plus_uconst(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* value = stack->get(); + pst_dwarf_value* value = pst_dwarf_stack_get(stack, 0); if(value) { uint64_t op = decode_uleb128((unsigned char*)&op1); - uint64_t v = 0; - if(!value->get_generic(v)) { - stack->ctx->log(SEVERITY_ERROR, "Wrong size of stack value for operation %s(%d)", map->op_name, value->size); - return false; - } - v += op; - value->replace(&v, sizeof(v), value->type); + value->value.uint64 += op; return true; } @@ -686,7 +534,7 @@ bool dw_op_plus_uconst(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word o // Register location descriptions. Describe an object (or a piece of an object) that resides in a register. // A register location description must stand alone as the entire description of an object or a piece of an object. // The DW_OP_regx operation has a single unsigned LEB128 literal operand that encodes the name of a register -bool dw_op_reg_x(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_reg_x(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { if(map->op_num != DW_OP_regx && (map->op_num < DW_OP_reg0 || map->op_num > DW_OP_reg31)) { return false; @@ -699,7 +547,7 @@ bool dw_op_reg_x(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dw regno = map->op_num - DW_OP_reg0; } - stack->push(®no, sizeof(regno), DWARF_TYPE_REGISTER_LOC); + pst_dwarf_stack_push(stack, ®no, sizeof(regno), DWARF_TYPE_REGISTER_LOC); return true; } @@ -707,7 +555,7 @@ bool dw_op_reg_x(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dw // DWARF5, section 2.5.1.2 Register values are used to describe an object (or a piece of an object) that is located in memory at an address that is contained in a register (possibly offset by some constant) // The DW_OP_bregx operation provides the sum of two values specified by its two operands. // The first operand is a register number which is specified by an unsigned LEB128 number. The second operand is a signed LEB128 offset. -bool dw_op_breg_x(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_breg_x(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { if(map->op_num != DW_OP_bregx && (map->op_num < DW_OP_breg0 || map->op_num > DW_OP_breg31)) { return false; @@ -725,26 +573,25 @@ bool dw_op_breg_x(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, D unw_word_t val = 0; int ret = unw_get_reg(stack->ctx->curr_frame, regno, &val); if(ret) { - stack->ctx->log(SEVERITY_ERROR, "%s: Failed to get register 0x%X value. Error: %d", __PRETTY_FUNCTION__, regno, ret); return false; } val += off; - stack->push(&val, sizeof(val), DWARF_TYPE_GENERIC); + pst_dwarf_stack_push(stack, &val, sizeof(val), DWARF_TYPE_GENERIC); return true; } // The DW_OP_lit operations encode the unsigned literal values from 0 through 31, inclusive. // Operations other than DW_OP_const_type push a value with the generic type. -bool dw_op_lit_x(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_lit_x(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { if(map->op_num < DW_OP_lit0 || map->op_num > DW_OP_lit31) { return false; } uint64_t val = map->op_num - DW_OP_lit0; - stack->push(&val, sizeof(val), DWARF_TYPE_GENERIC); + pst_dwarf_stack_push(stack, &val, sizeof(val), DWARF_TYPE_GENERIC); return true; } @@ -752,10 +599,10 @@ bool dw_op_lit_x(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dw // The DW_OP_stack_value operation specifies that the object does not exist in memory but its value is nonetheless known and is at the top of the DWARF // expression stack. In this form of location description, the DWARF expression represents the actual value of the object, rather than its location. // The DW_OP_stack_value operation terminates the expression. -bool dw_op_stack_value(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_stack_value(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { - dwarf_value* v = stack->get(); + pst_dwarf_value* v = pst_dwarf_stack_get(stack, 0); if(v) { v->type = DWARF_TYPE_GENERIC; return true; @@ -765,30 +612,30 @@ bool dw_op_stack_value(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word o } // The DW_OP_call_frame_cfa operation pushes the value of the CFA, obtained from the Call Frame Information (see Section 6.4 on page 171). -bool dw_op_call_frame_cfa(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_call_frame_cfa(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { // since we are already know SP value, just push it to DWARF stack // unw_word_t sp; // int ret = unw_get_reg(stack->ctx->curr_frame, UNW_REG_SP, &sp); // if(ret) { -// stack->ctx->log(SEVERITY_ERROR, "%s: failed to get register 0x%X value. Error: %d", __PRETTY_FUNCTION__, UNW_REG_SP, ret); +// pst_log(SEVERITY_ERROR, "%s: failed to get register 0x%X value. Error: %d", __PRETTY_FUNCTION__, UNW_REG_SP, ret); // return false; // } - stack->push(&stack->ctx->cfa, sizeof(stack->ctx->cfa), /*DWARF_TYPE_MEMORY_LOC | */DWARF_TYPE_GENERIC); + pst_dwarf_stack_push(stack, &stack->ctx->cfa, sizeof(stack->ctx->cfa), /*DWARF_TYPE_MEMORY_LOC | */DWARF_TYPE_GENERIC); return true; } // The DW_OP_fbreg operation provides a signed LEB128 offset from the address specified by the location description in the DW_AT_frame_base // attribute of the current function. This is typically a stack pointer register plus or minus some offset -bool dw_op_fbreg(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) +bool dw_op_fbreg(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2) { // since in signal handler we are know SP value, just use it as DW_AT_frame_base unw_word_t sp; // int ret = unw_get_reg(stack->ctx->curr_frame, UNW_REG_SP, &sp); // if(ret) { -// stack->ctx->log(SEVERITY_ERROR, "Failed to get register 0x%X value. Error: %d", __PRETTY_FUNCTION__, UNW_REG_SP, ret); +// pst_log(SEVERITY_ERROR, "Failed to get register 0x%X value. Error: %d", __PRETTY_FUNCTION__, UNW_REG_SP, ret); // return false; // } @@ -796,7 +643,7 @@ bool dw_op_fbreg(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dw int64_t off = decode_sleb128((unsigned char*)&op1); sp += off; - stack->push(&sp, sizeof(sp), DWARF_TYPE_MEMORY_LOC | DWARF_TYPE_GENERIC); + pst_dwarf_stack_push(stack, &sp, sizeof(sp), DWARF_TYPE_MEMORY_LOC | DWARF_TYPE_GENERIC); return true; } @@ -989,107 +836,3 @@ const dwarf_op_map* find_op_map(int op) return NULL; } - -bool __dwarf_stack::get_value(uint64_t& value) -{ - if(!Size()) { - return false; - } - - value = 0; - dwarf_value* v = get(); - v->get_uint(value); - if(v->type & DWARF_TYPE_REGISTER_LOC) { - // dereference register location - uint64_t regno = value; - int ret = unw_get_reg(ctx->curr_frame, regno, &value); - if(ret) { - ctx->log(SEVERITY_ERROR, "Failed to get value of register 0x%X. Error: %d", regno, ret); - return false; - } - } else if(v->type & DWARF_TYPE_MEMORY_LOC) { - // dereference memory location - uint64_t addr = *(uint64_t*)v->value; - value = *((uint64_t*)addr); - } - - return true; -} - -bool __dwarf_stack::calc_expression(Dwarf_Op *exprs, int expr_len, Dwarf_Attribute* attr, pst_function* fun) -{ - clear(); - for (int i = 0; i < expr_len; i++) { - const dwarf_op_map* map = find_op_map(exprs[i].atom); - if(!map) { - ctx->log(SEVERITY_ERROR, "Unknown operation type 0x%hhX(0x%lX, 0x%lX)", exprs[i].atom, exprs[i].number, exprs[i].number2); - return false; - } - - pst_dwarf_op* op = new pst_dwarf_op(exprs[i].atom, exprs[i].number, exprs[i].number2); - expr.InsertLast(op); - - dwarf_value* v = get(); - // dereference register location there if it is not last in stack - if(v && (v->type & DWARF_TYPE_REGISTER_LOC)) { - unw_word_t value = 0; - uint64_t regno = *((uint64_t*)v->value); - int ret = unw_get_reg(ctx->curr_frame, regno, &value); - if(ret) { - ctx->log(SEVERITY_ERROR, "Failed to ger value of register 0x%X. Error: %d", regno, ret); - return false; - } - v->replace(&value, sizeof(value), DWARF_TYPE_GENERIC); - } - - // handle there because it contains sub-expression of a Location in caller's frame - if(map->op_num == DW_OP_GNU_entry_value) { - if(!fun) { - ctx->log(SEVERITY_ERROR, "Cannot calculate DW_OP_GNU_entry_value expression while function is undefined"); - return false; - } - if(!fun->parent) { - ctx->log(SEVERITY_ERROR, "Function has not parent while calculate DW_OP_GNU_entry_value expression"); - return false; - } - // This opcode has two operands, the first one is uleb128 length and the second is block of that length, containing either a - // simple register or DWARF expression - Dwarf_Attribute attr_mem; - if(!dwarf_getlocation_attr(attr, exprs, &attr_mem)) { - Dwarf_Op *expr; - size_t exprlen; - if (dwarf_getlocation(&attr_mem, &expr, &exprlen) == 0) { - pst_call_site* cs = fun->parent->find_call_site(fun); - if(!cs) { - ctx->log(SEVERITY_ERROR, "Failed to find call site while calculate DW_OP_GNU_entry_value expression"); - return false; - } - pst_dwarf_expr loc; loc.setup(expr, exprlen); - pst_call_site_param* param = cs->find_param(loc); - if(!param) { - ctx->log(SEVERITY_ERROR, "Failed to find call site parameter while calculate DW_OP_GNU_entry_value expression"); - return false; - } - - push(¶m->value, sizeof(param->value), DWARF_TYPE_GENERIC); - - continue; - } else { - ctx->log(SEVERITY_ERROR, "Failed to get DW_OP_GNU_entry_value attr location"); - return false; - } - } else { - ctx->log(SEVERITY_ERROR, "Failed to get DW_OP_GNU_entry_value attr expression"); - return false; - } - } - - if(!map->operation(this, map, exprs[i].number, exprs[i].number2)) { - ctx->log(SEVERITY_ERROR, "Failed to calculate %s(0x%lX, 0x%lX) operation", map->op_name, exprs[i].number, exprs[i].number2); - return false; - } - - } - - return true; -} diff --git a/framework/dwarf_operations.h b/framework/dwarf_operations.h index 873508f..233ee9f 100644 --- a/framework/dwarf_operations.h +++ b/framework/dwarf_operations.h @@ -3,124 +3,15 @@ #include #include "common.h" -#include "linkedlist.h" -#include "sysutils.h" +#include "list_head.h" #include "context.h" +#include "dwarf_handler.h" -typedef enum { - DWARF_TYPE_INVALID = 0, // no type - DWARF_TYPE_SIGNED = 1, // signed type - DWARF_TYPE_UNSIGNED = 2, // unsigned type - DWARF_TYPE_CONST = 4, // constant signed/unsigned type - DWARF_TYPE_GENERIC = 8, // size of machine address type - DWARF_TYPE_CHAR = 16, // 1 byte size - DWARF_TYPE_FLOAT = 32, // machine-dependent floating point size - DWARF_TYPE_REGISTER_LOC = 64, // value located in register specified as 'value' - DWARF_TYPE_MEMORY_LOC = 128, // value located in memory address specified as 'value' - DWARF_TYPE_PIECE = 256, // piece of whole value located in current value - DWARF_TYPE_SHORT = 512, // 2 byte size - DWARF_TYPE_INT = 1024, // 4 byte size - DWARF_TYPE_LONG = 2048 // 8 byte size -} dwarf_value_type; - -typedef struct __dwarf_value : public SC_ListNode { - __dwarf_value(char*v, uint32_t s, int t) - { - size = s; - value = (char*)malloc(s); - memcpy(value, v, s); - type = t; - } - - ~__dwarf_value() - { - if(value) { - free(value); - value = NULL; - size = 0; - } - } - - void replace(void* v, uint32_t s, int t) - { - if(value) { - free(value); - size = 0; - type = DWARF_TYPE_INVALID; - } - - value = (char*)malloc(s); - memcpy(value, v, s); - size = s; - type = t; - } - - bool get_uint(uint64_t& v); - bool get_int(int64_t& v); - bool get_generic(uint64_t& v); - - char* value; - uint32_t size; // size in bytes except of 'DWARF_TYPE_PIECE', in such case in bits - int type; // bitmask of DWARF_TYPE_XXX -} dwarf_value; - -typedef struct __dwarf_stack : public SC_ListHead { - __dwarf_stack(pst_context* c) : ctx(c) { - attr = NULL; - } - - ~__dwarf_stack() { - for(pst_dwarf_op* op = (pst_dwarf_op*)expr.First(); op; op = (pst_dwarf_op*)expr.First()) { - expr.Remove(op); - delete op; - } - } - - void clear() { - for(dwarf_value* v = (dwarf_value*)First(); v; v = (dwarf_value*)First()) { - Remove(v); - delete v; - } - } - - void push(void* v, uint32_t s, int t) { - dwarf_value* value = new dwarf_value((char*)v, s, t); - InsertFirst(value); - } - - void push(dwarf_value* value) { - InsertFirst(value); - } - - dwarf_value* pop() { - dwarf_value* value = (dwarf_value*)First(); - if(value) { - Remove(value); - } - - return value; - } - - dwarf_value* get(uint32_t idx = 0) { - dwarf_value* value = NULL; - for(value = (dwarf_value*)First(); value && idx; value = (dwarf_value*)Next(value)) { - idx--; - } - - return value; - } - - bool calc_expression(Dwarf_Op *exprs, int expr_len, Dwarf_Attribute* attr, pst_function* fun = NULL); - bool get_value(uint64_t& value); - - Dwarf_Attribute* attr; // attribute which expression currently processed - SC_ListHead expr; // DWARF expression - pst_context* ctx; -} dwarf_stack; +#include "dwarf_stack.h" typedef struct __dwarf_op_map dwarf_op_map; -typedef bool (*dwarf_operation)(dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2); +typedef bool (*dwarf_operation)(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2); typedef struct __dwarf_op_map { int op_num; // DWARF Operation DW_OP_XXX @@ -129,4 +20,3 @@ typedef struct __dwarf_op_map { } dwarf_op_map; const dwarf_op_map* find_op_map(int op); -int find_regnum(uint32_t op); diff --git a/framework/dwarf_parameter.cpp b/framework/dwarf_parameter.cpp new file mode 100644 index 0000000..83f95f9 --- /dev/null +++ b/framework/dwarf_parameter.cpp @@ -0,0 +1,297 @@ +/* + * dwarf_parameter.cpp + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#include +#include + +#include "dwarf_parameter.h" +#include "dwarf_utils.h" +#include "context.h" + +// +// pst_type +// +void pst_type_init(pst_type* t, const char* name, uint32_t type) +{ + list_node_init(&t->node); + t->name = pst_strdup(name); + t->type = type; + t->allocated = false; +} + +pst_type* pst_type_new(const char* name, uint32_t type) +{ + pst_type* nt = pst_alloc(pst_type); + if(nt) { + pst_type_init(nt, name, type); + nt->allocated = true; + } + + return nt; +} + +void pst_type_fini(pst_type* t) +{ + pst_free(t->name); + if(t->allocated) { + pst_free(t); + } +} + +// +// pst_parameter +// +void clear(pst_parameter* param) +{ + pst_type* t = NULL; + struct list_node *pos, *tn; + list_for_each_entry_safe(t, pos, tn, ¶m->types, node) { + list_del(&t->node); + pst_type_fini(t); + } +} + +pst_type* next_type(pst_parameter* param, pst_type* t) +{ + list_node* n = (t == NULL) ? list_first(¶m->types) : list_next(&t->node); + pst_type* ret = NULL; + if(n) { + ret = list_entry(n, pst_type, node); + } + + return ret; +} + +bool pst_parameter_print_dwarf(pst_parameter* param) +{ + if(list_count(¶m->types)) { + if(!param->is_return) { + if(param->has_value) { + param->ctx->print(param->ctx, "%s %s = 0x%lX", next_type(param, NULL)->name, param->name, param->location.value); + } else { + param->ctx->print(param->ctx, "%s %s = ", next_type(param, NULL)->name, param->name); + } + } else { + param->ctx->print(param->ctx, "%s", next_type(param, NULL)->name); + } + } else { + if(param->has_value) { + param->ctx->print(param->ctx, "%s = 0x%lX", param->name, param->location.value); + } else { + param->ctx->print(param->ctx, "%s = ", param->name); + } + } + return true; +} + +pst_type* pst_parameter_add_type(pst_parameter* param, const char* name, int type) +{ + pst_new(pst_type, t, name, type); + list_add_bottom(¶m->types, &t->node); + + return t; +} + +bool pst_parameter_handle_type(pst_parameter* param, Dwarf_Attribute* base) +{ + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; + + // get DIE of return type + Dwarf_Die ret_die; + + if(!dwarf_formref_die(base, &ret_die)) { + pst_log(SEVERITY_ERROR, "Failed to get parameter DIE"); + return false; + } + + switch (dwarf_tag(&ret_die)) { + case DW_TAG_base_type: { + // get Size attribute and it's value + param->size = 0; + attr = dwarf_attr(&ret_die, DW_AT_byte_size, &attr_mem); + if(attr) { + dwarf_formudata(attr, ¶m->size); + } + pst_log(SEVERITY_DEBUG, "base type '%s'(%lu)", dwarf_diename(&ret_die), param->size); + pst_parameter_add_type(param, dwarf_diename(&ret_die), DW_TAG_base_type); + param->type = DW_TAG_base_type; + + attr = dwarf_attr(&ret_die, DW_AT_encoding, &attr_mem); + if(attr) { + param->enc_type = 0; + dwarf_formudata(attr, ¶m->enc_type); + } + break; + } + case DW_TAG_array_type: + pst_log(SEVERITY_DEBUG, "array type"); + pst_parameter_add_type(param, "[]", DW_TAG_array_type); + break; + case DW_TAG_structure_type: + pst_log(SEVERITY_DEBUG, "structure type"); + pst_parameter_add_type(param, "struct", DW_TAG_structure_type); + break; + case DW_TAG_union_type: + pst_log(SEVERITY_DEBUG, "union type"); + pst_parameter_add_type(param, "union", DW_TAG_union_type); + break; + case DW_TAG_class_type: + pst_log(SEVERITY_DEBUG, "class type"); + pst_parameter_add_type(param, "class", DW_TAG_class_type); + break; + case DW_TAG_pointer_type: + pst_log(SEVERITY_DEBUG, "pointer type"); + pst_parameter_add_type(param, "*", DW_TAG_pointer_type); + break; + case DW_TAG_enumeration_type: + pst_log(SEVERITY_DEBUG, "enumeration type"); + pst_parameter_add_type(param, "enum", DW_TAG_enumeration_type); + break; + case DW_TAG_const_type: + pst_log(SEVERITY_DEBUG, "constant type"); + pst_parameter_add_type(param, "const", DW_TAG_const_type); + break; + case DW_TAG_subroutine_type: + pst_log(SEVERITY_DEBUG, "Skipping subroutine type"); + break; + case DW_TAG_typedef: + pst_log(SEVERITY_DEBUG, "typedef '%s' type", dwarf_diename(&ret_die)); + pst_parameter_add_type(param, dwarf_diename(&ret_die), DW_TAG_typedef); + break; + default: + pst_log(SEVERITY_WARNING, "Unknown 0x%X tag type", dwarf_tag(&ret_die)); + break; + } + + attr = dwarf_attr(&ret_die, DW_AT_type, &attr_mem); + if(attr) { + return pst_parameter_handle_type(param, attr); + } + + return true; +} + +bool pst_parameter_handle_dwarf(pst_parameter* param, Dwarf_Die* result, pst_function* fun) +{ + param->die = result; + + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; + + param->name = pst_strdup(dwarf_diename(result)); + param->is_variable = (dwarf_tag(result) == DW_TAG_variable); + + dwarf_decl_line(result, (int*)¶m->line); + // Get reference to attribute type of the parameter/variable + attr = dwarf_attr(result, DW_AT_type, &attr_mem); + pst_log(SEVERITY_DEBUG, "---> Handle '%s' %s", param->name, dwarf_tag(result) == DW_TAG_formal_parameter ? "parameter" : "variable"); + if(attr) { + pst_parameter_handle_type(param, attr); + } + + if(dwarf_hasattr(result, DW_AT_location)) { + // determine location of parameter in stack/heap or CPU registers + attr = dwarf_attr(result, DW_AT_location, &attr_mem); + Dwarf_Addr pc; + unw_get_reg(param->ctx->curr_frame, UNW_REG_IP, &pc); + + if(handle_location(param->ctx, attr, ¶m->location, pc, fun)) { + param->has_value = true; + } else { + pst_log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", param->ctx->buff); + } + } else if(dwarf_hasattr(result, DW_AT_const_value)) { + // no locations definitions, value is constant, known by DWARF directly + attr = dwarf_attr(result, DW_AT_const_value, &attr_mem); + switch (dwarf_whatform(attr)) { + case DW_FORM_string: + // do nothing for now + pst_log(SEVERITY_WARNING, "Const value form DW_FORM_string value = %s.", dwarf_formstring(attr)); + break; + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + dwarf_formudata(attr, ¶m->location.value); + param->has_value = true; + break; + case DW_FORM_sdata: + dwarf_formsdata(attr, (int64_t*)¶m->location.value); + param->has_value = true; + break; + case DW_FORM_udata: + dwarf_formudata(attr, ¶m->location.value); + param->has_value = true; + break; + } + if(param->has_value) { + pst_log(SEVERITY_DEBUG, "Parameter constant value: 0x%lX", param->location.value); + } + } + + // Additionally handle these attributes: + // 1. DW_AT_default_value to get information about default value for DW_TAG_formal_parameter type of function + // A DW_AT_default_value attribute for a formal parameter entry. The value of + // this attribute may be a constant, or a reference to the debugging information + // entry for a variable, or a reference to a debugging information entry containing a DWARF procedure + + // 2. DW_AT_variable_parameter + // A DW_AT_variable_parameter attribute, which is a flag, if a formal + // parameter entry represents a parameter whose value in the calling function + // may be modified by the callee. The absence of this attribute implies that the + // parameter’s value in the calling function cannot be modified by the callee. + + // 3. DW_AT_abstract_origin + // In place of these omitted attributes, each concrete inlined instance entry has a DW_AT_abstract_origin attribute that may be used to obtain the + // missing information (indirectly) from the associated abstract instance entry. The value of the abstract origin attribute is a reference to the associated abstract + // instance entry. + + + return true; +} + +void pst_parameter_init(pst_parameter* param, pst_context* ctx) +{ + list_node_init(¶m->node); + param->die = NULL; + param->name = NULL; + param->line = 0; + param->size = 0; + param->type = 0; + param->enc_type = 0; + list_head_init(¶m->types); + param->is_return = false; + param->is_variable = false; + param->has_value = false; + param->ctx = ctx; + pst_dwarf_expr_init(¶m->location); + param->allocated = false; +} + +pst_parameter* pst_parameter_new(pst_context* ctx) +{ + pst_parameter* param = pst_alloc(pst_parameter); + if(param) { + pst_parameter_init(param, ctx); + param->allocated = true; + } + + return param; +} +void pst_parameter_fini(pst_parameter* param) +{ + clear(param); + if(param->name) { + pst_free(param->name); + } + if(param->allocated) { + pst_free(param); + } +} + + diff --git a/framework/dwarf_parameter.h b/framework/dwarf_parameter.h new file mode 100644 index 0000000..31abf0f --- /dev/null +++ b/framework/dwarf_parameter.h @@ -0,0 +1,59 @@ +/* + * dwarf_parameter.h + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#ifndef FRAMEWORK_DWARF_PARAMETER_H_ +#define FRAMEWORK_DWARF_PARAMETER_H_ + +#include +#include + +#include "list_head.h" +#include "context.h" +#include "dwarf_expression.h" + +typedef struct __pst_type { + list_node node; // uplink + + char* name; // type name + uint32_t type; // DW_AT_XXX type + bool allocated; // whether this struct was allocated or not +} pst_type; +void pst_type_init(pst_type* t, const char* name, uint32_t type); +pst_type* pst_type_new(const char* name, uint32_t type); +void pst_type_fini(pst_type* t); + +typedef struct __pst_function pst_function; + +typedef struct pst_parameter{ + list_node node; // uplink. !!! must be first !!! + + // fields + Dwarf_Die* die; // DWARF DIE containing parameter's definition + char* name; // parameter's name + uint32_t line; // line of parameter definition + Dwarf_Word size; // size of parameter in bytes + int type; // type of parameter in DW_TAG_XXX types enumeration + Dwarf_Word enc_type; // if 'type' is DW_TAG_Base_type, then 'base_type' holds DW_AT_ATE_XXX base type encoding type + list_head types; // list of parameter's definitions i.e. 'typedef', 'uint32_t' + bool is_return; // whether this parameter is return value of the function + bool is_variable; // whether this parameter is function variable or argument of function + bool has_value; // whether we got value of parameter or not + pst_context* ctx; + pst_dwarf_expr location; + + bool allocated; +} pst_parameter; +void pst_parameter_init(pst_parameter* param, pst_context* ctx); +pst_parameter* pst_parameter_new(pst_context* ctx); +void pst_parameter_fini(pst_parameter* param); + +bool pst_parameter_handle_dwarf(pst_parameter* param, Dwarf_Die* result, pst_function* fun); +bool pst_parameter_print_dwarf(pst_parameter* param); +bool pst_parameter_handle_type(pst_parameter* param, Dwarf_Attribute* base); +pst_type* pst_parameter_add_type(pst_parameter* param, const char* name, int type); + +#endif /* FRAMEWORK_DWARF_PARAMETER_H_ */ diff --git a/framework/dwarf_stack.cpp b/framework/dwarf_stack.cpp new file mode 100644 index 0000000..51df01a --- /dev/null +++ b/framework/dwarf_stack.cpp @@ -0,0 +1,296 @@ +/* + * dwarf_stack.cpp + * + * Created on: Jan 29, 2020 + * Author: nnosov + */ + +#include +#include +#include + +#include "allocator.h" +#include "list_head.h" + +#include "dwarf_stack.h" + +#include "dwarf_handler.h" +#include "dwarf_operations.h" + +// ----------------------------------------------------------------------------------- +// DWARF Stack value +// ----------------------------------------------------------------------------------- +void pst_dwarf_value_set(pst_dwarf_value* value, void* v, uint32_t s, int t) +{ + pst_assert(value && v && s > 0 && s <= sizeof(value->value)); + + // clean-up all bits + value->value.uint64 = 0; + value->type = t; + + if(t & DWARF_TYPE_SIGNED) { + switch(s) { + case 1: + value->value.int8 = *(int8_t*)v; + break; + case 2: + value->value.int16 = *(int16_t*)v; + break; + case 4: + value->value.int32 = *(int32_t*)v; + break; + default: + value->value.int64 = *(int64_t*)v; + break; + + } + } else { + // GENERIC, SIGNED etc + switch(s) { + case 1: + value->value.uint8 = *(uint8_t*)v; + break; + case 2: + value->value.uint16 = *(uint16_t*)v; + break; + case 4: + value->value.uint32 = *(uint32_t*)v; + break; + default: + value->value.uint64 = *(uint64_t*)v; + break; + + } + } +} + +void pst_dwarf_value_init(pst_dwarf_value* dv, char* v, uint32_t s, int t) +{ + pst_assert(dv && s <= sizeof(dv->value)); + + list_node_init(&dv->node); + dv->type = t; + dv->allocated = false; + pst_dwarf_value_set(dv, v, s, t); +} + +pst_dwarf_value* pst_dwarf_value_new(char* v, uint32_t s, int t) +{ + pst_assert(v); + + pst_dwarf_value* nv = pst_alloc(pst_dwarf_value); + if(nv) { + pst_dwarf_value_init(nv, v, s, t); + nv->allocated = true; + } + + + return nv; +} + +void pst_dwarf_value_fini(pst_dwarf_value* value) +{ + pst_assert(value); + + if(value->allocated) { + pst_free(value); + } else { + value->type = DWARF_TYPE_INVALID; + value->value.uint64 = 0; + } +} + + +// ----------------------------------------------------------------------------------- +// DWARF stack +// ----------------------------------------------------------------------------------- +void pst_dwarf_stack_push(pst_dwarf_stack* st, void* v, uint32_t s, int t) +{ + pst_new(pst_dwarf_value, value, (char*)v, s, t); + list_add_head(&st->values, &value->node); +} + +void pst_dwarf_stack_push_value(pst_dwarf_stack* st, pst_dwarf_value* value) +{ + list_add_head(&st->values, &value->node); +} + +pst_dwarf_value* pst_dwarf_stack_pop(pst_dwarf_stack* st) +{ + pst_dwarf_value* value = (pst_dwarf_value*)list_first(&st->values); + if(value) { + list_del(&value->node); + } + + return value; +} + +pst_dwarf_value* pst_dwarf_stack_get(pst_dwarf_stack* st, uint32_t idx) +{ + pst_dwarf_value* value = NULL; + struct list_node *pos; + list_for_each_entry(value, pos, &st->values, node) { + if(!idx) { + break; + } + idx--; + } + + return value; +} + +bool pst_dwarf_stack_get_value(pst_dwarf_stack* st, uint64_t* value) +{ + assert(st && value); + + if(!list_count(&st->values)) { + return false; + } + + pst_dwarf_value* v = pst_dwarf_stack_get(st, 0); + if(v->type & DWARF_TYPE_REGISTER_LOC) { + // dereference register location + int ret = unw_get_reg(st->ctx->curr_frame, v->value.uint64, value); + if(ret) { + pst_log(SEVERITY_ERROR, "Failed to get value of register 0x%X. Error: %d", v->value.uint64, ret); + return false; + } + } else if(v->type & DWARF_TYPE_MEMORY_LOC) { + // dereference memory location + *value = *((uint64_t*)v->value.uint64); + } else { + *value = v->value.uint64; + } + + return true; +} + +void pst_dwarf_stack_clear(pst_dwarf_stack* st) +{ + pst_dwarf_value* value = NULL; + struct list_node *pos, *tn; + list_for_each_entry_safe(value, pos, tn, &st->values, node) { + list_del(&value->node); + pst_dwarf_value_fini(value); + } + + pst_dwarf_op* op = NULL; + list_for_each_entry_safe(op, pos, tn, &st->expr, node) { + list_del(&op->node); + pst_dwarf_op_fini(op); + } +} + +bool pst_dwarf_stack_calc(pst_dwarf_stack* st, Dwarf_Op *exprs, int expr_len, Dwarf_Attribute* attr, pst_function* fun) +{ + pst_dwarf_stack_clear(st); + + for (int i = 0; i < expr_len; i++) { + const dwarf_op_map* map = find_op_map(exprs[i].atom); + if(!map) { + pst_log(SEVERITY_ERROR, "Unknown operation type 0x%hhX(0x%lX, 0x%lX)", exprs[i].atom, exprs[i].number, exprs[i].number2); + return false; + } + + pst_new(pst_dwarf_op, op, exprs[i].atom, exprs[i].number, exprs[i].number2); + list_add_bottom(&st->expr, &op->node); + + pst_dwarf_value* v = pst_dwarf_stack_get(st, 0); + // dereference register location there if it is not last in stack + if(v && (v->type & DWARF_TYPE_REGISTER_LOC)) { + unw_word_t value = 0; + uint64_t regno = *((uint64_t*)v->value.uint64); + int ret = unw_get_reg(st->ctx->curr_frame, regno, &value); + if(ret) { + pst_log(SEVERITY_ERROR, "Failed to ger value of register 0x%X. Error: %d", regno, ret); + return false; + } + pst_dwarf_value_set(v, &value, sizeof(value), DWARF_TYPE_GENERIC); + } + + // handle there because it contains sub-expression of a Location in caller's frame + if(map->op_num == DW_OP_GNU_entry_value) { + if(!fun || !fun->parent) { + pst_log(SEVERITY_ERROR, "Cannot calculate DW_OP_GNU_entry_value expression while function and it's caller is undefined"); + return false; + } + + // This opcode has two operands, the first one is uleb128 length and the second is block of that length, containing either a + // simple register or DWARF expression + Dwarf_Attribute attr_mem; + if(!dwarf_getlocation_attr(attr, exprs, &attr_mem)) { + Dwarf_Op *expr; + size_t exprlen; + if (dwarf_getlocation(&attr_mem, &expr, &exprlen) == 0) { + pst_call_site* cs = pst_call_site_storage_find(&fun->parent->call_sites, fun); + if(!cs) { + pst_log(SEVERITY_ERROR, "Failed to find call site while calculate DW_OP_GNU_entry_value expression"); + return false; + } + pst_dwarf_expr loc; + pst_dwarf_expr_init(&loc); + pst_dwarf_expr_setup(&loc, expr, exprlen); + pst_call_site_param* param = pst_call_site_find(cs, &loc); + pst_dwarf_expr_fini(&loc); + if(!param) { + pst_log(SEVERITY_ERROR, "Failed to find call site parameter while calculate DW_OP_GNU_entry_value expression"); + return false; + } + + pst_dwarf_stack_push(st, ¶m->value, sizeof(param->value), DWARF_TYPE_GENERIC); + + continue; + } else { + pst_log(SEVERITY_ERROR, "Failed to get DW_OP_GNU_entry_value attr location"); + return false; + } + } else { + pst_log(SEVERITY_ERROR, "Failed to get DW_OP_GNU_entry_value attr expression"); + return false; + } + } + + if(!map->operation(st, map, exprs[i].number, exprs[i].number2)) { + pst_log(SEVERITY_ERROR, "Failed to calculate %s(0x%lX, 0x%lX) operation", map->op_name, exprs[i].number, exprs[i].number2); + return false; + } + + } + + return true; +} + +void pst_dwarf_stack_init(pst_dwarf_stack* st, pst_context* ctx) +{ + pst_assert(st && ctx); + + list_head_init(&st->expr); + list_head_init(&st->values); + st->ctx = ctx; + st->allocated = false; +} + +pst_dwarf_stack* pst_dwarf_stack_new(pst_context* ctx) +{ + pst_assert(ctx); + + pst_new(pst_dwarf_stack, ns, ctx); + if(ns) { + pst_dwarf_stack_init(ns, ctx); + ns->allocated = true; + } + + return ns; +} + +void pst_dwarf_stack_fini(pst_dwarf_stack* st) +{ + pst_assert(st); + + pst_dwarf_stack_clear(st); + if(st->allocated) { + pst_free(st); + } else { + st->ctx = NULL; + } +} diff --git a/framework/dwarf_stack.h b/framework/dwarf_stack.h new file mode 100644 index 0000000..8ad6d97 --- /dev/null +++ b/framework/dwarf_stack.h @@ -0,0 +1,97 @@ +/* + * dwarf_stack.h + * + * Created on: Jan 29, 2020 + * Author: nnosov + */ + +#ifndef FRAMEWORK_DWARF_STACK_H_ +#define FRAMEWORK_DWARF_STACK_H_ + +#include + +#include "common.h" +#include "allocator.h" +#include "context.h" +#include "dwarf_handler.h" + +// ----------------------------------------------------------------------------------- +// DWARF Stack value +// ----------------------------------------------------------------------------------- + +// DWARF Stack value types, bitmask +typedef enum { + DWARF_TYPE_INVALID = 0, // no type + DWARF_TYPE_SIGNED = 1, // signed type + DWARF_TYPE_UNSIGNED = 2, // unsigned type + DWARF_TYPE_CONST = 4, // constant signed/unsigned type + DWARF_TYPE_GENERIC = 8, // size of machine address type + DWARF_TYPE_CHAR = 16, // 1 byte size + DWARF_TYPE_FLOAT = 32, // machine-dependent floating point size + DWARF_TYPE_REGISTER_LOC = 64, // value located in register specified as 'value' + DWARF_TYPE_MEMORY_LOC = 128, // value located in memory address specified as 'value' + DWARF_TYPE_PIECE = 256, // piece of whole value located in current value + DWARF_TYPE_SHORT = 512, // 2 byte size + DWARF_TYPE_INT = 1024, // 4 byte size + DWARF_TYPE_LONG = 2048 // 8 byte size +} pst_dwarf_value_type; + +// DWARF Stack value storage +typedef union { + uint64_t uint64_v; +#define uint64 uint64_v + int64_t int64_v; +#define int64 int64_v + uint32_t uint32_v[2]; +#define uint32 uint32_v[0] + int32_t int32_v[2]; +#define int32 int32_v[0] + uint16_t uint16_v[4]; +#define uint16 uint16_v[0] + int16_t int16_v[4]; +#define int16 int16_v[0] + uint8_t uint8_v[8]; +#define uint8 uint8_v[0] + int8_t int8_v[8]; +#define int8 int8_v[0] + void* ptr; +} pst_sized_value; + +typedef struct __pst_dwarf_value { + list_node node; // uplink, !!! must be 1st field in structure !!! + + // fields + pst_sized_value value; // value itself + int type; // value type. bitmask of DWARF_TYPE_XXX + bool allocated; +} pst_dwarf_value; + +void pst_dwarf_value_init(pst_dwarf_value* value, char* v, uint32_t s, int t); +pst_dwarf_value* pst_dwarf_value_new(char* v, uint32_t s, int t); +void pst_dwarf_value_fini(pst_dwarf_value* value); +void pst_dwarf_value_set(pst_dwarf_value* value, void* v, uint32_t s, int t); + +// ----------------------------------------------------------------------------------- +// DWARF stack +// ----------------------------------------------------------------------------------- + +typedef struct __pst_dwarf_stack { + list_head expr; // DWARF expression + list_head values; // list of values on the stack + pst_context* ctx; // context of execution + bool allocated; // whether this object was allocated or not +} pst_dwarf_stack; + +void pst_dwarf_stack_init(pst_dwarf_stack* st, pst_context* ctx); +pst_dwarf_stack* pst_dwarf_stack_new(pst_context* ctx); +void pst_dwarf_stack_fini(pst_dwarf_stack* st); + +bool pst_dwarf_stack_calc(pst_dwarf_stack* st, Dwarf_Op *exprs, int expr_len, Dwarf_Attribute* attr, pst_function* fun = NULL); +void pst_dwarf_stack_clear(pst_dwarf_stack* st); +bool pst_dwarf_stack_get_value(pst_dwarf_stack* st, uint64_t* value); +pst_dwarf_value* pst_dwarf_stack_get(pst_dwarf_stack* st, uint32_t idx = 0); +pst_dwarf_value* pst_dwarf_stack_pop(pst_dwarf_stack* st); +void pst_dwarf_stack_push_value(pst_dwarf_stack* st, pst_dwarf_value* value); +void pst_dwarf_stack_push(pst_dwarf_stack* st, void* v, uint32_t s, int t); + +#endif /* FRAMEWORK_DWARF_STACK_H_ */ diff --git a/framework/dwarf_utils.cpp b/framework/dwarf_utils.cpp new file mode 100644 index 0000000..fed3937 --- /dev/null +++ b/framework/dwarf_utils.cpp @@ -0,0 +1,67 @@ +/* + * dwarf_utils.cpp + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#include + +#include "dwarf_utils.h" +#include "dwarf_stack.h" + +int regname_callback (void *arg, int regno, const char *setname, const char *prefix, const char *regname, int bits, int type) +{ + reginfo* info = (reginfo*)arg; + if(info->regno == regno) { + snprintf(info->regname, sizeof(info->regname), "%s %s%s", setname, prefix, regname); + } + + return 0; +} + +bool handle_location(pst_context* ctx, Dwarf_Attribute* attr, pst_dwarf_expr* loc, Dwarf_Addr pc, pst_function* fun = NULL) +{ + ctx->clean_print(ctx); + Dwarf_Addr offset = pc - ctx->base_addr; + + pst_decl(pst_dwarf_stack, stack, ctx); + Dwarf_Op *expr; + size_t exprlen; + bool ret = false; + if(dwarf_hasform(attr, DW_FORM_exprloc)) { + // Location expression (exprloc class of location in DWARF terms) + if(dwarf_getlocation(attr, &expr, &exprlen) == 0) { + pst_dwarf_expr_setup(loc, expr, exprlen); + ctx->print_expr(ctx, expr, exprlen, attr); + ret = pst_dwarf_stack_calc(&stack, expr, exprlen, attr, fun); + pst_dwarf_stack_get_value(&stack, &loc->value); + } + } else if(dwarf_hasform(attr, DW_FORM_sec_offset)) { + // Location list (loclist class of location in DWARF terms) + Dwarf_Addr base, start, end; + ptrdiff_t off = 0; + + // handle list of possible locations of parameter + for(int i = 0; (off = dwarf_getlocations (attr, off, &base, &start, &end, &expr, &exprlen)) > 0; ++i) { + ctx->print_expr(ctx, expr, exprlen, attr); + if(offset >= start && offset <= end) { + pst_dwarf_expr_setup(loc, expr, exprlen); + // actual location, try to calculate Location expression + ret = pst_dwarf_stack_calc(&stack, expr, exprlen, attr, fun); + pst_dwarf_stack_get_value(&stack, &loc->value); + } else { + // Location skipped due to don't match current PC offset + pst_log(SEVERITY_DEBUG, "Skip Location list expression: [%d] (low_offset: 0x%" PRIx64 ", high_offset: 0x%" PRIx64 "), \"%s\"", i, start, end, ctx->buff); + } + } + + } else { + pst_log(SEVERITY_WARNING, "Unknown location attribute form = 0x%X, code = 0x%X, ", attr->form, attr->code); + } + + pst_dwarf_stack_fini(&stack); + + return ret; +} + diff --git a/framework/dwarf_utils.h b/framework/dwarf_utils.h new file mode 100644 index 0000000..fde682a --- /dev/null +++ b/framework/dwarf_utils.h @@ -0,0 +1,29 @@ +/* + * dwarf_utils.h + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#ifndef FRAMEWORK_DWARF_UTILS_H_ +#define FRAMEWORK_DWARF_UTILS_H_ + +#include + +#include "dwarf_expression.h" +#include "context.h" +#include "dwarf_handler.h" + +typedef struct __reginfo { + __reginfo() { + regname[0] = 0; + regno = -1; + } + char regname[32]; + int regno; +} reginfo; + +int regname_callback (void *arg, int regno, const char *setname, const char *prefix, const char *regname, int bits, int type); +bool handle_location(pst_context* ctx, Dwarf_Attribute* attr, pst_dwarf_expr* loc, Dwarf_Addr pc, pst_function* fun); + +#endif /* FRAMEWORK_DWARF_UTILS_H_ */ diff --git a/framework/hash_multimap.cpp b/framework/hash_multimap.cpp new file mode 100644 index 0000000..0a3cf64 --- /dev/null +++ b/framework/hash_multimap.cpp @@ -0,0 +1,327 @@ +/* ============================================================================= + * CDL (Configuration Definition Language) validator $Revision: 1.4 $ + * (C)2004-2007 Nikolay Nosov. All rights reserved. + * + * File: $RCSfile: hash_multimap.c,v $ + * Purpose: Hash Multimap implementation + * Written by: Nikolay Nosov + * Last modified: $Date: 2008/06/21 12:15:53 $ by $Author: nnosov $. + * + * For more information please visit + * http://nsoft.volgocity.ru/cdl or + * http://cdl.sourceforge.net + * ===========================================================================*/ +#include +#include +#include +#include "hash_multimap.h" + +/* Default hash function +@param key hash key +@param size hash key size in bytes +@return hash value, depends of the hash key +*/ +static unsigned int default_hash_fn(const void *key, int size) +{ + register int i, j; + unsigned int cs = 0; + unsigned int ps = 0; + + if(key) + { + for(i = 0, j = 0; i < size; i++, j++) + { + ps |= ((unsigned int)((const char*)key)[i]) << (j * 8); + if(j == 3 || j == (size - 1)) + { + cs ^= ps; + ps = 0; + j = 0; + } + } + } + + return cs; +} + +static int default_compare_fn(const void *key1, const void *key2, const int size) +{ + return !memcmp(key1, key2, size); +} + +/** Initialize hash node. Must be called before first usage of the node. + +@param node pointer to the hash node descriptor +*/ +void hash_node_init(struct hash_node *node) +{ + node->key = NULL; + node->key_size = 0; + list_node_init(&node->node); +} + +/** Cleanup hash node + +@param node pointer to the hash node descriptor +*/ +void hash_node_cleanup(struct hash_node *node) +{ + if(node->key) free(node->key); + list_node_init(&node->node); + node->key = NULL; + node->key_size = 0; +} + +/** Initialize hash table + +@param map pointer to the hash table descriptor +@param hash_shift size of the hash table in bits. Should be greater than +HASH_MIN_SHIFT and less than HASH_MAX_SHIFT. In case of less than HASH_MIN_SHIFT, +will be followed by HASH_MIN_SHIFT, in case of greater HASH_MAX_SHIFT will be +followed by HASH_MAX_SHIFT +@param hf pointer to the user defined hash function. If NULL, then default hash +function will be used +@param cf pointer to the user defined compare function. if NULL, then the default +compare function will be used +*/ +int hash_head_init(struct hash_head *map, unsigned int hash_shift, _hash_fn hf, _compare_fn cf) +{ + int i; + + if(map) + { + if(hash_shift <= HASH_MIN_SHIFT) { + map->hash_shift = HASH_MIN_SHIFT; + } else if(hash_shift >= HASH_MAX_SHIFT) { + map->hash_shift = HASH_MAX_SHIFT; + } else { + map->hash_shift = hash_shift; + } + + map->hash_size = 1UL << map->hash_shift; + map->hash_mask = (map->hash_size - 1); + + map->bucket = (struct list_head *)malloc(sizeof(struct list_head) * map->hash_size); + if(map->bucket) + { + for(i = 0; i < map->hash_size; i++) + { + list_head_init(map->bucket + i); + } + + if(hf) { + map->hash_fn = hf; + } else { + map->hash_fn = default_hash_fn; + } + + if(cf) { + map->compare_fn = cf; + } else { + map->compare_fn = default_compare_fn; + } + } + else + return ENOMEM; + } + else + return ENODEV; + + return 0; +} + +/** Cleanup hash table descriptor + +@param map pointer to the hash table descriptor +*/ +void hash_head_cleanup(struct hash_head *map) +{ + struct list_node *p, *n; + struct hash_node *node; + int i; + + if(map) + { + if(map->bucket) + { + for(i = 0; i < map->hash_size; i++) + { + list_for_each_safe(p, n, map->bucket + i) + { + node = list_entry(p, struct hash_node, node); + hash_node_cleanup(node); + } + } + free(map->bucket); + } + } +} + +/** Find first node which key is equal to the key represented + +@param map pointer to the hash table descriptor +@param key pointer to the hash key +@param key_size size of the hash key +@return pointer to the key found, or NULL in case of hash table have not nodes +with the key equals to the key represented +*/ +struct hash_node* hash_find(struct hash_head *map, const void *key, int key_size) +{ + struct list_head *list; + struct list_node *n; + struct hash_node *node; + + list = map->bucket + (map->hash_fn(key, key_size) & map->hash_mask); + list_for_each(n, list) + { + node = list_entry(n, struct hash_node, node); + if(key_size == node->key_size) + { + if(map->compare_fn(key, node->key, key_size)) return node; + } + } + + return NULL; +} + +/** Find next node, which key is equal to the key represented + +@param hn1 pointer to the previous node found. +@return pointer to the next node with the same key, or NULL in case of the current +node is the last node with appropriate key +*/ +struct hash_node* hash_find_next(struct hash_head *map, struct hash_node *hn1) +{ + struct list_node *n = &hn1->node; + struct hash_node *hn2; + + + while((n = list_next(n)) != NULL) + { + hn2 = list_entry(n, struct hash_node, node); + if(hn1->key_size == hn2->key_size) + { + if(map->compare_fn(hn1->key, hn2->key, hn2->key_size)) return hn2; + } + } + + return NULL; +} + +/** Add node to the hash table + +@param map pointer to the hash table +@param node pointer to the node, which will be added to the table +@param key pointer to the hash key +@param key_size size of the hash key + +@return zero in case of success, ENOMEM in case of error +*/ +int hash_add(struct hash_head *map, struct hash_node *node, const void *key, int key_size) +{ + node->key = (char*)malloc(key_size); + if(node->key) + { + memcpy(node->key, key, key_size); + node->key_size = key_size; + list_add_bottom(map->bucket + (map->hash_fn(key, key_size) & map->hash_mask), &node->node); + + return 0; + } + + return ENOMEM; +} + +/** Remove node from the hash table + +@param node pointer to the node descriptor +*/ +void hash_del(struct hash_node *node) +{ + list_del_init(&node->node); + hash_node_cleanup(node); +} + +/** Initialize iterator for usage. Must be called before first iterator usage. + +@param map pointer to the hash table descriptor +@param iter pointer to the iterator descriptor +*/ +void hash_iterator_init(struct hash_head *map, struct hash_iterator *iter) +{ + iter->map = map; + iter->map_idx = 0; + iter->current = NULL; +} + +/** Move iterator to the first node in the table, and return pointer to the node + +@param iter pointer to the iterator descriptor +@return pointer to the first node in the table, or NULL in case of error +*/ +struct hash_node * hash_node_first(struct hash_iterator *iter) +{ + iter->current = list_first(iter->map->bucket); + iter->map_idx = 0; + + if (iter->current) + { + return list_entry(iter->current, struct hash_node, node); + } + + return NULL; +} + +/** Move iterator to the next node in the table and return pointer to the node + +@param iter pointer to the iterator descriptor +@return pointer to the next node in the table, or NULL in case of error +*/ +struct hash_node * hash_node_next(struct hash_iterator *iter) +{ + if(iter->map_idx >= iter->map->hash_size) return NULL; + + if(iter->current) { + iter->current = list_next(iter->current); + } + if(iter->current) { + return list_entry(iter->current, struct hash_node, node); + } + + while(iter->map_idx < iter->map->hash_size) { + iter->current = list_first(iter->map->bucket + iter->map_idx); + iter->map_idx++; + if(iter->current) { + return list_entry(iter->current, struct hash_node, node); + } + } + + return NULL; +} + +/** Remove current node, pointed by iterator, and move iterator to the next node + +@param iter pointer to the iterator descriptor +*/ +void hash_node_del(struct hash_iterator *iter) +{ + struct list_node *tmp; + + tmp = iter->current; + hash_node_next(iter); + list_del_init(tmp); +} + +/** Return total count of elements in the hash table + +@param head pointer to the hash table descriptor +*/ +int hash_count(struct hash_head *head) +{ + int i; + int count = 0; + + for(i = 0; i < head->hash_size; i++) count += list_count(head->bucket + i); + + return count; +} diff --git a/framework/hash_multimap.h b/framework/hash_multimap.h new file mode 100644 index 0000000..acaec75 --- /dev/null +++ b/framework/hash_multimap.h @@ -0,0 +1,94 @@ +#ifndef HASH_MULTIMAP_H +#define HASH_MULTIMAP_H + +#include "list_head.h" + +#define HASH_MIN_SHIFT (8) +#define HASH_MAX_SHIFT (15) + +#define hash_entry(ptr, type, member) container_of(ptr, type, member) + +/** Hash multimap node descriptor + +@param key hash key value +@param key_size hash key size in bytes +@param node uplink to hash multimap table +*/ +struct hash_node +{ + char *key; + int key_size; + struct list_node node; +}; + +/** Hash function prototype + +@param key hash key value +@param key_size hash key size in bytes +@return hash value +*/ +typedef unsigned int (* _hash_fn)(const void *key, int key_size); + +/** Compare function prototype + * + * @param key1 pointer to the first key for comparison + * @param key2 pointer to the second key for comparison + * @param size size of compared part of the keys in bytes + * + * @return zero if keys equals each other, othervize 1 +*/ +typedef int (*_compare_fn)(const void *key1, const void *key2, int size); + +/** Hash multimap table descriptor + +@param hash_shift size of hash table as power of 2 +@param hash_size size of the hash table +@param hash_mask mask for the hash value +@param bucket pointer to the array of linked lists. in other words, pointer to +hash multimap table +@param count count of the nodes in the hash table +*/ +struct hash_head +{ + unsigned char hash_shift; + unsigned short hash_size; + unsigned int hash_mask; + struct list_head *bucket; + //int count; + + _hash_fn hash_fn; + _compare_fn compare_fn; +}; + +/** Hash table iterator + +@param map pointer to the hash table descriptor, used for iteration +@param current pointer to the current list node (bucket[map_idx]) +@param map_idx current bucket index +*/ +struct hash_iterator +{ + struct hash_head *map; // pointer to the hash map header + struct list_node *current; // pointer to current list node in the bucket[map_idx] list + unsigned int map_idx; // index of current list in the hash map bucket +}; + +void hash_node_init(struct hash_node *node); +void hash_node_cleanup(struct hash_node *node); + +int hash_head_init(struct hash_head *map, unsigned int hash_shift = HASH_MIN_SHIFT, _hash_fn hf = 0, _compare_fn cf = 0); +void hash_head_cleanup(struct hash_head *map); + +struct hash_node* hash_find(struct hash_head *map, const void *key, int key_size); +struct hash_node* hash_find_next(struct hash_head *map, struct hash_node *node); + +int hash_add(struct hash_head *map, struct hash_node *node, const void *key, int key_size); +void hash_del(struct hash_node *node); + +void hash_iterator_init(struct hash_head *map, struct hash_iterator *iter); +struct hash_node * hash_node_first(struct hash_iterator *iter); +struct hash_node * hash_node_next(struct hash_iterator *iter); +void hash_node_del(struct hash_iterator *iter); +int hash_count(struct hash_head *head); + +#endif // HASH_MULTIMAP_H diff --git a/framework/linkedlist.h b/framework/linkedlist.h deleted file mode 100644 index c2cf61d..0000000 --- a/framework/linkedlist.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * linkedlist.h - * - * Created on: Oct 24, 2013 - * Author: nnosov - * - * Simple double linked list implementation. Base class. - */ - -#ifndef LINKEDLIST_H_ -#define LINKEDLIST_H_ - -//system -#include - -typedef struct __SC_ListNode -{ - __SC_ListNode() - { - pPrev = pNext = 0; - } - - bool IsListed() - { - return (pNext || pPrev); - } - - __SC_ListNode* pNext; - __SC_ListNode* pPrev; -} SC_ListNode; - - -class SC_ListHead -{ - -public: - SC_ListHead() - { - mHead.pPrev = mHead.pNext = &mHead; - size = 0; - } - - virtual ~SC_ListHead() - { - Clear(); - } - - void Clear() - { - for(SC_ListNode* node = (SC_ListNode*)First(); node; node = (SC_ListNode*)First()) - { - Remove(node); - } - } - - bool IsEmpty() const - { - return (mHead.pNext == &mHead); - } - - SC_ListNode* First() - { - return IsEmpty() ? 0 : mHead.pNext; - } - - SC_ListNode* Last() - { - return IsEmpty() ? 0 : mHead.pPrev; - } - - SC_ListNode* Next(SC_ListNode* node) - { - return (!node || node == Last() || !node->IsListed()) ? 0 : node->pNext; - } - - SC_ListNode* Prev(SC_ListNode* node) - { - return (!node || node == First() || !node->IsListed()) ? 0 : node->pPrev; - } - - void InsertFirst(SC_ListNode* node) - { - node->pNext = mHead.pNext; - mHead.pNext = node; - node->pNext->pPrev = node; - node->pPrev = &mHead; - - size++; - } - - void InsertLast(SC_ListNode* node) - { - node->pNext = &mHead; - node->pPrev = mHead.pPrev; - node->pPrev->pNext = node; - mHead.pPrev = node; - - size++; - } - - void InsertAfter(SC_ListNode* pPrev, SC_ListNode* p) - { - p->pNext = pPrev->pNext; - p->pPrev = p->pNext->pPrev; - p->pNext->pPrev = p; - p->pPrev->pNext = p; - size++; - } - - void InsertBefore(SC_ListNode* pNext, SC_ListNode* p) - { - p->pNext = pNext; - p->pPrev = pNext->pPrev; - pNext->pPrev = p; - p->pPrev->pNext = p; - size++; - } - - void Remove(SC_ListNode* node) - { - node->pPrev->pNext = node->pNext; - node->pNext->pPrev = node->pPrev; - node->pNext = node->pPrev = 0; - - size--; - } - - uint32_t Size() - { - return size; - } - -protected: - SC_ListNode mHead; //previous element in list - uint32_t size; //total number of elements in list -}; - -#endif /* LINKEDLIST_H_ */ diff --git a/framework/list_head.h b/framework/list_head.h new file mode 100644 index 0000000..f793a58 --- /dev/null +++ b/framework/list_head.h @@ -0,0 +1,338 @@ +/******************************************************************************* +Copyright (c) 2006, Nikolay Nosov +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ + +#ifndef __LIST_HEAD_H__ +#define __LIST_HEAD_H__ + +/** +Double linked list for user space implementation +*/ + +#include /* for NULL declaration */ + +/** Get offset of a member +@param TYPE the type of the struct +@param MEMBER the name of the member, which offset to be computed +*/ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +/** Casts a member of a structure out to the containing structure + @param ptr the pointer to the member. + @param type the type of the container struct this is embedded in + @param member the name of the member within the struct. +*/ + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type, member) );}) + +/** Get the struct for this entry + @param ptr the &struct list_head pointer. + @param type the type of the struct this is embedded in. + @param member the name of the list_struct within the struct +*/ + +#define list_entry(ptr, type, member) container_of(ptr, type, member) + +/** Iterate over a list + @param pos the &struct list_head to use as a loop counter. + @param head the head for your list. +*/ + +#define list_for_each(pos, head) \ + for (pos = (head)->first; pos; \ + pos = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define list_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; \ + pos && ({ tpos = list_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + + +/** Iterate over list of given type safe against removal of list entry + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct list_node to use as a loop cursor. + * @n: another &struct list_node to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_node within the struct. +*/ +#define list_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = list_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + + +/** Iterate over a list backwards + @param pos the &struct list_head to use as a loop counter. + @param head the head for your list. +*/ + +#define list_for_each_prev(pos, head) \ + for (pos = (head)->last; pos; \ + pos = pos->prev) + + +/** Iterate over a list safe against removal of list entry + @param pos the &struct list_head to use as a loop counter. + @param n another &struct list_head to use as temporary storage + @param head the head for your list. +*/ + +#if !defined(list_for_each_safe) +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ + pos = n) +#endif + +/* to compiler be happy */ + +struct list_head; + +/** Node of the list +@param next next node in the list +@param prev previous node in the list +@param head head of the list +*/ + +typedef struct list_node +{ + struct list_node *next, *prev; + struct list_head *head; +} list_node; + + + +/** Head of the list +@param first first node in the list +@param last last node in the list +@param count cont of the nodes +*/ + +typedef struct list_head +{ + struct list_node *first, *last; + int count; +} list_head; + +/** +Compile time initialization of the head struct +*/ + +#define LIST_HEAD_INIT { .first = NULL, .last = NULL, .count = 0 } + +/** +Compile time initialization of the node struct +*/ + +#define LIST_NODE_INIT { .prev = NULL, .next = NULL, .head = NULL } + +/** Runtime initialization of the head struct +(should be applied before first using of the list) +@param node head, which to be initialized. +*/ + +static inline void list_head_init(struct list_head *head) +{ + head->first = head->last = NULL; + head->count = 0; +} + + +/** Runtime initialization of the node struct +(should be applied before first using of the node) +@param node node, which to be initialized. +*/ + +static inline void list_node_init(struct list_node *node) +{ + node->next = node->prev = NULL; + node->head = NULL; +} + +/** Add node to begin of the list +@param head the head of the list +@param node node of the list, which to be added +*/ + +static inline void list_add_head(struct list_head *head, struct list_node *node) +{ + struct list_node *pfirst = head->first; + head->first = node; + node->next = pfirst; + node->prev = NULL; + node->head = head; + + if(pfirst) pfirst->prev = node; + else head->last = node; + + head->count++; +} + +/** Add node to the end of the list +@param head the head of the list; +@param node node of the list, which to be added +*/ + +static inline void list_add_bottom(struct list_head *head, struct list_node *node) +{ + struct list_node *plast = head->last; + + head->last = node; + node->next = NULL; + node->prev = plast; + node->head = head; + + if(plast) plast->next = node; + else head->first = node; + + head->count++; +} + +/** Add node in the list after given 'base' node +@param base the base node, after that node will be added +@param node node of the list, which to be added +*/ + +static inline void list_add_after(struct list_node *base, struct list_node *node) +{ + struct list_node *pnext = base->next; + + base->next = node; + node->next = pnext; + node->prev = base; + node->head = base->head; + + if(pnext) pnext->prev = node; + else base->head->last = node; + + base->head->count++; +} + +/** Adds node in the list before given 'base' node +@param base the base node, before that node will be added +@param node node of the list, which to be added +*/ + +static inline void list_add_before(struct list_node *base, struct list_node *node) +{ + struct list_node *pprev = base->prev; + + base->prev = node; + node->next = base; + node->prev = pprev; + node->head = base->head; + + if(pprev) pprev->next = node; + else base->head->first = node; + base->head->count++; +} + +/** Remove node from the list +@param node the node, which to be removed +*/ + +static inline void list_del(struct list_node *node) +{ + if(node->head) + { + if(node->prev) node->prev->next = node->next; + else node->head->first = node->next; + if(node->next) node->next->prev = node->prev; + else node->head->last = node->prev; + node->head->count--; + } + + node->prev = node->next = NULL; + node->head = NULL; +} + +static inline void list_del_init(struct list_node *node) +{ + list_del(node); + list_node_init(node); +} + +/** Count of the nodes in the list +@param head the head of the list +@return count of the nodes in the list +*/ + +static inline int list_count(struct list_head *head) +{ + return head->count; +} + +/** First element of the list +@param head the head of the list +@return pointer to first element of the list +*/ + +static inline struct list_node* list_first(struct list_head *head) +{ + return head->first; +} + +/** Last element of the list +@param head the head of the list +@return pointer to last element of the list +*/ + +static inline struct list_node* list_last(struct list_head *head) +{ + return head->last; +} + +/** Next element of the list +@param head current element of the list +@return pointer to next element relatively of current +*/ + +static inline struct list_node* list_next(struct list_node *node) +{ + return node->next; +} + +/** Previous element of the list +@param head current element of the list +@return pointer to previous element relatively of current +*/ + +static inline struct list_node* list_prev(struct list_node *node) +{ + return node->prev; +} + +#endif // __LIST_HEAD_H__ + diff --git a/framework/logger/log.cpp b/framework/logger/log.cpp index d043ab9..b8676bc 100644 --- a/framework/logger/log.cpp +++ b/framework/logger/log.cpp @@ -1,55 +1,246 @@ #include "log.h" #include +#include -#define LOG_BUFF_SIZE (1024*1024) - - -const char * const SC_LogBase::mSeverityStrings[] = { +const char * const severity_map[] = { " [DEBUG] : ", " [INFO] : ", " [WARNING]: ", " [ERROR] : " }; -char SC_LogBase::mString[LOG_BUFF_SIZE]; -uint32_t SC_LogBase::mStringSize = sizeof(SC_LogBase::mString); - -SC_LogBase::SC_LogBase(uint32_t id, const char* source) : mID(id) +void format_string(pst_log* log, const char* fmt, va_list args) { - mStringLen = 0; - mCurrentSeverity = SEVERITY_DEBUG; - if(source) { - mSource = source; - } + log->mStringLen += vsnprintf(log->mString + log->mStringLen, log->mStringSize - log->mStringLen, fmt, args); } -SC_LogBase::~SC_LogBase() -{ - Close(); -} - -void SC_LogBase::FormatString(const char* fmt, va_list args) -{ - mStringLen += vsnprintf(mString + mStringLen, mStringSize - mStringLen, fmt, args); -} - -void SC_LogBase::FormatPrefix(SC_LogSeverity severity) +void format_prefix(pst_log* log, SC_LogSeverity severity) { time_t rawTime; struct tm * timeinfo; time(&rawTime); timeinfo = gmtime(&rawTime); - mStringLen += strftime(mString, sizeof(mString), "%d-%m-%Y %H:%M:%S", timeinfo); - strncpy(mString + mStringLen, mSeverityStrings[(int)severity], strlen(mSeverityStrings[(int)severity])); - mStringLen += strlen(mSeverityStrings[(int)severity]); + log->mStringLen += strftime(log->mString, sizeof(log->mString), "%d-%m-%Y %H:%M:%S", timeinfo); + strncpy(log->mString + log->mStringLen, severity_map[(int)severity], strlen(severity_map[(int)severity])); + log->mStringLen += strlen(severity_map[(int)severity]); } -void SC_LogBase::FormatPostfix() +void format_postfix(pst_log* log) { - if((mStringLen + 2) < mStringSize) { - mString[mStringLen++] = '\n'; - mString[mStringLen++] = 0; + if((log->mStringLen + 2) < log->mStringSize) { + log->mString[log->mStringLen++] = '\n'; + log->mString[log->mStringLen++] = 0; } } + +//variable argument number logging +void log(pst_log* log, SC_LogSeverity severity, const char* fmt, ...) +{ + if (severity < log->mCurrentSeverity) + return; + + pthread_mutex_lock(&log->mLock); + + if(!log->is_opened(log)) { + log->open(log); + } + + log->mStringLen = 0; + format_prefix(log, severity); + va_list args; + va_start(args, fmt); + format_string(log, fmt, args); + va_end(args); + format_postfix(log); + log->send_message(log, severity); + + pthread_mutex_unlock(&log->mLock); +} + +void log_init_base(pst_log* plog, const char* source) +{ + // methods + plog->log = log; + + // fields + plog->mpSource = 0; + if(source) { + plog->mpSource = strdup(source); + } + plog->mString[0] = 0; + plog->mStringSize = sizeof(plog->mString); + plog->mStringLen = 0; + plog->mCurrentSeverity = SEVERITY_DEBUG; + pthread_mutex_init(&plog->mLock, NULL); + plog->child = 0; +} + +void pst_log_fini(pst_log* log) +{ + pthread_mutex_destroy(&log->mLock); + log->close(log); + if(log->mpSource) { + free(log->mpSource); + log->mpSource = 0; + } +} + + +// +// Console logger implementation +// +#define RED "\e[0;31m" +#define GREEN "\e[0;32m" +#define YELLOW "\e[1;33m" +#define NC "\e[0m" // No Color + +//actually sends message to the source +void send_msg_console(pst_log* log, SC_LogSeverity severity) +{ + const char* color = NC; + switch(severity) { + case SEVERITY_DEBUG: + color = NC; + break; + case SEVERITY_ERROR: + color = RED; + break; + case SEVERITY_INFO: + color = GREEN; + break; + case SEVERITY_WARNING: + color = YELLOW; + break; + default: + color = NC; + break; + } + + fprintf(stderr, "%s%s%s", color, log->mString, NC); +} + +void close_console(pst_log* log) { + // do nothing +} + +bool open_console(pst_log* log) { + return true; +} + +bool is_opened_console(pst_log* log) { + return true; +} + +void pst_log_init_console(pst_log* log) +{ + log_init_base(log, NULL); + log->close = close_console; + log->open = open_console; + log->is_opened = is_opened_console; + log->send_message = send_msg_console; +} + +// +// Log to file +// + +typedef struct _file_spec { + FILE* fd; + uint64_t max_bytes; + uint64_t num_bytes; + char* dir; + char* fname; +} file_spec; + +//open file for writing +bool open_file(pst_log* log) +{ + file_spec* fsp = (file_spec*)log->child; + if(!fsp) { + fprintf(stderr, "Wrong initialization of File-based logger\n"); + return false; + } + + if(!fsp->fd) { + // extract log and path names + uint32_t namelen = strlen(log->mpSource); + if(namelen) { + const char* ptr = strrchr(log->mpSource, '/'); + uint32_t pathlen = 0; + if(ptr) { + pathlen = ptr - log->mpSource + 1; + namelen -= pathlen + 1; + } + + fsp->dir = strndup(log->mpSource, pathlen); + fsp->fname = strndup(log->mpSource + pathlen, namelen); + + //open the file finally + fsp->fd = fopen(log->mpSource, "a+"); + } + } + + return (fsp->fd != 0); +} + +void close_file(pst_log* log) +{ + file_spec* fsp = (file_spec*)log->child; + if(!fsp) { + fprintf(stderr, "Wrong initialization of File-based logger while close\n"); + return; + } + + if(fsp->fd) { + fclose(fsp->fd); + } + + fsp->fd = 0; + fsp->max_bytes = 0; +} + +bool is_file_opened(pst_log* log) +{ + file_spec* fsp = (file_spec*)log->child; + if(!fsp) { + fprintf(stderr, "Wrong initialization of File-based logger while check open\n"); + return false; + } + return (fsp->fd != 0); +} + +void send_msg_file(pst_log* log, SC_LogSeverity severity) +{ + file_spec* fsp = (file_spec*)log->child; + if(!fsp) { + fprintf(stderr, "Wrong initialization of File-based logger while check send message\n"); + return; + } + if(fsp->fd && fsp->num_bytes < fsp->max_bytes) { + if(fputs(log->mString, fsp->fd) >= 0) { + fflush(fsp->fd); + fsp->num_bytes += log->mStringLen; + } else { + fclose(fsp->fd); + fsp->fd = 0; + } + } +} + +void pst_log_init_file(pst_log* log, const char* path, uint64_t max_bytes) +{ + log_init_base(log, path); + file_spec* fsp = (file_spec*)malloc(sizeof(file_spec)); + fsp->fd = 0; + fsp->max_bytes = max_bytes; + fsp->num_bytes = 0; + log->child = fsp; + + log->close = close_file; + log->open = open_file; + log->is_opened = is_file_opened; + log->send_message = send_msg_file; + +} diff --git a/framework/logger/log.h b/framework/logger/log.h index 4409fb1..ce46118 100644 --- a/framework/logger/log.h +++ b/framework/logger/log.h @@ -1,15 +1,12 @@ -#ifndef LOG_H_ -#define LOG_H_ +#ifndef PST_LOG_H_ +#define PST_LOG_H_ #include #include #include #include -#include - -#include "mutex.h" - -using std::string; +#include +#include // Log message severity enum SC_LogSeverity { @@ -21,128 +18,33 @@ enum SC_LogSeverity { SEVERITY_MAX }; +#define LOG_BUFF_SIZE (1024*1024) -class SC_LogBase -{ -protected: +typedef struct __pst_log pst_log; - SC_LogBase(uint32_t id, const char* source); +typedef struct __pst_log { + // methods + void (*close) (pst_log* log); + bool (*open) (pst_log* log); + bool (*is_opened) (pst_log* log); + void (*log) (pst_log* log, SC_LogSeverity severity, const char* fmt, ...); + void (*send_message) (pst_log* log, SC_LogSeverity severity); -public: + // fields + char* mpSource; // source for store log messages (file, IP:port or something else) - virtual ~SC_LogBase(); + char mString[LOG_BUFF_SIZE]; // formatted string to be printed + uint32_t mStringSize; // size of the 'mString' buffer + uint32_t mStringLen; // length of the formatted string - //variable argument number logging - inline virtual void Log(SC_LogSeverity severity, const char* fmt, ...) - { - if (severity < mCurrentSeverity) - return; + SC_LogSeverity mCurrentSeverity; // maximum severity value to be logged + pthread_mutex_t mLock; - mLock.Lock(); + void* child; +} pst_log; - if(!IsOpened()) - { - //print header if present - if(Open() && mHeader.size()) - { - SendMessage((char*)mHeader.c_str(), severity); - } - } +void pst_log_init_console(pst_log* log); +void pst_log_init_file(pst_log* log, const char* path, uint64_t max_bytes = 64 * 1024 * 1024/* 64Mb */); +void pst_log_fini(pst_log* log); - mStringLen = 0; - FormatPrefix(severity); - va_list args; - va_start(args, fmt); - FormatString(fmt, args); - va_end(args); - FormatPostfix(); - SendMessage(mString, severity); - - mLock.Unlock(); - } - - //open source for send messages - virtual bool Open() = 0; - - //close source - virtual void Close(){} - - //whether source already opened - virtual bool IsOpened() = 0; - - //returns source path - string& GetSource() - { - return mSource; - } - - //assign source path - inline void SetSource(const char* source) - { - Close(); - mSource = source; - } - - void SetSource(string& source) - { - SetSource(source.c_str()); - } - - //assign Log header - inline void SetHeader(const char* header) - { - mHeader = string(header) + '\n'; - } - - //return Log ID - inline uint32_t GetID() const - { - return mID; - } - - //set Log ID - inline void SetID(uint32_t id) - { - mID = id; - } - - //return current severity threshold applied to the messages - inline SC_LogSeverity GetCurrentSeverity() const - { - return mCurrentSeverity; - } - - //set severity threshold to be applied to the messages - inline void SetCurrentSeverity(SC_LogSeverity severity) - { - mCurrentSeverity = severity; - } - -protected: - - //actually sends message to the source - virtual void SendMessage(const char* msg, SC_LogSeverity severity = SEVERITY_DEBUG) = 0; - - //format message prefix - virtual void FormatPrefix(SC_LogSeverity severity); - - //format message body - virtual void FormatString(const char* fmt, va_list args); - - //format message postfix - virtual void FormatPostfix(); - -protected: - - uint32_t mID; //log ID, associated with source - string mSource; //source for store log messages (file, IP address or sometsing else) - string mHeader; //header of the Log - static char mString[]; //formatted string to be printed - static uint32_t mStringSize; //size of the string buffer - uint32_t mStringLen; //length of the string - SC_LogSeverity mCurrentSeverity; //maximum severity value to be logged - static const char * const mSeverityStrings[]; - SC_Mutex mLock; -}; - -#endif /* LOG_H_ */ +#endif /* PST_LOG_H_ */ diff --git a/framework/logger/log_console.h b/framework/logger/log_console.h deleted file mode 100644 index eb2991c..0000000 --- a/framework/logger/log_console.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef SC_LOGCONSOLE_H -#define SC_LOGCONSOLE_H - -#include "log.h" - -#define RED "\e[0;31m" -#define GREEN "\e[0;32m" -#define YELLOW "\e[1;33m" -#define NC "\e[0m" // No Color - -class SC_LogConsole : public SC_LogBase { -public: - - SC_LogConsole(uint32_t id, const char* source) : SC_LogBase(id, source) { } - - //open file for writing - inline virtual bool Open() { - return true; - } - - //close file opened - inline virtual void Close() { } - - //whether file already opened or not - inline virtual bool IsOpened() { - return true; - } -protected: - inline virtual void SendMessage(const char* msg, SC_LogSeverity severity) { - const char* color = NC; - switch(severity) { - case SEVERITY_DEBUG: - color = NC; - break; - case SEVERITY_ERROR: - color = RED; - break; - case SEVERITY_INFO: - color = GREEN; - break; - case SEVERITY_WARNING: - color = YELLOW; - break; - default: - color = NC; - break; - } - - fprintf(stderr, "%s%s%s", color, msg, NC); - } -}; - -#endif // SC_LOGCONSOLE_H diff --git a/framework/logger/log_file.h b/framework/logger/log_file.h deleted file mode 100644 index f1ef869..0000000 --- a/framework/logger/log_file.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef LOG_FILE_H_ -#define LOG_FILE_H_ - -//framework -#include "log.h" -#include -#include - -using std::string; - -//============================================================================================= -// SWI Logger to file logging class declaration -//============================================================================================= -class SC_LogFile : public SC_LogBase -{ -public: - - SC_LogFile(uint32_t id, const char* source) : SC_LogBase(id, source) - { - mFile = 0; - //2GByte by default - mMaxFileSize = 0x0000000080000000ULL; - mBytesCount = 0; - } - - //open file for writing - inline virtual bool Open() - { - if(!mFile) { - // extract log and path names - uint32_t namelen = mSource.size(); - if(namelen) { - const char* ptr = strrchr(mSource.c_str(), '/'); - uint32_t pathlen = 0; - if(ptr) { - pathlen = ptr - mSource.c_str() + 1; - namelen -= pathlen + 1; - } - - mLogPath.append(mSource.c_str(), pathlen); - - mLogName.append((mSource.c_str() + pathlen), namelen); - - //open the file finally - mFile = fopen(mSource.c_str(), "a+"); - } - } - - return (mFile != 0); - } - - //close file opened - inline virtual void Close() - { - if(mFile) { - fclose(mFile); - } - - mFile = 0; - mBytesCount = 0; - } - - //whether file already opened or not - inline virtual bool IsOpened() - { - return (mFile != 0); - } - - inline void SetMaxFileSize(uint64_t size) - { - mMaxFileSize = size; - } - - inline uint64_t GetMaxFileSize() - { - return mMaxFileSize; - } - - inline FILE* GetLogFD() - { - return mFile; - } - -protected: - - inline virtual void SendMessage(const char* msg, SC_LogSeverity severity = SEVERITY_DEBUG) - { - if(mFile && mBytesCount < mMaxFileSize) { - if(fputs(msg, mFile) >= 0) { - fflush(mFile); - mBytesCount += mStringLen; - } else { - fclose(mFile); - mFile = 0; - } - } - } - -protected: - - string mLogPath; //path to the log file - string mLogName; //name of the log file - FILE* mFile; //log file descriptor - uint64_t mMaxFileSize; //maximum number bytes allowed to be written to the file - uint64_t mBytesCount; //number of bytes actually written -}; - -#endif /* LOG_FILE_H_ */ diff --git a/framework/mutex.h b/framework/mutex.h deleted file mode 100644 index ba42e80..0000000 --- a/framework/mutex.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef MUTEX_H_ -#define MUTEX_H_ - -#include -#include - -class SC_Mutex { -public: - SC_Mutex() { - pthread_mutex_init(&m, NULL); - } - SC_Mutex(SC_Mutex const&) { - pthread_mutex_init(&m, NULL); - } - SC_Mutex& operator=(SC_Mutex const& rhs) { - if (&rhs != this) { - pthread_mutex_init(&m, NULL); - } - return *this; - } - ~SC_Mutex() { - pthread_mutex_destroy(&m); - } - - void Lock() { - pthread_mutex_lock(&m); - } - - void Unlock() { - pthread_mutex_unlock(&m); - } - - bool Trylock() { - return (pthread_mutex_trylock(&m) == 0); - } -private: - pthread_mutex_t m; -}; - -#endif /* MUTEX_H_ */ diff --git a/framework/registers.cpp b/framework/registers.cpp index 6c029d5..31dfe4f 100644 --- a/framework/registers.cpp +++ b/framework/registers.cpp @@ -84,6 +84,7 @@ dwarf_reg_map reg_map[] = { {0x1d, "XMM12", DW_OP_breg29}, {0x1e, "XMM13", DW_OP_breg30}, {0x1f, "XMM14", DW_OP_breg31}, + {0x20, "XMM15", 0xff}, // no mapping to dwarf registers }; int regnum = sizeof(reg_map) / sizeof(dwarf_reg_map); @@ -128,23 +129,23 @@ pst_reg_error pst_get_reg(pst_context* ctx, int regno, uint64_t& regval) ctx->print_expr_block(ops, nops, str, sizeof(str)); dwarf_stack st(ctx); if(st.calc_expression(ops, nops, NULL) && st.get_value(regval)) { - ctx->log(SEVERITY_DEBUG, "CFI register 0x%X(%s) expression: %s ==> %#lX", regno, reg_map[regno].regname, str, regval); + pst_log(SEVERITY_DEBUG, "CFI register 0x%X(%s) expression: %s ==> %#lX", regno, reg_map[regno].regname, str, regval); return REG_OK; } else { - ctx->log(SEVERITY_ERROR, "Failed to calculate register 0x%X(%s) CFI expression %s", regno, reg_map[regno].regname, str); + pst_log(SEVERITY_ERROR, "Failed to calculate register 0x%X(%s) CFI expression %s", regno, reg_map[regno].regname, str); return REG_EXPR_ERROR; } } else { - ctx->log(SEVERITY_DEBUG, "CFI expression for register 0x%X(%s) is SAME VALUE", regno, reg_map[regno].regname); + pst_log(SEVERITY_DEBUG, "CFI expression for register 0x%X(%s) is SAME VALUE", regno, reg_map[regno].regname); return REG_SAME; } } else { - ctx->log(SEVERITY_DEBUG, "CFI expression for register 0x%X(%s) is UNDEFINED", regno, reg_map[regno].regname); + pst_log(SEVERITY_DEBUG, "CFI expression for register 0x%X(%s) is UNDEFINED", regno, reg_map[regno].regname); return REG_UNDEFINED; } } else { - ctx->log(SEVERITY_ERROR, "Failed to get CFI expression for register 0x%X", regno); + pst_log(SEVERITY_ERROR, "Failed to get CFI expression for register 0x%X", regno); } return REG_CFI_ERROR; diff --git a/framework/registers.h b/framework/registers.h index cfb6d05..045cb0c 100644 --- a/framework/registers.h +++ b/framework/registers.h @@ -3,8 +3,6 @@ #include #include -#include "linkedlist.h" - #include "context.h" typedef struct __dwarf_reg_map { @@ -23,15 +21,4 @@ typedef enum { REG_EXPR_ERROR } pst_reg_error; -typedef struct __pst_frame : public SC_ListNode { - __pst_frame() { - memset(reg_state, REG_UNDEFINED, sizeof(reg_state)); - memset(regs, 0, sizeof(regs)); - cfa = 0; - } - int32_t reg_state[128]; - uint64_t regs[128]; - uint64_t cfa; -} pst_frame; - pst_reg_error pst_get_reg(pst_context* ctx, int regno, uint64_t& regval); diff --git a/framework/sysutils.cpp b/framework/sysutils.cpp deleted file mode 100644 index 02ddcad..0000000 --- a/framework/sysutils.cpp +++ /dev/null @@ -1,1207 +0,0 @@ -/* - * sysutils.cpp - * - * Created on: Dec 28, 2019 - * Author: nnosov - */ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "sysutils.h" -#include "logger/log.h" - -#define USE_LIBUNWIND - -#ifdef USE_LIBUNWIND -#include -#endif - -#include "common.h" -#include "dwarf_operations.h" - -// dwfl_addrsegment() possibly can be used to check address validity -// dwarf_getattrs() allows to enumerate all DIE attributes -// dwarf_getfuncs() allows to enumerate functions within CU - -typedef struct __reginfo { - __reginfo() { - regname[0] = 0; - regno = -1; - } - char regname[32]; - int regno; -} reginfo; - -int regname_callback (void *arg, int regno, const char *setname, const char *prefix, const char *regname, int bits, int type) -{ - reginfo* info = (reginfo*)arg; - if(info->regno == regno) { - snprintf(info->regname, sizeof(info->regname), "%s %s%s", setname, prefix, regname); - } - - return 0; -} - -bool handle_location(pst_context* ctx, Dwarf_Attribute* attr, pst_dwarf_expr& loc, Dwarf_Addr pc, char* str, uint32_t strsize, __pst_function* fun = NULL) -{ - str[0] = 0; - Dwarf_Addr offset = pc - ctx->base_addr; - - dwarf_stack stack(ctx); - Dwarf_Op *expr; - size_t exprlen; - if(dwarf_hasform(attr, DW_FORM_exprloc)) { - // Location expression (exprloc class of location in DWARF terms) - if(dwarf_getlocation(attr, &expr, &exprlen) == 0) { - loc.setup(expr, exprlen); - ctx->print_expr_block (expr, exprlen, str, strsize, attr); - if(stack.calc_expression(expr, exprlen, attr, fun) && stack.get_value(loc.value)) { - return true; - } else { - return false; - } - } - } else if(dwarf_hasform(attr, DW_FORM_sec_offset)) { - // Location list (loclist class of location in DWARF terms) - Dwarf_Addr base, start, end; - ptrdiff_t off = 0; - - // handle list of possible locations of parameter - for(int i = 0; (off = dwarf_getlocations (attr, off, &base, &start, &end, &expr, &exprlen)) > 0; ++i) { - ctx->print_expr_block (expr, exprlen, str, strsize, attr); - if(offset >= start && offset <= end) { - loc.setup(expr, exprlen); - // actual location, try to calculate Location expression - if(stack.calc_expression(expr, exprlen, attr, fun) && stack.get_value(loc.value), fun) { - ctx->log(SEVERITY_DEBUG, "Location expression: (low_offset: 0x%" PRIx64 ", high_offset: 0x%" PRIx64 "), \"%s\" ==> 0x%lX", start, end, str, loc.value); - return true; - } else { - return false; - } - } else { - // Location skipped due to don't match current PC offset - ctx->log(SEVERITY_DEBUG, "Skip Location list expression: [%d] (low_offset: 0x%" PRIx64 ", high_offset: 0x%" PRIx64 "), \"%s\"", i, start, end, str); - } - } - - } else { - ctx->log(SEVERITY_WARNING, "Unknown location attribute form = 0x%X, code = 0x%X, ", attr->form, attr->code); - } - - - return false; -} - -bool __pst_dwarf_expr::operator== (__pst_dwarf_expr &rhs) -{ - if(Size() != rhs.Size()) { - return false; - } - - pst_dwarf_op* lop = next_op(NULL); - pst_dwarf_op* rop = rhs.next_op(NULL); - while(lop && rop) { - if(lop->operation == rop->operation && lop->arg1 == rop->arg1 && lop->arg2 == rop->arg2) { - return true; - } - lop = next_op(lop); - rop = rhs.next_op(rop); - } - - return false; -} - -pst_dwarf_op* __pst_dwarf_expr::add_op(uint8_t operation, uint64_t arg1, uint64_t arg2) -{ - pst_dwarf_op* op = new pst_dwarf_op(operation, arg1, arg2); - InsertLast(op); - - return op; -} - -pst_dwarf_op* __pst_dwarf_expr::next_op(pst_dwarf_op* op) -{ - pst_dwarf_op* ret = NULL; - if(!op) { - ret = (pst_dwarf_op*)First(); - } else { - ret = (pst_dwarf_op*)Next(op); - } - - return ret; -} - -bool __pst_dwarf_expr::print_op(const char* fmt, ...) -{ - bool nret = true; - - va_list args; - va_start(args, fmt); - int size = sizeof(buff) - offset; - int ret = vsnprintf(buff + offset, size, fmt, args); - if(ret >= size || ret < 0) { - nret = false; - } - offset += ret; - va_end(args); - - return nret; -} - -void __pst_dwarf_expr::clean() -{ - for(pst_dwarf_op* op = (pst_dwarf_op*)First(); op; op = (pst_dwarf_op*)First()) { - Remove(op); - delete op; - } -} - -void __pst_dwarf_expr::setup(Dwarf_Op* expr, size_t exprlen) -{ - clean(); - for(size_t i = 0; i < exprlen; ++i) { - add_op(expr[i].atom, expr[i].number, expr[i].number2); - } -} - -bool __pst_parameter::print_dwarf() -{ - if(types.Size()) { - if(!is_return) { - if(has_value) { - ctx->print("%s %s = 0x%lX", next_type(NULL)->name.c_str(), name.c_str(), location.value); - } else { - ctx->print("%s %s = ", next_type(NULL)->name.c_str(), name.c_str()); - } - } else { - ctx->print("%s", next_type(NULL)->name.c_str()); - } - } else { - if(has_value) { - ctx->print("%s = 0x%lX", name.c_str(), location.value); - } else { - ctx->print("%s = ", name.c_str()); - } - } - return true; -} - -bool __pst_parameter::handle_type(Dwarf_Attribute* param) -{ - Dwarf_Attribute attr_mem; - Dwarf_Attribute* attr; - - // get DIE of return type - Dwarf_Die ret_die; - - if(!dwarf_formref_die(param, &ret_die)) { - ctx->log(SEVERITY_ERROR, "Failed to get parameter DIE"); - return false; - } - - switch (dwarf_tag(&ret_die)) { - case DW_TAG_base_type: { - // get Size attribute and it's value - size = 0; - attr = dwarf_attr(&ret_die, DW_AT_byte_size, &attr_mem); - if(attr) { - dwarf_formudata(attr, &size); - } - ctx->log(SEVERITY_DEBUG, "base type '%s'(%lu)", dwarf_diename(&ret_die), size); - add_type(dwarf_diename(&ret_die), DW_TAG_base_type); - type = DW_TAG_base_type; - - attr = dwarf_attr(&ret_die, DW_AT_encoding, &attr_mem); - if(attr) { - enc_type = 0; - dwarf_formudata(attr, &enc_type); - } - break; - } - case DW_TAG_array_type: - ctx->log(SEVERITY_DEBUG, "array type"); - add_type("[]", DW_TAG_array_type); - break; - case DW_TAG_structure_type: - ctx->log(SEVERITY_DEBUG, "structure type"); - add_type("struct", DW_TAG_structure_type); - break; - case DW_TAG_union_type: - ctx->log(SEVERITY_DEBUG, "union type"); - add_type("union", DW_TAG_union_type); - break; - case DW_TAG_class_type: - ctx->log(SEVERITY_DEBUG, "class type"); - add_type("class", DW_TAG_class_type); - break; - case DW_TAG_pointer_type: - ctx->log(SEVERITY_DEBUG, "pointer type"); - add_type("*", DW_TAG_pointer_type); - break; - case DW_TAG_enumeration_type: - ctx->log(SEVERITY_DEBUG, "enumeration type"); - add_type("enum", DW_TAG_enumeration_type); - break; - case DW_TAG_const_type: - ctx->log(SEVERITY_DEBUG, "constant type"); - add_type("const", DW_TAG_const_type); - break; - case DW_TAG_subroutine_type: - ctx->log(SEVERITY_DEBUG, "Skipping subroutine type"); - break; - case DW_TAG_typedef: - ctx->log(SEVERITY_DEBUG, "typedef '%s' type", dwarf_diename(&ret_die)); - add_type(dwarf_diename(&ret_die), DW_TAG_typedef); - break; - default: - ctx->log(SEVERITY_WARNING, "Unknown 0x%X tag type", dwarf_tag(&ret_die)); - break; - } - - attr = dwarf_attr(&ret_die, DW_AT_type, &attr_mem); - if(attr) { - return handle_type(attr); - } - - return true; -} - -pst_type* __pst_parameter::add_type(const char* name, int type) -{ - pst_type* t = new pst_type(name, type); - types.InsertLast(t); - - return t; -} - -void __pst_parameter::clear() -{ - for(pst_type* t = (pst_type*)types.First(); t; t = (pst_type*)types.First()) { - types.Remove(t); - delete t; - } -} - -pst_type* __pst_parameter::next_type(pst_type* t) -{ - pst_type* next = NULL; - if(!t) { - next = (pst_type*)types.First(); - } else { - next = (pst_type*)types.Next(t); - } - - return next; -} - -bool __pst_parameter::handle_dwarf(Dwarf_Die* result, __pst_function* fun) -{ - die = result; - - Dwarf_Attribute attr_mem; - Dwarf_Attribute* attr; - - name = dwarf_diename(result); - is_variable = (dwarf_tag(result) == DW_TAG_variable); - - dwarf_decl_line(result, (int*)&line); - // Get reference to attribute type of the parameter/variable - attr = dwarf_attr(result, DW_AT_type, &attr_mem); - ctx->log(SEVERITY_DEBUG, "---> Handle '%s' %s", name.c_str(), dwarf_tag(result) == DW_TAG_formal_parameter ? "parameter" : "variable"); - if(attr) { - handle_type(attr); - } - - if(dwarf_hasattr(result, DW_AT_location)) { - // determine location of parameter in stack/heap or CPU registers - attr = dwarf_attr(result, DW_AT_location, &attr_mem); - Dwarf_Addr pc; - unw_get_reg(ctx->curr_frame, UNW_REG_IP, &pc); - - dwarf_stack stack(ctx); - char str[1024]; str[0] = 0; - if(handle_location(ctx, attr, location, pc, str, sizeof(str), fun)) { - has_value = true; - } else { - ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", str); - } - } else if(dwarf_hasattr(result, DW_AT_const_value)) { - // no locations definitions, value is constant, known by DWARF directly - attr = dwarf_attr(result, DW_AT_const_value, &attr_mem); - switch (dwarf_whatform(attr)) { - case DW_FORM_string: - // do nothing for now - ctx->log(SEVERITY_WARNING, "Const value form DW_FORM_string value = %s.", dwarf_formstring(attr)); - break; - case DW_FORM_data1: - case DW_FORM_data2: - case DW_FORM_data4: - case DW_FORM_data8: - dwarf_formudata(attr, &location.value); - has_value = true; - break; - case DW_FORM_sdata: - dwarf_formsdata(attr, (int64_t*)&location.value); - has_value = true; - break; - case DW_FORM_udata: - dwarf_formudata(attr, &location.value); - has_value = true; - break; - } - if(has_value) { - ctx->log(SEVERITY_DEBUG, "Parameter constant value: 0x%lX", location.value); - } - } - - // Additionally handle these attributes: - // 1. DW_AT_default_value to get information about default value for DW_TAG_formal_parameter type of function - // A DW_AT_default_value attribute for a formal parameter entry. The value of - // this attribute may be a constant, or a reference to the debugging information - // entry for a variable, or a reference to a debugging information entry containing a DWARF procedure - - // 2. DW_AT_variable_parameter - // A DW_AT_variable_parameter attribute, which is a flag, if a formal - // parameter entry represents a parameter whose value in the calling function - // may be modified by the callee. The absence of this attribute implies that the - // parameter’s value in the calling function cannot be modified by the callee. - - // 3. DW_AT_abstract_origin - // In place of these omitted attributes, each concrete inlined instance entry has a DW_AT_abstract_origin attribute that may be used to obtain the - // missing information (indirectly) from the associated abstract instance entry. The value of the abstract origin attribute is a reference to the associated abstract - // instance entry. - - - return true; -} - -void __pst_call_site_param::set_location(Dwarf_Op* expr, size_t exprlen) -{ - location.setup(expr, exprlen); -} - -pst_call_site_param* __pst_call_site::add_param() -{ - pst_call_site_param* p = new pst_call_site_param(ctx); - params.InsertLast(p); - - return p; -} - -void __pst_call_site::del_param(pst_call_site_param* p) -{ - params.Remove(p); - - delete p; -} - -pst_call_site_param* __pst_call_site::next_param(pst_call_site_param* p) -{ - pst_call_site_param* param = NULL; - if(p == NULL) { - param = (pst_call_site_param*)params.First(); - } else { - param = (pst_call_site_param*)params.Next(p); - } - - return param; -} - -pst_call_site_param* __pst_call_site::find_param(pst_dwarf_expr& expr) -{ - for(pst_call_site_param* param = next_param(NULL); param; param = next_param(param)) { - if(param->location == expr) { - return param; - } - } - - return NULL; -} - -bool __pst_call_site::handle_dwarf(Dwarf_Die* child) -{ - Dwarf_Attribute attr_mem; - Dwarf_Attribute* attr; - - do { - switch(dwarf_tag(child)) - { - case DW_TAG_GNU_call_site_parameter: { - Dwarf_Addr pc; - unw_get_reg(ctx->curr_frame, UNW_REG_IP, &pc); - - char str[1024]; - // expression represent where callee parameter will be stored - pst_call_site_param* param = add_param(); - if(dwarf_hasattr(child, DW_AT_location)) { - // handle location expression here - // determine location of parameter in stack/heap or CPU registers - attr = dwarf_attr(child, DW_AT_location, &attr_mem); - if(!handle_location(ctx, attr, param->location, pc, str, sizeof(str))) { - ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", str); - del_param(param); - return false; - } - } - - // expression represents call parameter's value - if(dwarf_hasattr(child, DW_AT_GNU_call_site_value)) { - // handle value expression here - attr = dwarf_attr(child, DW_AT_GNU_call_site_value, &attr_mem); - dwarf_stack stack(ctx); - pst_dwarf_expr loc; - if(handle_location(ctx, attr, loc, pc, str, sizeof(str))) { - param->value = loc.value; - ctx->log(SEVERITY_DEBUG, "\tDW_AT_GNU_call_site_value:\"%s\" ==> 0x%lX", str, param->value); - } else { - ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", str); - del_param(param); - return false; - } - } - break; - } - default: - break; - } - } while (dwarf_siblingof (child, child) == 0); - - return true; -} - -pst_parameter* __pst_function::add_param() -{ - pst_parameter* p = new pst_parameter(ctx); - params.InsertLast(p); - - return p; -} - -void __pst_function::del_param(pst_parameter* p) -{ - params.Remove(p); - delete p; -} - -void __pst_function::clear() -{ - for(pst_parameter* p = (pst_parameter*)params.First(); p; p = (pst_parameter*)params.First()) { - params.Remove(p); - delete p; - } - - for(pst_call_site* p = (pst_call_site*)call_sites.First(); p; p = (pst_call_site*)call_sites.First()) { - call_sites.Remove(p); - delete p; - } - - mCallStToTarget.Clean(); - mCallStToTarget.Clean(); -} - -pst_parameter* __pst_function::next_param(pst_parameter* p) -{ - pst_parameter* next = NULL; - if(!p) { - next = (pst_parameter*)params.First(); - } else { - next = (pst_parameter*)params.Next(p); - } - - return next; -} - -pst_call_site* __pst_function::add_call_site(uint64_t target, const char* origin) -{ - pst_call_site* st = new pst_call_site(ctx, target, origin); - call_sites.InsertLast(st); - if(target) { - mCallStToTarget.Insert(&target, sizeof(target), st); - } - - if(origin) { - mCallStToOrigin.Insert(origin, strlen(origin), st); - } - - return st; -} -void __pst_function::del_call_site(pst_call_site* st) -{ - call_sites.Remove(st); - if(st->target && mCallStToTarget.Lookup(&st->target, sizeof(st->target))) { - mCallStToTarget.Remove(); - } - - if(st->origin && mCallStToTarget.Lookup(st->origin, strlen(st->origin))) { - mCallStToTarget.Remove(); - } - - delete st; -} -pst_call_site* __pst_function::next_call_site(pst_call_site* st) -{ - pst_call_site* next = NULL; - if(st) { - next = (pst_call_site*)call_sites.First(); - } else { - next = (pst_call_site*)call_sites.Next(st); - } - - return next; -} - -pst_call_site* __pst_function::call_site_by_origin(const char* origin) -{ - return (pst_call_site*)mCallStToOrigin.Lookup(origin, strlen(origin)); -} - -pst_call_site* __pst_function::call_site_by_target(uint64_t target) -{ - return (pst_call_site*)mCallStToTarget.Lookup(&target, sizeof(target)); -} - -pst_call_site* __pst_function::find_call_site(pst_function* callee) -{ - uint64_t start_pc = ctx->base_addr + callee->lowpc; - pst_call_site* cs = (pst_call_site*)mCallStToTarget.Lookup(&start_pc, sizeof(start_pc)); - if(!cs) { - cs = (pst_call_site*)mCallStToOrigin.Lookup(callee->name.c_str(), strlen(callee->name.c_str())); - } - - return cs; -} - -bool __pst_function::print_dwarf() -{ - char* at = NULL; - if(!asprintf(&at, " at %s:%d, %p", file.c_str(), line, (void*)pc)) { - return false; - } - if(at[4] == ':' && at[5] == '-') { - free(at); - if(!asprintf(&at, " at %p", (void*)pc)) { - return false; - } - } - //ctx->print("%s:%d: ", file.c_str(), line); - - // handle return parameter and be safe if function haven't parameters (for example, dwar info for function is absent) - pst_parameter* param = next_param(NULL); - if(param && param->is_return) { - // print return value type, function name and start list of parameters - param->print_dwarf(); - ctx->print(" %s(", name.c_str()); - param = next_param(param); - } else { - ctx->print("%s(", name.c_str()); - } - - bool first = true; bool start_variable = false; - for(; param; param = next_param(param)) { - if(param->is_return) { - // print return value type, function name and start list of parameters - param->print_dwarf(); - ctx->print(" %s(", name.c_str()); - continue; - } - - if(param->is_variable) { - if(!start_variable) { - ctx->print(")%s\n", at); - ctx->print("{\n"); - start_variable = true; - } - if(param->line) { - ctx->print("%04u: ", param->line); - } else { - ctx->print(" "); - } - param->print_dwarf(); - ctx->print(";\n"); - } else { - if(first) { - first = false; - } else { - ctx->print(", "); - } - param->print_dwarf(); - } - } - - if(!start_variable) { - ctx->print(");%s\n", at); - } else { - ctx->print("}\n"); - } - - free(at); - return true; -} - -bool __pst_function::handle_lexical_block(Dwarf_Die* result) -{ - uint64_t lowpc = 0, highpc = 0; const char* origin_name = ""; - dwarf_lowpc(result, &lowpc); - dwarf_highpc(result, &lowpc); - - Dwarf_Attribute attr_mem; - Dwarf_Die origin; - if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) { - origin_name = dwarf_diename(&origin); - Dwarf_Die child; - if(dwarf_child (&origin, &child) == 0) { - do { - switch (dwarf_tag (&child)) - { - case DW_TAG_variable: - case DW_TAG_formal_parameter: - ctx->log(SEVERITY_DEBUG, "Abstract origin: %s('%s')", dwarf_diename(&origin), dwarf_diename (&child)); - break; - // Also handle DW_TAG_unspecified_parameters (unknown number of arguments i.e. fun(arg1, ...); - default: - break; - } - } while (dwarf_siblingof (&child, &child) == 0); - } - } - const char* die_name = ""; - if(dwarf_diename(result)) { - die_name = dwarf_diename(result); - } - ctx->log(SEVERITY_DEBUG, "Lexical block with name '%s', tag 0x%X and origin '%s' found. lowpc = 0x%lX, highpc = 0x%lX", die_name, dwarf_tag (result), origin_name, lowpc, highpc); - Dwarf_Die child; - if(dwarf_child (result, &child) == 0) { - do { - switch (dwarf_tag (&child)) { - case DW_TAG_lexical_block: - handle_lexical_block(&child); - break; - case DW_TAG_variable: { - pst_parameter* param = add_param(); - if(!param->handle_dwarf(&child, this)) { - del_param(param); - } - break; - } - case DW_TAG_GNU_call_site: - handle_call_site(&child); - break; - case DW_TAG_inlined_subroutine: - ctx->log(SEVERITY_DEBUG, "Skipping Lexical block tag 'DW_TAG_inlined_subroutine'"); - break; - default: - ctx->log(SEVERITY_DEBUG, "Unknown Lexical block tag 0x%X", dwarf_tag(&child)); - break; - } - }while (dwarf_siblingof (&child, &child) == 0); - } - - return true; -} - -// DW_AT_low_pc should point to the offset from process base address which is actually PC of current function, usually. -// further handle DW_AT_abstract_origin attribute of DW_TAG_GNU_call_site DIE to determine what DIE is referenced by it. -// probably by invoke by: -// Dwarf_Die *scopes; -// int n = dwarf_getscopes_die (funcdie, &scopes); // where 'n' is the number of scopes -// if (n <= 0) -> FAILURE -// see handle_function() in elfutils/tests/funcscopes.c -> handle_function() -> print_vars() -// DW_TAG_GNU_call_site_parameter is defined under child DIE of DW_TAG_GNU_call_site and defines value of subroutine before calling it -// relates to DW_OP_GNU_entry_value() handling in callee function to determine the value of an argument/variable of the callee -// get DIE of return type -bool __pst_function::handle_call_site(Dwarf_Die* result) -{ - Dwarf_Die origin; - Dwarf_Attribute attr_mem; - Dwarf_Attribute* attr; - - ctx->log(SEVERITY_DEBUG, "***** DW_TAG_GNU_call_site contents:"); - // reference to DIE which represents callee's parameter if compiler knows where it is at compile time - const char* oname = NULL; - if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) { - oname = dwarf_diename(&origin); - ctx->log(SEVERITY_DEBUG, "\tDW_AT_abstract_origin: '%s'", oname); - } - - // The call site may have a DW_AT_call_site_target attribute which is a DWARF expression. For indirect calls or jumps where it is unknown at - // compile time which subprogram will be called the expression computes the address of the subprogram that will be called. - uint64_t target = 0; - if(dwarf_hasattr (result, DW_AT_GNU_call_site_target)) { - attr = dwarf_attr(result, DW_AT_GNU_call_site_target, &attr_mem); - if(attr) { - char str[1024]; str[0] = 0; - pst_dwarf_expr expr; - if(handle_location(ctx, &attr_mem, expr, pc, str, sizeof(str), this)) { - target = expr.value; - ctx->log(SEVERITY_DEBUG, "\tDW_AT_GNU_call_site_target: %#lX", target); - } - } - } - - if(target == 0 && oname == NULL) { - ctx->log(SEVERITY_ERROR, "Cannot determine both call-site target and origin"); - return false; - } - - Dwarf_Die child; - if(dwarf_child (result, &child) == 0) { - pst_call_site* st = add_call_site(target, oname); - if(!st->handle_dwarf(&child)) { - del_call_site(st); - return false; - } - } - - return true; -} - -bool __pst_function::handle_dwarf(Dwarf_Die* d) -{ - die = d; - get_frame(); - - Dwarf_Attribute attr_mem; - Dwarf_Attribute* attr; - - // get list of offsets from process base address of continuous memory ranges where function's code resides -// if(dwarf_haspc(d, pc)) { - dwarf_lowpc(d, &lowpc); - dwarf_highpc(d, &highpc); -// } else { -// ctx->log(SEVERITY_ERROR, "Function's '%s' DIE hasn't definitions of memory offsets of function's code", dwarf_diename(d)); -// return false; -// } - - unw_proc_info_t info; - unw_get_proc_info(&cursor, &info); - - ctx->log(SEVERITY_INFO, "Function %s(...): LOW_PC = %#lX, HIGH_PC = %#lX, offset from base address: 0x%lX, START_PC = 0x%lX, offset from start of function: 0x%lX", - dwarf_diename(d), lowpc, highpc, pc - ctx->base_addr, info.start_ip, info.start_ip - ctx->base_addr); - ctx->print_registers(0x0, 0x10); - ctx->log(SEVERITY_INFO, "Function %s(...): CFA: %#lX %s", dwarf_diename(d), parent ? parent->sp : 0, ctx->get_print()); - ctx->log(SEVERITY_INFO, "Function %s(...): %s", dwarf_diename(d), ctx->get_print()); - - // determine function's stack frame base - attr = dwarf_attr(die, DW_AT_frame_base, &attr_mem); - if(attr) { - if(dwarf_hasform(attr, DW_FORM_exprloc)) { - Dwarf_Op *expr; - size_t exprlen; - if (dwarf_getlocation (attr, &expr, &exprlen) == 0) { - char str[1024]; str[0] = 0; - ctx->print_expr_block (expr, exprlen, str, sizeof(str), attr); - dwarf_stack stack(ctx); - if(stack.calc_expression(expr, exprlen, attr, this)) { - uint64_t value; - if(stack.get_value(value)) { - ctx->log(SEVERITY_DEBUG, "DW_AT_framebase expression: \"%s\" ==> 0x%lX", str, value); - } else { - ctx->log(SEVERITY_ERROR, "Failed to get value of calculated DW_AT_framebase expression: %s", str); - } - } else { - ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_framebase expression: %s", str); - } - } else { - ctx->log(SEVERITY_WARNING, "Unknown attribute form = 0x%X, code = 0x%X", attr->form, attr->code); - } - } - } - - // Get reference to return attribute type of the function - // may be to use dwfl_module_return_value_location() instead - pst_parameter* ret_p = add_param(); ret_p->is_return = true; - attr = dwarf_attr(die, DW_AT_type, &attr_mem); - if(attr) { - if(!ret_p->handle_type(attr)) { - ctx->log(SEVERITY_ERROR, "Failed to handle return parameter type for function %s(...)", name.c_str()); - del_param(ret_p); - } - } else { - ret_p->add_type("void", 0); - } - - // handle and save additionally these attributes: - // 1. string of DW_AT_linkage_name (mangled name of program if any) - // A debugging information entry may have a DW_AT_linkage_name attribute - // whose value is a null-terminated string containing the object file linkage name - // associated with the corresponding entity. - - // 2. flag DW_AT_external attribute - // A DW_AT_external attribute, which is a flag, if the name of a variable is - // visible outside of its enclosing compilation unit. - - // 3. A subroutine entry may contain a DW_AT_main_subprogram attribute which is - // a flag whose presence indicates that the subroutine has been identified as the - // starting function of the program. If more than one subprogram contains this flag, - // any one of them may be the starting subroutine of the program. - - - Dwarf_Die result; - if(dwarf_child(die, &result) != 0) - return false; - - // went through parameters and local variables of the function - do { - switch (dwarf_tag(&result)) { - case DW_TAG_formal_parameter: - case DW_TAG_variable: { - pst_parameter* param = add_param(); - if(!param->handle_dwarf(&result, this)) { - del_param(param); - } - - break; - } - case DW_TAG_GNU_call_site: - handle_call_site(&result); - break; - - // case DW_TAG_inlined_subroutine: - // /* Recurse further down */ - // HandleFunction(&result); - // break; - case DW_TAG_lexical_block: { - handle_lexical_block(&result); - break; - } - // Also handle: - // DW_TAG_unspecified_parameters (unknown number of arguments i.e. fun(arg1, ...); - // DW_AT_inline - default: - ctx->log(SEVERITY_DEBUG, "Unknown TAG of function: 0x%X", dwarf_tag(&result)); - break; - } - } while(dwarf_siblingof(&result, &result) == 0); - - return true; -} - -bool __pst_function::unwind(Dwarf_Addr addr) -{ - pc = addr; - - Dwfl_Line *dwline = dwfl_getsrc(ctx->dwfl, addr); - if(dwline != NULL) { - Dwarf_Addr addr; - const char* filename = dwfl_lineinfo (dwline, &addr, &line, NULL, NULL, NULL); - if(filename) { - const char* str = strrchr(filename, '/'); - if(str && *str != 0) { - str++; - } else { - str = filename; - } - file = str; - ctx->print("%s:%d", str, line); - } else { - ctx->print("%p", (void*)addr); - } - } else { - ctx->print("%p", (void*)addr); - } - - const char* addrname = dwfl_module_addrname(ctx->module, addr); - char* demangle_name = NULL; - if(addrname) { - int status; - demangle_name = abi::__cxa_demangle(addrname, NULL, NULL, &status); - char* function_name = NULL; - if(asprintf(&function_name, "%s%s", demangle_name ? demangle_name : addrname, demangle_name ? "" : "()") == -1) { - ctx->log(SEVERITY_ERROR, "Failed to allocate memory"); - return false; - } - ctx->print(" --> %s", function_name); - - char* str = strchr(function_name, '('); - if(str) { - *str = 0; - } - name = function_name; - free(function_name); - } - - if(demangle_name) { - free(demangle_name); - } - - return true; -} - -bool __pst_function::get_frame() -{ - // get CFI (Call Frame Information) for current module - // from handle_cfi() - Dwarf_Addr mod_bias = 0; - Dwarf_CFI* cfi = dwfl_module_eh_cfi(ctx->module, &mod_bias); // rty .eh_cfi first - if(!cfi) { // then try .debug_fame second - cfi = dwfl_module_dwarf_cfi(ctx->module, &mod_bias); - } - if(!cfi) { - ctx->log(SEVERITY_ERROR, "Cannot find CFI for module"); - return false; - } - - // get frame of CFI for address - int result = dwarf_cfi_addrframe (cfi, pc - mod_bias, &frame); - if (result != 0) { - ctx->log(SEVERITY_ERROR, "Failed to find CFI frame for module"); - return false; - } - - // setup context to match frame - ctx->frame = frame; - - // get return register and PC range for function - Dwarf_Addr start = pc; - Dwarf_Addr end = pc; - bool signalp; - int ra_regno = dwarf_frame_info (frame, &start, &end, &signalp); - if(ra_regno >= 0) { - start += mod_bias; - end += mod_bias; - reginfo info; info.regno = ra_regno; - dwfl_module_register_names(ctx->module, regname_callback, &info); - ctx->log(SEVERITY_INFO, "Function %s(...): '.eh/debug frame' info: PC range: => [%#" PRIx64 ", %#" PRIx64 "], return register: %s, in_signal = %s", - name.c_str(), start, end, info.regname, signalp ? "true" : "false"); - } else { - ctx->log(SEVERITY_WARNING, "Return address register info unavailable (%s)", dwarf_errmsg(0)); - } - - // finally get CFA (Canonical Frame Address) - // Point cfa_ops to dummy to match print_detail expectations. - // (nops == 0 && cfa_ops != NULL => "undefined") - Dwarf_Op dummy; - Dwarf_Op *cfa_ops = &dummy; - size_t cfa_nops; - if(dwarf_frame_cfa(frame, &cfa_ops, &cfa_nops)) { - ctx->log(SEVERITY_ERROR, "Failed to get CFA for frame"); - return false; - } - char str[1024]; str[0] = 0; - ctx->print_expr_block (cfa_ops, cfa_nops, str, sizeof(str)); - dwarf_stack st(ctx); - if(st.calc_expression(cfa_ops, cfa_nops, NULL) && st.get_value(cfa)) { - //ctx.sp = v; - ctx->log(SEVERITY_INFO, "Function %s(...): CFA expression: %s ==> %#lX", name.c_str(), str, cfa); - - // setup context to match CFA for frame - ctx->cfa = cfa; - } else { - ctx->log(SEVERITY_ERROR, "Failed to calculate CFA expression"); - } - - return true; -} - -bool __pst_handler::get_dwarf_function(pst_function* fun) -{ - Dwarf_Addr mod_cu = 0; - // get CU(Compilation Unit) debug definition - ctx.module = dwfl_addrmodule(ctx.dwfl, fun->pc); - Dwarf_Die* cdie = dwfl_module_addrdie(ctx.module, fun->pc, &mod_cu); - //Dwarf_Die* cdie = dwfl_addrdie(dwfl, addr, &mod_bias); - if(!cdie) { - ctx.log(SEVERITY_INFO, "Failed to find DWARF DIE for address %X", fun->pc); - return false; - } - - if(dwarf_tag(cdie) != DW_TAG_compile_unit) { - ctx.log(SEVERITY_DEBUG, "Skipping non-cu die. DWARF tag: 0x%X, name = %s", dwarf_tag(cdie), dwarf_diename(cdie)); - return false; - } - - Dwarf_Die result; - if(dwarf_child(cdie, &result)) { - ctx.log(SEVERITY_ERROR, "No child DIE found for CU %s", dwarf_diename(cdie)); - return false; - } - - bool nret = false; - - do { - int tag = dwarf_tag(&result); - if(tag == DW_TAG_subprogram || tag == DW_TAG_entry_point || tag == DW_TAG_inlined_subroutine) { - //ctx.log(SEVERITY_DEBUG, "function die name %s", dwarf_diename(&result)); - if(!strcmp(fun->name.c_str(), dwarf_diename(&result))) { - return fun->handle_dwarf(&result); - } - } - } while(dwarf_siblingof(&result, &result) == 0); - - return nret; -} - -pst_function* __pst_handler::add_function(__pst_function* parent) -{ - pst_function* f = new pst_function(&ctx, parent); - functions.InsertLast(f); - - return f; -} - -void __pst_handler::del_function(pst_function* f) -{ - functions.Remove(f); - delete f; -} - -void __pst_handler::clear() -{ - for(pst_function* f = (pst_function*)functions.First(); f; f = (pst_function*)functions.First()) { - functions.Remove(f); - delete f; - } -} - -pst_function* __pst_handler::next_function(pst_function* f) -{ - pst_function* next = NULL; - if(!f) { - next = (pst_function*)functions.First(); - } else { - next = (pst_function*)functions.Next(f); - } - - return next; -} - -pst_function* __pst_handler::last_function() -{ - return (pst_function*)functions.Last(); -} - -bool __pst_handler::handle_dwarf() -{ - ctx.clean_print(); - Dl_info info; - - //for(pst_function* fun = next_function(NULL); fun; fun = next_function(fun)) { - for(pst_function* fun = (pst_function*)functions.Last(); fun; fun = (pst_function*)functions.Prev(fun)) { - dladdr((void*)(fun->pc), &info); - ctx.module = dwfl_addrmodule(ctx.dwfl, fun->pc); - ctx.base_addr = (uint64_t)info.dli_fbase; - ctx.log(SEVERITY_INFO, "Function %s(...): module name: %s, base address: %p, CFA: %#lX", fun->name.c_str(), info.dli_fname, info.dli_fbase, fun->parent ? fun->parent->sp : 0); - ctx.curr_frame = &fun->cursor; - ctx.sp = fun->sp; - ctx.cfa = fun->cfa; - - ctx.curr_frame = &fun->cursor; - get_dwarf_function(fun); - } - - return true; -} - -void __pst_handler::print_dwarf() -{ - ctx.clean_print(); - ctx.print("DWARF-based stack trace information:\n"); - uint32_t idx = 0; - for(pst_function* function = next_function(NULL); function; function = next_function(function)) { - ctx.print("[%-2u] ", idx); idx++; - function->print_dwarf(); - ctx.print("\n"); - } -} - -char *debuginfo_path = NULL; -Dwfl_Callbacks callbacks = { - .find_elf = dwfl_linux_proc_find_elf, - .find_debuginfo = dwfl_standard_find_debuginfo, - .section_address = dwfl_offline_section_address, - .debuginfo_path = &debuginfo_path, -}; - -#include - -bool __pst_handler::unwind() -{ - ctx.clean_print(); -#ifdef REG_RIP // x86_64 - caller = (void *) ctx.hcontext->uc_mcontext.gregs[REG_RIP]; - ctx.sp = ctx.hcontext->uc_mcontext.gregs[REG_RSP]; - ctx.log(SEVERITY_DEBUG, "Original caller's SP: %#lX", ctx.sp); -#elif defined(REG_EIP) // x86_32 - caller_address = (void *) uctx->uc_mcontext.gregs[REG_EIP]); -#elif defined(__arm__) - caller_address = (void *) uctx->uc_mcontext.arm_pc); -#elif defined(__aarch64__) - caller_address = (void *) uctx->uc_mcontext.pc); -#elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) - caller_address = (void *) uctx->uc_mcontext.regs->nip); -#elif defined(__s390x__) - caller_address = (void *) uctx->uc_mcontext.psw.addr); -#elif defined(__APPLE__) && defined(__x86_64__) - caller_address = (void *) uctx->uc_mcontext->__ss.__rip); -#else -# error "unknown architecture!" -#endif - - //handle = dlopen(NULL, RTLD_NOW); - Dl_info info; - dladdr(caller, &info); - ctx.base_addr = (uint64_t)info.dli_fbase; - ctx.log(SEVERITY_INFO, "Process address information: PC address: %p, base address: %p, object name: %s", caller, info.dli_fbase, info.dli_fname); - - ctx.dwfl = dwfl_begin(&callbacks); - if(ctx.dwfl == NULL) { - ctx.print("Failed to initialize libdw session for parse stack frames"); - return false; - } - - if(dwfl_linux_proc_report(ctx.dwfl, getpid()) != 0 || dwfl_report_end(ctx.dwfl, NULL, NULL) !=0) { - ctx.print("Failed to parse debug section of executable"); - return false; - } - - ctx.print("Stack trace: caller = %p\n", caller); - - unw_getcontext(&ctx.context); - unw_init_local(&ctx.cursor, &ctx.context); - ctx.curr_frame = &ctx.cursor; - for(int i = 0, skip = 1; unw_step(ctx.curr_frame) > 0; ++i) { - if(unw_get_reg(ctx.curr_frame, UNW_REG_IP, &addr)) { - ctx.log(SEVERITY_DEBUG, "Failed to get IP value"); - continue; - } - unw_word_t sp; - if(unw_get_reg(ctx.curr_frame, UNW_REG_SP, &sp)) { - ctx.log(SEVERITY_DEBUG, "Failed to get SP value"); - continue; - } - - if(addr == (uint64_t)caller) { - skip = 0; - } else if(skip) { - ctx.log(SEVERITY_DEBUG, "Skipping frame #%d: PC = %#lX, SP = %#lX", i, addr, sp); - continue; - } - - ctx.print("[%-2d] ", i); - ctx.module = dwfl_addrmodule(ctx.dwfl, addr); - pst_function* last = last_function(); - pst_function* fun = add_function(NULL); - fun->sp = sp; - ctx.log(SEVERITY_DEBUG, "Analyze frame #%d: PC = %#lX, SP = %#lX", i, addr, sp); - if(!fun->unwind(addr)) { - del_function(fun); - } else { - if(last) { - last->parent = fun; - } - //get_frame(fun); - } - ctx.print("\n"); - } - - return true; -} diff --git a/framework/sysutils.h b/framework/sysutils.h deleted file mode 100644 index e3eb27a..0000000 --- a/framework/sysutils.h +++ /dev/null @@ -1,272 +0,0 @@ -/* - * sysutils.h - * - * Created on: Dec 28, 2019 - * Author: nnosov - */ - -#ifndef SC_SYSUTILS_H_ -#define SC_SYSUTILS_H_ - -//system -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "context.h" -#include "dictionary.h" -#include "data_entry.h" - - -// DWARF operation (represents our own DW_OP_XXX) -typedef struct __pst_dwarf_op : public SC_ListNode { - __pst_dwarf_op(uint8_t op, uint64_t a1, uint64_t a2) { - operation = op; arg1 = a1; arg2 = a2; - } - uint8_t operation; - uint64_t arg1; - uint64_t arg2; -} pst_dwarf_op; - -typedef struct __pst_dwarf_expr : public SC_ListHead { - __pst_dwarf_expr() { - has_value = false; value = 0; offset = 0; buff[0] = 0; - } - ~__pst_dwarf_expr() { - clean(); - } - pst_dwarf_op* add_op(uint8_t op, uint64_t arg1, uint64_t arg2); - pst_dwarf_op* next_op(pst_dwarf_op* op); - void clean(); - void setup(Dwarf_Op* expr, size_t exprlen); - void set_value(uint64_t v) { - has_value = true; - value = v; - } - bool print_op(const char* fmt, ...); - - bool operator== (__pst_dwarf_expr &rhs); - - bool has_value; - uint64_t value; - char buff[512]; - uint16_t offset; -} pst_dwarf_expr; - -typedef struct __pst_type : public SC_ListNode { - __pst_type(const char* n, uint32_t t) : type(t) { - if(n) { - name = n; - } - } - std::string name; // type name - uint32_t type; // DW_AT_XXX type -} pst_type; - -typedef struct __pst_function pst_function; - -typedef struct __pst_parameter : public SC_ListNode { - __pst_parameter(pst_context* c) : ctx(c) - { - die = NULL; - size = 0; - type = 0; - enc_type = 0; - is_return = false; - is_variable = false; - has_value = false; - line = 0; - } - - ~__pst_parameter() - { - clear(); - } - - void clear(); - pst_type* add_type(const char* name, int type); - pst_type* next_type(pst_type* t); - - bool handle_dwarf(Dwarf_Die* d, __pst_function* fun); - bool handle_type(Dwarf_Attribute* param); - bool print_dwarf(); - - Dwarf_Die* die; // DWARF DIE containing parameter's definition - std::string name; // parameter's name - uint32_t line; // line of parameter definition - Dwarf_Word size; // size of parameter in bytes - int type; // type of parameter in DW_TAG_XXX types enumeration - Dwarf_Word enc_type; // if 'type' is DW_TAG_Base_type, then 'base_type' holds DW_AT_ATE_XXX base type encoding type - SC_ListHead types; // list of parameter's definitions i.e. 'typedef', 'uint32_t' - bool is_return; // whether this parameter is return value of the function - bool is_variable;// whether this parameter is function variable or argument of function - bool has_value; // whether we got value of parameter or not - pst_dwarf_expr location; - pst_context* ctx; -} pst_parameter; - -// DW_TAG_call_site_parameter -typedef struct __pst_call_site_param : public SC_ListNode { - __pst_call_site_param(pst_context* ctx) { - param = NULL; name = NULL; value = 0; - } - - ~__pst_call_site_param() { - if(name) { - free(name); name = NULL; - } - } - - void set_location(Dwarf_Op* expr, size_t exprlen); - - Dwarf_Die* param; // reference to parameter DIE in callee (DW_AT_call_parameter) - char* name; // name of parameter if present (DW_AT_name) - pst_dwarf_expr location; // DWARF stack containing location expression - uint64_t value; // parameter's value -} pst_call_site_param; - -// DW_TAG_call_site -typedef struct __pst_call_site : public SC_ListNode { - __pst_call_site(pst_context* c, uint64_t tgt, const char* orn) { - target = tgt; - if(orn) { - origin = strdup(orn); - } else { - origin = NULL; - } - die = NULL; - ctx = c; - } - - ~__pst_call_site() { - if(origin) { - free(origin); - origin = NULL; - } - } - - pst_call_site_param* add_param(); - void del_param(pst_call_site_param* p); - pst_call_site_param* next_param(pst_call_site_param* p); - pst_call_site_param* find_param(pst_dwarf_expr& expr); - - bool handle_dwarf(Dwarf_Die* die); - - uint64_t target; // pointer to callee function's (it's Low PC + base address) - char* origin; // name of callee function - Dwarf_Die* die; // DIE of function for which this call site has parameters - SC_ListHead params; // list of parameters and their values - pst_context* ctx; -} pst_call_site; - -typedef struct __pst_function : public SC_ListNode { - __pst_function(pst_context* _ctx, __pst_function* _parent) : ctx(_ctx) { - pc = 0; - line = -1; - die = NULL; - lowpc = 0; - highpc = 0; - memcpy(&cursor, _ctx->curr_frame, sizeof(cursor)); - parent = _parent; - sp = 0; - cfa = 0; - frame = NULL; - } - - ~__pst_function() { - clear(); - if(frame) { - free(frame); - } - } - - void clear(); - pst_parameter* add_param(); - void del_param(pst_parameter* p); - pst_parameter* next_param(pst_parameter* p); - - pst_call_site* add_call_site(uint64_t target, const char* origin); - void del_call_site(pst_call_site* st); - pst_call_site* next_call_site(pst_call_site* st); - pst_call_site* call_site_by_origin(const char* origin); - pst_call_site* call_site_by_target(uint64_t target); - - bool unwind(Dwarf_Addr addr); - bool handle_dwarf(Dwarf_Die* d); - bool print_dwarf(); - bool handle_lexical_block(Dwarf_Die* result); - bool handle_call_site(Dwarf_Die* result); - pst_call_site* find_call_site(__pst_function* callee); - - bool get_frame(); - - Dwarf_Addr lowpc; // offset to start of the function against base address - Dwarf_Addr highpc; // offset to the next address after the end of the function against base address - Dwarf_Die* die; // DWARF DIE containing definition of the function - std::string name; // function's name - SC_ListHead params; // function's parameters - - // call-site - SC_ListHead call_sites; // Call-Site definitions - SC_Dict mCallStToTarget; // map pointer to caller to call-site - SC_Dict mCallStToOrigin; // map caller name to call-site - - unw_word_t pc; // address between lowpc & highpc (plus base address offset). actually, currently executed command - unw_word_t sp; // SP register in function's frame - unw_word_t cfa; // CFA (Canonical Frame Adress) of the function - unw_cursor_t cursor; // copy of stack state of the function - int line; // line in code where function is defined - std::string file; // file name (DWARF Compilation Unit) where function is defined - __pst_function* parent; // parent function in call trace (caller) - Dwarf_Frame* frame; // function's stack frame - pst_context* ctx; // context of unwinding -} pst_function; - -typedef struct __pst_handler { - __pst_handler(ucontext_t* hctx) : ctx(hctx) - { - caller = NULL; - frame = NULL; - addr = 0; - handle = 0; - } - - ~__pst_handler() - { - if(handle) { - dlclose(handle); - } - - clear(); - } - - void clear(); - pst_function* add_function(__pst_function* fun); - void del_function(pst_function* f); - pst_function* next_function(pst_function* f); - pst_function* last_function(); - - bool handle_dwarf(); - void print_dwarf(); - bool unwind(); - bool get_dwarf_function(pst_function* fun); - - pst_context ctx; // context of unwinding - void* handle; // process handle - Dwarf_Addr addr; // address of currently processed function - Dwarf_Frame* frame; // currently processed stack frame - void* caller; // pointer to the function which requested to unwind stack - SC_ListHead functions; // list of functions in stack frame -} pst_handler; - -#endif /* SC_SYSUTILS_H_ */ diff --git a/main.cpp b/main.cpp index 7ccb4b6..a7b8a10 100644 --- a/main.cpp +++ b/main.cpp @@ -1,14 +1,9 @@ //system -#include #include #include -#include "logger/log_console.h" - -#include "sysutils.h" - -static SC_LogConsole log_local(0, "/tmp/unspecified.file"); -SC_LogBase* logger = &log_local; +#include "context.h" +#include "dwarf_handler.h" typedef enum { DEF_1 = 1, @@ -64,22 +59,22 @@ void FatalSignalHandler(int sig, siginfo_t* info, void* context) fatal_error_in_progress = 1; - logger->Log(SEVERITY_ERROR, "%s signal handled", strsignal(sig)); + pst_log(SEVERITY_ERROR, "%s signal handled", strsignal(sig)); bool ret = false; - pst_handler handler((ucontext_t*)context); + pst_decl(pst_handler, handler, (ucontext_t*)context); if((context != 0) && (sig == SIGSEGV || sig == SIGABRT || sig == SIGBUS || sig == SIGFPE)) { - ret = handler.unwind(); + ret = pst_handler_unwind(&handler); } if(ret) { - logger->Log(SEVERITY_INFO, "%s", handler.ctx.get_print()); - handler.handle_dwarf(); - handler.print_dwarf(); - logger->Log(SEVERITY_INFO, "%s", handler.ctx.get_print()); + pst_log(SEVERITY_INFO, "%s", handler.ctx.buff); + pst_handler_handle_dwarf(&handler); + pst_handler_print_dwarf(&handler); + pst_log(SEVERITY_INFO, "%s", handler.ctx.buff); } else { - logger->Log(SEVERITY_ERROR, "No stack trace obtained"); + pst_log(SEVERITY_ERROR, "No stack trace obtained"); } // comment out line below to prevent coredump @@ -102,9 +97,9 @@ void SignalHandler(int sig) // const char* str_sig = strsignal(sig); // logger->Log(SEVERITY_INFO, "%s received.", str_sig); if(sig == SIGUSR1) { - logger->Log(SEVERITY_INFO, "Maintenance mode enabled."); + pst_log(SEVERITY_INFO, "Maintenance mode enabled."); } else if(sig == SIGUSR2) { - logger->Log(SEVERITY_INFO, "Maintenance mode disabled."); + pst_log(SEVERITY_INFO, "Maintenance mode disabled."); } // signal (sig, SIG_DFL); // raise(sig); @@ -181,7 +176,7 @@ void SetSignalHandler(sig_handler_t handler) limit.rlim_max = 1073741824; if (setrlimit(RLIMIT_CORE, &limit)) { - logger->Log(SEVERITY_DEBUG, "Failed to set limit for core dump file size"); + pst_log(SEVERITY_DEBUG, "Failed to set limit for core dump file size"); } } @@ -197,6 +192,7 @@ void ResetSignalHandler() { int main(int argc, char* argv[]) { + pst_log_init_console(&logger); //logger->SetCurrentSeverity(SEVERITY_INFO); SetSignalHandler(SigusrHandler);