diff --git a/CMakeLists.txt b/CMakeLists.txt index a2cc5f15ac8..df711a87a7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,10 @@ endif () if (COMPILER_CLANG) # clang: warning: argument unused during compilation: '-specs=/usr/share/dpkg/no-pie-compile.specs' [-Wunused-command-line-argument] set (COMMON_WARNING_FLAGS "${COMMON_WARNING_FLAGS} -Wno-unused-command-line-argument") + # generate ranges for fast "addr2line" search + if (NOT CMAKE_BUILD_TYPE_UC STREQUAL "RELEASE") + set(COMPILER_FLAGS "${COMPILER_FLAGS} -gdwarf-aranges") + endif () endif () option (ENABLE_TESTS "Enables tests" ON) diff --git a/dbms/CMakeLists.txt b/dbms/CMakeLists.txt index c8056ada4e6..99db33de3f2 100644 --- a/dbms/CMakeLists.txt +++ b/dbms/CMakeLists.txt @@ -159,6 +159,11 @@ if (OS_FREEBSD) target_compile_definitions (clickhouse_common_io PUBLIC CLOCK_MONOTONIC_COARSE=CLOCK_MONOTONIC_FAST) endif () +if (USE_UNWIND) + target_compile_definitions (clickhouse_common_io PRIVATE USE_UNWIND=1) + target_include_directories (clickhouse_common_io SYSTEM BEFORE PRIVATE ${UNWIND_INCLUDE_DIR}) +endif () + add_subdirectory(src/Common/ZooKeeper) add_subdirectory(src/Common/Config) diff --git a/dbms/src/Common/Dwarf.cpp b/dbms/src/Common/Dwarf.cpp index 45a5116642e..798eb08cc52 100644 --- a/dbms/src/Common/Dwarf.cpp +++ b/dbms/src/Common/Dwarf.cpp @@ -183,66 +183,6 @@ void skipPadding(std::string_view & sp, const char * start, size_t alignment) } } -// Simplify a path -- as much as we can while not moving data around... -/*void simplifyPath(std::string_view & sp) -{ - // Strip leading slashes and useless patterns (./), leaving one initial - // slash. - for (;;) - { - if (sp.empty()) - { - return; - } - - // Strip leading slashes, leaving one. - while (sp.startsWith("//")) - { - sp.remove_prefix(1); - } - - if (sp.startsWith("/./")) - { - // Note 2, not 3, to keep it absolute - sp.remove_prefix(2); - continue; - } - - if (sp.removePrefix("./")) - { - // Also remove any subsequent slashes to avoid making this path absolute. - while (sp.startsWith('/')) - { - sp.remove_prefix(1); - } - continue; - } - - break; - } - - // Strip trailing slashes and useless patterns (/.). - for (;;) - { - if (sp.empty()) - { - return; - } - - // Strip trailing slashes, except when this is the root path. - while (sp.size() > 1 && sp.removeSuffix('/')) - { - } - - if (sp.removeSuffix("/.")) - { - continue; - } - - break; - } -}*/ - } @@ -271,10 +211,6 @@ Dwarf::Path::Path(std::string_view baseDir, std::string_view subDir, std::string baseDir_ = {}; // subDir_ is absolute } -// simplifyPath(baseDir_); -// simplifyPath(subDir_); -// simplifyPath(file_); - // Make sure it's never the case that baseDir_ is empty, but subDir_ isn't. if (baseDir_.empty()) { diff --git a/dbms/src/Common/Exception.h b/dbms/src/Common/Exception.h index ee897962228..6b0656f4828 100644 --- a/dbms/src/Common/Exception.h +++ b/dbms/src/Common/Exception.h @@ -6,7 +6,7 @@ #include -#include +#include namespace Poco { class Logger; } diff --git a/dbms/src/Common/QueryProfiler.cpp b/dbms/src/Common/QueryProfiler.cpp index 5aafa35df94..08399a49d2e 100644 --- a/dbms/src/Common/QueryProfiler.cpp +++ b/dbms/src/Common/QueryProfiler.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libs/libcommon/src/StackTrace.cpp b/dbms/src/Common/StackTrace.cpp similarity index 83% rename from libs/libcommon/src/StackTrace.cpp rename to dbms/src/Common/StackTrace.cpp index 8323a737fdf..f842b71d15a 100644 --- a/libs/libcommon/src/StackTrace.cpp +++ b/dbms/src/Common/StackTrace.cpp @@ -1,16 +1,16 @@ -#include #include #include -#include -#include -#include -#include +#include +#include +#include +#include + +#include +#include +#include +#include -#if USE_UNWIND -#define UNW_LOCAL_ONLY -#include -#endif std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext_t & context) { @@ -168,9 +168,9 @@ void * getCallerAddress(const ucontext_t & context) #endif #elif defined(__aarch64__) return reinterpret_cast(context.uc_mcontext.pc); -#endif - +#else return nullptr; +#endif } StackTrace::StackTrace() @@ -195,6 +195,12 @@ StackTrace::StackTrace(NoCapture) { } + +#if USE_UNWIND +extern "C" int unw_backtrace(void **, int); +#endif + + void StackTrace::tryCapture() { size = 0; @@ -227,50 +233,43 @@ std::string StackTrace::toStringImpl(const Frames & frames, size_t size) if (size == 0) return ""; - char ** symbols = backtrace_symbols(frames.data(), size); - if (!symbols) - return ""; + const DB::SymbolIndex & symbol_index = DB::SymbolIndex::instance(); + std::unordered_map dwarfs; - std::stringstream backtrace; - try + std::stringstream out; + + for (size_t i = 0; i < size; ++i) { - for (size_t i = 0; i < size; i++) + out << "#" << i << " " << frames[i] << " "; + auto symbol = symbol_index.findSymbol(frames[i]); + if (symbol) { - /// We do "demangling" of names. The name is in parenthesis, before the '+' character. - - char * name_start = nullptr; - char * name_end = nullptr; - std::string demangled_name; int status = 0; - - if (nullptr != (name_start = strchr(symbols[i], '(')) - && nullptr != (name_end = strchr(name_start, '+'))) - { - ++name_start; - *name_end = '\0'; - demangled_name = demangle(name_start, status); - *name_end = '+'; - } - - backtrace << i << ". "; - - if (0 == status && name_start && name_end) - { - backtrace.write(symbols[i], name_start - symbols[i]); - backtrace << demangled_name << name_end; - } - else - backtrace << symbols[i]; - - backtrace << std::endl; + out << demangle(symbol->name, status); } - } - catch (...) - { - free(symbols); - throw; + else + out << "?"; + + out << " "; + + if (auto object = symbol_index.findObject(frames[i])) + { + if (std::filesystem::exists(object->name)) + { + auto dwarf_it = dwarfs.try_emplace(object->name, *object->elf).first; + + DB::Dwarf::LocationInfo location; + if (dwarf_it->second.findAddress(uintptr_t(object->address_begin) + uintptr_t(frames[i]), location, DB::Dwarf::LocationInfoMode::FAST)) + out << location.file.toString() << ":" << location.line; + else + out << object->name; + } + } + else + out << "?"; + + out << "\n"; } - free(symbols); - return backtrace.str(); + return out.str(); } diff --git a/libs/libcommon/include/common/StackTrace.h b/dbms/src/Common/StackTrace.h similarity index 100% rename from libs/libcommon/include/common/StackTrace.h rename to dbms/src/Common/StackTrace.h diff --git a/dbms/src/Common/SymbolIndex.cpp b/dbms/src/Common/SymbolIndex.cpp index b315abead73..ff04ea35eaa 100644 --- a/dbms/src/Common/SymbolIndex.cpp +++ b/dbms/src/Common/SymbolIndex.cpp @@ -1,6 +1,4 @@ #include -#include -#include #include #include @@ -11,6 +9,9 @@ #include +namespace DB +{ + namespace { @@ -25,7 +26,7 @@ namespace /// Based on the code of musl-libc and the answer of Kanalpiroge on /// https://stackoverflow.com/questions/15779185/list-all-the-functions-symbols-on-the-fly-in-c-code-on-a-linux-architecture void collectSymbolsFromProgramHeaders(dl_phdr_info * info, - std::vector & symbols) + std::vector & symbols) { /* Iterate over all headers of the current shared lib * (first call is for the executable itself) */ @@ -129,13 +130,10 @@ void collectSymbolsFromProgramHeaders(dl_phdr_info * info, if (!sym_name) continue; - DB::SymbolIndex::Symbol symbol; + SymbolIndex::Symbol symbol; symbol.address_begin = reinterpret_cast(info->dlpi_addr + elf_sym[sym_index].st_value); symbol.address_end = reinterpret_cast(info->dlpi_addr + elf_sym[sym_index].st_value + elf_sym[sym_index].st_size); - int unused = 0; - symbol.name = demangle(sym_name, unused); - symbol.object = info->dlpi_name; - + symbol.name = sym_name; symbols.push_back(std::move(symbol)); } @@ -148,10 +146,10 @@ void collectSymbolsFromProgramHeaders(dl_phdr_info * info, void collectSymbolsFromELFSymbolTable( dl_phdr_info * info, - const DB::Elf & elf, - const DB::Elf::Section & symbol_table, - const DB::Elf::Section & string_table, - std::vector & symbols) + const Elf & elf, + const Elf::Section & symbol_table, + const Elf::Section & string_table, + std::vector & symbols) { /// Iterate symbol table. const ElfSym * symbol_table_entry = reinterpret_cast(symbol_table.begin()); @@ -170,13 +168,13 @@ void collectSymbolsFromELFSymbolTable( /// Find the name in strings table. const char * symbol_name = strings + symbol_table_entry->st_name; - DB::SymbolIndex::Symbol symbol; + if (!symbol_name) + continue; + + SymbolIndex::Symbol symbol; symbol.address_begin = reinterpret_cast(info->dlpi_addr + symbol_table_entry->st_value); symbol.address_end = reinterpret_cast(info->dlpi_addr + symbol_table_entry->st_value + symbol_table_entry->st_size); - int unused = 0; - symbol.name = demangle(symbol_name, unused); - symbol.object = info->dlpi_name; - + symbol.name = symbol_name; symbols.push_back(std::move(symbol)); } } @@ -184,15 +182,15 @@ void collectSymbolsFromELFSymbolTable( bool searchAndCollectSymbolsFromELFSymbolTable( dl_phdr_info * info, - const DB::Elf & elf, + const Elf & elf, unsigned section_header_type, const char * string_table_name, - std::vector & symbols) + std::vector & symbols) { - std::optional symbol_table; - std::optional string_table; + std::optional symbol_table; + std::optional string_table; - if (!elf.iterateSections([&](const DB::Elf::Section & section, size_t) + if (!elf.iterateSections([&](const Elf::Section & section, size_t) { if (section.header.sh_type == section_header_type) symbol_table.emplace(section); @@ -213,8 +211,8 @@ bool searchAndCollectSymbolsFromELFSymbolTable( void collectSymbolsFromELF(dl_phdr_info * info, - std::vector & symbols, - std::vector & objects) + std::vector & symbols, + std::vector & objects) { std::string object_name = info->dlpi_name; @@ -230,16 +228,17 @@ void collectSymbolsFromELF(dl_phdr_info * info, if (ec) return; - DB::Elf elf(object_name); - - DB::SymbolIndex::Object object; + SymbolIndex::Object object; + object.elf = std::make_unique(object_name); object.address_begin = reinterpret_cast(info->dlpi_addr); - object.address_end = reinterpret_cast(info->dlpi_addr + elf.size()); + object.address_end = reinterpret_cast(info->dlpi_addr + object.elf->size()); object.name = object_name; objects.push_back(std::move(object)); - searchAndCollectSymbolsFromELFSymbolTable(info, elf, SHT_SYMTAB, ".strtab", symbols); - searchAndCollectSymbolsFromELFSymbolTable(info, elf, SHT_DYNSYM, ".dynstr", symbols); + searchAndCollectSymbolsFromELFSymbolTable(info, *objects.back().elf, SHT_SYMTAB, ".strtab", symbols); + + /// Unneeded because they were parsed from "program headers" of loaded objects. + //searchAndCollectSymbolsFromELFSymbolTable(info, *objects.back().elf, SHT_DYNSYM, ".dynstr", symbols); } @@ -253,7 +252,7 @@ int collectSymbols(dl_phdr_info * info, size_t, void * data_ptr) * (e.g. on a 32 bit system, ElfW(Dyn*) becomes "Elf32_Dyn*") */ - DB::SymbolIndex::Data & data = *reinterpret_cast(data_ptr); + SymbolIndex::Data & data = *reinterpret_cast(data_ptr); collectSymbolsFromProgramHeaders(info, data.symbols); collectSymbolsFromELF(info, data.symbols, data.objects); @@ -285,9 +284,6 @@ const T * find(const void * address, const std::vector & vec) } -namespace DB -{ - void SymbolIndex::update() { dl_iterate_phdr(collectSymbols, &data.symbols); diff --git a/dbms/src/Common/SymbolIndex.h b/dbms/src/Common/SymbolIndex.h index 9d1dceb2c91..41a773f5f4a 100644 --- a/dbms/src/Common/SymbolIndex.h +++ b/dbms/src/Common/SymbolIndex.h @@ -2,6 +2,8 @@ #include #include +#include +#include namespace DB @@ -9,16 +11,20 @@ namespace DB /** Allow to quickly find symbol name from address. * Used as a replacement for "dladdr" function which is extremely slow. + * It works better than "dladdr" because it also allows to search private symbols, that are not participated in shared linking. */ -class SymbolIndex +class SymbolIndex : public ext::singleton { +protected: + friend class ext::singleton; + SymbolIndex() { update(); } + public: struct Symbol { const void * address_begin; const void * address_end; - const char * object; - std::string name; /// demangled NOTE Can use Arena for strings + const char * name; }; struct Object @@ -26,11 +32,9 @@ public: const void * address_begin; const void * address_end; std::string name; + std::unique_ptr elf; }; - SymbolIndex() { update(); } - void update(); - const Symbol * findSymbol(const void * address) const; const Object * findObject(const void * address) const; @@ -44,6 +48,8 @@ public: }; private: Data data; + + void update(); }; } diff --git a/dbms/src/Common/TraceCollector.cpp b/dbms/src/Common/TraceCollector.cpp index e66a580289d..13d2061c810 100644 --- a/dbms/src/Common/TraceCollector.cpp +++ b/dbms/src/Common/TraceCollector.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/dbms/src/Common/tests/symbol_index.cpp b/dbms/src/Common/tests/symbol_index.cpp index 37a044939b7..c6a22b1266b 100644 --- a/dbms/src/Common/tests/symbol_index.cpp +++ b/dbms/src/Common/tests/symbol_index.cpp @@ -18,9 +18,9 @@ int main(int argc, char ** argv) return 1; } - SymbolIndex symbol_index; + const SymbolIndex & symbol_index = SymbolIndex::instance(); - for (const auto & elem : symbol_index.objects()) + for (const auto & elem : symbol_index.symbols()) std::cout << elem.name << ": " << elem.address_begin << " ... " << elem.address_end << "\n"; const void * address = reinterpret_cast(std::stoull(argv[1], nullptr, 16)); @@ -41,10 +41,12 @@ int main(int argc, char ** argv) Dwarf dwarf(elf); Dwarf::LocationInfo location; - if (dwarf.findAddress(uintptr_t(address), location, Dwarf::LocationInfoMode::FULL)) + if (dwarf.findAddress(uintptr_t(address), location, Dwarf::LocationInfoMode::FAST)) std::cerr << location.file.toString() << ":" << location.line << "\n"; else std::cerr << "Dwarf: Not found\n"; + std::cerr << StackTrace().toString() << "\n"; + return 0; } diff --git a/dbms/src/Functions/registerFunctions.cpp b/dbms/src/Functions/registerFunctions.cpp index 178f085e1ad..eba9a96e5e0 100644 --- a/dbms/src/Functions/registerFunctions.cpp +++ b/dbms/src/Functions/registerFunctions.cpp @@ -40,7 +40,7 @@ void registerFunctionsIntrospection(FunctionFactory &); void registerFunctionsNull(FunctionFactory &); void registerFunctionsFindCluster(FunctionFactory &); void registerFunctionsJSON(FunctionFactory &); -void registerFunctionSymbolizeAddress(FunctionFactory &); +void registerFunctionsIntrospection(FunctionFactory &); void registerFunctions() { @@ -79,7 +79,7 @@ void registerFunctions() registerFunctionsNull(factory); registerFunctionsFindCluster(factory); registerFunctionsJSON(factory); - registerFunctionSymbolizeAddress(factory); + registerFunctionsIntrospection(factory); } } diff --git a/dbms/src/Functions/registerFunctionsIntrospection.cpp b/dbms/src/Functions/registerFunctionsIntrospection.cpp new file mode 100644 index 00000000000..0797d21c361 --- /dev/null +++ b/dbms/src/Functions/registerFunctionsIntrospection.cpp @@ -0,0 +1,16 @@ +namespace DB +{ + +class FunctionFactory; + +void registerFunctionSymbolizeAddress(FunctionFactory & factory); +void registerFunctionDemangle(FunctionFactory & factory); + +void registerFunctionsIntrospection(FunctionFactory & factory) +{ + registerFunctionSymbolizeAddress(factory); + registerFunctionDemangle(factory); +} + +} + diff --git a/dbms/src/Functions/symbolizeAddress.cpp b/dbms/src/Functions/symbolizeAddress.cpp index b4fef649814..454fc94b7bf 100644 --- a/dbms/src/Functions/symbolizeAddress.cpp +++ b/dbms/src/Functions/symbolizeAddress.cpp @@ -15,7 +15,6 @@ namespace ErrorCodes { extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_TYPE_OF_ARGUMENT; - extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; } @@ -60,7 +59,7 @@ public: void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override { - static SymbolIndex symbol_index; + const SymbolIndex & symbol_index = SymbolIndex::instance(); const ColumnPtr & column = block.getByPosition(arguments[0]).column; const ColumnUInt64 * column_concrete = checkAndGetColumn(column.get()); @@ -74,7 +73,7 @@ public: for (size_t i = 0; i < input_rows_count; ++i) { if (const auto * symbol = symbol_index.findSymbol(reinterpret_cast(data[i]))) - result_column->insertDataWithTerminatingZero(symbol->name.data(), symbol->name.size() + 1); + result_column->insertDataWithTerminatingZero(symbol->name, strlen(symbol->name) + 1); else result_column->insertDefault(); } diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index cec36f42469..fc2ada171d1 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -50,7 +50,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libs/libcommon/CMakeLists.txt b/libs/libcommon/CMakeLists.txt index 8ebd9bddc8d..ce8c5801613 100644 --- a/libs/libcommon/CMakeLists.txt +++ b/libs/libcommon/CMakeLists.txt @@ -23,12 +23,10 @@ add_library (common src/getThreadNumber.cpp src/sleep.cpp src/argsToConfig.cpp - src/StackTrace.cpp src/Pipe.cpp src/phdr_cache.cpp include/common/SimpleCache.h - include/common/StackTrace.h include/common/Types.h include/common/DayNum.h include/common/DateLUT.h @@ -68,14 +66,6 @@ add_library (common ${CONFIG_COMMON}) -if (USE_UNWIND) - target_compile_definitions (common PRIVATE USE_UNWIND=1) - target_include_directories (common BEFORE PRIVATE ${UNWIND_INCLUDE_DIR}) - if (NOT USE_INTERNAL_UNWIND_LIBRARY_FOR_EXCEPTION_HANDLING) - target_link_libraries (common PRIVATE ${UNWIND_LIBRARY}) - endif () -endif () - # When testing for memory leaks with Valgrind, dont link tcmalloc or jemalloc. if (USE_JEMALLOC) diff --git a/libs/libdaemon/src/BaseDaemon.cpp b/libs/libdaemon/src/BaseDaemon.cpp index aa4993acead..16bcb132d37 100644 --- a/libs/libdaemon/src/BaseDaemon.cpp +++ b/libs/libdaemon/src/BaseDaemon.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include