From 1f3a6237acf6f79ed260c981e7039a9cb13e93d4 Mon Sep 17 00:00:00 2001 From: Nikolai Nosov Date: Sat, 4 Jan 2020 14:28:08 +0400 Subject: [PATCH] pstrace: initial commit --- .cproject | 112 ++++++++ .project | 27 ++ Makefile | 114 ++++++++ framework/logger/log.cpp | 55 ++++ framework/logger/log.h | 148 ++++++++++ framework/logger/log_console.h | 29 ++ framework/logger/log_file.h | 108 +++++++ framework/mutex.h | 40 +++ framework/sysutils.cpp | 509 +++++++++++++++++++++++++++++++++ framework/sysutils.h | 30 ++ main.cpp | 199 +++++++++++++ receips.txt | 2 + simple.cpp | 18 ++ 13 files changed, 1391 insertions(+) create mode 100644 .cproject create mode 100644 .project create mode 100644 Makefile create mode 100644 framework/logger/log.cpp create mode 100644 framework/logger/log.h create mode 100644 framework/logger/log_console.h create mode 100644 framework/logger/log_file.h create mode 100644 framework/mutex.h create mode 100644 framework/sysutils.cpp create mode 100644 framework/sysutils.h create mode 100644 main.cpp create mode 100644 receips.txt create mode 100644 simple.cpp diff --git a/.cproject b/.cproject new file mode 100644 index 0000000..e756c1f --- /dev/null +++ b/.cproject @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..5a0810a --- /dev/null +++ b/.project @@ -0,0 +1,27 @@ + + + trace + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f3832f6 --- /dev/null +++ b/Makefile @@ -0,0 +1,114 @@ +#use Bash instead of SH +export SHELL := /bin/bash + +# echo command color definitions +ifndef NO_COLOR +RED=\e[0;31m +GREEN=\e[0;32m +YELLOW=\e[1;33m +NC=\e[0m # No Color +COLOR=-fdiagnostics-color +else +RED= +GREEN= +YELLOW= +NC= +COLOR= +endif + +CXX = g++ +CC = gcc +RM = rm -f +AR = ar rvs + +BUILD_DIR = ./build +RESULT_DIR = ./build + +BIN = $(RESULT_DIR)/trace + +# Generate module software version and build version +#$(shell \ +# STR=`cat $(BUILD_DIR)/version.h 2> /dev/null | grep "SOFT_BUILD" | sed 's/[a-z A-Z#\-\._]//g'`; \ +# let "STR += 1";\ +# echo "#ifndef VERSION_H_" > $(BUILD_DIR)/version.h; \ +# echo "#define VERSION_H_" >> $(BUILD_DIR)/version.h; \ +# echo "/* Automatically generated file, DO NOT EDIT it !!! */" >> $(BUILD_DIR)/version.h; \ +# echo "" >> $(BUILD_DIR)/version.h; \ +# echo "#define SOFT_BUILD $$STR" >> $(BUILD_DIR)/version.h; \ +# echo "#define USER_BUILD \"$${USER}\"" >> $(BUILD_DIR)/version.h; \ +# echo "#define DATE_BUILD \"`date +"%D %T"`\"" >> $(BUILD_DIR)/version.h; \ +# echo "#define REV_BUILD \"`git rev-parse --short HEAD`\"" >> $(BUILD_DIR)/version.h; \ +# echo "" >> $(BUILD_DIR)/version.h; \ +# echo "#endif /* VERSION_H_ */" >> $(BUILD_DIR)/version.h \ +# ) + +#source file search path. add here new directories which contains files specified for $(SRC) +VPATH = ./ + +SRC = $(shell find . -name '*.cpp') +#OBJ = $(patsubst %.cpp,%.o,$(addprefix $(BUILD_DIR)/,$(notdir $(SRC)))) +OBJ = $(BUILD_DIR)/main.o + +LIBS = -L./ -lpthread -lpq -lpqtypes -luuid -lrabbitmq -ljson-c -lcrypto -ldl -lcurl -ldw -lxml2 -lmicrohttpd -larchive -lmagic +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 + + +.PHONY: all clean $(BIN) + +all: $(BIN) + +$(ATR_LIBS): + @make -C ./framework + +clean: + ${RM} $(BUILD_DIR)/*.o $(BUILD_DIR)/*.dep $(BIN) $(BUILD_DIR)/prepare.bld $(RESULT_DIR)/prepare.res $(BUILD_DIR)/version.h + @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 + +$(BUILD_DIR)/prepare.bld: + @if [ ! -e $(BUILD_DIR) ]; then mkdir -vp $(BUILD_DIR); fi + @touch $@ + +$(RESULT_DIR)/prepare.res: + @if [ ! -e $(RESULT_DIR) ]; then mkdir -vp $(RESULT_DIR); fi + @touch $@ + +$(BIN): $(BUILD_DIR)/prepare.bld $(RESULT_DIR)/prepare.res $(OBJ) $(ATR_LIBS) + gcc -ggdb -O3 -o simple simple.cpp + @make -C ./framework + @printf "Create %-60s" $@ + @OUT=$$($(CXX) $(COLOR) -o $@ $(OBJ) $(ATR_LIBS) $(LIBS) 2>&1); \ + if [ $$? -ne "0" ]; \ + then echo -e "${RED}[FAILED]${NC}"; echo -e "$$OUT"; \ + else \ + if [ -n "$$OUT" ]; \ + then echo -e "${YELLOW}[DONE]${NC}"; echo -e "'$$OUT'"; \ + else \ + echo -e "${GREEN}[DONE]${NC}"; \ + fi; \ + fi + +$(BUILD_DIR)/%.o: %.cpp +#compile source code directly to $BUILD_DIR directory + @printf "Building %-60s" $@ + @OUT=$$($(CXX) $(COLOR) -o $@ -c $< $(FLAGS) $(INCS) 2>&1); \ + if [ $$? -ne "0" ]; \ + then echo -e "${RED}[FAILED]${NC}"; echo -e "$$OUT"; \ + else \ + if [ -n "$$OUT" ]; \ + then echo -e "${YELLOW}[DONE]${NC}"; echo -e "'$$OUT'"; \ + else \ + echo -e "${GREEN}[DONE]${NC}"; \ + fi; \ + fi +#create dependencies + @$(CXX) -MM -MT '$@' -c $< > $@.dep $(FLAGS) $(INCS) + +#include dependencies for track changes in source code and related header files +DEPEND := $(OBJ:.o=.o.dep) +-include $(DEPEND) diff --git a/framework/logger/log.cpp b/framework/logger/log.cpp new file mode 100644 index 0000000..d043ab9 --- /dev/null +++ b/framework/logger/log.cpp @@ -0,0 +1,55 @@ +#include "log.h" + +#include + +#define LOG_BUFF_SIZE (1024*1024) + + +const char * const SC_LogBase::mSeverityStrings[] = { + " [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) +{ + mStringLen = 0; + mCurrentSeverity = SEVERITY_DEBUG; + if(source) { + mSource = source; + } +} + +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) +{ + 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]); +} + +void SC_LogBase::FormatPostfix() +{ + if((mStringLen + 2) < mStringSize) { + mString[mStringLen++] = '\n'; + mString[mStringLen++] = 0; + } +} diff --git a/framework/logger/log.h b/framework/logger/log.h new file mode 100644 index 0000000..5602e4a --- /dev/null +++ b/framework/logger/log.h @@ -0,0 +1,148 @@ +#ifndef LOG_H_ +#define LOG_H_ + +#include +#include +#include +#include +#include + +#include "mutex.h" + +using std::string; + +// Log message severity +enum SC_LogSeverity { + SEVERITY_DEBUG = 0, + SEVERITY_INFO, + SEVERITY_WARNING, + SEVERITY_ERROR, + //---------------- + SEVERITY_MAX +}; + + +class SC_LogBase +{ +protected: + + SC_LogBase(uint32_t id, const char* source); + +public: + + virtual ~SC_LogBase(); + + //variable argument number logging + inline virtual void Log(SC_LogSeverity severity, const char* fmt, ...) + { + if (severity < mCurrentSeverity) + return; + + mLock.Lock(); + + if(!IsOpened()) + { + //print header if present + if(Open() && mHeader.size()) + { + SendMessage((char*)mHeader.c_str()); + } + } + + mStringLen = 0; + FormatPrefix(severity); + va_list args; + va_start(args, fmt); + FormatString(fmt, args); + va_end(args); + FormatPostfix(); + SendMessage(mString); + + 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) = 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_ */ diff --git a/framework/logger/log_console.h b/framework/logger/log_console.h new file mode 100644 index 0000000..548a785 --- /dev/null +++ b/framework/logger/log_console.h @@ -0,0 +1,29 @@ +#ifndef SC_LOGCONSOLE_H +#define SC_LOGCONSOLE_H + +#include "log.h" + +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) { + fprintf(stderr, "%s", msg); + } +}; + +#endif // SC_LOGCONSOLE_H diff --git a/framework/logger/log_file.h b/framework/logger/log_file.h new file mode 100644 index 0000000..08aa43c --- /dev/null +++ b/framework/logger/log_file.h @@ -0,0 +1,108 @@ +#ifndef LOG_FILE_H_ +#define LOG_FILE_H_ + +//framework +#include "log.h" +#include +#include + +using std::string; + +//============================================================================================= +// SWI Logger to file logging class declaration +//============================================================================================= +class SC_LogFile : public SC_LogBase +{ +public: + + SC_LogFile(uint32_t id, const char* source) : SC_LogBase(id, source) + { + mFile = 0; + //2GByte by default + mMaxFileSize = 0x0000000080000000ULL; + mBytesCount = 0; + } + + //open file for writing + inline virtual bool Open() + { + if(!mFile) { + // extract log and path names + uint32_t namelen = mSource.size(); + if(namelen) { + const char* ptr = strrchr(mSource.c_str(), '/'); + uint32_t pathlen = 0; + if(ptr) { + pathlen = ptr - mSource.c_str() + 1; + namelen -= pathlen + 1; + } + + mLogPath.append(mSource.c_str(), pathlen); + + mLogName.append((mSource.c_str() + pathlen), namelen); + + //open the file finally + mFile = fopen(mSource.c_str(), "a+"); + } + } + + return (mFile != 0); + } + + //close file opened + inline virtual void Close() + { + if(mFile) { + fclose(mFile); + } + + mFile = 0; + mBytesCount = 0; + } + + //whether file already opened or not + inline virtual bool IsOpened() + { + return (mFile != 0); + } + + inline void SetMaxFileSize(uint64_t size) + { + mMaxFileSize = size; + } + + inline uint64_t GetMaxFileSize() + { + return mMaxFileSize; + } + + inline FILE* GetLogFD() + { + return mFile; + } + +protected: + + inline virtual void SendMessage(const char* msg) + { + if(mFile && mBytesCount < mMaxFileSize) { + if(fputs(msg, mFile) >= 0) { + fflush(mFile); + mBytesCount += mStringLen; + } else { + fclose(mFile); + mFile = 0; + } + } + } + +protected: + + string mLogPath; //path to the log file + string mLogName; //name of the log file + FILE* mFile; //log file descriptor + uint64_t mMaxFileSize; //maximum number bytes allowed to be written to the file + uint64_t mBytesCount; //number of bytes actually written +}; + +#endif /* LOG_FILE_H_ */ diff --git a/framework/mutex.h b/framework/mutex.h new file mode 100644 index 0000000..ba42e80 --- /dev/null +++ b/framework/mutex.h @@ -0,0 +1,40 @@ +#ifndef MUTEX_H_ +#define MUTEX_H_ + +#include +#include + +class SC_Mutex { +public: + SC_Mutex() { + pthread_mutex_init(&m, NULL); + } + SC_Mutex(SC_Mutex const&) { + pthread_mutex_init(&m, NULL); + } + SC_Mutex& operator=(SC_Mutex const& rhs) { + if (&rhs != this) { + pthread_mutex_init(&m, NULL); + } + return *this; + } + ~SC_Mutex() { + pthread_mutex_destroy(&m); + } + + void Lock() { + pthread_mutex_lock(&m); + } + + void Unlock() { + pthread_mutex_unlock(&m); + } + + bool Trylock() { + return (pthread_mutex_trylock(&m) == 0); + } +private: + pthread_mutex_t m; +}; + +#endif /* MUTEX_H_ */ diff --git a/framework/sysutils.cpp b/framework/sysutils.cpp new file mode 100644 index 0000000..4032d6d --- /dev/null +++ b/framework/sysutils.cpp @@ -0,0 +1,509 @@ +/* + * sysutils.cpp + * + * Created on: Dec 28, 2019 + * Author: nnosov + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "logger/log.h" +#include "sysutils.h" + +extern SC_LogBase* logger; + +char __stack_trace[8192]; + + +Dwarf_Frame* frame; +Dwfl_Module* module; + +// 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 + +static void print_detail (int result, const Dwarf_Op *ops, size_t nops, Dwarf_Addr bias, const char* prefix); + +bool is_location_form(int form) +{ + if (form == DW_FORM_block1 || + form == DW_FORM_block2 || + form == DW_FORM_block4 || + form == DW_FORM_block || + form == DW_FORM_data4 || + form == DW_FORM_data8 || + form == DW_FORM_sec_offset) { + return true; + } + return false; +} + +char* GetExecutableName(char* path, uint32_t size) +{ + char link[PATH_MAX]; + snprintf(link, sizeof(link), "/proc/%d/exe", getpid()); + int nret = readlink(link, path, size); + if (nret == -1) { + return NULL; + } + + // trailing zero + path[nret] = 0; + + return path; +} + +#include + +typedef struct __reginfo { + __reginfo() { + regname[0] = 0; + regno = -1; + } + char regname[1024]; + int regno; +} reginfo; + +int regname_callback (void *arg, int regno, const char *setname, const char *prefix, const char *regname, int bits, int type) +{ + reginfo* info = (reginfo*)arg; + if(info->regno == regno) { + snprintf(info->regname, sizeof(info->regname), "%s %s%s", setname, prefix, regname); + } + + return 0; +} + +int reginfo_callback (void *arg, int regno, const char *setname, const char *prefix, const char *regname, int bits, int type) +{ + reginfo* info = (reginfo*)arg; + + //logger->Log(SEVERITY_DEBUG, "%s: info_reg = 0x%hhX, reg = 0x%hhX", __FUNCTION__, info->regno, regno); + if(info->regno == regno) { + snprintf(info->regname, sizeof(info->regname), "%s %s%s", setname, prefix, regname); + Dwarf_Op ops_mem[3]; + Dwarf_Op* ops; + size_t nops; + int ret = dwarf_frame_register(frame, info->regno, ops_mem, &ops, &nops); + if(ret != 0) { + logger->Log(SEVERITY_DEBUG, "Failed to get CFI expression for register %s", info->regname); + return 0; + } + if(nops == 0 && ops == ops_mem) { + logger->Log(SEVERITY_DEBUG, "CFI expression for register %s is UNDEFINED", info->regname); + return 0; + } + if(nops == 0 && ops == NULL) { + logger->Log(SEVERITY_DEBUG, "CFI expression for register %s is SAME VALUE", info->regname); + return 0; + } + logger->Log(SEVERITY_DEBUG, "Found CFI expression for register %s, number of expressions: %d", info->regname, nops); + for(size_t i = 0; i < nops; ++i) { + logger->Log(SEVERITY_DEBUG, "\t[%d] operation: 0x%hhX, operand1: 0x%lX, operand2: 0x%lx, offset: 0x%lX", i, ops[i].atom, ops[i].number, ops[i].number2, ops[i].offset); + } + } + + return 0; +} + +void HandleType(Dwarf_Attribute* param, bool is_return = false) +{ + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; + + // get DIE of return type + Dwarf_Die ret_die; + + if(dwarf_formref_die(param, &ret_die)) { + switch (dwarf_tag(&ret_die)) { + case DW_TAG_base_type: { + // get Size attribute and it's value + Dwarf_Word size = 0; + attr = dwarf_attr(&ret_die, DW_AT_byte_size, &attr_mem); + if(attr) { + dwarf_formudata(attr, &size); + } + logger->Log(SEVERITY_INFO, "base type '%s'(%lu)", dwarf_diename(&ret_die), size); + break; + } + case DW_TAG_array_type: + logger->Log(SEVERITY_INFO, "array type"); + break; + case DW_TAG_pointer_type: + logger->Log(SEVERITY_INFO, "pointer type"); + break; + case DW_TAG_enumeration_type: + logger->Log(SEVERITY_INFO, "enumeration type"); + break; + case DW_TAG_const_type: + logger->Log(SEVERITY_INFO, "constant type"); + break; + case DW_TAG_subroutine_type: + logger->Log(SEVERITY_INFO, "subroutine type"); + break; + case DW_TAG_typedef: + logger->Log(SEVERITY_INFO, "typedef '%s' type", dwarf_diename(&ret_die)); + break; + default: + logger->Log(SEVERITY_INFO, "Unknown 0x%X tag type", dwarf_tag(&ret_die)); + break; + } + attr = dwarf_attr(&ret_die, DW_AT_type, &attr_mem); + if(attr) { + HandleType(attr); + } + } +} + +void +print_expr_block (Dwarf_Attribute *attr, Dwarf_Op *exprs, int len, + Dwarf_Addr addr, int depth) +{ + printf ("{"); + for (int i = 0; i < len; i++) + { + printf ("%s", (i + 1 < len ? ", " : "")); + } + printf ("}"); +} + +void HandleParameter(Dwarf_Die* result) +{ + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; + + // Get reference to attribute type of the parameter/variable + attr = dwarf_attr(result, DW_AT_type, &attr_mem); + logger->Log(SEVERITY_INFO, "Handle '%s' %s", dwarf_diename(result), dwarf_tag(result) == DW_TAG_formal_parameter ? "parameter" : "variable"); + if(attr) { + HandleType(attr); + } + + // determine location of parameter in stack/heap or CPU registers + attr = dwarf_attr(result, DW_AT_location, &attr_mem); + if(attr) { + logger->Log(SEVERITY_DEBUG, "parameter location attribute form = 0x%X, code = 0x%X", attr->form, attr->code); + if(dwarf_hasform(attr, DW_FORM_exprloc)) { + Dwarf_Op *expr; + size_t exprlen; + if (dwarf_getlocation(attr, &expr, &exprlen) == 0) { + logger->Log(SEVERITY_DEBUG, "Found DW_AT_location expression"); + print_detail(0, expr, exprlen, 0, "\tLocation "); + if(expr[0].atom >= DW_OP_reg0 && expr[0].atom <= DW_OP_bregx) { + Dwarf_Op ops_mem[3]; + Dwarf_Op* ops; + size_t nops; + int ret = dwarf_frame_register(frame, expr[0].atom, ops_mem, &ops, &nops); + if(ret != 0) { + logger->Log(SEVERITY_DEBUG, "Failed to get CFI expression for register 0x%hhX", expr[0].atom); + return; + } + if(nops == 0 && ops == ops_mem) { + logger->Log(SEVERITY_DEBUG, "CFI expression for register 0x%hhX is UNDEFINED", expr[0].atom); + return; + } + if(nops == 0 && ops == NULL) { + logger->Log(SEVERITY_DEBUG, "CFI expression for register 0x%hhX is SAME VALUE", expr[0].atom); + return; + } + logger->Log(SEVERITY_DEBUG, "Found CFI expression for register 0x%hhX, number of expressions: %d", expr[0].atom, nops); + for(size_t i = 0; i < nops; ++i) { + logger->Log(SEVERITY_DEBUG, "\t[%d] operation: 0x%hhX, operand1: 0x%lX, operand2: 0x%lx, offset: 0x%lX", i, ops[i].atom, ops[i].number, ops[i].number2, ops[i].offset); + } + } + } + } else if(dwarf_hasform(attr, DW_FORM_sec_offset)) { + Dwarf_Addr base, start, end; + ptrdiff_t off = 0; + Dwarf_Op *fb_expr; + size_t fb_exprlen; + while ((off = dwarf_getlocations (attr, off, &base, &start, &end, &fb_expr, &fb_exprlen)) > 0) { + printf (" (%" PRIx64 ",%" PRIx64 ") ", start, end); + print_expr_block (attr, fb_expr, fb_exprlen, start, 0); + printf ("\n"); + } + + + Dwarf_Word value; + + int ret = dwarf_formudata(attr, &value); + if(ret >= 0) { + logger->Log(SEVERITY_DEBUG, "Found DW_AT_location offset = 0x%lX", value); + } + } else { + logger->Log(SEVERITY_WARNING, "Unknown attribute form = 0x%X, code = 0x%X", attr->form, attr->code); + } + + } +} + +void HandleFunction(Dwarf_Die* func, const char* fname) +{ + if(!strcmp(fname, dwarf_diename(func))) { + logger->Log(SEVERITY_INFO, "Found function in debug info. DWARF tag: 0x%X, name = %s(...)", dwarf_tag(func), dwarf_diename(func)); + + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; + + // determine function's stack frame base + attr = dwarf_attr(func, 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) { + logger->Log(SEVERITY_DEBUG, "Found DW_AT_framebase expression"); + print_detail(0, expr, exprlen, 0, "\tLocation "); + } else { + logger->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 + attr = dwarf_attr(func, DW_AT_type, &attr_mem); + if(attr) { + logger->Log(SEVERITY_INFO, "Handle return parameter"); + HandleType(attr, true); + } else { + logger->Log(SEVERITY_DEBUG, "return attr name = 'void(0)'"); + } + + Dwarf_Die result; + if (dwarf_child(func, &result) != 0) + return; + + // went through parameters and local variables of the function + do { + switch (dwarf_tag(&result)) { + case DW_TAG_formal_parameter: + case DW_TAG_variable: + HandleParameter(&result); + break; +// case DW_TAG_inlined_subroutine: +// /* Recurse further down */ +// HandleFunction(&result, dwarf_diename(&result)); +// break; + default: + break; + } + } while(dwarf_siblingof(&result, &result) == 0); + } + return; +} + +static void print_detail (int result, const Dwarf_Op *ops, size_t nops, Dwarf_Addr bias, const char* prefix) +{ + printf("\t%s ", prefix); + if (result < 0) { + printf("indeterminate (%s)\n", dwarf_errmsg (-1)); + } else if (nops == 0) { + printf("%s\n", ops == NULL ? "same_value" : "undefined"); + } else { + printf("%s expression:", result == 0 ? "location" : "value"); + for (size_t i = 0; i < nops; ++i) { + printf (" 0x%X (offset: 0x%lX)", ops[i].atom, ops[i].offset); + if (ops[i].number2 == 0) { + if (ops[i].atom == DW_OP_addr) { + printf ("(%#" PRIx64 ")", ops[i].number + bias); + } else if (ops[i].number != 0) { + printf ("(%" PRIx64 ")", ops[i].number); + } + } + else { + printf ("(%" PRIx64 ",%" PRIx64 ")", ops[i].number, ops[i].number2); + } + } + puts(""); + } +} + +void HandleCompilationUnit(Dwfl_Module* module, Dwarf_Addr addr, const char* fname) +{ + Dwarf_Addr mod_bias = 0; + // get function debug definition + Dwarf_Die* cdie = dwfl_module_addrdie(module, addr, &mod_bias); + logger->Log(SEVERITY_DEBUG, "Function bias in module is 0x%lX", mod_bias); + //Dwarf_Die* cdie = dwfl_addrdie(dwfl, addr, &mod_bias); + if(!cdie) { + logger->Log(SEVERITY_INFO, "Failed to find DWARF DIE for address %X", addr); + return; + } + + // get CFI (Call Frame Information) for current module + // from handle_cfi() + Dwarf_CFI* cfi = dwfl_module_eh_cfi(module, &mod_bias); + if(cfi) { + // get frame of CFI for address + logger->Log(SEVERITY_INFO, "Found CFI for module %s", dwarf_diename(cdie)); + int result = dwarf_cfi_addrframe (cfi, addr - mod_bias, &frame); + if (result == 0) { + // get frame information + logger->Log(SEVERITY_INFO, "Found CFI frame for module %s", dwarf_diename(cdie)); + Dwarf_Addr start = addr; + Dwarf_Addr end = addr; + bool signalp; + int ra_regno = dwarf_frame_info (frame, &start, &end, &signalp); + if (ra_regno >= 0) + { + start += mod_bias; + end += mod_bias; + } + logger->Log(SEVERITY_DEBUG, "Per '.eh_frame' info has %#" PRIx64 " => [%#" PRIx64 ", %#" PRIx64 "] in_signal = %s", addr, start, end, signalp ? "true" : "false"); + if (ra_regno < 0) + logger->Log(SEVERITY_DEBUG, "return address register unavailable (%s)", dwarf_errmsg (0)); + else { + reginfo info; info.regno = ra_regno; + dwfl_module_register_names(module, regname_callback, &info); + logger->Log(SEVERITY_DEBUG, "return address in reg%u%s ==> %s", ra_regno, signalp ? " (signal frame)" : "", info.regname); + } + + // 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; + result = dwarf_frame_cfa(frame, &cfa_ops, &cfa_nops); + + print_detail (result, cfa_ops, cfa_nops, mod_bias, "\tCFA "); + } + + } + + if(dwarf_tag(cdie) != DW_TAG_compile_unit) { + logger->Log(SEVERITY_DEBUG, "Skipping non-cu die. DWARF tag: 0x%X, name = %s", dwarf_tag(cdie), dwarf_diename(cdie)); + return; + } + + logger->Log(SEVERITY_DEBUG, "Enumerating Compilation Unit '%s' to lookup for function %s()", dwarf_diename(cdie), fname); + Dwarf_Die result; + if(dwarf_child(cdie, &result)) { + logger->Log(SEVERITY_INFO, "No child DIE found for CU"); + return; + } + + do { + switch (dwarf_tag(&result)) { + case DW_TAG_subprogram: + case DW_TAG_entry_point: + case DW_TAG_inlined_subroutine: + HandleFunction(&result, fname); + break; + default: + //logger->Log(SEVERITY_INFO, "Unknown tag 0x%X for die '%s'", dwarf_tag(&result), dwarf_diename(&result)); + break; + } + } while(dwarf_siblingof(&result, &result) == 0); +} + +const char* LibdwTraceCallStack(ucontext_t* uctx) +{ + void * caller_address = 0; +#ifdef REG_RIP // x86_64 + caller_address = (void *) uctx->uc_mcontext.gregs[REG_RIP]; +#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 + void * array[50]; + int size = backtrace(array, 50); + int skipped = 0; + for(int i = 0; i < size && array[i] != caller_address; ++i, ++skipped); + __stack_trace[0] = 0; uint32_t offset = 0; + offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "Stack trace(caller = %p. Total stack frames: %d, skipped: %d):\n", + caller_address, size, skipped); + + 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, + }; + + Dwfl* dwfl = dwfl_begin(&callbacks); + if(dwfl == NULL) { + offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "Failed to initialize libdw session for parse stack frames"); + return __stack_trace; + } + + if(dwfl_linux_proc_report(dwfl, getpid()) != 0 || dwfl_report_end(dwfl, NULL, NULL) !=0) { + offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "Failed to parse debug section of executable"); + return __stack_trace; + } + + for (int i = skipped, idx = 0; i < size ; ++i, ++idx) { + offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "[%-2d] ", idx); + + Dwarf_Addr addr = (uintptr_t)array[i]; + Dwfl_Line *line = dwfl_getsrc(dwfl, addr); + if(line != NULL) { + int nline; + Dwarf_Addr addr; + const char* filename = dwfl_lineinfo (line, &addr, &nline, NULL, NULL, NULL); + if(filename) { + const char* str = strrchr(filename, '/'); + if(str && *str != 0) { + str++; + } else { + str = filename; + } + offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "%s:%d", str, nline); + } else { + offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "%p", array[i]); + } + } else { + offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "%p", array[i]); + } + + module = dwfl_addrmodule(dwfl, addr); + const char* addrname = dwfl_module_addrname(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) { + logger->Log(SEVERITY_ERROR, "Failed to allocate memory"); + return __stack_trace; + } + offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, " --> %s", function_name); + + char* str = strchr(function_name, '('); + if(str) { + *str = 0; + } + HandleCompilationUnit(module, addr, function_name); + free(function_name); + } + + if(demangle_name) { + free(demangle_name); + } + + offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "\n"); + } + + return __stack_trace; +} diff --git a/framework/sysutils.h b/framework/sysutils.h new file mode 100644 index 0000000..ce9ac05 --- /dev/null +++ b/framework/sysutils.h @@ -0,0 +1,30 @@ +/* + * sysutils.h + * + * Created on: Dec 28, 2019 + * Author: nnosov + */ + +#ifndef SC_SYSUTILS_H_ +#define SC_SYSUTILS_H_ + +//system +#include +#include +#include +#include +#include +#include +#include + +using std::string; + +// stack trace string buffer +extern char __stack_trace[8192]; + +// libdw-based trace call stack implementation +const char* LibdwTraceCallStack(ucontext_t* uctx); + +char* GetExecutableName(char* path, uint32_t size); + +#endif /* SC_SYSUTILS_H_ */ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..a9ae941 --- /dev/null +++ b/main.cpp @@ -0,0 +1,199 @@ +//system +#include +#include +#include + +#include "logger/log_console.h" + +#include "sysutils.h" + +static SC_LogConsole log_local(0, "/tmp/unspecified.file"); +SC_LogBase* logger = &log_local; + +typedef enum { + DEF_1 = 1, + DEF_2, + DEF_3 +} my_int; + +void Fun2(int arg1, uint32_t arg2) +{ + int* ptr = NULL; + *ptr = arg1; +} + +uint32_t Fun1(const int arg1, my_int arg2, uint32_t arg3) +{ + int my_local = arg1 + 2; + printf("%d", my_local); + Fun2(my_local, arg3); + return arg2; +} + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*sig_handler_t)(int sig); + +void SetSignalHandler(sig_handler_t handler = 0); + +void SigusrHandler(int sig) +{ + // just do nothing on SIGUSR1 & SIGUSR2 + SetSignalHandler(SigusrHandler); +} + +volatile sig_atomic_t fatal_error_in_progress = 0; + +void FatalSignalHandler(int sig, siginfo_t* info, void* context) +{ + // Since this handler is established for more than one kind of signal, + // it might still get invoked recursively by delivery of some other kind + // of signal. Use a static variable to keep track of that. + if (fatal_error_in_progress) { + raise (sig); + } + + fatal_error_in_progress = 1; + + logger->Log(SEVERITY_ERROR, "%s signal handled", strsignal(sig)); + const char* trace = 0; + if((context != 0) && (sig == SIGSEGV || sig == SIGABRT || sig == SIGBUS || sig == SIGFPE)) + { + trace = LibdwTraceCallStack((ucontext_t*)context); + } + + if(trace) { + logger->Log(SEVERITY_DEBUG, "%s", trace); + } else { + logger->Log(SEVERITY_ERROR, "No stack trace obtained"); + } + + // comment out line below to prevent coredump + // exit(EXIT_FAILURE); + + // Now reraise the signal. + // We reactivate the signal’s default handling, which is to terminate the process. + // We could just call exit or abort, but reraising the signal sets the return status + // from the process correctly. + signal (sig, SIG_DFL); + raise (sig); +} + +void SignalHandler(int sig) +{ +// if (handled_error_in_progress) +// raise (sig); +// handled_error_in_progress = 1; + +// const char* str_sig = strsignal(sig); +// logger->Log(SEVERITY_INFO, "%s received.", str_sig); + if(sig == SIGUSR1) { + logger->Log(SEVERITY_INFO, "Maintenance mode enabled."); + } else if(sig == SIGUSR2) { + logger->Log(SEVERITY_INFO, "Maintenance mode disabled."); + } +// signal (sig, SIG_DFL); +// raise(sig); + // reset to our handler back + SetSignalHandler(); +} + +#include +#include + +void SetSignalHandler(sig_handler_t handler) +{ + //signals set + sigset_t ss; + sigfillset(&ss); + + //remove previous handlers +// sigdelset(&ss, SIGINT); + sigdelset(&ss, SIGUSR1); + sigdelset(&ss, SIGUSR2); + sigdelset(&ss, SIGSEGV); + sigdelset(&ss, SIGABRT); + sigdelset(&ss, SIGBUS); + sigdelset(&ss, SIGFPE); + sigdelset(&ss, SIGCHLD); + sigdelset(&ss, SIGTERM); //without replacement + + //signal action + struct sigaction sa; + sa.sa_flags = SA_SIGINFO; + sa.sa_mask = ss; + + // cleanup + sa.sa_handler = NULL; + sa.sa_sigaction = NULL; + +// //set interrupt signal +// sa.sa_handler = SignalHandler; +// sigaction(SIGINT, &sa, 0); + + //set stop signal + sa.sa_handler = (handler == NULL) ? SignalHandler : handler; + sigaction(SIGUSR1, &sa, 0); + + sa.sa_handler = SignalHandler; + sigaction(SIGUSR2, &sa, 0); + + // cleanup + sa.sa_handler = NULL; + sa.sa_sigaction = NULL; + + //set SIGSEGV + sa.sa_sigaction = FatalSignalHandler; + sigaction(SIGSEGV, &sa, 0); + + //set SIGABRT + sa.sa_sigaction = FatalSignalHandler; + sigaction(SIGABRT, &sa, 0); + + //set SIGBUS + sa.sa_sigaction = FatalSignalHandler; + sigaction(SIGBUS, &sa, 0); + + //set FPE signal + sa.sa_sigaction = FatalSignalHandler; + sigaction(SIGFPE, &sa, 0); + + //enable signals set + sigprocmask(SIG_BLOCK, &ss, 0); + + //set core dump size to 1Gb + struct rlimit limit; + limit.rlim_cur = 1073741824; + limit.rlim_max = 1073741824; + if (setrlimit(RLIMIT_CORE, &limit)) + { + logger->Log(SEVERITY_DEBUG, "Failed to set limit for core dump file size"); + } +} + +void ResetSignalHandler() { + signal(SIGINT, SIG_DFL); + signal(SIGABRT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGSEGV, SIG_DFL); + signal(SIGUSR1, SIG_DFL); + signal(SIGUSR2, SIG_DFL); + signal(SIGCHLD, SIG_DFL); +} + +int main(int argc, char* argv[]) +{ + + SetSignalHandler(SigusrHandler); + + Fun1(1, DEF_2, 3); + + return 0; +} diff --git a/receips.txt b/receips.txt new file mode 100644 index 0000000..b1441b9 --- /dev/null +++ b/receips.txt @@ -0,0 +1,2 @@ +#produce very simple debug info vithout sources +echo 'void foo () {}' | gcc -g -O99 -o - -S -xc - -dA | grep frame_base \ No newline at end of file diff --git a/simple.cpp b/simple.cpp new file mode 100644 index 0000000..9df250a --- /dev/null +++ b/simple.cpp @@ -0,0 +1,18 @@ +#include + + +void do_stuff(int my_arg) +{ + int my_local = my_arg + 2; + int i; + + for (i = 0; i < my_local; ++i) + printf("i = %d\n", i); +} + + +int main() +{ + do_stuff(2); + return 0; +}