From 48d139997b6abca9f9824451bb47bf6837591932 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 4 Jun 2025 08:02:35 +1200 Subject: [PATCH] 'Generic Commit' --- ipdemo/src/assert.hpp | 158 +++++++++++++++++++++++++----------------- 1 file changed, 96 insertions(+), 62 deletions(-) diff --git a/ipdemo/src/assert.hpp b/ipdemo/src/assert.hpp index 20c7025..086c825 100644 --- a/ipdemo/src/assert.hpp +++ b/ipdemo/src/assert.hpp @@ -14,6 +14,17 @@ #include #include +// ANSI color codes +namespace colors { + constexpr const char* reset = "\033[0m"; + constexpr const char* red = "\033[31m"; + constexpr const char* green = "\033[32m"; + constexpr const char* yellow = "\033[33m"; + constexpr const char* blue = "\033[34m"; + constexpr const char* magenta = "\033[35m"; + constexpr const char* cyan = "\033[36m"; +} + // Simple RAII wrapper for file descriptor class FileDescriptor { int fd_ = -1; @@ -25,36 +36,66 @@ public: FileDescriptor& operator=(const FileDescriptor&) = delete; }; -std::string get_addr2line_info(const char* executable, void* addr) { +struct SourceInfo { + std::string function; + std::string file; + int line = 0; +}; + +SourceInfo get_source_info(const char* executable, void* addr) { char cmd[512]; - snprintf(cmd, sizeof(cmd), "addr2line -f -p -e %s %p 2>/dev/null", executable, addr); + snprintf(cmd, sizeof(cmd), "addr2line -f -e %s %p 2>/dev/null", executable, addr); FILE* pipe = popen(cmd, "r"); - if (!pipe) return ""; + if (!pipe) return {}; - char buffer[1024]; - std::string result; - while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { - result += buffer; + SourceInfo info; + char func_buf[1024] = {0}; + char file_buf[1024] = {0}; + int line = 0; + + // Read function name + if (fgets(func_buf, sizeof(func_buf), pipe)) { + // Remove trailing newline + size_t len = strlen(func_buf); + if (len > 0 && func_buf[len-1] == '\n') { + func_buf[len-1] = '\0'; + } + info.function = func_buf; + + // Demangle the function name + int status = 0; + char* demangled = abi::__cxa_demangle(func_buf, nullptr, nullptr, &status); + if (demangled) { + info.function = demangled; + free(demangled); + } } + + // Read file and line number + if (fgets(file_buf, sizeof(file_buf), pipe)) { + char* colon = strchr(file_buf, ':'); + if (colon) { + *colon = '\0'; + info.line = atoi(colon + 1); + + // Extract just the filename + const char* slash = strrchr(file_buf, '/'); + info.file = (slash ? slash + 1 : file_buf); + } + } + pclose(pipe); - - // Remove trailing newline if present - if (!result.empty() && result.back() == '\n') { - result.pop_back(); - } - return result; + return info; } void print_stacktrace() { unw_cursor_t cursor; unw_context_t context; - unw_word_t offset, pc; - char sym[256]; - char *name = sym; - int status; - int frame_num = 0; + unw_word_t pc; char exe_path[1024] = {0}; + bool found_main = false; + std::vector frames; // Get the path to the current executable ssize_t count = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); @@ -68,56 +109,49 @@ void print_stacktrace() { unw_getcontext(&context); unw_init_local(&cursor, &context); - std::cerr << "Stack trace (most recent call first):\n"; - - // Walk up the stack + // First pass: collect frames until we find main while (unw_step(&cursor) > 0) { - unw_get_reg(&cursor, UNW_REG_IP, &pc); - if (pc == 0) { + if (unw_get_reg(&cursor, UNW_REG_IP, &pc) != 0 || pc == 0) { break; } - std::cerr << "#" << frame_num++ << " "; + SourceInfo info = get_source_info(exe_path, reinterpret_cast(pc)); - // Get symbol name - if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) { - // Demangle the C++ name - int status = 0; - size_t length = sizeof(sym); - name = abi::__cxa_demangle(sym, nullptr, &length, &status); - - // Get line info using addr2line - std::string line_info = get_addr2line_info(exe_path, (void*)pc); - - Dl_info info; - if (dladdr(reinterpret_cast(pc), &info) && info.dli_fname) { - const char* base = strrchr(info.dli_fname, '/'); - std::cerr << (base ? base + 1 : info.dli_fname) << ": "; - } - - if (name) { - std::cerr << name; - free(name); - } else { - std::cerr << sym; - } - - std::cerr << " + 0x" << std::hex << offset; - - if (!line_info.empty()) { - std::cerr << " at " << line_info; - } - - std::cerr << " [0x" << std::hex << pc << "]\n"; - } else { - std::cerr << "[unknown] [0x" << std::hex << pc << "]"; - // Try to get at least some info with addr2line even for unknown symbols - std::string line_info = get_addr2line_info(exe_path, (void*)pc); - if (!line_info.empty()) { - std::cerr << " at " << line_info; - } - std::cerr << "\n"; + // Stop collecting after main() + if (!info.function.empty() && + (info.function.find("main") != std::string::npos || + info.function.find("__libc_start_main") != std::string::npos)) { + found_main = true; + frames.push_back(info); + break; } + + frames.push_back(info); + } + + // Print the collected frames in reverse order (most recent last) + std::cerr << colors::yellow << "Stack trace (most recent call last):" << colors::reset << "\n"; + + for (size_t i = frames.size(); i-- > 0; ) { + const auto& frame = frames[i]; + std::cerr << " "; // Indent each line + + if (frame.function.empty()) { + std::cerr << colors::red << "[unknown function]" << colors::reset; + } else { + std::cerr << colors::green << frame.function << colors::reset; + } + + if (!frame.file.empty() && frame.line > 0) { + std::cerr << " at " << colors::blue << frame.file << colors::reset + << ":" << colors::magenta << frame.line << colors::reset; + } + + std::cerr << "\n"; + } + + if (!found_main) { + std::cerr << " " << colors::yellow << "[truncated - main() not found]" << colors::reset << "\n"; } }