Enabled line numbers in stack traces

This commit is contained in:
Alexey Milovidov 2019-07-30 01:26:44 +03:00
parent 15dc6d1818
commit 372c4d89b2
17 changed files with 130 additions and 177 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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())
{

View File

@ -6,7 +6,7 @@
#include <Poco/Exception.h>
#include <common/StackTrace.h>
#include <Common/StackTrace.h>
namespace Poco { class Logger; }

View File

@ -5,7 +5,7 @@
#include <common/Pipe.h>
#include <common/phdr_cache.h>
#include <common/config_common.h>
#include <common/StackTrace.h>
#include <Common/StackTrace.h>
#include <common/StringRef.h>
#include <common/logger_useful.h>
#include <Common/CurrentThread.h>

View File

@ -1,16 +1,16 @@
#include <common/StackTrace.h>
#include <common/SimpleCache.h>
#include <common/demangle.h>
#include <sstream>
#include <cstring>
#include <cxxabi.h>
#include <execinfo.h>
#include <Common/StackTrace.h>
#include <Common/SymbolIndex.h>
#include <Common/Dwarf.h>
#include <Common/Elf.h>
#include <sstream>
#include <filesystem>
#include <unordered_map>
#include <cstring>
#if USE_UNWIND
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#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<void *>(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 "<Empty trace>";
char ** symbols = backtrace_symbols(frames.data(), size);
if (!symbols)
return "<Invalid trace>";
const DB::SymbolIndex & symbol_index = DB::SymbolIndex::instance();
std::unordered_map<std::string, DB::Dwarf> dwarfs;
std::stringstream backtrace;
try
{
for (size_t i = 0; i < size; i++)
{
/// We do "demangling" of names. The name is in parenthesis, before the '+' character.
std::stringstream out;
char * name_start = nullptr;
char * name_end = nullptr;
std::string demangled_name;
for (size_t i = 0; i < size; ++i)
{
out << "#" << i << " " << frames[i] << " ";
auto symbol = symbol_index.findSymbol(frames[i]);
if (symbol)
{
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;
out << demangle(symbol->name, status);
}
else
backtrace << symbols[i];
out << "?";
backtrace << std::endl;
}
}
catch (...)
out << " ";
if (auto object = symbol_index.findObject(frames[i]))
{
free(symbols);
throw;
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();
}

View File

@ -1,6 +1,4 @@
#include <Common/SymbolIndex.h>
#include <Common/Elf.h>
#include <common/demangle.h>
#include <algorithm>
#include <optional>
@ -11,6 +9,9 @@
#include <filesystem>
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<DB::SymbolIndex::Symbol> & symbols)
std::vector<SymbolIndex::Symbol> & 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<const void *>(info->dlpi_addr + elf_sym[sym_index].st_value);
symbol.address_end = reinterpret_cast<const void *>(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<DB::SymbolIndex::Symbol> & symbols)
const Elf & elf,
const Elf::Section & symbol_table,
const Elf::Section & string_table,
std::vector<SymbolIndex::Symbol> & symbols)
{
/// Iterate symbol table.
const ElfSym * symbol_table_entry = reinterpret_cast<const ElfSym *>(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<const void *>(info->dlpi_addr + symbol_table_entry->st_value);
symbol.address_end = reinterpret_cast<const void *>(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<DB::SymbolIndex::Symbol> & symbols)
std::vector<SymbolIndex::Symbol> & symbols)
{
std::optional<DB::Elf::Section> symbol_table;
std::optional<DB::Elf::Section> string_table;
std::optional<Elf::Section> symbol_table;
std::optional<Elf::Section> 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<DB::SymbolIndex::Symbol> & symbols,
std::vector<DB::SymbolIndex::Object> & objects)
std::vector<SymbolIndex::Symbol> & symbols,
std::vector<SymbolIndex::Object> & 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<Elf>(object_name);
object.address_begin = reinterpret_cast<const void *>(info->dlpi_addr);
object.address_end = reinterpret_cast<const void *>(info->dlpi_addr + elf.size());
object.address_end = reinterpret_cast<const void *>(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<DB::SymbolIndex::Data *>(data_ptr);
SymbolIndex::Data & data = *reinterpret_cast<SymbolIndex::Data *>(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<T> & vec)
}
namespace DB
{
void SymbolIndex::update()
{
dl_iterate_phdr(collectSymbols, &data.symbols);

View File

@ -2,6 +2,8 @@
#include <vector>
#include <string>
#include <ext/singleton.h>
#include <Common/Elf.h>
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<SymbolIndex>
{
protected:
friend class ext::singleton<SymbolIndex>;
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> 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();
};
}

View File

@ -3,7 +3,7 @@
#include <Core/Field.h>
#include <Poco/Logger.h>
#include <common/Pipe.h>
#include <common/StackTrace.h>
#include <Common/StackTrace.h>
#include <common/logger_useful.h>
#include <IO/ReadHelpers.h>
#include <IO/ReadBufferFromFileDescriptor.h>

View File

@ -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<void*>(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;
}

View File

@ -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);
}
}

View File

@ -0,0 +1,16 @@
namespace DB
{
class FunctionFactory;
void registerFunctionSymbolizeAddress(FunctionFactory & factory);
void registerFunctionDemangle(FunctionFactory & factory);
void registerFunctionsIntrospection(FunctionFactory & factory)
{
registerFunctionSymbolizeAddress(factory);
registerFunctionDemangle(factory);
}
}

View File

@ -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<ColumnUInt64>(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<const void *>(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();
}

View File

@ -50,7 +50,7 @@
#include <Parsers/ASTCreateQuery.h>
#include <Parsers/ParserCreateQuery.h>
#include <Parsers/parseQuery.h>
#include <common/StackTrace.h>
#include <Common/StackTrace.h>
#include <Common/Config/ConfigProcessor.h>
#include <Common/ZooKeeper/ZooKeeper.h>
#include <Common/ShellCommand.h>

View File

@ -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)

View File

@ -15,7 +15,7 @@
#include <common/logger_useful.h>
#include <common/ErrorHandlers.h>
#include <common/Pipe.h>
#include <common/StackTrace.h>
#include <Common/StackTrace.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <iostream>