diff --git a/CMakeLists.txt b/CMakeLists.txt index edd5bf6..e267f8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,10 +23,10 @@ if (NOT CMAKE_BUILD_TYPE) endif() #-------------------------------------------------------------------------------------------------- find_package(X11 REQUIRED) -# find_package(Libssh2 REQUIRED) +find_package(Libssh2 REQUIRED) # find_package(Libevent REQUIRED) -# find_package(OpenSSL REQUIRED) -# find_package(ZLIB REQUIRED) +find_package(OpenSSL REQUIRED) +find_package(ZLIB REQUIRED) #-------------------------------------------------------------------------------------------------- file(GLOB_RECURSE SOURCES ${CMAKE_SOURCE_DIR}/*.c @@ -82,6 +82,10 @@ foreach(IT_SOURCE ${SOURCES}) # libevent::libevent # openssl::openssl # ZLIB::ZLIB + backtrace # https://github.com/ianlancetaylor/libbacktrace - for boost::stacktrace + ssh2 + unwind + event ) # Targets - Build, run single test diff --git a/Libs/Boost/Stacktrace.cpp b/Libs/Boost/Stacktrace.cpp index 1918bdc..c65a6f9 100644 --- a/Libs/Boost/Stacktrace.cpp +++ b/Libs/Boost/Stacktrace.cpp @@ -1,6 +1,11 @@ /** * \file Stacktrace.cpp * \brief boost::stacktrace + * + * \see https://github.com/ianlancetaylor/libbacktrace + * sudo apt-get install libbacktrace-dev + * + * g++ -DBOOST_STACKTRACE_USE_ADDR2LINE -ggdb -rdynamic Stacktrace.cpp -o bb -lboost_stacktrace_addr2line -ldl -O0 -fno-omit-frame-pointer -lboost_stacktrace_addr2line && ./bb */ @@ -12,22 +17,64 @@ #include #endif -#if (BOOST_VERSION > 0) - #define BOOST_STACKTRACE_USE_ADDR2LINE 1 +#if 1 || (BOOST_VERSION > 0) + // NOTE: BOOST_STACKTRACE_USE_ADDR2LINE - not working + #define BOOST_STACKTRACE_USE_BACKTRACE 1 #include #endif //------------------------------------------------------------------------------------------------- +std::string +executableName( + const boost::stacktrace::frame &a_frame +) +{ + // The first frame usually represents the main function or entry point + if ( a_frame.empty() ) { + return "Unknown executable"; // Fallback if unable to determine + } + + const auto addr = a_frame.address(); + if (addr == nullptr) { + return {}; + } + + Dl_info info {}; + if (::dladdr(addr, &info) == 0) { + return {}; + } + + if (info.dli_fname == nullptr) { + return {}; + } + + return info.dli_fname; +} +//------------------------------------------------------------------------------------------------- int main(int , char **) { -#if (BOOST_VERSION > 0) - const auto &aRv = boost::stacktrace::stacktrace(); +#if 1 || (BOOST_VERSION > 0) + const auto &stackTrace = boost::stacktrace::stacktrace(); + + for (const auto &it_frame : stackTrace.as_vector()) { + std::cout << "------------------------------" << std::endl; - std::cout << aRv.as_vector() << std::endl; + if (0) { + std::cout << STD_TRACE_VAR(it_frame) << '\n' << std::endl; + } else { + std::cout + << "module: " << ::executableName(it_frame) << "\n" + << "\n" + << STD_TRACE_VAR(it_frame.name()) << "\n" + << STD_TRACE_VAR(it_frame.address()) << "\n" + << STD_TRACE_VAR(it_frame.source_file()) << "\n" + << STD_TRACE_VAR(it_frame.source_line()) << std::endl; + } + } #else std::cout << "Boost - not instaled, skip" << std::endl; #endif - return EXIT_SUCCESS; + return EXIT_SUCCESS; } //------------------------------------------------------------------------------------------------- diff --git a/OS/Unix/Backtrace_2.cpp b/OS/Unix/Backtrace_2.cpp deleted file mode 100644 index 6f9dbd8..0000000 --- a/OS/Unix/Backtrace_2.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/** - * \file Backtrace.cpp - * \brief Bactrace - * - * \code{.console} - * - * \endcode - * - * \todo - */ - - -//------------------------------------------------------------------------------------------------- -#include -#include -//#include - -#if __cpp_lib_stacktrace - #include -#else - #include - #include - #include -#endif -//------------------------------------------------------------------------------------------------- -// Function to execute addr2line and get file and line number -std::string -getFileLine( - const void *a_frame -) -{ - char addrStr[20 + 1] {}; - std::sprintf(addrStr, "%p", a_frame); - - // Prepare the command: addr2line -e
- const std::string cmd = - // "addr2line -e Backtrace_2.exe -f -p " + std::string(addrStr); - // "addr2line -e ./Backtrace_2.exe -f -C " + std::string(addrStr); - "addr2line -f -e ./Backtrace_2.exe " + std::string(addrStr); - // std::cout << STD_TRACE_VAR(cmd) << std::endl; - - // Run addr2line command to get file and line number - FILE *pipe = ::popen(cmd.c_str(), "r"); - STD_TEST_PTR(pipe); - - std::string result; - - char buffer[256] {}; - while (std::fgets(buffer, sizeof(buffer), pipe) != nullptr) { - result += buffer; - } - - ::pclose(pipe); - - return result; -} -//------------------------------------------------------------------------------------------------- -void -printStackTrace() -{ -#if __cpp_lib_stacktrace - #warning "__cpp_lib_stacktrace - defined" - - auto trace = std::stacktrace::current(); - std::cout << std::to_string(trace) << std::endl; -#else - const int frames_max = 10; - void *frames[frames_max] {}; - - // Capture the stack trace - int addr_size = ::backtrace(frames, frames_max); - if (addr_size == 0) { - std::cerr << "No stack trace available" << std::endl; - return; - } - - for (int i = 0; i < addr_size; ++i) { - std::cout << "\n"; - - const void *frame = frames[i]; - - Dl_info info {}; - int iRv = ::dladdr(frame, &info); - if (iRv == 0) { - std::printf("%-3d %p: [no symbol]\n", i, frame); - continue; - } - - int status {-1}; - char *demangledName = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status); - if (status == 0 && - demangledName != nullptr) - { - std::printf("%-3d %p: %s (+%ld) [%s]", - i, - frame, - demangledName, - (char *)frame - (char *)info.dli_saddr, - info.dli_fname); - - std::free(demangledName); - demangledName = nullptr; - } else { - std::printf("%-3d %p: %s [%s]", - i, - frame, - "[n/a]", - info.dli_fname); - } - - // Get file and line information from addr2line - const std::string &fileLine = ::getFileLine(frame); - std::cout << "\n" << STD_TRACE_VAR(fileLine); - } -#endif -} -//------------------------------------------------------------------------------------------------- -void -testFunction() -{ - ::printStackTrace(); -} -//------------------------------------------------------------------------------------------------- -int -main(int, char** argv) -{ - std::cout << STD_TRACE_VAR(argv[0]) << std::endl; - - ::testFunction(); - - return EXIT_SUCCESS; -} -//------------------------------------------------------------------------------------------------- - - -#if OUTPUT - -argv[0]: ././Backtrace_2.exe - -0 0x6118eab11ce9: printStackTrace() (+138) [././Backtrace_2.exe] -fileLine: ?? -??:0 - -1 0x6118eab11f9f: testFunction() (+13) [././Backtrace_2.exe] -fileLine: ?? -??:0 - -2 0x6118eab11ffa: [n/a] [././Backtrace_2.exe] -fileLine: ?? -??:0 - -3 0x738d46229d90: [n/a] [/lib/x86_64-linux-gnu/libc.so.6] -fileLine: ?? -??:0 - -4 0x738d46229e40: [n/a] [/lib/x86_64-linux-gnu/libc.so.6] -fileLine: ?? -??:0 - -5 0x6118eab11805: [n/a] [././Backtrace_2.exe] -fileLine: ?? -??:0 - -#endif diff --git a/OS/Unix/Backtrace_2.sh b/OS/Unix/Backtrace_2.sh deleted file mode 100755 index f87d56d..0000000 --- a/OS/Unix/Backtrace_2.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - - -CPP_BASE_NAME="./Backtrace_2" - -echo "Compile ${CPP_BASE_NAME}.cpp ..." -c++ -g -O0 ./${CPP_BASE_NAME}.cpp -rdynamic -ldl -fpermissive -o ./${CPP_BASE_NAME}.exe - -echo "Run..." -./${CPP_BASE_NAME}.exe diff --git a/OS/Unix/Backtrace_addr2line.cpp b/OS/Unix/Backtrace_addr2line.cpp new file mode 100644 index 0000000..fbe7d69 --- /dev/null +++ b/OS/Unix/Backtrace_addr2line.cpp @@ -0,0 +1,378 @@ +/** + * \file Backtrace_addr2line.cpp + * \brief Bactrace by addr2line + * + * \code{.console} + * + * \endcode + * + * \todo + */ + + +//------------------------------------------------------------------------------------------------- +#include +#include +#include + +#include +#include +#include +//------------------------------------------------------------------------------------------------- + +/************************************************************************************************** +* MappingEntry +* +**************************************************************************************************/ + +//------------------------------------------------------------------------------------------------- +class MappingEntry + /// \see /proc/self/maps +{ +public: +///\name ctors, dtor +///\{ + MappingEntry() = default; + MappingEntry(const std::string &startStr, const std::string &endStr, + const std::string &offsetFromBaseStr); + ~MappingEntry() = default; +///\} + + bool containsAddr(const std::uintptr_t addr) const; + uintptr_t addr() const; + +private: + const uintptr_t _start {}; + const uintptr_t _end {}; + const uintptr_t _offsetFromBase {}; + + uintptr_t _hexStrToInt(const std::string &str) const; +}; +//------------------------------------------------------------------------------------------------- +MappingEntry::MappingEntry( + const std::string &a_startStr, + const std::string &a_endStr, + const std::string &a_offsetFromBaseStr +) : + _start { _hexStrToInt(a_startStr) }, + _end { _hexStrToInt(a_endStr) }, + _offsetFromBase { _hexStrToInt(a_offsetFromBaseStr) } +{ +} +//------------------------------------------------------------------------------------------------- +bool +MappingEntry::containsAddr( + const std::uintptr_t a_addr +) const +{ + STD_TEST(a_addr != 0); + + return (a_addr >= _start && a_addr < _end); +} +//------------------------------------------------------------------------------------------------- +uintptr_t +MappingEntry::addr() const +{ + return (_start - _offsetFromBase); +} +//------------------------------------------------------------------------------------------------- +uintptr_t +MappingEntry::_hexStrToInt( + const std::string &a_str +) const +{ + STD_TEST(!a_str.empty()); + + uintptr_t out {}; + + std::stringstream ss; + ss << std::hex << a_str; + ss >> out; + + // whole stream read, with no errors + if (ss.eof() && + !ss.fail()) + { + return out; + } + + throw std::invalid_argument(std::string("can't convert '") + a_str + "' to hex"); +} +//------------------------------------------------------------------------------------------------- + + +/************************************************************************************************** +* Mappings +* +**************************************************************************************************/ + +//------------------------------------------------------------------------------------------------- +class Mappings + /// \see /proc/self/maps +{ +public: +///\name ctors, dtor +///\{ + Mappings() = delete; + Mappings(const std::uintptr_t addr, const bool positionIndependent); + ~Mappings() = default; +///\} + + std::uintptr_t offset() const; + +private: + const std::uintptr_t _addr {}; + const bool _positionIndependent {}; + + std::uintptr_t _getOwnProcAddrBase(const std::uintptr_t addr) const; + MappingEntry _parseProcMapsLine(const std::string &map_entry) const; +}; +//------------------------------------------------------------------------------------------------- +Mappings::Mappings( + const std::uintptr_t a_addr, + const bool a_positionIndependent +) : + _addr {a_addr}, + _positionIndependent {a_positionIndependent} +{ + STD_TEST(a_addr != 0); +} +//------------------------------------------------------------------------------------------------- +std::uintptr_t +Mappings::offset() const +{ + std::uintptr_t addr_base {}; + + if (_positionIndependent) { + addr_base = _getOwnProcAddrBase(_addr); + } + + const uintptr_t offset = _addr - addr_base; + + return offset; +} +//------------------------------------------------------------------------------------------------- +std::uintptr_t +Mappings::_getOwnProcAddrBase( + const std::uintptr_t a_addr +) const +{ + std::ifstream maps_file("/proc/self/maps"); + STD_TEST(maps_file.is_open()); + + for (std::string map_entry; std::getline(maps_file, map_entry); ) { + MappingEntry mapping = _parseProcMapsLine(map_entry); + if ( mapping.containsAddr(a_addr) ) { + return mapping.addr(); + } + } + + STD_TEST(false); + + return {}; +} +//------------------------------------------------------------------------------------------------- +MappingEntry +Mappings::_parseProcMapsLine( + const std::string &a_map_entry +) const +{ + std::string mapping_range_str; + std::string permissions_str; + std::string offset_from_base_str; + { + std::istringstream line_stream(a_map_entry); + if (!std::getline(line_stream, mapping_range_str, ' ') || + !std::getline(line_stream, permissions_str, ' ') || + !std::getline(line_stream, offset_from_base_str, ' ')) + { + return {}; + } + } + + std::string mapping_start_str; + std::string mapping_end_str; + { + std::istringstream mapping_range_stream(mapping_range_str); + if (!std::getline(mapping_range_stream, mapping_start_str, '-') || + !std::getline(mapping_range_stream, mapping_end_str)) + { + return {}; + } + } + + try { + MappingEntry mapping(mapping_start_str, mapping_end_str, offset_from_base_str); + + return mapping; + } + catch (const std::invalid_argument &e) { + return {}; + } +} +//------------------------------------------------------------------------------------------------- + + +/************************************************************************************************** +* Impl +* +**************************************************************************************************/ + +//------------------------------------------------------------------------------------------------- +// Function to execute addr2line and get file and line number +std::string +getAddr2line( + const void *a_frame +) +{ + STD_TEST_PTR(a_frame); + + std::string result; + + std::string cmd; + { + char addrStr[20 + 1] {}; + std::sprintf(addrStr, "%p", a_frame); + + cmd = + "addr2line " + "--exe=Backtrace_addr2line.exe " + // "--functions --demangle --inlines --pretty-print " + + std::string(addrStr); + } + + { + FILE *pipe = ::popen(cmd.c_str(), "r"); + STD_TEST_PTR(pipe); + + char buffer[256] {}; + while (std::fgets(buffer, sizeof(buffer), pipe) != nullptr) { + result += buffer; + } + + ::pclose(pipe); + } + + // Trimming + while ( + !result.empty() && + (result[result.size() - 1] == '\n' || result[result.size() - 1] == '\r') + ) + { + result.erase(result.size() - 1); + } + + return result; +} +//------------------------------------------------------------------------------------------------- +std::string +getFileLine( + const void *a_addr, + const bool a_positionIndependent +) +{ + STD_TEST_PTR(a_addr); + + Mappings mappings(reinterpret_cast(a_addr), a_positionIndependent); + + const std::string sRv = ::getAddr2line( reinterpret_cast(mappings.offset()) ); + if (sRv.empty() || + sRv[0] == '?') + { + return {}; + } + + return sRv; +} +//------------------------------------------------------------------------------------------------- + + +/************************************************************************************************** +* Test +* +**************************************************************************************************/ + +//------------------------------------------------------------------------------------------------- +void +printStackTrace() +{ + const int frames_max = 10; + void *frames[frames_max] {}; + + // Capture the stack trace + int addr_size = ::backtrace(frames, frames_max); + if (addr_size == 0) { + std::cerr << "No stack trace available" << std::endl; + return; + } + + for (int i = 0; i < addr_size; ++i) { + const void *frame = frames[i]; + + Dl_info info {}; + int iRv = ::dladdr(frame, &info); + if (iRv == 0) { + std::printf("%-3d %p: [no symbol]\n", i, frame); + continue; + } + + // SourceInfoOption::funcName + { + int status {-1}; + char *demangledName = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status); + if (status == 0 && + demangledName != nullptr) + { + std::printf("%-3d [%s] %p: %s (+%ld)", + i, + info.dli_fname, + frame, + demangledName, + reinterpret_cast(frame) - reinterpret_cast(info.dli_saddr)); + + std::free(demangledName); + demangledName = nullptr; + } else { + std::printf("%-3d [%s] %p: %s", + i, + info.dli_fname, + frame, + "[n/a]"); + } + } + + // SourceInfoOption::filePath, SourceInfoOption::lineNum + { + std::string fileLine = ::getFileLine(frame, false); + if ( fileLine.empty() ) { + fileLine = ::getFileLine(frame, true); + } + + if (fileLine.empty()) { + fileLine = "[n/a]"; + } + + std::cout << " " << "at " << fileLine << std::endl; + } + } +} +//------------------------------------------------------------------------------------------------- +void +testFunction() +{ + ::printStackTrace(); +} +//------------------------------------------------------------------------------------------------- +int +main(int, char**) +{ + ::testFunction(); + + return EXIT_SUCCESS; +} +//------------------------------------------------------------------------------------------------- + + +#if OUTPUT + +#endif diff --git a/OS/Unix/Backtrace_addr2line.sh b/OS/Unix/Backtrace_addr2line.sh new file mode 100755 index 0000000..1eedf2f --- /dev/null +++ b/OS/Unix/Backtrace_addr2line.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# +# \file Backtrace_addr2line.sh +# \brief Compile +# + + +#-------------------------------------------------------------------------------------------------- +# vars +CPP_BASE_NAME="Backtrace_addr2line" + +CPP_STANDART="c++20" +INCLUDE_DIR="/home/skynowa/Projects/CppTest" +#--------------------------------------------------------------------------------------------------- +# Clean +if [ -f "${CPP_BASE_NAME}.exe" ]; then + echo "${CPP_BASE_NAME}.exe - Clean..." + + rm "${CPP_BASE_NAME}.exe" +fi +#--------------------------------------------------------------------------------------------------- +# Build +echo "${CPP_BASE_NAME}.cpp - Compile..." + +c++ -std=${CPP_STANDART} -g -O0 -rdynamic ./${CPP_BASE_NAME}.cpp -ldl -fno-omit-frame-pointer -fno-inline -no-pie -fpermissive -I ${INCLUDE_DIR} -o ./${CPP_BASE_NAME}.exe +#--------------------------------------------------------------------------------------------------- +# Run +echo -e "${CPP_BASE_NAME}.exe - Run...\n" + +./${CPP_BASE_NAME}.exe +#--------------------------------------------------------------------------------------------------- +echo -e "\nOK.\n" +#---------------------------------------------------------------------------------------------------