From d5bc700e961cad84e5370e4482b7b5359f607a80 Mon Sep 17 00:00:00 2001 From: Maosu Zhao Date: Wed, 10 Jul 2024 16:41:51 +0800 Subject: [PATCH 1/3] [DeviceSanitizer] Implement symbolizer for more readable information --- CMakeLists.txt | 14 +++++ source/loader/CMakeLists.txt | 9 +++ source/loader/layers/sanitizer/common.hpp | 4 +- .../layers/sanitizer/linux/backtrace.cpp | 62 ++++++++++++++++++- .../layers/sanitizer/linux/symbolizer.cpp | 60 ++++++++++++++++++ 5 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 source/loader/layers/sanitizer/linux/symbolizer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f05a107881..40a9711a40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,11 @@ cmake_minimum_required(VERSION 3.20.0 FATAL_ERROR) project(unified-runtime VERSION 0.10.0) +# Check if unified runtime is built as a standalone project. +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR OR UR_STANDALONE_BUILD) + set(UR_STANDALONE_BUILD TRUE) +endif() + include(GNUInstallDirs) include(CheckCXXSourceCompiles) include(CMakePackageConfigHelpers) @@ -37,6 +42,7 @@ option(UR_USE_MSAN "enable MemorySanitizer" OFF) option(UR_USE_TSAN "enable ThreadSanitizer" OFF) option(UR_ENABLE_TRACING "enable api tracing through xpti" OFF) option(UR_ENABLE_SANITIZER "enable device sanitizer" ON) +option(UR_ENABLE_SYMBOLIZER "enable Symoblizer for Sanitizer" OFF) option(UMF_BUILD_SHARED_LIBRARY "Build UMF as shared library" ON) option(UMF_ENABLE_POOL_TRACKING "Build UMF with pool tracking" ON) option(UR_BUILD_ADAPTER_L0 "Build the Level-Zero adapter" OFF) @@ -163,6 +169,14 @@ if(UR_ENABLE_SANITIZER) else() add_compile_definitions(UR_ENABLE_SANITIZER) endif() + + if(UR_ENABLE_SYMBOLIZER AND UR_STANDALONE_BUILD) + find_package(LLVM REQUIRED) + endif() +else() + if(UR_ENABLE_SYMBOLIZER) + message(FATAL_ERROR "Symbolizer must be enabled with Sanitizer layer") + endif() endif() if(UR_USE_ASAN) diff --git a/source/loader/CMakeLists.txt b/source/loader/CMakeLists.txt index 35f32a5424..5a7a419954 100644 --- a/source/loader/CMakeLists.txt +++ b/source/loader/CMakeLists.txt @@ -158,6 +158,15 @@ if(UR_ENABLE_SANITIZER) ${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/linux/sanitizer_utils.cpp ) + if(UR_ENABLE_SYMBOLIZER) + target_sources(ur_loader + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/linux/symbolizer.cpp + ) + target_include_directories(ur_loader PRIVATE ${LLVM_INCLUDE_DIRS}) + target_link_libraries(ur_loader PRIVATE LLVMSymbolize) + endif() + target_include_directories(ur_loader PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer" "${CMAKE_CURRENT_SOURCE_DIR}/../" diff --git a/source/loader/layers/sanitizer/common.hpp b/source/loader/layers/sanitizer/common.hpp index 445fedb1d8..f99c2d7f77 100644 --- a/source/loader/layers/sanitizer/common.hpp +++ b/source/loader/layers/sanitizer/common.hpp @@ -132,8 +132,8 @@ using BacktraceInfo = std::string; struct SourceInfo { std::string file; std::string function; - int line; - int column; + int line = 0; + int column = 0; }; enum class DeviceType : uint64_t { UNKNOWN = 0, CPU, GPU_PVC, GPU_DG2 }; diff --git a/source/loader/layers/sanitizer/linux/backtrace.cpp b/source/loader/layers/sanitizer/linux/backtrace.cpp index 87c822d036..befe944762 100644 --- a/source/loader/layers/sanitizer/linux/backtrace.cpp +++ b/source/loader/layers/sanitizer/linux/backtrace.cpp @@ -12,8 +12,50 @@ #include #include +extern "C" { + +__attribute__((weak)) bool SymbolizeCode(const std::string ModuleName, + uint64_t ModuleOffset, + std::string &Result); +} + namespace ur_sanitizer_layer { +std::string ExtractModuleName(const char *Symbol) { + auto s1 = std::strrchr(Symbol, '('); + return std::string(Symbol, s1 - Symbol); +} + +// Parse symbolizer output in the following formats: +// +// :[:] +SourceInfo ParseSymbolizerOutput(std::string Output) { + SourceInfo Info; + // Parse function name + size_t End = Output.find_first_of('\n'); + assert(End != std::string::npos); + Info.function = Output.substr(0, End); + // Parse file name + size_t Start = End + 1; + End = Output.find_first_of(':', Start); + assert(End != std::string::npos); + Info.file = Output.substr(Start, End - Start); + // Parse line number + Start = End + 1; + End = Output.find_first_of(":\n", Start); + assert(End != std::string::npos); + Info.line = std::stoi(Output.substr(Start, End - Start)); + // Parse column number if exists + if (Output[End] == ':') { + Start = End + 1; + End = Output.find_first_of("\n", Start); + assert(End != std::string::npos); + Info.column = std::stoi(Output.substr(Start, End - Start)); + } + + return Info; +} + StackTrace GetCurrentBacktrace() { void *Frames[MAX_BACKTRACE_FRAMES]; int FrameCount = backtrace(Frames, MAX_BACKTRACE_FRAMES); @@ -25,8 +67,24 @@ StackTrace GetCurrentBacktrace() { StackTrace Stack; for (int i = 0; i < FrameCount; i++) { - BacktraceInfo addr_info(Symbols[i]); - Stack.stack.emplace_back(std::move(addr_info)); + if (SymbolizeCode != nullptr) { + std::string Result; + std::string ModuleName = ExtractModuleName(Symbols[i]); + if (SymbolizeCode(ModuleName, (uint64_t)Frames[i], Result)) { + SourceInfo SrcInfo = ParseSymbolizerOutput(Result); + std::ostringstream OS; + if (SrcInfo.file != "??") { + OS << "in " << SrcInfo.function << " " << SrcInfo.file + << ":" << SrcInfo.line << ":" << SrcInfo.column; + } else { + OS << "in " << SrcInfo.function << " (" << ModuleName << "+" + << Frames[i] << ")"; + } + Stack.stack.emplace_back(OS.str()); + continue; + } + } + Stack.stack.emplace_back(Symbols[i]); } free(Symbols); diff --git a/source/loader/layers/sanitizer/linux/symbolizer.cpp b/source/loader/layers/sanitizer/linux/symbolizer.cpp new file mode 100644 index 0000000000..7f11a60867 --- /dev/null +++ b/source/loader/layers/sanitizer/linux/symbolizer.cpp @@ -0,0 +1,60 @@ +/* + * + * Copyright (C) 2024 Intel Corporation + * + * Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions. + * See LICENSE.TXT + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + */ +#include "llvm/DebugInfo/Symbolize/DIPrinter.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.h" + +namespace ur_sanitizer_layer { + +llvm::symbolize::LLVMSymbolizer *GetSymbolizer() { + static llvm::symbolize::LLVMSymbolizer Symbolizer; + return &Symbolizer; +} + +llvm::symbolize::PrinterConfig GetPrinterConfig() { + llvm::symbolize::PrinterConfig Config; + Config.Pretty = false; + Config.PrintAddress = false; + Config.PrintFunctions = true; + Config.SourceContextLines = 0; + Config.Verbose = false; + return Config; +} + +} // namespace ur_sanitizer_layer + +extern "C" { + +bool SymbolizeCode(const std::string ModuleName, uint64_t ModuleOffset, + std::string &Result) { + llvm::raw_string_ostream OS(Result); + llvm::symbolize::Request Request{ModuleName, ModuleOffset}; + llvm::symbolize::PrinterConfig Config = + ur_sanitizer_layer::GetPrinterConfig(); + llvm::symbolize::ErrorHandler EH = [&](const llvm::ErrorInfoBase &ErrorInfo, + llvm::StringRef ErrorBanner) { + OS << ErrorBanner; + ErrorInfo.log(OS); + OS << '\n'; + }; + auto Printer = + std::make_unique(OS, EH, Config); + + auto ResOrErr = ur_sanitizer_layer::GetSymbolizer()->symbolizeInlinedCode( + ModuleName, + {ModuleOffset, llvm::object::SectionedAddress::UndefSection}); + + if (!ResOrErr) { + return false; + } + Printer->print(Request, *ResOrErr); + ur_sanitizer_layer::GetSymbolizer()->pruneCache(); + return true; +} +} \ No newline at end of file From c066e6a9c55a43157a77ae3afb51db1d4e512e34 Mon Sep 17 00:00:00 2001 From: Maosu Zhao Date: Wed, 10 Jul 2024 17:08:06 +0800 Subject: [PATCH 2/3] Add new line --- source/loader/layers/sanitizer/linux/symbolizer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/loader/layers/sanitizer/linux/symbolizer.cpp b/source/loader/layers/sanitizer/linux/symbolizer.cpp index 7f11a60867..5dd120e1f0 100644 --- a/source/loader/layers/sanitizer/linux/symbolizer.cpp +++ b/source/loader/layers/sanitizer/linux/symbolizer.cpp @@ -32,7 +32,7 @@ llvm::symbolize::PrinterConfig GetPrinterConfig() { extern "C" { bool SymbolizeCode(const std::string ModuleName, uint64_t ModuleOffset, - std::string &Result) { + std::string &Result) { llvm::raw_string_ostream OS(Result); llvm::symbolize::Request Request{ModuleName, ModuleOffset}; llvm::symbolize::PrinterConfig Config = @@ -57,4 +57,4 @@ bool SymbolizeCode(const std::string ModuleName, uint64_t ModuleOffset, ur_sanitizer_layer::GetSymbolizer()->pruneCache(); return true; } -} \ No newline at end of file +} From dac2a0feb2a583bbd3fb99b8e3d1d5f687f9a69d Mon Sep 17 00:00:00 2001 From: Maosu Zhao Date: Thu, 11 Jul 2024 14:06:09 +0800 Subject: [PATCH 3/3] Do symolize when it's really needed --- CMakeLists.txt | 2 +- .../layers/sanitizer/linux/backtrace.cpp | 62 +-------------- source/loader/layers/sanitizer/stacktrace.cpp | 75 +++++++++++++++++++ 3 files changed, 78 insertions(+), 61 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 40a9711a40..9fd39ea676 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,7 @@ option(UR_USE_MSAN "enable MemorySanitizer" OFF) option(UR_USE_TSAN "enable ThreadSanitizer" OFF) option(UR_ENABLE_TRACING "enable api tracing through xpti" OFF) option(UR_ENABLE_SANITIZER "enable device sanitizer" ON) -option(UR_ENABLE_SYMBOLIZER "enable Symoblizer for Sanitizer" OFF) +option(UR_ENABLE_SYMBOLIZER "enable symoblizer for sanitizer" OFF) option(UMF_BUILD_SHARED_LIBRARY "Build UMF as shared library" ON) option(UMF_ENABLE_POOL_TRACKING "Build UMF with pool tracking" ON) option(UR_BUILD_ADAPTER_L0 "Build the Level-Zero adapter" OFF) diff --git a/source/loader/layers/sanitizer/linux/backtrace.cpp b/source/loader/layers/sanitizer/linux/backtrace.cpp index befe944762..b336dc3675 100644 --- a/source/loader/layers/sanitizer/linux/backtrace.cpp +++ b/source/loader/layers/sanitizer/linux/backtrace.cpp @@ -12,50 +12,8 @@ #include #include -extern "C" { - -__attribute__((weak)) bool SymbolizeCode(const std::string ModuleName, - uint64_t ModuleOffset, - std::string &Result); -} - namespace ur_sanitizer_layer { -std::string ExtractModuleName(const char *Symbol) { - auto s1 = std::strrchr(Symbol, '('); - return std::string(Symbol, s1 - Symbol); -} - -// Parse symbolizer output in the following formats: -// -// :[:] -SourceInfo ParseSymbolizerOutput(std::string Output) { - SourceInfo Info; - // Parse function name - size_t End = Output.find_first_of('\n'); - assert(End != std::string::npos); - Info.function = Output.substr(0, End); - // Parse file name - size_t Start = End + 1; - End = Output.find_first_of(':', Start); - assert(End != std::string::npos); - Info.file = Output.substr(Start, End - Start); - // Parse line number - Start = End + 1; - End = Output.find_first_of(":\n", Start); - assert(End != std::string::npos); - Info.line = std::stoi(Output.substr(Start, End - Start)); - // Parse column number if exists - if (Output[End] == ':') { - Start = End + 1; - End = Output.find_first_of("\n", Start); - assert(End != std::string::npos); - Info.column = std::stoi(Output.substr(Start, End - Start)); - } - - return Info; -} - StackTrace GetCurrentBacktrace() { void *Frames[MAX_BACKTRACE_FRAMES]; int FrameCount = backtrace(Frames, MAX_BACKTRACE_FRAMES); @@ -67,24 +25,8 @@ StackTrace GetCurrentBacktrace() { StackTrace Stack; for (int i = 0; i < FrameCount; i++) { - if (SymbolizeCode != nullptr) { - std::string Result; - std::string ModuleName = ExtractModuleName(Symbols[i]); - if (SymbolizeCode(ModuleName, (uint64_t)Frames[i], Result)) { - SourceInfo SrcInfo = ParseSymbolizerOutput(Result); - std::ostringstream OS; - if (SrcInfo.file != "??") { - OS << "in " << SrcInfo.function << " " << SrcInfo.file - << ":" << SrcInfo.line << ":" << SrcInfo.column; - } else { - OS << "in " << SrcInfo.function << " (" << ModuleName << "+" - << Frames[i] << ")"; - } - Stack.stack.emplace_back(OS.str()); - continue; - } - } - Stack.stack.emplace_back(Symbols[i]); + BacktraceInfo addr_info(Symbols[i]); + Stack.stack.emplace_back(addr_info); } free(Symbols); diff --git a/source/loader/layers/sanitizer/stacktrace.cpp b/source/loader/layers/sanitizer/stacktrace.cpp index 6dcf447765..cf28c8b091 100644 --- a/source/loader/layers/sanitizer/stacktrace.cpp +++ b/source/loader/layers/sanitizer/stacktrace.cpp @@ -13,6 +13,13 @@ #include "stacktrace.hpp" #include "ur_sanitizer_layer.hpp" +extern "C" { + +__attribute__((weak)) bool SymbolizeCode(const std::string ModuleName, + uint64_t ModuleOffset, + std::string &Result); +} + namespace ur_sanitizer_layer { namespace { @@ -21,6 +28,54 @@ bool Contains(const std::string &s, const char *p) { return s.find(p) != std::string::npos; } +// Parse back trace information in the following formats: +// ([function_name]+function_offset) [offset] +void ParseBacktraceInfo(BacktraceInfo BI, std::string &ModuleName, + uptr &Offset) { + // Parse module name + size_t End = BI.find_first_of('('); + assert(End != std::string::npos); + ModuleName = BI.substr(0, End); + // Parse offset + size_t Start = BI.find_first_of('['); + assert(Start != std::string::npos); + Start = BI.substr(Start + 1, 2) == "0x" ? Start + 3 : Start + 1; + End = BI.find_first_of(']'); + assert(End != std::string::npos); + Offset = std::stoull(BI.substr(Start, End), nullptr, 16); + return; +} + +// Parse symbolizer output in the following formats: +// +// :[:] +SourceInfo ParseSymbolizerOutput(std::string Output) { + SourceInfo Info; + // Parse function name + size_t End = Output.find_first_of('\n'); + assert(End != std::string::npos); + Info.function = Output.substr(0, End); + // Parse file name + size_t Start = End + 1; + End = Output.find_first_of(':', Start); + assert(End != std::string::npos); + Info.file = Output.substr(Start, End - Start); + // Parse line number + Start = End + 1; + End = Output.find_first_of(":\n", Start); + assert(End != std::string::npos); + Info.line = std::stoi(Output.substr(Start, End - Start)); + // Parse column number if exists + if (Output[End] == ':') { + Start = End + 1; + End = Output.find_first_of("\n", Start); + assert(End != std::string::npos); + Info.column = std::stoi(Output.substr(Start, End - Start)); + } + + return Info; +} + } // namespace void StackTrace::print() const { @@ -37,6 +92,26 @@ void StackTrace::print() const { Contains(BI, "libur_loader.so")) { continue; } + + if (&SymbolizeCode != nullptr) { + std::string Result; + std::string ModuleName; + uptr Offset; + ParseBacktraceInfo(BI, ModuleName, Offset); + if (SymbolizeCode(ModuleName, Offset, Result)) { + SourceInfo SrcInfo = ParseSymbolizerOutput(Result); + if (SrcInfo.file != "??") { + getContext()->logger.always(" #{} in {} {}:{}:{}", index, + SrcInfo.function, SrcInfo.file, + SrcInfo.line, SrcInfo.column); + } else { + getContext()->logger.always(" #{} in {} ({}+{})", index, + SrcInfo.function, ModuleName, + (void *)Offset); + } + continue; + } + } getContext()->logger.always(" #{} {}", index, BI); ++index; }