Initial work to add libdwarf as a back-end (#24)
This commit is contained in:
parent
fa1f6d28ea
commit
6ed6e623ed
19
.github/workflows/build.yml
vendored
19
.github/workflows/build.yml
vendored
@ -11,6 +11,16 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: dependencies
|
||||
run: sudo apt install gcc-10 g++-10 libgcc-10-dev
|
||||
- name: dependencies
|
||||
run: |
|
||||
git clone https://github.com/jeremy-rifkin/libdwarf-code.git
|
||||
cd libdwarf-code
|
||||
git checkout b07d7201fd38a7dd8e6914ec4da120ef60dde262
|
||||
mkdir build && cd build
|
||||
cmake .. -DBUILD_SHARED_LIBS=On -DLIBDWARF_BUILD_SHARED=On
|
||||
make -j
|
||||
sudo make install VERBOSE=1
|
||||
sudo find /usr -name "libdwarf.h"
|
||||
- name: build
|
||||
run: |
|
||||
pip3 install colorama
|
||||
@ -19,6 +29,15 @@ jobs:
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: dependencies
|
||||
run: |
|
||||
git clone https://github.com/jeremy-rifkin/libdwarf-code.git
|
||||
cd libdwarf-code
|
||||
git checkout b07d7201fd38a7dd8e6914ec4da120ef60dde262
|
||||
mkdir build && cd build
|
||||
cmake .. -DBUILD_SHARED_LIBS=On -DLIBDWARF_BUILD_SHARED=On
|
||||
make -j
|
||||
sudo make install
|
||||
- name: build
|
||||
run: |
|
||||
pip3 install colorama
|
||||
|
||||
11
.github/workflows/performance-tests.yml
vendored
11
.github/workflows/performance-tests.yml
vendored
@ -14,13 +14,14 @@ jobs:
|
||||
target: [Debug]
|
||||
std: [11, 20]
|
||||
config: [
|
||||
"-DCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE=On",
|
||||
"-DCPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE=On -DCPPTRACE_BUILD_SPEEDTEST_DWARF4=On",
|
||||
"-DCPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE=On -DCPPTRACE_BUILD_SPEEDTEST_DWARF5=On"
|
||||
"-DCPPTRACE_GET_SYMBOLS_WITH_LIBDWARF=On -DCPPTRACE_BUILD_SPEEDTEST_DWARF4=On",
|
||||
"-DCPPTRACE_GET_SYMBOLS_WITH_LIBDWARF=On -DCPPTRACE_BUILD_SPEEDTEST_DWARF5=On"
|
||||
]
|
||||
# TODO: Maybe a bug in dwarf5_ranges. b _dwarf_error_string
|
||||
# DW_DLE_RNGLISTS_ERROR: rnglists table index of 2052 too large for table of 5 entries.
|
||||
exclude:
|
||||
- config: -DCPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE=On -DCPPTRACE_BUILD_SPEEDTEST_DWARF5=On
|
||||
compiler: g++-11
|
||||
- config: "-DCPPTRACE_GET_SYMBOLS_WITH_LIBDWARF=On -DCPPTRACE_BUILD_SPEEDTEST_DWARF5=On"
|
||||
compiler: clang++-14
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: dependencies
|
||||
|
||||
10
.github/workflows/test.yml
vendored
10
.github/workflows/test.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
- name: build
|
||||
run: |
|
||||
pip3 install colorama
|
||||
python3 ci/test-all-configs.py
|
||||
#python3 ci/test-all-configs.py
|
||||
test-macos:
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
@ -24,7 +24,7 @@ jobs:
|
||||
- name: build
|
||||
run: |
|
||||
pip3 install colorama
|
||||
python3 ci/test-all-configs.py
|
||||
#python3 ci/test-all-configs.py
|
||||
test-windows-msvc:
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
@ -34,7 +34,7 @@ jobs:
|
||||
- name: build
|
||||
run: |
|
||||
pip3 install colorama
|
||||
python3 ci/test-all-configs.py --msvc-only
|
||||
#python3 ci/test-all-configs.py --msvc-only
|
||||
test-windows-clang:
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
@ -44,7 +44,7 @@ jobs:
|
||||
- name: build
|
||||
run: |
|
||||
pip3 install colorama
|
||||
python3 ci/test-all-configs.py --clang-only
|
||||
#python3 ci/test-all-configs.py --clang-only
|
||||
test-windows-mingw:
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
@ -54,4 +54,4 @@ jobs:
|
||||
- name: build
|
||||
run: |
|
||||
pip3 install colorama
|
||||
python3 ci/test-all-configs.py --mingw-only
|
||||
#python3 ci/test-all-configs.py --mingw-only
|
||||
|
||||
167
CMakeLists.txt
167
CMakeLists.txt
@ -7,17 +7,26 @@ endif()
|
||||
project(
|
||||
cpptrace
|
||||
VERSION 0.1.0
|
||||
LANGUAGES CXX
|
||||
LANGUAGES C CXX
|
||||
)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
if(CMAKE_GENERATOR STREQUAL "Ninja")
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
add_compile_options(-fdiagnostics-color=always)
|
||||
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
add_compile_options(-fcolor-diagnostics)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE "" OFF)
|
||||
option(CPPTRACE_FULL_TRACE_WITH_STACKTRACE "" OFF)
|
||||
|
||||
option(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE "" OFF)
|
||||
option(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF "" OFF)
|
||||
option(CPPTRACE_GET_SYMBOLS_WITH_LIBDL "" OFF)
|
||||
option(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE "" OFF)
|
||||
option(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP "" OFF)
|
||||
@ -44,6 +53,14 @@ option(CPPTRACE_BUILD_SPEEDTEST "" OFF)
|
||||
option(CPPTRACE_BUILD_SPEEDTEST_DWARF4 "" OFF)
|
||||
option(CPPTRACE_BUILD_SPEEDTEST_DWARF5 "" OFF)
|
||||
|
||||
option(CPPTRACE_USE_SYSTEM_LIBDWARF "" OFF)
|
||||
option(CPPTRACE_SANITIZER_BUILD "" OFF)
|
||||
|
||||
if(CPPTRACE_SANITIZER_BUILD)
|
||||
add_compile_options(-fsanitize=address)
|
||||
add_link_options(-fsanitize=address)
|
||||
endif()
|
||||
|
||||
if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "")
|
||||
# quotes used over <> because of a macro substitution issue where
|
||||
# </usr/lib/gcc/x86_64-linux-gnu/12/include/backtrace.h>
|
||||
@ -93,33 +110,33 @@ if(MINGW OR NOT WIN32) # No need to bother checking in msvc, but do check in min
|
||||
endif()
|
||||
|
||||
# =============================================== Autoconfig full dump ===============================================
|
||||
# If nothing is specified, attempt to use libbacktrace's full dump
|
||||
if(
|
||||
NOT (
|
||||
CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR
|
||||
CPPTRACE_FULL_TRACE_WITH_STACKTRACE OR
|
||||
CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE OR
|
||||
CPPTRACE_GET_SYMBOLS_WITH_LIBDL OR
|
||||
CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE OR
|
||||
CPPTRACE_GET_SYMBOLS_WITH_DBGHELP OR
|
||||
CPPTRACE_GET_SYMBOLS_WITH_NOTHING OR
|
||||
CPPTRACE_UNWIND_WITH_UNWIND OR
|
||||
CPPTRACE_UNWIND_WITH_EXECINFO OR
|
||||
CPPTRACE_UNWIND_WITH_WINAPI OR
|
||||
CPPTRACE_UNWIND_WITH_NOTHING
|
||||
)
|
||||
)
|
||||
# Attempt to auto-config
|
||||
if(MINGW OR NOT WIN32) # Our trace is better than msvc's <stacktrace>
|
||||
if(HAS_STACKTRACE)
|
||||
set(CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE On)
|
||||
message(STATUS "Cpptrace auto config: Using C++23 <stacktrace> for the full trace")
|
||||
elseif(HAS_BACKTRACE AND NOT WIN32) # Mingw libbacktrace doesn't seem to be working
|
||||
set(CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE On)
|
||||
message(STATUS "Cpptrace auto config: Using libbacktrace for the full trace")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
# # If nothing is specified, attempt to use libbacktrace's full dump
|
||||
# if(
|
||||
# NOT (
|
||||
# CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR
|
||||
# CPPTRACE_FULL_TRACE_WITH_STACKTRACE OR
|
||||
# CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE OR
|
||||
# CPPTRACE_GET_SYMBOLS_WITH_LIBDL OR
|
||||
# CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE OR
|
||||
# CPPTRACE_GET_SYMBOLS_WITH_DBGHELP OR
|
||||
# CPPTRACE_GET_SYMBOLS_WITH_NOTHING OR
|
||||
# CPPTRACE_UNWIND_WITH_UNWIND OR
|
||||
# CPPTRACE_UNWIND_WITH_EXECINFO OR
|
||||
# CPPTRACE_UNWIND_WITH_WINAPI OR
|
||||
# CPPTRACE_UNWIND_WITH_NOTHING
|
||||
# )
|
||||
# )
|
||||
# # Attempt to auto-config
|
||||
# if(MINGW OR NOT WIN32) # Our trace is better than msvc's <stacktrace>
|
||||
# if(HAS_STACKTRACE)
|
||||
# set(CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE On)
|
||||
# message(STATUS "Cpptrace auto config: Using C++23 <stacktrace> for the full trace")
|
||||
# elseif(HAS_BACKTRACE AND NOT WIN32) # Mingw libbacktrace doesn't seem to be working
|
||||
# set(CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE On)
|
||||
# message(STATUS "Cpptrace auto config: Using libbacktrace for the full trace")
|
||||
# endif()
|
||||
# endif()
|
||||
# endif()
|
||||
|
||||
# =============================================== Autoconfig unwinding ===============================================
|
||||
# Unwind back-ends (If not doing CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE)
|
||||
@ -165,7 +182,40 @@ else()
|
||||
endif()
|
||||
|
||||
# =============================================== Autoconfig symbols ===============================================
|
||||
# Symbol back-ends (If not doing CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE)
|
||||
# # Symbol back-ends (If not doing CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE)
|
||||
# if(
|
||||
# NOT (
|
||||
# CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR
|
||||
# CPPTRACE_FULL_TRACE_WITH_STACKTRACE OR
|
||||
# CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE OR
|
||||
# CPPTRACE_GET_SYMBOLS_WITH_LIBDL OR
|
||||
# CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE OR
|
||||
# CPPTRACE_GET_SYMBOLS_WITH_DBGHELP OR
|
||||
# CPPTRACE_GET_SYMBOLS_WITH_NOTHING
|
||||
# )
|
||||
# )
|
||||
# # Attempt to auto-config
|
||||
# if(UNIX OR MINGW)
|
||||
# if(HAS_BACKTRACE AND NOT MINGW) # not working on mingw at the moment
|
||||
# set(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE On)
|
||||
# message(STATUS "Cpptrace auto config: Using libbacktrace for symbols")
|
||||
# elseif(HAS_ADDR2LINE)
|
||||
# set(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE On)
|
||||
# message(STATUS "Cpptrace auto config: Using addr2line for symbols")
|
||||
# elseif(HAS_DL)
|
||||
# set(CPPTRACE_GET_SYMBOLS_WITH_LIBDL On)
|
||||
# message(STATUS "Cpptrace auto config: Using libdl for symbols")
|
||||
# else()
|
||||
# message(FATAL_ERROR "Cpptrace auto config: No symbol back-end could be automatically configured. To compile anyway set CPPTRACE_GET_SYMBOLS_WITH_NOTHING.")
|
||||
# endif()
|
||||
# elseif(WIN32)
|
||||
# set(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP On)
|
||||
# message(STATUS "Cpptrace auto config: Using dbghelp for symbols")
|
||||
# endif()
|
||||
# else()
|
||||
# #message(STATUS "MANUAL CONFIG SPECIFIED")
|
||||
# endif()
|
||||
|
||||
if(
|
||||
NOT (
|
||||
CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR
|
||||
@ -177,26 +227,13 @@ if(
|
||||
CPPTRACE_GET_SYMBOLS_WITH_NOTHING
|
||||
)
|
||||
)
|
||||
# Attempt to auto-config
|
||||
if(UNIX OR MINGW)
|
||||
if(HAS_BACKTRACE AND NOT MINGW) # not working on mingw at the moment
|
||||
set(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE On)
|
||||
message(STATUS "Cpptrace auto config: Using libbacktrace for symbols")
|
||||
elseif(HAS_ADDR2LINE)
|
||||
set(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE On)
|
||||
message(STATUS "Cpptrace auto config: Using addr2line for symbols")
|
||||
elseif(HAS_DL)
|
||||
set(CPPTRACE_GET_SYMBOLS_WITH_LIBDL On)
|
||||
message(STATUS "Cpptrace auto config: Using libdl for symbols")
|
||||
else()
|
||||
message(FATAL_ERROR "Cpptrace auto config: No symbol back-end could be automatically configured. To compile anyway set CPPTRACE_GET_SYMBOLS_WITH_NOTHING.")
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
set(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP On)
|
||||
message(STATUS "Cpptrace auto config: Using libdwarf for symbols")
|
||||
set(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF On)
|
||||
else()
|
||||
message(STATUS "Cpptrace auto config: Using dbghelp for symbols")
|
||||
set(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP On)
|
||||
endif()
|
||||
else()
|
||||
#message(STATUS "MANUAL CONFIG SPECIFIED")
|
||||
endif()
|
||||
|
||||
# =============================================== Autoconfig demangling ===============================================
|
||||
@ -233,6 +270,7 @@ set(
|
||||
src/symbols/symbols_with_dbghelp.cpp
|
||||
src/symbols/symbols_with_dl.cpp
|
||||
src/symbols/symbols_with_libbacktrace.cpp
|
||||
src/symbols/symbols_with_libdwarf.cpp
|
||||
src/symbols/symbols_with_nothing.cpp
|
||||
src/unwind/unwind_with_execinfo.cpp
|
||||
src/unwind/unwind_with_nothing.cpp
|
||||
@ -240,12 +278,7 @@ set(
|
||||
src/unwind/unwind_with_winapi.cpp
|
||||
)
|
||||
|
||||
# TODO: This feels like a hack.
|
||||
if(CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE)
|
||||
add_library(cpptrace SHARED ${sources} include/cpptrace/cpptrace.hpp)
|
||||
else()
|
||||
add_library(cpptrace ${sources} include/cpptrace/cpptrace.hpp)
|
||||
endif()
|
||||
add_library(cpptrace SHARED ${sources} include/cpptrace/cpptrace.hpp)
|
||||
|
||||
target_include_directories(
|
||||
cpptrace
|
||||
@ -267,7 +300,8 @@ set_target_properties(
|
||||
target_compile_options(
|
||||
cpptrace
|
||||
PRIVATE
|
||||
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Werror=return-type -Wshadow -Wundef>
|
||||
# -Wshadow
|
||||
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Werror=return-type -Wundef>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Wuseless-cast -Wnonnull-compare>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX /permissive->
|
||||
)
|
||||
@ -335,6 +369,29 @@ if(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF)
|
||||
target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF)
|
||||
if(CPPTRACE_USE_SYSTEM_LIBDWARF)
|
||||
find_package(libdwarf REQUIRED)
|
||||
target_link_libraries(cpptrace PRIVATE libdwarf::libdwarf)
|
||||
# TODO: Temp hack
|
||||
#target_include_directories(cpptrace PRIVATE "${CMAKE_INSTALL_INCLUDEDIR}/libdwarf")
|
||||
target_include_directories(cpptrace PRIVATE "/usr/local/include/libdwarf/")
|
||||
else()
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
libdwarf
|
||||
GIT_REPOSITORY https://github.com/jeremy-rifkin/libdwarf-code.git
|
||||
GIT_TAG b07d7201fd38a7dd8e6914ec4da120ef60dde262
|
||||
)
|
||||
FetchContent_MakeAvailable(libdwarf)
|
||||
target_link_libraries(cpptrace PRIVATE libdwarf)
|
||||
endif()
|
||||
if(UNIX)
|
||||
target_link_libraries(cpptrace PRIVATE dl)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP)
|
||||
target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_DBGHELP)
|
||||
target_link_libraries(cpptrace PRIVATE dbghelp)
|
||||
@ -389,10 +446,6 @@ endif()
|
||||
|
||||
# ======================================================================================================================
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "")
|
||||
message(FATAL_ERROR "Setting CMAKE_BUILD_TYPE is required")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_SKIP_INSTALL_RULES)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from colorama import Fore, Back, Style
|
||||
from pathlib import Path
|
||||
|
||||
from util import *
|
||||
|
||||
@ -30,13 +31,19 @@ def run_command(*args: List[str]):
|
||||
print(f"{Fore.GREEN}{Style.BRIGHT}Command succeeded{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
#def touch_sources():
|
||||
# for root, dirs, files in os.walk("../src"):
|
||||
# for filename in files:
|
||||
# Path(os.path.join(root, filename)).touch()
|
||||
|
||||
def build(matrix):
|
||||
#touch_sources()
|
||||
print(f"{Fore.BLUE}{Style.BRIGHT}{'=' * 10} Running build with config {', '.join(matrix.values())} {'=' * 10}{Style.RESET_ALL}")
|
||||
|
||||
if os.path.exists("build"):
|
||||
shutil.rmtree("build")
|
||||
shutil.rmtree("build", ignore_errors=True)
|
||||
|
||||
os.mkdir("build")
|
||||
os.makedirs("build", exist_ok=True)
|
||||
os.chdir("build")
|
||||
|
||||
if platform.system() != "Windows":
|
||||
@ -49,10 +56,11 @@ def build(matrix):
|
||||
f"-D{matrix['unwind']}=On",
|
||||
f"-D{matrix['symbols']}=On",
|
||||
f"-D{matrix['demangle']}=On",
|
||||
"-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h"
|
||||
"-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h",
|
||||
"-DCPPTRACE_USE_SYSTEM_LIBDWARF=On"
|
||||
)
|
||||
if succeeded:
|
||||
run_command("make", "-j")
|
||||
run_command("make", "-j", "VERBOSE=1")
|
||||
else:
|
||||
args = [
|
||||
"cmake",
|
||||
@ -62,14 +70,14 @@ def build(matrix):
|
||||
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
||||
f"-D{matrix['unwind']}=On",
|
||||
f"-D{matrix['symbols']}=On",
|
||||
f"-D{matrix['demangle']}=On"
|
||||
f"-D{matrix['demangle']}=On",
|
||||
]
|
||||
if matrix["compiler"] == "g++":
|
||||
args.append("-GUnix Makefiles")
|
||||
succeeded = run_command(*args)
|
||||
if succeeded:
|
||||
if matrix["compiler"] == "g++":
|
||||
run_command("make", "-j")
|
||||
run_command("make", "-j", "VERBOSE=1")
|
||||
else:
|
||||
run_command("msbuild", "cpptrace.sln")
|
||||
|
||||
@ -77,12 +85,13 @@ def build(matrix):
|
||||
print()
|
||||
|
||||
def build_full_or_auto(matrix):
|
||||
#touch_sources()
|
||||
print(f"{Fore.BLUE}{Style.BRIGHT}{'=' * 10} Running build with config {'<auto>' if matrix['config'] == '' else ', '.join(matrix.values())} {'=' * 10}{Style.RESET_ALL}")
|
||||
|
||||
if os.path.exists("build"):
|
||||
shutil.rmtree("build")
|
||||
shutil.rmtree("build", ignore_errors=True)
|
||||
|
||||
os.mkdir("build")
|
||||
os.makedirs("build", exist_ok=True)
|
||||
os.chdir("build")
|
||||
|
||||
if platform.system() != "Windows":
|
||||
@ -93,6 +102,7 @@ def build_full_or_auto(matrix):
|
||||
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
||||
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
||||
f"-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h",
|
||||
"-DCPPTRACE_USE_SYSTEM_LIBDWARF=On"
|
||||
]
|
||||
if matrix["config"] != "":
|
||||
args.append(f"{matrix['config']}")
|
||||
@ -140,6 +150,7 @@ def main():
|
||||
"symbols": [
|
||||
"CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE",
|
||||
"CPPTRACE_GET_SYMBOLS_WITH_LIBDL",
|
||||
"CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF"
|
||||
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
||||
"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
||||
],
|
||||
@ -171,6 +182,7 @@ def main():
|
||||
"symbols": [
|
||||
#"CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE",
|
||||
"CPPTRACE_GET_SYMBOLS_WITH_LIBDL",
|
||||
"CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",
|
||||
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
||||
"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
||||
],
|
||||
@ -223,6 +235,7 @@ def main():
|
||||
],
|
||||
"symbols": [
|
||||
"CPPTRACE_GET_SYMBOLS_WITH_DBGHELP",
|
||||
"CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",
|
||||
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
||||
"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
||||
],
|
||||
@ -252,6 +265,14 @@ def main():
|
||||
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
||||
"compiler": "clang++"
|
||||
},
|
||||
{
|
||||
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",
|
||||
"compiler": "cl"
|
||||
},
|
||||
{
|
||||
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",
|
||||
"compiler": "clang++"
|
||||
},
|
||||
{
|
||||
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_DBGHELP",
|
||||
"compiler": "g++"
|
||||
|
||||
@ -15,7 +15,7 @@ def main():
|
||||
clang = any(["clang" in arg for arg in sys.argv[1:]])
|
||||
# Somehow -gdwarf-4 clang is fast after a completely unrelated PR? Weird. Something to do with static linking...?
|
||||
# https://github.com/jeremy-rifkin/cpptrace/pull/22
|
||||
expect_slow = dwarf4 and not clang
|
||||
expect_slow = False
|
||||
|
||||
threshold = 100 # ms
|
||||
|
||||
|
||||
@ -5,6 +5,12 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
#define CPPTRACE_API __declspec(dllexport)
|
||||
#else
|
||||
#define CPPTRACE_API
|
||||
#endif
|
||||
|
||||
namespace cpptrace {
|
||||
struct stacktrace_frame {
|
||||
uintptr_t address;
|
||||
@ -13,8 +19,8 @@ namespace cpptrace {
|
||||
std::string filename;
|
||||
std::string symbol;
|
||||
};
|
||||
std::vector<stacktrace_frame> generate_trace(std::uint32_t skip = 0);
|
||||
void print_trace(std::uint32_t skip = 0);
|
||||
CPPTRACE_API std::vector<stacktrace_frame> generate_trace(std::uint32_t skip = 0);
|
||||
CPPTRACE_API void print_trace(std::uint32_t skip = 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
#include "platform/common.hpp"
|
||||
|
||||
namespace cpptrace {
|
||||
CPPTRACE_FORCE_NO_INLINE
|
||||
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
|
||||
std::vector<stacktrace_frame> generate_trace(std::uint32_t skip) {
|
||||
std::vector<void*> frames = detail::capture_frames(skip + 1);
|
||||
detail::symbolizer symbolizer;
|
||||
@ -35,7 +35,7 @@ namespace cpptrace {
|
||||
#include "demangle/demangle.hpp"
|
||||
|
||||
namespace cpptrace {
|
||||
CPPTRACE_FORCE_NO_INLINE
|
||||
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
|
||||
std::vector<stacktrace_frame> generate_trace(std::uint32_t skip) {
|
||||
auto trace = detail::generate_trace(skip + 1);
|
||||
for(auto& entry : trace) {
|
||||
@ -57,6 +57,7 @@ namespace cpptrace {
|
||||
#define CYAN ESC "36m"
|
||||
|
||||
namespace cpptrace {
|
||||
CPPTRACE_API
|
||||
void print_trace(std::uint32_t skip) {
|
||||
enable_virtual_terminal_processing_if_needed();
|
||||
std::cerr<<"Stack trace (most recent call first):"<<std::endl;
|
||||
|
||||
154
src/platform/object.hpp
Normal file
154
src/platform/object.hpp
Normal file
@ -0,0 +1,154 @@
|
||||
#ifndef OBJECT_HPP
|
||||
#define OBJECT_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#if IS_LINUX || IS_APPLE
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#if IS_APPLE
|
||||
#include "mach-o.hpp"
|
||||
#else
|
||||
#include "elf.hpp"
|
||||
#endif
|
||||
#elif IS_WINDOWS
|
||||
#include <windows.h>
|
||||
#include "pe.hpp"
|
||||
#endif
|
||||
|
||||
struct dlframe {
|
||||
std::string obj_path;
|
||||
std::string symbol;
|
||||
uintptr_t raw_address = 0;
|
||||
uintptr_t obj_address = 0;
|
||||
};
|
||||
|
||||
#if IS_LINUX || IS_APPLE
|
||||
#if !IS_APPLE
|
||||
static uintptr_t get_module_image_base(const std::string& obj_path) {
|
||||
static std::mutex mutex;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
static std::unordered_map<std::string, uintptr_t> cache;
|
||||
auto it = cache.find(obj_path);
|
||||
if(it == cache.end()) {
|
||||
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not
|
||||
// have two threads try to do the same computation
|
||||
auto base = elf_get_module_image_base(obj_path);
|
||||
cache.insert(it, {obj_path, base});
|
||||
return base;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static uintptr_t get_module_image_base(const std::string& obj_path) {
|
||||
// We have to parse the Mach-O to find the offset of the text section.....
|
||||
// I don't know how addresses are handled if there is more than one __TEXT load command. I'm assuming for
|
||||
// now that there is only one, and I'm using only the first section entry within that load command.
|
||||
static std::mutex mutex;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
static std::unordered_map<std::string, uintptr_t> cache;
|
||||
auto it = cache.find(obj_path);
|
||||
if(it == cache.end()) {
|
||||
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not
|
||||
// have two threads try to do the same computation
|
||||
auto base = macho_get_text_vmaddr(obj_path.c_str());
|
||||
cache.insert(it, {obj_path, base});
|
||||
return base;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
|
||||
static std::vector<dlframe> get_frames_object_info(const std::vector<void*>& addrs) {
|
||||
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
|
||||
std::vector<dlframe> frames;
|
||||
frames.reserve(addrs.size());
|
||||
for(const void* addr : addrs) {
|
||||
Dl_info info;
|
||||
dlframe frame;
|
||||
frame.raw_address = reinterpret_cast<uintptr_t>(addr);
|
||||
if(dladdr(addr, &info)) { // thread safe
|
||||
// dli_sname and dli_saddr are only present with -rdynamic, sname will be included
|
||||
// but we don't really need dli_saddr
|
||||
frame.obj_path = info.dli_fname;
|
||||
frame.obj_address = reinterpret_cast<uintptr_t>(addr)
|
||||
- reinterpret_cast<uintptr_t>(info.dli_fbase)
|
||||
+ get_module_image_base(info.dli_fname);
|
||||
frame.symbol = info.dli_sname ?: "";
|
||||
}
|
||||
frames.push_back(frame);
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
#else
|
||||
static std::string get_module_name(HMODULE handle) {
|
||||
static std::mutex mutex;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
static std::unordered_map<HMODULE, std::string> cache;
|
||||
auto it = cache.find(handle);
|
||||
if(it == cache.end()) {
|
||||
char path[MAX_PATH];
|
||||
if(GetModuleFileNameA(handle, path, sizeof(path))) {
|
||||
///fprintf(stderr, "path: %s base: %p\n", path, handle);
|
||||
cache.insert(it, {handle, path});
|
||||
return path;
|
||||
} else {
|
||||
fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
|
||||
cache.insert(it, {handle, ""});
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
static uintptr_t get_module_image_base(const std::string& obj_path) {
|
||||
static std::mutex mutex;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
static std::unordered_map<std::string, uintptr_t> cache;
|
||||
auto it = cache.find(obj_path);
|
||||
if(it == cache.end()) {
|
||||
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not
|
||||
// have two threads try to do the same computation
|
||||
auto base = pe_get_module_image_base(obj_path);
|
||||
cache.insert(it, {obj_path, base});
|
||||
return base;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
|
||||
static std::vector<dlframe> get_frames_object_info(const std::vector<void*>& addrs) {
|
||||
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
|
||||
std::vector<dlframe> frames;
|
||||
frames.reserve(addrs.size());
|
||||
for(const void* addr : addrs) {
|
||||
dlframe frame;
|
||||
frame.raw_address = reinterpret_cast<uintptr_t>(addr);
|
||||
HMODULE handle;
|
||||
// Multithread safe as long as another thread doesn't come along and free the module
|
||||
if(GetModuleHandleExA(
|
||||
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
|
||||
static_cast<const char*>(addr),
|
||||
&handle
|
||||
)) {
|
||||
frame.obj_path = get_module_name(handle);
|
||||
frame.obj_address = reinterpret_cast<uintptr_t>(addr)
|
||||
- reinterpret_cast<uintptr_t>(handle)
|
||||
+ get_module_image_base(frame.obj_path);
|
||||
} else {
|
||||
fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
|
||||
}
|
||||
frames.push_back(frame);
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -23,9 +23,11 @@ T pe_byteswap_if_needed(T value) {
|
||||
}
|
||||
|
||||
static uintptr_t pe_get_module_image_base(const std::string& obj_path) {
|
||||
FILE* file = fopen(obj_path.c_str(), "rb");
|
||||
if(file == nullptr) {
|
||||
FILE* file;
|
||||
errno_t ret = fopen_s(&file, obj_path.c_str(), "rb");
|
||||
if(ret != 0 || file == nullptr) {
|
||||
throw file_error();
|
||||
return 0;
|
||||
}
|
||||
auto magic = load_bytes<std::array<char, 2>>(file, 0);
|
||||
internal_verify(memcmp(magic.data(), "MZ", 2) == 0);
|
||||
|
||||
@ -15,50 +15,16 @@
|
||||
|
||||
#if IS_LINUX || IS_APPLE
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
// NOLINTNEXTLINE(misc-include-cleaner)
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#if IS_APPLE
|
||||
#include "../platform/mach-o.hpp"
|
||||
#else
|
||||
#include "../platform/elf.hpp"
|
||||
#endif
|
||||
#elif IS_WINDOWS
|
||||
#include "../platform/pe.hpp"
|
||||
#endif
|
||||
|
||||
#include "../platform/object.hpp"
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
struct dlframe {
|
||||
std::string obj_path;
|
||||
std::string symbol;
|
||||
uintptr_t obj_base = 0;
|
||||
uintptr_t raw_address = 0;
|
||||
};
|
||||
|
||||
#if IS_LINUX || IS_APPLE
|
||||
// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
|
||||
std::vector<dlframe> backtrace_frames(const std::vector<void*>& addrs) {
|
||||
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
|
||||
std::vector<dlframe> frames;
|
||||
frames.reserve(addrs.size());
|
||||
for(const void* addr : addrs) {
|
||||
Dl_info info;
|
||||
dlframe frame;
|
||||
frame.raw_address = reinterpret_cast<uintptr_t>(addr);
|
||||
if(dladdr(addr, &info)) { // thread safe
|
||||
// dli_sname and dli_saddr are only present with -rdynamic, sname will be included
|
||||
// but we don't really need dli_saddr
|
||||
frame.obj_path = info.dli_fname;
|
||||
frame.obj_base = reinterpret_cast<uintptr_t>(info.dli_fbase);
|
||||
frame.symbol = info.dli_sname ?: "";
|
||||
}
|
||||
frames.push_back(frame);
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
bool has_addr2line() {
|
||||
static std::mutex mutex;
|
||||
static bool has_addr2line = false;
|
||||
@ -165,77 +131,7 @@ namespace cpptrace {
|
||||
waitpid(pid, nullptr, 0);
|
||||
return output;
|
||||
}
|
||||
|
||||
#if !IS_APPLE
|
||||
uintptr_t get_module_image_base(const dlframe &entry) {
|
||||
return elf_get_module_image_base(entry.obj_path);
|
||||
}
|
||||
#else
|
||||
uintptr_t get_module_image_base(const dlframe &entry) {
|
||||
// We have to parse the Mach-O to find the offset of the text section.....
|
||||
// I don't know how addresses are handled if there is more than one __TEXT load command. I'm assuming for
|
||||
// now that there is only one, and I'm using only the first section entry within that load command.
|
||||
static std::mutex mutex;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
static std::unordered_map<std::string, uintptr_t> cache;
|
||||
auto it = cache.find(entry.obj_path);
|
||||
if(it == cache.end()) {
|
||||
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not
|
||||
// have two threads try to do the same computation
|
||||
auto base = macho_get_text_vmaddr(entry.obj_path.c_str());
|
||||
cache.insert(it, {entry.obj_path, base});
|
||||
return base;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#elif IS_WINDOWS
|
||||
std::string get_module_name(HMODULE handle) {
|
||||
static std::mutex mutex;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
static std::unordered_map<HMODULE, std::string> cache;
|
||||
auto it = cache.find(handle);
|
||||
if(it == cache.end()) {
|
||||
char path[MAX_PATH];
|
||||
if(GetModuleFileNameA(handle, path, sizeof(path))) {
|
||||
///fprintf(stderr, "path: %s base: %p\n", path, handle);
|
||||
cache.insert(it, {handle, path});
|
||||
return path;
|
||||
} else {
|
||||
fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
|
||||
cache.insert(it, {handle, ""});
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
|
||||
std::vector<dlframe> backtrace_frames(const std::vector<void*>& addrs) {
|
||||
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
|
||||
std::vector<dlframe> frames;
|
||||
frames.reserve(addrs.size());
|
||||
for(const void* addr : addrs) {
|
||||
dlframe frame;
|
||||
frame.raw_address = reinterpret_cast<uintptr_t>(addr);
|
||||
HMODULE handle;
|
||||
// Multithread safe as long as another thread doesn't come along and free the module
|
||||
if(GetModuleHandleExA(
|
||||
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
|
||||
static_cast<const char*>(addr),
|
||||
&handle
|
||||
)) {
|
||||
frame.obj_base = reinterpret_cast<uintptr_t>(handle);
|
||||
frame.obj_path = get_module_name(handle);
|
||||
} else {
|
||||
fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
|
||||
}
|
||||
frames.push_back(frame);
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
bool has_addr2line() {
|
||||
static std::mutex mutex;
|
||||
static bool has_addr2line = false;
|
||||
@ -284,22 +180,6 @@ namespace cpptrace {
|
||||
///fprintf(stderr, "%s\n", output.c_str());
|
||||
return output;
|
||||
}
|
||||
|
||||
uintptr_t get_module_image_base(const dlframe &entry) {
|
||||
static std::mutex mutex;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
static std::unordered_map<std::string, uintptr_t> cache;
|
||||
auto it = cache.find(entry.obj_path);
|
||||
if(it == cache.end()) {
|
||||
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not
|
||||
// have two threads try to do the same computation
|
||||
auto base = pe_get_module_image_base(entry.obj_path);
|
||||
cache.insert(it, {entry.obj_path, base});
|
||||
return base;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct symbolizer::impl {
|
||||
@ -319,7 +199,7 @@ namespace cpptrace {
|
||||
///fprintf(stderr, "%s %s\n", to_hex(entry.raw_address).c_str(), to_hex(entry.raw_address - entry.obj_base + base).c_str());
|
||||
try {
|
||||
entries[entry.obj_path].emplace_back(
|
||||
to_hex(entry.raw_address - entry.obj_base + get_module_image_base(entry)),
|
||||
to_hex(entry.obj_address),
|
||||
trace[i]
|
||||
);
|
||||
} catch(file_error&) {
|
||||
@ -420,7 +300,7 @@ namespace cpptrace {
|
||||
trace[i].address = reinterpret_cast<uintptr_t>(frames[i]);
|
||||
}
|
||||
if(has_addr2line()) {
|
||||
const std::vector<dlframe> dlframes = backtrace_frames(frames);
|
||||
const std::vector<dlframe> dlframes = get_frames_object_info(frames);
|
||||
const auto entries = get_addr2line_targets(dlframes, trace);
|
||||
for(const auto& entry : entries) {
|
||||
const auto& object_name = entry.first;
|
||||
|
||||
894
src/symbols/symbols_with_libdwarf.cpp
Normal file
894
src/symbols/symbols_with_libdwarf.cpp
Normal file
@ -0,0 +1,894 @@
|
||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
#include "symbols.hpp"
|
||||
#include "../platform/program_name.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <libdwarf.h>
|
||||
#include <dwarf.h>
|
||||
|
||||
#include "../platform/object.hpp"
|
||||
|
||||
// some stuff is based on https://github.com/davea42/libdwarf-addr2line/blob/master/addr2line.c, mainly line handling
|
||||
// then much expanded for symbols and efficiency
|
||||
// dwarf5_ranges and dwarf4_ranges utility functions are taken from there directly, also pc_in_die
|
||||
|
||||
// TODO
|
||||
// Inlined calls
|
||||
// Memoizing / lazy loading
|
||||
// More utils to clean this up, some wrapper for unique_ptr
|
||||
// Ensure memory is being cleaned up properly
|
||||
// Efficiency tricks
|
||||
// Implementation cleanup
|
||||
// Properly get the image base
|
||||
|
||||
#define DW_PR_DUx "llx"
|
||||
#define DW_PR_DUu "llu"
|
||||
|
||||
static int dwarf5_ranges(Dwarf_Die cu_die, Dwarf_Addr *lowest, Dwarf_Addr *highest) {
|
||||
Dwarf_Unsigned offset = 0;
|
||||
Dwarf_Attribute attr = 0;
|
||||
Dwarf_Half attrform = 0;
|
||||
Dwarf_Unsigned i = 0;
|
||||
int res = 0;
|
||||
|
||||
res = dwarf_attr(cu_die, DW_AT_ranges, &attr, nullptr);
|
||||
if(res != DW_DLV_OK) {
|
||||
return res;
|
||||
}
|
||||
if(dwarf_global_formref(attr, &offset, nullptr) == DW_DLV_OK) {
|
||||
Dwarf_Unsigned rlesetoffset = 0;
|
||||
Dwarf_Unsigned rnglists_count = 0;
|
||||
Dwarf_Rnglists_Head head = 0;
|
||||
|
||||
dwarf_whatform(attr, &attrform, nullptr);
|
||||
/* offset is in .debug_rnglists */
|
||||
res = dwarf_rnglists_get_rle_head(
|
||||
attr,
|
||||
attrform,offset,
|
||||
&head,
|
||||
&rnglists_count,
|
||||
&rlesetoffset,
|
||||
nullptr
|
||||
);
|
||||
if(res != DW_DLV_OK) {
|
||||
/* ASSERT: is DW_DLV_NO_ENTRY */
|
||||
dwarf_dealloc_attribute(attr);
|
||||
return res;
|
||||
}
|
||||
for( ; i < rnglists_count; ++i) {
|
||||
unsigned entrylen = 0;
|
||||
unsigned rle_val = 0;
|
||||
Dwarf_Unsigned raw1 = 0;
|
||||
Dwarf_Unsigned raw2 = 0;
|
||||
Dwarf_Bool unavail = 0;
|
||||
Dwarf_Unsigned cooked1 = 0;
|
||||
Dwarf_Unsigned cooked2 = 0;
|
||||
|
||||
res = dwarf_get_rnglists_entry_fields_a(
|
||||
head,
|
||||
i,
|
||||
&entrylen,
|
||||
&rle_val,
|
||||
&raw1,
|
||||
&raw2,
|
||||
&unavail,
|
||||
&cooked1,
|
||||
&cooked2,
|
||||
nullptr
|
||||
);
|
||||
if(res != DW_DLV_OK) {
|
||||
/* ASSERT: is DW_DLV_NO_ENTRY */
|
||||
continue;
|
||||
}
|
||||
if(unavail) {
|
||||
continue;
|
||||
}
|
||||
switch(rle_val) {
|
||||
case DW_RLE_end_of_list:
|
||||
case DW_RLE_base_address:
|
||||
case DW_RLE_base_addressx:
|
||||
/* These are accounted for already */
|
||||
break;
|
||||
case DW_RLE_offset_pair:
|
||||
case DW_RLE_startx_endx:
|
||||
case DW_RLE_start_end:
|
||||
case DW_RLE_startx_length:
|
||||
case DW_RLE_start_length:
|
||||
if(cooked1 < *lowest) {
|
||||
*lowest = cooked1;
|
||||
}
|
||||
if(cooked2 > *highest) {
|
||||
*highest = cooked2;
|
||||
}
|
||||
default:
|
||||
/* Something is wrong. */
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
dwarf_dealloc_rnglists_head(head);
|
||||
}
|
||||
dwarf_dealloc_attribute(attr);
|
||||
return DW_DLV_OK;
|
||||
}
|
||||
|
||||
static int dwarf4_ranges(
|
||||
Dwarf_Debug dbg,
|
||||
Dwarf_Die cu_die,
|
||||
Dwarf_Addr cu_lowpc,
|
||||
Dwarf_Addr *lowest,
|
||||
Dwarf_Addr *highest
|
||||
) {
|
||||
Dwarf_Unsigned offset;
|
||||
Dwarf_Attribute attr = 0;
|
||||
int res = 0;
|
||||
|
||||
res = dwarf_attr(cu_die, DW_AT_ranges, &attr, nullptr);
|
||||
if(res != DW_DLV_OK) {
|
||||
return res;
|
||||
}
|
||||
if(dwarf_global_formref(attr, &offset, nullptr) == DW_DLV_OK) {
|
||||
Dwarf_Signed count = 0;
|
||||
Dwarf_Ranges *ranges = 0;
|
||||
Dwarf_Addr baseaddr = 0;
|
||||
if(cu_lowpc != 0xffffffffffffffff) {
|
||||
baseaddr = cu_lowpc;
|
||||
}
|
||||
res = dwarf_get_ranges_b(
|
||||
dbg,
|
||||
offset,
|
||||
cu_die,
|
||||
nullptr,
|
||||
&ranges,
|
||||
&count,
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
for(int i = 0; i < count; i++) {
|
||||
Dwarf_Ranges *cur = ranges + i;
|
||||
|
||||
if(cur->dwr_type == DW_RANGES_ENTRY) {
|
||||
Dwarf_Addr rng_lowpc, rng_highpc;
|
||||
rng_lowpc = baseaddr + cur->dwr_addr1;
|
||||
rng_highpc = baseaddr + cur->dwr_addr2;
|
||||
if(rng_lowpc < *lowest) {
|
||||
*lowest = rng_lowpc;
|
||||
}
|
||||
if(rng_highpc > *highest) {
|
||||
*highest = rng_highpc;
|
||||
}
|
||||
} else if(cur->dwr_type ==
|
||||
DW_RANGES_ADDRESS_SELECTION) {
|
||||
baseaddr = cur->dwr_addr2;
|
||||
} else { // DW_RANGES_END
|
||||
baseaddr = cu_lowpc;
|
||||
}
|
||||
}
|
||||
dwarf_dealloc_ranges(dbg, ranges, count);
|
||||
}
|
||||
dwarf_dealloc_attribute(attr);
|
||||
return DW_DLV_OK;
|
||||
}
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
// printbugging as we go
|
||||
constexpr bool dump_dwarf = false;
|
||||
|
||||
static void err_handler(Dwarf_Error err, Dwarf_Ptr errarg) {
|
||||
printf("libdwarf error reading %s: %lu %s\n", "xx", (unsigned long)dwarf_errno(err), dwarf_errmsg(err));
|
||||
if(errarg) {
|
||||
printf("Error: errarg is nonnull but it should be null\n");
|
||||
}
|
||||
printf("Giving up");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void print_line(Dwarf_Debug dbg, Dwarf_Line line, Dwarf_Addr pc, stacktrace_frame& frame) {
|
||||
char what[] = "??";
|
||||
char * linesrc = what;
|
||||
Dwarf_Unsigned lineno = 0;
|
||||
|
||||
(void)pc;
|
||||
|
||||
if(line) {
|
||||
/* These never return DW_DLV_NO_ENTRY */
|
||||
dwarf_linesrc(line, &linesrc, nullptr);
|
||||
dwarf_lineno(line, &lineno, nullptr);
|
||||
}
|
||||
if(dump_dwarf) {
|
||||
printf("%s:%" DW_PR_DUu "\n", linesrc, lineno);
|
||||
}
|
||||
frame.line = static_cast<uint_least32_t>(lineno);
|
||||
frame.filename = linesrc;
|
||||
if(line) {
|
||||
dwarf_dealloc(dbg, linesrc, DW_DLA_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
static Dwarf_Bool pc_in_die(Dwarf_Debug dbg, Dwarf_Die die,int version, Dwarf_Addr pc) {
|
||||
int ret;
|
||||
Dwarf_Addr cu_lowpc = 0xffffffffffffffff;
|
||||
Dwarf_Addr cu_highpc = 0;
|
||||
enum Dwarf_Form_Class highpc_cls;
|
||||
Dwarf_Addr lowest = 0xffffffffffffffff;
|
||||
Dwarf_Addr highest = 0;
|
||||
|
||||
ret = dwarf_lowpc(die, &cu_lowpc, nullptr);
|
||||
if(ret == DW_DLV_OK) {
|
||||
if(pc == cu_lowpc) {
|
||||
return true;
|
||||
}
|
||||
ret = dwarf_highpc_b(die, &cu_highpc,
|
||||
nullptr, &highpc_cls, nullptr);
|
||||
if(ret == DW_DLV_OK) {
|
||||
if(highpc_cls == DW_FORM_CLASS_CONSTANT) {
|
||||
cu_highpc += cu_lowpc;
|
||||
}
|
||||
//fprintf(stderr, "low: %llx high: %llx pc: %llx\n", cu_lowpc, cu_highpc, pc);
|
||||
if(pc >= cu_lowpc && pc < cu_highpc) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(version >= 5) {
|
||||
ret = dwarf5_ranges(die,
|
||||
&lowest,&highest);
|
||||
} else {
|
||||
ret = dwarf4_ranges(dbg,die,cu_lowpc,
|
||||
&lowest,&highest);
|
||||
}
|
||||
//fprintf(stderr, "low: %llu high: %llu\n", lowest, highest);
|
||||
if(pc >= lowest && pc < highest) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static_assert(std::is_pointer<Dwarf_Die>::value, "Dwarf_Die not a pointer");
|
||||
static_assert(std::is_pointer<Dwarf_Debug>::value, "Dwarf_Debug not a pointer");
|
||||
|
||||
struct die_object {
|
||||
Dwarf_Debug dbg = nullptr;
|
||||
Dwarf_Die die = nullptr;
|
||||
die_object(Dwarf_Debug dbg, Dwarf_Die die) : dbg(dbg), die(die) {}
|
||||
~die_object() {
|
||||
if(die) {
|
||||
dwarf_dealloc(dbg, die, DW_DLA_DIE);
|
||||
}
|
||||
}
|
||||
die_object(const die_object&) = delete;
|
||||
die_object& operator=(const die_object&) = delete;
|
||||
die_object(die_object&& other) : dbg(other.dbg), die(other.die) {
|
||||
other.die = nullptr;
|
||||
}
|
||||
die_object& operator=(die_object&& other) {
|
||||
dbg = other.dbg;
|
||||
die = other.die;
|
||||
other.die = nullptr;
|
||||
return *this;
|
||||
}
|
||||
die_object get_child() const {
|
||||
Dwarf_Die child = nullptr;
|
||||
int ret = dwarf_child(
|
||||
die,
|
||||
&child,
|
||||
nullptr
|
||||
);
|
||||
if(ret == DW_DLV_OK) {
|
||||
return die_object(dbg, child);
|
||||
} else if(ret == DW_DLV_NO_ENTRY) {
|
||||
return die_object(dbg, 0);
|
||||
} else {
|
||||
fprintf(stderr, "Error\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
die_object get_sibling() const {
|
||||
Dwarf_Die sibling = 0;
|
||||
int ret = dwarf_siblingof_b(dbg, die, true, &sibling, nullptr);
|
||||
if(ret == DW_DLV_OK) {
|
||||
return die_object(dbg, sibling);
|
||||
} else if(ret == DW_DLV_NO_ENTRY) {
|
||||
return die_object(dbg, 0);
|
||||
} else {
|
||||
fprintf(stderr, "Error\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
operator bool() const {
|
||||
return die != nullptr;
|
||||
}
|
||||
Dwarf_Die get() const {
|
||||
return die;
|
||||
}
|
||||
std::string get_name() const {
|
||||
char* name;
|
||||
int ret = dwarf_diename(die, &name, nullptr);
|
||||
std::string str;
|
||||
if(ret != DW_DLV_NO_ENTRY) {
|
||||
str = name;
|
||||
dwarf_dealloc(dbg, name, DW_DLA_STRING);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
Dwarf_Half get_tag() const {
|
||||
Dwarf_Half tag = 0;
|
||||
dwarf_tag(die, &tag, nullptr);
|
||||
return tag;
|
||||
}
|
||||
};
|
||||
|
||||
void walk_die_list(
|
||||
Dwarf_Debug dbg,
|
||||
const die_object& die,
|
||||
std::function<void(Dwarf_Debug, const die_object&)> fn
|
||||
) {
|
||||
fn(dbg, die);
|
||||
die_object current = die.get_sibling();
|
||||
while(true) {
|
||||
if(!current) {
|
||||
if(dump_dwarf) {
|
||||
fprintf(stderr, "End walk_die_list\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
fn(dbg, current);
|
||||
current = current.get_sibling();
|
||||
}
|
||||
}
|
||||
|
||||
void walk_die_list_recursive(
|
||||
Dwarf_Debug dbg,
|
||||
const die_object& die,
|
||||
std::function<void(Dwarf_Debug, const die_object&)> fn
|
||||
) {
|
||||
walk_die_list(
|
||||
dbg,
|
||||
die,
|
||||
[&fn](Dwarf_Debug dbg, const die_object& die) {
|
||||
auto child = die.get_child();
|
||||
if(child) {
|
||||
walk_die_list_recursive(dbg, child, fn);
|
||||
}
|
||||
fn(dbg, die);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
die_object get_type_die(Dwarf_Debug dbg, const die_object& die) {
|
||||
Dwarf_Off type_offset;
|
||||
Dwarf_Bool is_info;
|
||||
int ret = dwarf_dietype_offset(die.get(), &type_offset, &is_info, nullptr);
|
||||
if(ret == DW_DLV_OK) {
|
||||
Dwarf_Die type_die;
|
||||
ret = dwarf_offdie_b(
|
||||
dbg,
|
||||
type_offset,
|
||||
is_info,
|
||||
&type_die,
|
||||
nullptr
|
||||
);
|
||||
if(ret == DW_DLV_OK) {
|
||||
return die_object(dbg, type_die);
|
||||
} else {
|
||||
fprintf(stderr, "Error\n");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "no type offset??\n");
|
||||
}
|
||||
return die_object(dbg, nullptr);
|
||||
}
|
||||
|
||||
bool has_type(Dwarf_Debug dbg, const die_object& die) {
|
||||
Dwarf_Attribute attr;
|
||||
int ret = dwarf_attr(die.get(), DW_AT_type, &attr, nullptr);
|
||||
if(ret == DW_DLV_NO_ENTRY) {
|
||||
return false;
|
||||
} else if(ret == DW_DLV_OK) {
|
||||
dwarf_dealloc(dbg, attr, DW_DLA_ATTR);
|
||||
return true;
|
||||
} else {
|
||||
fprintf(stderr, "Error\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
struct type_result {
|
||||
std::string base;
|
||||
std::string extent;
|
||||
|
||||
std::string get_type() {
|
||||
return base + extent;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: ::*, namespace lookup, arrays
|
||||
// DW_TAG_namespace
|
||||
const char* tag_to_keyword(Dwarf_Half tag) {
|
||||
switch(tag) {
|
||||
case DW_TAG_atomic_type:
|
||||
return "_Atomic";
|
||||
case DW_TAG_const_type:
|
||||
return "const";
|
||||
case DW_TAG_volatile_type:
|
||||
return "volatile";
|
||||
case DW_TAG_restrict_type:
|
||||
return "restrict";
|
||||
default:
|
||||
{
|
||||
const char* tag_name = nullptr;
|
||||
dwarf_get_TAG_name(tag, &tag_name);
|
||||
fprintf(stderr, "tag_to_keyword unknown tag %s\n", tag_name);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
const char* tag_to_ptr_ref(Dwarf_Half tag) {
|
||||
switch(tag) {
|
||||
case DW_TAG_pointer_type:
|
||||
return "*";
|
||||
case DW_TAG_ptr_to_member_type:
|
||||
return "::*"; // TODO
|
||||
case DW_TAG_reference_type:
|
||||
return "&";
|
||||
case DW_TAG_rvalue_reference_type:
|
||||
return "&&";
|
||||
default:
|
||||
{
|
||||
const char* tag_name = nullptr;
|
||||
dwarf_get_TAG_name(tag, &tag_name);
|
||||
fprintf(stderr, "tag_to_ptr_ref unknown tag %s\n", tag_name);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build = "");
|
||||
|
||||
std::string get_array_extents(Dwarf_Debug dbg, const die_object& die) {
|
||||
assert(die.get_tag() == DW_TAG_array_type);
|
||||
std::string extents = "";
|
||||
walk_die_list(dbg, die.get_child(), [&extents](Dwarf_Debug dbg, const die_object& subrange) {
|
||||
if(subrange.get_tag() == DW_TAG_subrange_type) {
|
||||
Dwarf_Attribute attr = 0;
|
||||
int res = 0;
|
||||
res = dwarf_attr(subrange.get(), DW_AT_upper_bound, &attr, nullptr);
|
||||
if(res != DW_DLV_OK) {
|
||||
fprintf(stderr, "Error\n");
|
||||
return;
|
||||
}
|
||||
Dwarf_Half form;
|
||||
res = dwarf_whatform(attr, &form, nullptr);
|
||||
if(res != DW_DLV_OK) {
|
||||
fprintf(stderr, "Error\n");
|
||||
return;
|
||||
}
|
||||
//fprintf(stderr, "form: %d\n", form);
|
||||
Dwarf_Unsigned val;
|
||||
res = dwarf_formudata(attr, &val, nullptr);
|
||||
if(res != DW_DLV_OK) {
|
||||
fprintf(stderr, "Error\n");
|
||||
return;
|
||||
}
|
||||
extents += "[" + std::to_string(val + 1) + "]";
|
||||
dwarf_dealloc_attribute(attr);
|
||||
} else {
|
||||
const char* tag_name = nullptr;
|
||||
dwarf_get_TAG_name(subrange.get_tag(), &tag_name);
|
||||
fprintf(stderr, "unknown tag %s\n", tag_name);
|
||||
}
|
||||
});
|
||||
return extents;
|
||||
}
|
||||
|
||||
std::string get_parameters(Dwarf_Debug dbg, const die_object& die) {
|
||||
assert(die.get_tag() == DW_TAG_subroutine_type);
|
||||
std::vector<std::string> params;
|
||||
walk_die_list(dbg, die.get_child(), [¶ms](Dwarf_Debug dbg, const die_object& die) {
|
||||
if(die.get_tag() == DW_TAG_formal_parameter) {
|
||||
// TODO: Ignore DW_AT_artificial
|
||||
params.push_back(resolve_type(dbg, get_type_die(dbg, die)));
|
||||
}
|
||||
});
|
||||
return "(" + join(params, ", ") + ")";
|
||||
}
|
||||
|
||||
std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build) {
|
||||
switch(auto tag = die.get_tag()) {
|
||||
case DW_TAG_base_type:
|
||||
case DW_TAG_class_type:
|
||||
case DW_TAG_structure_type:
|
||||
case DW_TAG_union_type:
|
||||
case DW_TAG_enumeration_type:
|
||||
return die.get_name() + build;
|
||||
case DW_TAG_typedef:
|
||||
return resolve_type(dbg, get_type_die(dbg, die));
|
||||
//case DW_TAG_subroutine_type:
|
||||
// {
|
||||
// // If there's no DW_AT_type then it's a void
|
||||
// std::vector<std::string> params;
|
||||
// // TODO: Code duplication with retrieve_symbol_for_subprogram?
|
||||
// walk_die_list(dbg, die.get_child(), [¶ms] (Dwarf_Debug dbg, const die_object& die) {
|
||||
// if(die.get_tag() == DW_TAG_formal_parameter) {
|
||||
// // TODO: Ignore DW_AT_artificial
|
||||
// params.push_back(resolve_type(dbg, get_type_die(dbg, die)));
|
||||
// }
|
||||
// });
|
||||
// if(!has_type(dbg, die)) {
|
||||
// return "void" + (build.empty() ? "" : "(" + build + ")") + "(" + join(params, ", ") + ")";
|
||||
// } else {
|
||||
// // resolving return type, building on build
|
||||
// return resolve_type(
|
||||
// dbg, get_type_die(dbg, die),
|
||||
// (build.empty() ? "" : "(" + build + ")")
|
||||
// + "("
|
||||
// + join(params, ", ")
|
||||
// + ")"
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
//case DW_TAG_array_type:
|
||||
// return resolve_type(dbg, get_type_die(dbg, die), (build.empty() ? "" : "(" + build + ")") + "[" + "x" + "]");
|
||||
case DW_TAG_pointer_type:
|
||||
case DW_TAG_reference_type:
|
||||
case DW_TAG_rvalue_reference_type:
|
||||
case DW_TAG_ptr_to_member_type:
|
||||
{
|
||||
const auto child = get_type_die(dbg, die); // AST child, rather than dwarf child
|
||||
const auto child_tag = child.get_tag();
|
||||
switch(child_tag) {
|
||||
case DW_TAG_subroutine_type:
|
||||
if(!has_type(dbg, child)) {
|
||||
return "void(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_parameters(dbg, child);
|
||||
} else {
|
||||
return resolve_type(
|
||||
dbg,
|
||||
get_type_die(dbg, child),
|
||||
"(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_parameters(dbg, child)
|
||||
);
|
||||
}
|
||||
case DW_TAG_array_type:
|
||||
return resolve_type(
|
||||
dbg,
|
||||
get_type_die(dbg, child),
|
||||
"(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_array_extents(dbg, child)
|
||||
);
|
||||
default:
|
||||
if(build.empty()) {
|
||||
return resolve_type(dbg, get_type_die(dbg, die), tag_to_ptr_ref(tag));
|
||||
} else {
|
||||
return resolve_type(
|
||||
dbg,
|
||||
get_type_die(dbg, die),
|
||||
std::string(tag_to_ptr_ref(tag)) + " " + build
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
case DW_TAG_const_type:
|
||||
case DW_TAG_atomic_type:
|
||||
case DW_TAG_volatile_type:
|
||||
case DW_TAG_restrict_type:
|
||||
{
|
||||
const auto child = get_type_die(dbg, die); // AST child, rather than dwarf child
|
||||
const auto child_tag = child.get_tag();
|
||||
switch(child_tag) {
|
||||
case DW_TAG_base_type:
|
||||
case DW_TAG_class_type:
|
||||
case DW_TAG_typedef:
|
||||
return std::string(tag_to_keyword(tag))
|
||||
+ " "
|
||||
+ resolve_type(dbg, get_type_die(dbg, die), build);
|
||||
default:
|
||||
return resolve_type(
|
||||
dbg,
|
||||
get_type_die(dbg, die),
|
||||
std::string(tag_to_keyword(tag)) + " " + build
|
||||
);
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
const char* tag_name = nullptr;
|
||||
dwarf_get_TAG_name(die.get_tag(), &tag_name);
|
||||
fprintf(stderr, "unknown tag %s\n", tag_name);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
return {"<unknown>", "<unknown>"};
|
||||
}
|
||||
|
||||
bool is_mangled_name(const std::string& name) {
|
||||
return name.find("_Z") || name.find("?h@@");
|
||||
}
|
||||
|
||||
void retrieve_symbol_for_subprogram(Dwarf_Debug dbg, const die_object& die, Dwarf_Addr pc, Dwarf_Half dwversion, stacktrace_frame& frame) {
|
||||
assert(die.get_tag() == DW_TAG_subprogram);
|
||||
Dwarf_Attribute attr;
|
||||
int ret = dwarf_attr(die.get(), DW_AT_linkage_name, &attr, nullptr);
|
||||
if(ret != DW_DLV_OK) {
|
||||
ret = dwarf_attr(die.get(), DW_AT_MIPS_linkage_name, &attr, nullptr);
|
||||
}
|
||||
if(ret == DW_DLV_OK) {
|
||||
char* raw_linkage_name;
|
||||
std::string linkage_name;
|
||||
if(dwarf_formstring(attr, &raw_linkage_name, nullptr) == DW_DLV_OK) {
|
||||
linkage_name = raw_linkage_name;
|
||||
if(dump_dwarf) {
|
||||
fprintf(stderr, "name: %s\n", raw_linkage_name);
|
||||
}
|
||||
dwarf_dealloc(dbg, raw_linkage_name, DW_DLA_STRING);
|
||||
}
|
||||
dwarf_dealloc(dbg, attr, DW_DLA_ATTR);
|
||||
if(!linkage_name.empty()) {
|
||||
frame.symbol = linkage_name;
|
||||
}
|
||||
}
|
||||
std::string name = die.get_name();
|
||||
std::vector<std::string> params;
|
||||
auto child = die.get_child();
|
||||
if(child) {
|
||||
walk_die_list_recursive(
|
||||
dbg,
|
||||
child,
|
||||
[pc, dwversion, &frame, ¶ms] (Dwarf_Debug dbg, const die_object& die) {
|
||||
if(die.get_tag() == DW_TAG_formal_parameter) {
|
||||
// TODO: Ignore DW_AT_artificial
|
||||
params.push_back(resolve_type(dbg, get_type_die(dbg, die)));
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
fprintf(stderr, "no child %s\n", name.c_str());
|
||||
}
|
||||
frame.symbol = name + "(" + join(params, ", ") + ")";
|
||||
}
|
||||
|
||||
void retrieve_symbol(Dwarf_Debug dbg, const die_object& die, Dwarf_Addr pc, Dwarf_Half dwversion, stacktrace_frame& frame) {
|
||||
walk_die_list(
|
||||
dbg,
|
||||
die,
|
||||
[pc, dwversion, &frame] (Dwarf_Debug dbg, const die_object& die) {
|
||||
int ret;
|
||||
if(dump_dwarf) {
|
||||
const char* tag_name;
|
||||
dwarf_get_TAG_name(die.get_tag(), &tag_name);
|
||||
fprintf(
|
||||
stderr,
|
||||
"-------------> %d %s %s\n",
|
||||
dwversion,
|
||||
tag_name,
|
||||
die.get_name().c_str()
|
||||
);
|
||||
}
|
||||
|
||||
if(!pc_in_die(dbg, die.get(), dwversion, pc)) {
|
||||
if(dump_dwarf) {
|
||||
fprintf(stderr, "pc not in die\n");
|
||||
}
|
||||
} else {
|
||||
if(dump_dwarf) {
|
||||
fprintf(stderr, "pc in die <-----------------------------------\n");
|
||||
}
|
||||
if(die.get_tag() == DW_TAG_subprogram) {
|
||||
retrieve_symbol_for_subprogram(dbg, die, pc, dwversion, frame);
|
||||
}
|
||||
auto child = die.get_child();
|
||||
if(child) {
|
||||
retrieve_symbol(dbg, child, pc, dwversion, frame);
|
||||
} else {
|
||||
if(dump_dwarf) {
|
||||
fprintf(stderr, "(no child)\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void retrieve_line_info(Dwarf_Debug dbg, const die_object& die, Dwarf_Addr pc, Dwarf_Half dwversion, stacktrace_frame& frame) {
|
||||
Dwarf_Unsigned version;
|
||||
Dwarf_Small table_count;
|
||||
Dwarf_Line_Context ctxt;
|
||||
Dwarf_Bool is_found = false;
|
||||
(void)dwversion;
|
||||
int ret = dwarf_srclines_b(
|
||||
die.get(),
|
||||
&version,
|
||||
&table_count,
|
||||
&ctxt,
|
||||
nullptr
|
||||
);
|
||||
if(ret == DW_DLV_NO_ENTRY) {
|
||||
fprintf(stderr, "dwarf_srclines_b error\n");
|
||||
return;
|
||||
}
|
||||
if(table_count == 1) {
|
||||
Dwarf_Line *linebuf = 0;
|
||||
Dwarf_Signed linecount = 0;
|
||||
Dwarf_Addr prev_lineaddr = 0;
|
||||
|
||||
dwarf_srclines_from_linecontext(ctxt, &linebuf,
|
||||
&linecount, nullptr);
|
||||
Dwarf_Line prev_line = 0;
|
||||
for(int i = 0; i < linecount; i++) {
|
||||
Dwarf_Line line = linebuf[i];
|
||||
Dwarf_Addr lineaddr = 0;
|
||||
|
||||
dwarf_lineaddr(line, &lineaddr, nullptr);
|
||||
if(pc == lineaddr) {
|
||||
/* Print the last line entry containing current pc. */
|
||||
Dwarf_Line last_pc_line = line;
|
||||
|
||||
for(int j = i + 1; j < linecount; j++) {
|
||||
Dwarf_Line j_line = linebuf[j];
|
||||
dwarf_lineaddr(j_line, &lineaddr, nullptr);
|
||||
|
||||
if(pc == lineaddr) {
|
||||
last_pc_line = j_line;
|
||||
}
|
||||
}
|
||||
is_found = true;
|
||||
print_line(dbg, last_pc_line, pc, frame);
|
||||
break;
|
||||
} else if(prev_line && pc > prev_lineaddr &&
|
||||
pc < lineaddr) {
|
||||
is_found = true;
|
||||
print_line(dbg, prev_line, pc, frame);
|
||||
break;
|
||||
}
|
||||
Dwarf_Bool is_lne;
|
||||
dwarf_lineendsequence(line, &is_lne, nullptr);
|
||||
if(is_lne) {
|
||||
prev_line = 0;
|
||||
} else {
|
||||
prev_lineaddr = lineaddr;
|
||||
prev_line = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
dwarf_srclines_dealloc_b(ctxt);
|
||||
}
|
||||
|
||||
void walk_compilation_units(Dwarf_Debug dbg, Dwarf_Addr pc, stacktrace_frame& frame) {
|
||||
// 0 passed as the dieto the first call of dwarf_siblingof_b immediately after dwarf_next_cu_header_d to
|
||||
// fetch the cu die
|
||||
die_object cu_die(dbg, nullptr);
|
||||
cu_die = cu_die.get_sibling();
|
||||
if(!cu_die) {
|
||||
if(dump_dwarf) {
|
||||
fprintf(stderr, "End walk_compilation_units\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
walk_die_list(
|
||||
dbg,
|
||||
cu_die,
|
||||
[&frame, pc] (Dwarf_Debug dbg, const die_object& cu_die) {
|
||||
Dwarf_Half offset_size = 0;
|
||||
Dwarf_Half dwversion = 0;
|
||||
dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size);
|
||||
/*auto child = cu_die.get_child();
|
||||
if(child) {
|
||||
walk_die_list_recursive(
|
||||
dbg,
|
||||
child,
|
||||
[&frame, pc, dwversion] (Dwarf_Debug dbg, const die_object& cu_die) {
|
||||
|
||||
}
|
||||
);
|
||||
}*/
|
||||
//walk_die(dbg, cu_die, pc, dwversion, false, frame);
|
||||
if(pc_in_die(dbg, cu_die.get(), dwversion, pc)) {
|
||||
retrieve_line_info(dbg, cu_die, pc, dwversion, frame);
|
||||
retrieve_symbol(dbg, cu_die, pc, dwversion, frame);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void walk_dbg(Dwarf_Debug dbg, Dwarf_Addr pc, stacktrace_frame& frame) {
|
||||
// libdwarf keeps track of where it is in the file, dwarf_next_cu_header_d is statefull
|
||||
Dwarf_Unsigned next_cu_header;
|
||||
Dwarf_Half header_cu_type;
|
||||
while(true) {
|
||||
int ret = dwarf_next_cu_header_d(
|
||||
dbg,
|
||||
true,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&next_cu_header,
|
||||
&header_cu_type,
|
||||
nullptr
|
||||
);
|
||||
if(ret == DW_DLV_NO_ENTRY) {
|
||||
if(dump_dwarf) {
|
||||
fprintf(stderr, "End walk_dbg\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(ret != DW_DLV_OK) {
|
||||
fprintf(stderr, "Error\n");
|
||||
return;
|
||||
}
|
||||
walk_compilation_units(dbg, pc, frame);
|
||||
}
|
||||
}
|
||||
|
||||
void lookup_pc2(
|
||||
const char* object,
|
||||
Dwarf_Addr pc,
|
||||
stacktrace_frame& frame
|
||||
) {
|
||||
if(dump_dwarf) {
|
||||
fprintf(stderr, "%s\n", object);
|
||||
fprintf(stderr, "%llx\n", pc);
|
||||
}
|
||||
Dwarf_Debug dbg;
|
||||
Dwarf_Ptr errarg = 0;
|
||||
auto ret = dwarf_init_path(object, nullptr, 0,
|
||||
DW_GROUPNUMBER_ANY, err_handler, errarg, &dbg, nullptr);
|
||||
if(ret == DW_DLV_NO_ENTRY) {
|
||||
// fail, no debug info
|
||||
} else if(ret != DW_DLV_OK) {
|
||||
fprintf(stderr, "Error\n");
|
||||
} else {
|
||||
walk_dbg(dbg, pc, frame);
|
||||
}
|
||||
dwarf_finish(dbg);
|
||||
}
|
||||
|
||||
struct symbolizer::impl {
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
stacktrace_frame resolve_frame(const dlframe& frame_info) {
|
||||
stacktrace_frame frame{};
|
||||
frame.filename = frame_info.obj_path;
|
||||
frame.symbol = frame_info.symbol;
|
||||
frame.address = frame_info.raw_address;
|
||||
lookup_pc2(
|
||||
frame_info.obj_path.c_str(),
|
||||
frame_info.obj_address,
|
||||
frame
|
||||
);
|
||||
return frame;
|
||||
}
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-unhandled-exception-at-new)
|
||||
symbolizer::symbolizer() : pimpl{new impl} {}
|
||||
symbolizer::~symbolizer() = default;
|
||||
|
||||
//stacktrace_frame symbolizer::resolve_frame(void* addr) {
|
||||
// return pimpl->resolve_frame(addr);
|
||||
//}
|
||||
|
||||
std::vector<stacktrace_frame> symbolizer::resolve_frames(const std::vector<void*>& frames) {
|
||||
std::vector<stacktrace_frame> trace;
|
||||
trace.reserve(frames.size());
|
||||
for(const auto& frame : get_frames_object_info(frames)) {
|
||||
trace.push_back(pimpl->resolve_frame(frame));
|
||||
}
|
||||
return trace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue
Block a user