@@ -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
|
||||
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
|
||||
+33
-19
@@ -7,55 +7,69 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
+35
-16
@@ -5,17 +5,32 @@
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#ifndef FRAMEWORK_ALLOCATOR_H_
|
||||
#define FRAMEWORK_ALLOCATOR_H_
|
||||
#ifndef PST_ALLOCATOR_H_
|
||||
#define PST_ALLOCATOR_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
|
||||
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_ */
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
+11
-1
@@ -1,8 +1,18 @@
|
||||
#pragma once
|
||||
#ifndef __PST_COMMON_H__
|
||||
#define __PST_COMMON_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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__
|
||||
|
||||
+109
-58
@@ -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: <undef>", reg_map[i].regname);
|
||||
ctx->print(ctx, "%s: <undef>", 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;
|
||||
}
|
||||
|
||||
+22
-27
@@ -15,51 +15,46 @@
|
||||
#include <elfutils/libdwfl.h>
|
||||
|
||||
#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_ */
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
|
||||
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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
/*
|
||||
* dictionary.h
|
||||
*
|
||||
* Created on: Aug 5, 2014
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#ifndef DICTIONARY_H_
|
||||
#define DICTIONARY_H_
|
||||
|
||||
//system
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
//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_ */
|
||||
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* dwarf_call_site.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#include <dwarf.h>
|
||||
#include <elfutils/libdw.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
@@ -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 <inttypes.h>
|
||||
#include <elfutils/libdw.h>
|
||||
|
||||
#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_ */
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* dwarf_expression.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
@@ -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 <inttypes.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
|
||||
#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_ */
|
||||
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* dwarf_function.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#include <dwarf.h>
|
||||
#include <stdlib.h>
|
||||
#include <cxxabi.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* sysutils.cpp
|
||||
*
|
||||
* Created on: Dec 28, 2019
|
||||
* Author: nnosov
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include <cxxabi.h>
|
||||
#include <execinfo.h>
|
||||
#include <dwarf.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "dwarf_handler.h"
|
||||
#include "logger/log.h"
|
||||
|
||||
#define USE_LIBUNWIND
|
||||
|
||||
#ifdef USE_LIBUNWIND
|
||||
#include <libunwind.h>
|
||||
#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 <dlfcn.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* sysutils.h
|
||||
*
|
||||
* Created on: Dec 28, 2019
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#ifndef SC_SYSUTILS_H_
|
||||
#define SC_SYSUTILS_H_
|
||||
|
||||
//system
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
#include <ucontext.h>
|
||||
#include <libunwind.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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_ */
|
||||
+190
-447
File diff suppressed because it is too large
Load Diff
@@ -3,124 +3,15 @@
|
||||
#include <elfutils/libdwfl.h>
|
||||
|
||||
#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);
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* dwarf_parameter.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#include <dwarf.h>
|
||||
#include <elfutils/libdw.h>
|
||||
|
||||
#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 = <undefined>", 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 = <undefined>", 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <inttypes.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
|
||||
#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_ */
|
||||
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
* dwarf_stack.cpp
|
||||
*
|
||||
* Created on: Jan 29, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <dwarf.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
@@ -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 <stdint.h>
|
||||
|
||||
#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_ */
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* dwarf_utils.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#include <dwarf.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -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 <elfutils/libdw.h>
|
||||
|
||||
#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_ */
|
||||
@@ -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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 <stdint.h>
|
||||
|
||||
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_ */
|
||||
@@ -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 <stddef.h> /* 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__
|
||||
|
||||
+222
-31
@@ -1,55 +1,246 @@
|
||||
#include "log.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
|
||||
}
|
||||
|
||||
+26
-124
@@ -1,15 +1,12 @@
|
||||
#ifndef LOG_H_
|
||||
#define LOG_H_
|
||||
#ifndef PST_LOG_H_
|
||||
#define PST_LOG_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "mutex.h"
|
||||
|
||||
using std::string;
|
||||
#include <pthread.h>
|
||||
#include <limits.h>
|
||||
|
||||
// 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_ */
|
||||
|
||||
@@ -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
|
||||
@@ -1,108 +0,0 @@
|
||||
#ifndef LOG_FILE_H_
|
||||
#define LOG_FILE_H_
|
||||
|
||||
//framework
|
||||
#include "log.h"
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
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_ */
|
||||
@@ -1,40 +0,0 @@
|
||||
#ifndef MUTEX_H_
|
||||
#define MUTEX_H_
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
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_ */
|
||||
@@ -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;
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,272 +0,0 @@
|
||||
/*
|
||||
* sysutils.h
|
||||
*
|
||||
* Created on: Dec 28, 2019
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#ifndef SC_SYSUTILS_H_
|
||||
#define SC_SYSUTILS_H_
|
||||
|
||||
//system
|
||||
#include <string>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
#include <ucontext.h>
|
||||
#include <libunwind.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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_ */
|
||||
@@ -1,14 +1,9 @@
|
||||
//system
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user