diff --git a/sycl/CMakeLists.txt b/sycl/CMakeLists.txt index 3d7f41c838aa..34e9f953cf94 100644 --- a/sycl/CMakeLists.txt +++ b/sycl/CMakeLists.txt @@ -337,6 +337,16 @@ set( SYCL_TOOLCHAIN_DEPLOY_COMPONENTS ${XPTIFW_LIBS} ) +if (TARGET sycl-prof) + list(APPEND SYCL_TOOLCHAIN_DEPLOY_COMPONENTS sycl-prof) +endif() +if (TARGET sycl-sanitize) + list(APPEND SYCL_TOOLCHAIN_DEPLOY_COMPONENTS sycl-sanitize) +endif() +if (TARGET sycl-trace) + list(APPEND SYCL_TOOLCHAIN_DEPLOY_COMPONENTS sycl-trace) +endif() + if(OpenCL_INSTALL_KHRONOS_ICD_LOADER AND TARGET OpenCL-ICD) list(APPEND SYCL_TOOLCHAIN_DEPLOY_COMPONENTS OpenCL-ICD) endif() diff --git a/sycl/tools/CMakeLists.txt b/sycl/tools/CMakeLists.txt index a4f567482672..39805d41b949 100644 --- a/sycl/tools/CMakeLists.txt +++ b/sycl/tools/CMakeLists.txt @@ -1,7 +1,11 @@ add_subdirectory(sycl-ls) if (SYCL_ENABLE_XPTI_TRACING) - add_subdirectory(pi-trace) + if (UNIX) + add_subdirectory(sycl-prof) + add_subdirectory(sycl-trace) + add_subdirectory(sycl-sanitize) + endif() endif() # TODO: move each tool in its own sub-directory diff --git a/sycl/tools/pi-trace/CMakeLists.txt b/sycl/tools/pi-trace/CMakeLists.txt deleted file mode 100644 index e305fd22dbed..000000000000 --- a/sycl/tools/pi-trace/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -add_library(pi_trace SHARED pi_trace.cpp) -target_compile_definitions(pi_trace PRIVATE XPTI_CALLBACK_API_EXPORTS) -target_link_libraries(pi_trace PRIVATE xptifw) -if (TARGET OpenCL-Headers) - target_link_libraries(pi_trace PRIVATE OpenCL-Headers) -endif() - -target_include_directories(pi_trace PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/../xpti_helpers/" - "${sycl_inc_dir}" - "${sycl_src_dir}" -) - -add_dependencies(sycl-toolchain pi_trace) diff --git a/sycl/tools/sycl-prof/CMakeLists.txt b/sycl/tools/sycl-prof/CMakeLists.txt new file mode 100644 index 000000000000..87a8b0d3c8ed --- /dev/null +++ b/sycl/tools/sycl-prof/CMakeLists.txt @@ -0,0 +1,36 @@ +add_executable(sycl-prof + main.cpp +) + +target_include_directories(sycl-prof PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/../xpti_helpers/" +) + +target_link_libraries(sycl-prof PRIVATE + LLVMSupport +) + +target_compile_options(sycl-prof PRIVATE -fno-exceptions -fno-rtti) + +add_library(sycl_profiler_collector SHARED collector.cpp) +target_compile_definitions(sycl_profiler_collector PRIVATE XPTI_CALLBACK_API_EXPORTS) +target_link_libraries(sycl_profiler_collector PRIVATE xptifw) +if (TARGET OpenCL-Headers) + target_link_libraries(sycl_profiler_collector PRIVATE OpenCL-Headers) +endif() +target_include_directories(sycl_profiler_collector PRIVATE + "${sycl_inc_dir}" + "${sycl_src_dir}" +) + +target_compile_options(sycl_profiler_collector PRIVATE -g) + +add_dependencies(sycl-prof sycl_profiler_collector) +add_dependencies(sycl-toolchain sycl-prof) + +include(GNUInstallDirs) +install(TARGETS sycl-prof sycl_profiler_collector + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sycl-prof + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sycl-prof + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sycl-prof +) diff --git a/sycl/tools/sycl-prof/collector.cpp b/sycl/tools/sycl-prof/collector.cpp new file mode 100644 index 000000000000..0bcadfd67d07 --- /dev/null +++ b/sycl/tools/sycl-prof/collector.cpp @@ -0,0 +1,144 @@ +//==-------------- collector.cpp -------------------------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "writer.hpp" +#include "xpti/xpti_data_types.h" + +#include +#include + +#include +#include +#include +#include +#include + +namespace chrono = std::chrono; + +Writer *GWriter = nullptr; + +struct Measurements { + size_t TID; + size_t PID; + size_t TimeStamp; +}; + +unsigned long process_id() { return static_cast(getpid()); } + +static Measurements measure() { + size_t TID = std::hash{}(std::this_thread::get_id()); + size_t PID = process_id(); + auto Now = chrono::high_resolution_clock::now(); + size_t TS = chrono::time_point_cast(Now) + .time_since_epoch() + .count(); + + return Measurements{TID, PID, TS}; +} + +XPTI_CALLBACK_API void piBeginEndCallback(uint16_t TraceType, + xpti::trace_event_data_t *, + xpti::trace_event_data_t *, + uint64_t /*Instance*/, + const void *UserData); +XPTI_CALLBACK_API void taskBeginEndCallback(uint16_t TraceType, + xpti::trace_event_data_t *, + xpti::trace_event_data_t *, + uint64_t /*Instance*/, + const void *UserData); +XPTI_CALLBACK_API void waitBeginEndCallback(uint16_t TraceType, + xpti::trace_event_data_t *, + xpti::trace_event_data_t *, + uint64_t /*Instance*/, + const void *UserData); + +XPTI_CALLBACK_API void xptiTraceInit(unsigned int /*major_version*/, + unsigned int /*minor_version*/, + const char * /*version_str*/, + const char *StreamName) { + if (GWriter == nullptr) { + GWriter = new JSONWriter(std::getenv("SYCL_PROF_OUT_FILE")); + GWriter->init(); + } + + if (std::string_view(StreamName) == "sycl.pi") { + uint8_t StreamID = xptiRegisterStream(StreamName); + xptiRegisterCallback(StreamID, xpti::trace_function_begin, + piBeginEndCallback); + xptiRegisterCallback(StreamID, xpti::trace_function_end, + piBeginEndCallback); + } else if (std::string_view(StreamName) == "sycl") { + uint8_t StreamID = xptiRegisterStream(StreamName); + xptiRegisterCallback(StreamID, xpti::trace_task_begin, + taskBeginEndCallback); + xptiRegisterCallback(StreamID, xpti::trace_task_end, taskBeginEndCallback); + xptiRegisterCallback(StreamID, xpti::trace_wait_begin, + waitBeginEndCallback); + xptiRegisterCallback(StreamID, xpti::trace_wait_end, waitBeginEndCallback); + xptiRegisterCallback(StreamID, xpti::trace_barrier_begin, + waitBeginEndCallback); + xptiRegisterCallback(StreamID, xpti::trace_barrier_end, + waitBeginEndCallback); + } +} + +XPTI_CALLBACK_API void xptiTraceFinish(const char *) { GWriter->finalize(); } + +XPTI_CALLBACK_API void piBeginEndCallback(uint16_t TraceType, + xpti::trace_event_data_t *, + xpti::trace_event_data_t *, + uint64_t /*Instance*/, + const void *UserData) { + auto [TID, PID, TS] = measure(); + if (TraceType == xpti::trace_function_begin) { + GWriter->writeBegin(static_cast(UserData), "Plugin", PID, TID, + TS); + } else { + GWriter->writeEnd(static_cast(UserData), "Plugin", PID, TID, + TS); + } +} + +XPTI_CALLBACK_API void taskBeginEndCallback(uint16_t TraceType, + xpti::trace_event_data_t *, + xpti::trace_event_data_t *Event, + uint64_t /*Instance*/, + const void *) { + std::string_view Name = "unknown"; + + xpti::metadata_t *Metadata = xptiQueryMetadata(Event); + for (auto &Item : *Metadata) { + std::string_view Key{xptiLookupString(Item.first)}; + if (Key == "kernel_name" || Key == "memory_object") { + Name = xptiLookupString(Item.second); + } + } + + auto [TID, PID, TS] = measure(); + if (TraceType == xpti::trace_task_begin) { + GWriter->writeBegin(Name, "SYCL", PID, TID, TS); + } else { + GWriter->writeEnd(Name, "SYCL", PID, TID, TS); + } +} + +XPTI_CALLBACK_API void waitBeginEndCallback(uint16_t TraceType, + xpti::trace_event_data_t *, + xpti::trace_event_data_t *, + uint64_t /*Instance*/, + const void *UserData) { + auto [TID, PID, TS] = measure(); + if (TraceType == xpti::trace_wait_begin || + TraceType == xpti::trace_barrier_begin) { + GWriter->writeBegin(static_cast(UserData), "SYCL", PID, TID, + TS); + } else { + GWriter->writeEnd(static_cast(UserData), "SYCL", PID, TID, + TS); + } +} diff --git a/sycl/tools/sycl-prof/main.cpp b/sycl/tools/sycl-prof/main.cpp new file mode 100644 index 000000000000..6b1a96080f25 --- /dev/null +++ b/sycl/tools/sycl-prof/main.cpp @@ -0,0 +1,63 @@ +//==------------ main.cpp - SYCL Profiler Tool -----------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "launch.hpp" +#include "llvm/Support/CommandLine.h" + +#include +#include + +using namespace llvm; + +enum OutputFormatKind { JSON }; + +int main(int argc, char **argv, char *env[]) { + cl::opt OutputFormat( + "format", cl::desc("Set profiler output format:"), + cl::values( + // TODO performance summary + clEnumValN(JSON, "json", + "JSON file, compatible with chrome://tracing"))); + cl::opt OutputFilename("o", cl::desc("Specify output filename"), + cl::value_desc("filename"), cl::Required); + cl::opt TargetExecutable( + cl::Positional, cl::desc(""), cl::Required); + cl::list Argv(cl::ConsumeAfter, + cl::desc("...")); + + cl::ParseCommandLineOptions(argc, argv); + + std::vector NewEnv; + + { + size_t I = 0; + while (env[I] != nullptr) + NewEnv.emplace_back(env[I++]); + } + + std::string ProfOutFile = "SYCL_PROF_OUT_FILE=" + OutputFilename; + NewEnv.push_back(ProfOutFile); + NewEnv.push_back("XPTI_FRAMEWORK_DISPATCHER=libxptifw.so"); + NewEnv.push_back("XPTI_SUBSCRIBERS=libsycl_profiler_collector.so"); + NewEnv.push_back("XPTI_TRACE_ENABLE=1"); + + std::vector Args; + + Args.push_back(TargetExecutable); + std::copy(Argv.begin(), Argv.end(), std::back_inserter(Args)); + + int Err = launch(TargetExecutable, Args, NewEnv); + + if (Err) { + std::cerr << "Failed to launch target application. Error code " << Err + << "\n"; + return Err; + } + + return 0; +} diff --git a/sycl/tools/sycl-prof/writer.hpp b/sycl/tools/sycl-prof/writer.hpp new file mode 100644 index 000000000000..9870065beb35 --- /dev/null +++ b/sycl/tools/sycl-prof/writer.hpp @@ -0,0 +1,85 @@ +//==----------------- writer.hpp -------------------------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include +#include +#include + +class Writer { +public: + virtual void init() = 0; + virtual void finalize() = 0; + virtual void writeBegin(std::string_view Name, std::string_view Category, + size_t PID, size_t TID, size_t TimeStamp) = 0; + virtual void writeEnd(std::string_view Name, std::string_view Category, + size_t PID, size_t TID, size_t TimeStamp) = 0; + virtual ~Writer() = default; +}; + +class JSONWriter : public Writer { +public: + explicit JSONWriter(const std::string &OutPath) : MOutFile(OutPath) {} + + void init() final { + std::lock_guard _{MWriteMutex}; + + MOutFile << "{\n"; + MOutFile << " \"traceEvents\": [\n"; + } + + void writeBegin(std::string_view Name, std::string_view Category, size_t PID, + size_t TID, size_t TimeStamp) override { + std::lock_guard _{MWriteMutex}; + + if (!MOutFile.is_open()) + return; + + MOutFile << "{\"name\": \"" << Name << "\", "; + MOutFile << "\"cat\": \"" << Category << "\", "; + MOutFile << "\"ph\": \"B\", "; + MOutFile << "\"pid\": \"" << PID << "\", "; + MOutFile << "\"tid\": \"" << TID << "\", "; + MOutFile << "\"ts\": \"" << TimeStamp << "\"},"; + MOutFile << std::endl; + } + + void writeEnd(std::string_view Name, std::string_view Category, size_t PID, + size_t TID, size_t TimeStamp) override { + std::lock_guard _{MWriteMutex}; + + if (!MOutFile.is_open()) + return; + + MOutFile << "{\"name\": \"" << Name << "\", "; + MOutFile << "\"cat\": \"" << Category << "\", "; + MOutFile << "\"ph\": \"E\", "; + MOutFile << "\"pid\": \"" << PID << "\", "; + MOutFile << "\"tid\": \"" << TID << "\", "; + MOutFile << "\"ts\": \"" << TimeStamp << "\"},"; + MOutFile << std::endl; + } + + void finalize() final { + std::lock_guard _{MWriteMutex}; + + if (!MOutFile.is_open()) + return; + + MOutFile << "],\n"; + MOutFile << "\"displayTimeUnit\":\"ns\"\n}\n"; + MOutFile.close(); + } + + ~JSONWriter() { finalize(); } + +private: + std::mutex MWriteMutex; + std::ofstream MOutFile; +}; diff --git a/sycl/tools/sycl-sanitize/CMakeLists.txt b/sycl/tools/sycl-sanitize/CMakeLists.txt new file mode 100644 index 000000000000..9e21065a8341 --- /dev/null +++ b/sycl/tools/sycl-sanitize/CMakeLists.txt @@ -0,0 +1,34 @@ +add_executable(sycl-sanitize main.cpp) + +target_include_directories(sycl-sanitize PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/../xpti_helpers/" +) + +target_link_libraries(sycl-sanitize PRIVATE + LLVMSupport +) + +target_compile_options(sycl-sanitize PRIVATE -fno-exceptions -fno-rtti) + +add_library(sycl_sanitizer_collector SHARED collector.cpp) +target_compile_definitions(sycl_sanitizer_collector PRIVATE XPTI_CALLBACK_API_EXPORTS) +target_link_libraries(sycl_sanitizer_collector PRIVATE xptifw) +if (TARGET OpenCL-Headers) + target_link_libraries(sycl_sanitizer_collector PRIVATE OpenCL-Headers) +endif() + +target_include_directories(sycl_sanitizer_collector PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/../xpti_helpers/" + "${sycl_inc_dir}" + "${sycl_src_dir}" +) + +add_dependencies(sycl-sanitize sycl_sanitizer_collector) +add_dependencies(sycl-toolchain sycl-sanitize) + +include(GNUInstallDirs) +install(TARGETS sycl-sanitize sycl_sanitizer_collector + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sycl-sanitize + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sycl-sanitize + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sycl-sanitize +) diff --git a/sycl/tools/sycl-sanitize/collector.cpp b/sycl/tools/sycl-sanitize/collector.cpp new file mode 100644 index 000000000000..86b5bb74b693 --- /dev/null +++ b/sycl/tools/sycl-sanitize/collector.cpp @@ -0,0 +1,213 @@ +//==-------------- collector.cpp -------------------------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +/// \file collector.cpp +/// The SYCL sanitizer collector intercepts PI calls to find memory leaks in +/// usages of USM pointers. + +#include "xpti/xpti_trace_framework.h" + +#include "pi_arguments_handler.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +struct TracepointInfo { + std::string Source; + std::string Function; + uint32_t Line; +}; + +enum class AllocKind { host, device, shared }; + +struct AllocationInfo { + size_t Length; + AllocKind Kind; + TracepointInfo Location; +}; + +struct GlobalState { + std::mutex IOMutex; + std::map ActivePointers; + TracepointInfo LastTracepoint; + sycl::xpti_helpers::PiArgumentsHandler ArgHandlerPostCall; + sycl::xpti_helpers::PiArgumentsHandler ArgHandlerPreCall; +}; + +GlobalState *GS = nullptr; + +static void handleUSMHostAlloc(const pi_plugin &, std::optional, + void **ResultPtr, pi_context, + pi_usm_mem_properties *, size_t Size, + pi_uint32) { + AllocationInfo Info; + Info.Location = GS->LastTracepoint; + Info.Length = Size; + Info.Kind = AllocKind::host; + GS->ActivePointers[*ResultPtr] = Info; +} + +static void handleUSMDeviceAlloc(const pi_plugin &, std::optional, + void **ResultPtr, pi_context, pi_device, + pi_usm_mem_properties *, size_t Size, + pi_uint32) { + AllocationInfo Info; + Info.Location = GS->LastTracepoint; + Info.Length = Size; + Info.Kind = AllocKind::device; + GS->ActivePointers[*ResultPtr] = Info; +} + +static void handleUSMSharedAlloc(const pi_plugin &, std::optional, + void **ResultPtr, pi_context, pi_device, + pi_usm_mem_properties *, size_t Size, + pi_uint32) { + AllocationInfo Info; + Info.Location = GS->LastTracepoint; + Info.Length = Size; + Info.Kind = AllocKind::shared; + GS->ActivePointers[*ResultPtr] = Info; +} + +static void handleUSMFree(const pi_plugin &, std::optional, + pi_context, void *Ptr) { + if (GS->ActivePointers.count(Ptr) == 0) { + std::cerr << "Attempt to free pointer " << std::hex << Ptr; + std::cerr << " that was not allocated with SYCL USM APIs.\n"; + std::cerr << " Location: function " << GS->LastTracepoint.Function; + std::cerr << " at " << GS->LastTracepoint.Source << ":"; + std::cerr << std::dec << GS->LastTracepoint.Line << "\n"; + std::terminate(); + } + GS->ActivePointers.erase(Ptr); +} + +static void handleMemBufferCreate(const pi_plugin &, std::optional, + pi_context, pi_mem_flags, size_t Size, + void *HostPtr, pi_mem *, + const pi_mem_properties *) { + for (const auto &Alloc : GS->ActivePointers) { + const void *Begin = Alloc.first; + const void *End = + static_cast(Alloc.first) + Alloc.second.Length; + // Host pointer was allocated with USM APIs + if (HostPtr >= Begin && HostPtr <= End) { + bool NeedsTerminate = false; + if (Alloc.second.Kind != AllocKind::host) { + std::cerr << "Attempt to construct a buffer with non-host pointer.\n"; + NeedsTerminate = true; + } + + const void *HostEnd = static_cast(HostPtr) + Size; + if (HostEnd > End) { + std::cerr << "Buffer size exceeds allocated host memory size.\n"; + NeedsTerminate = true; + } + + if (NeedsTerminate) { + std::cerr << " Allocation location: "; + std::cerr << " function " << Alloc.second.Location.Function << " at "; + std::cerr << Alloc.second.Location.Source << ":" + << Alloc.second.Location.Line << "\n"; + std::cerr << " Buffer location: "; + std::cerr << " function " << GS->LastTracepoint.Function << " at "; + std::cerr << GS->LastTracepoint.Source << ":" << GS->LastTracepoint.Line + << "\n"; + std::terminate(); + } + break; + } + } +} + +XPTI_CALLBACK_API void tpCallback(uint16_t trace_type, + xpti::trace_event_data_t *parent, + xpti::trace_event_data_t *event, + uint64_t instance, const void *user_data); + +XPTI_CALLBACK_API void xptiTraceInit(unsigned int /*major_version*/, + unsigned int /*minor_version*/, + const char * /*version_str*/, + const char *StreamName) { + if (std::string_view(StreamName) == "sycl.pi.debug") { + GS = new GlobalState; + uint8_t StreamID = xptiRegisterStream(StreamName); + xptiRegisterCallback(StreamID, xpti::trace_function_with_args_begin, + tpCallback); + xptiRegisterCallback(StreamID, xpti::trace_function_with_args_end, + tpCallback); + + GS->ArgHandlerPostCall.set_piextUSMHostAlloc(handleUSMHostAlloc); + GS->ArgHandlerPostCall.set_piextUSMDeviceAlloc(handleUSMDeviceAlloc); + GS->ArgHandlerPostCall.set_piextUSMSharedAlloc(handleUSMSharedAlloc); + GS->ArgHandlerPreCall.set_piextUSMFree(handleUSMFree); + GS->ArgHandlerPreCall.set_piMemBufferCreate(handleMemBufferCreate); + } +} + +XPTI_CALLBACK_API void xptiTraceFinish(const char *StreamName) { + if (std::string_view(StreamName) == "sycl.pi.debug") { + bool hadLeak = false; + if (GS->ActivePointers.size() > 0) { + hadLeak = true; + std::cerr << "Found " << GS->ActivePointers.size() + << " leaked memory allocations\n"; + for (const auto &Ptr : GS->ActivePointers) { + std::cerr << "Leaked pointer: " << std::hex << Ptr.first << "\n"; + std::cerr << " Location: " + << "function " << Ptr.second.Location.Function << " at " + << Ptr.second.Location.Source << ":" << std::dec + << Ptr.second.Location.Line << "\n"; + } + } + + delete GS; + if (hadLeak) + exit(-1); + } +} + +XPTI_CALLBACK_API void tpCallback(uint16_t TraceType, + xpti::trace_event_data_t *, + xpti::trace_event_data_t *, + uint64_t /*Instance*/, const void *UserData) { + auto *Payload = xptiQueryPayloadByUID(xptiGetUniversalId()); + + if (Payload) { + if (Payload->source_file) + GS->LastTracepoint.Source = Payload->source_file; + else + GS->LastTracepoint.Source = ""; + GS->LastTracepoint.Function = Payload->name; + GS->LastTracepoint.Line = Payload->line_no; + } else { + GS->LastTracepoint.Function = ""; + GS->LastTracepoint.Source = ""; + GS->LastTracepoint.Line = 0; + } + + // Lock while we capture information + std::lock_guard Lock(GS->IOMutex); + + const auto *Data = static_cast(UserData); + const auto *Plugin = static_cast(Data->user_data); + if (TraceType == xpti::trace_function_with_args_begin) { + GS->ArgHandlerPreCall.handle(Data->function_id, *Plugin, std::nullopt, + Data->args_data); + } else if (TraceType == xpti::trace_function_with_args_end) { + const pi_result Result = *static_cast(Data->ret_data); + GS->ArgHandlerPostCall.handle(Data->function_id, *Plugin, Result, + Data->args_data); + } +} diff --git a/sycl/tools/sycl-sanitize/main.cpp b/sycl/tools/sycl-sanitize/main.cpp new file mode 100644 index 000000000000..3a89d8132f23 --- /dev/null +++ b/sycl/tools/sycl-sanitize/main.cpp @@ -0,0 +1,51 @@ +//==------------ main.cpp - SYCL Sanitizer Tool ----------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "launch.hpp" +#include "llvm/Support/CommandLine.h" + +#include +#include + +using namespace llvm; + +int main(int argc, char **argv, char *env[]) { + cl::opt TargetExecutable( + cl::Positional, cl::desc(""), cl::Required); + cl::list Argv(cl::ConsumeAfter, + cl::desc("...")); + + cl::ParseCommandLineOptions(argc, argv); + + std::vector NewEnv; + + { + size_t I = 0; + while (env[I] != nullptr) + NewEnv.push_back(env[I++]); + } + + NewEnv.push_back("XPTI_FRAMEWORK_DISPATCHER=libxptifw.so"); + NewEnv.push_back("XPTI_SUBSCRIBERS=libsycl_sanitizer_collector.so"); + NewEnv.push_back("XPTI_TRACE_ENABLE=1"); + + std::vector Args; + + Args.push_back(TargetExecutable); + std::copy(Argv.begin(), Argv.end(), std::back_inserter(Args)); + + int Err = launch(TargetExecutable, Args, NewEnv); + + if (Err) { + std::cerr << "Failed to launch target application. Error code " << Err + << "\n"; + return Err; + } + + return 0; +} diff --git a/sycl/tools/sycl-trace/CMakeLists.txt b/sycl/tools/sycl-trace/CMakeLists.txt new file mode 100644 index 000000000000..f07bc587b849 --- /dev/null +++ b/sycl/tools/sycl-trace/CMakeLists.txt @@ -0,0 +1,35 @@ +add_executable(sycl-trace main.cpp) + +target_include_directories(sycl-trace PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/../xpti_helpers/" +) + +target_compile_options(sycl-trace PRIVATE -fno-exceptions -fno-rtti) + +target_link_libraries(sycl-trace PRIVATE + LLVMSupport +) + +add_library(sycl_pi_trace_collector SHARED pi_trace_collector.cpp) +target_compile_definitions(sycl_pi_trace_collector PRIVATE XPTI_CALLBACK_API_EXPORTS) +target_link_libraries(sycl_pi_trace_collector PRIVATE xptifw) +if (TARGET OpenCL-Headers) + target_link_libraries(sycl_pi_trace_collector PRIVATE OpenCL-Headers) +endif() + +target_include_directories(sycl_pi_trace_collector PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/../xpti_helpers/" + "${sycl_inc_dir}" + "${sycl_src_dir}" +) + +add_dependencies(sycl-trace sycl_pi_trace_collector) +add_dependencies(sycl-toolchain sycl-trace) + +include(GNUInstallDirs) +install(TARGETS sycl-trace sycl_pi_trace_collector + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sycl-trace + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sycl-trace + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sycl-trace +) + diff --git a/sycl/tools/sycl-trace/main.cpp b/sycl/tools/sycl-trace/main.cpp new file mode 100644 index 000000000000..13a9524a54c3 --- /dev/null +++ b/sycl/tools/sycl-trace/main.cpp @@ -0,0 +1,58 @@ +//==------------ main.cpp - SYCL Tracing Tool ------------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "launch.hpp" +#include "llvm/Support/CommandLine.h" + +#include +#include + +using namespace llvm; + +enum ModeKind { PI }; + +int main(int argc, char **argv, char *env[]) { + cl::opt Mode( + "mode", cl::desc("Set tracing mode:"), + cl::values( + // TODO graph dot + clEnumValN(PI, "plugin", "Trace Plugin Interface calls"))); + cl::opt TargetExecutable( + cl::Positional, cl::desc(""), cl::Required); + cl::list Argv(cl::ConsumeAfter, + cl::desc("...")); + + cl::ParseCommandLineOptions(argc, argv); + + std::vector NewEnv; + + { + size_t I = 0; + while (env[I] != nullptr) + NewEnv.emplace_back(env[I++]); + } + + NewEnv.push_back("XPTI_FRAMEWORK_DISPATCHER=libxptifw.so"); + NewEnv.push_back("XPTI_SUBSCRIBERS=libsycl_pi_trace_collector.so"); + NewEnv.push_back("XPTI_TRACE_ENABLE=1"); + + std::vector Args; + + Args.push_back(TargetExecutable); + std::copy(Argv.begin(), Argv.end(), std::back_inserter(Args)); + + int Err = launch(TargetExecutable.c_str(), Args, NewEnv); + + if (Err) { + std::cerr << "Failed to launch target application. Error code " << Err + << "\n"; + return Err; + } + + return 0; +} diff --git a/sycl/tools/pi-trace/pi_trace.cpp b/sycl/tools/sycl-trace/pi_trace_collector.cpp similarity index 88% rename from sycl/tools/pi-trace/pi_trace.cpp rename to sycl/tools/sycl-trace/pi_trace_collector.cpp index d75b3eebf3bc..2f277ef3e459 100644 --- a/sycl/tools/pi-trace/pi_trace.cpp +++ b/sycl/tools/sycl-trace/pi_trace_collector.cpp @@ -43,16 +43,14 @@ XPTI_CALLBACK_API void xptiTraceInit(unsigned int /*major_version*/, const char *stream_name) { if (std::string_view(stream_name) == "sycl.pi.debug") { GStreamID = xptiRegisterStream(stream_name); - xptiRegisterCallback( - GStreamID, (uint16_t)xpti::trace_point_type_t::function_with_args_begin, - tpCallback); - xptiRegisterCallback( - GStreamID, (uint16_t)xpti::trace_point_type_t::function_with_args_end, - tpCallback); + xptiRegisterCallback(GStreamID, xpti::trace_function_with_args_begin, + tpCallback); + xptiRegisterCallback(GStreamID, xpti::trace_function_with_args_end, + tpCallback); #define _PI_API(api) \ ArgHandler.set##_##api( \ - [](const pi_plugin &, std::optional, auto &&... Args) { \ + [](const pi_plugin &, std::optional, auto &&...Args) { \ std::cout << "---> " << #api << "(" \ << "\n"; \ sycl::detail::pi::printArgs(Args...); \ @@ -71,8 +69,7 @@ XPTI_CALLBACK_API void tpCallback(uint16_t TraceType, xpti::trace_event_data_t * /*Parent*/, xpti::trace_event_data_t * /*Event*/, uint64_t /*Instance*/, const void *UserData) { - auto Type = static_cast(TraceType); - if (Type == xpti::trace_point_type_t::function_with_args_end) { + if (TraceType == xpti::trace_function_with_args_end) { // Lock while we print information std::lock_guard Lock(GIOMutex); diff --git a/sycl/tools/xpti_helpers/launch.hpp b/sycl/tools/xpti_helpers/launch.hpp new file mode 100644 index 000000000000..1e077f6964e9 --- /dev/null +++ b/sycl/tools/xpti_helpers/launch.hpp @@ -0,0 +1,39 @@ +//==----------------- launch.hpp -------------------------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include +#include +#include +#include + +inline std::vector toCStyle(const std::vector &Arr) { + std::vector CArr; + CArr.reserve(Arr.size() + 1); + std::transform(Arr.begin(), Arr.end(), std::back_inserter(CArr), + [](const std::string &str) { return str.data(); }); + + CArr.push_back(nullptr); + + return CArr; +} + +/// Launches external application. +/// +/// \param Cmd is a path (full, relative, executable name in PATH) to executable +/// \param Args is program arguments. First argument is executable name. Last +/// argument is nullptr. +/// \param Env is program environment variables. Last variable is nullptr. +int launch(const std::string &Cmd, const std::vector &Args, + const std::vector &Env) { + std::vector CArgs = toCStyle(Args); + std::vector CEnv = toCStyle(Env); + return execve(Cmd.data(), const_cast(CArgs.data()), + const_cast(CEnv.data())); +} diff --git a/xpti/include/xpti/xpti_data_types.h b/xpti/include/xpti/xpti_data_types.h index 86f92ad54167..40348aa10ed0 100644 --- a/xpti/include/xpti/xpti_data_types.h +++ b/xpti/include/xpti/xpti_data_types.h @@ -668,6 +668,14 @@ constexpr uint16_t trace_edge_create = static_cast(xpti::trace_point_type_t::edge_create); constexpr uint16_t trace_signal = static_cast(xpti::trace_point_type_t::signal); +constexpr uint16_t trace_function_begin = + static_cast(xpti::trace_point_type_t::function_begin); +constexpr uint16_t trace_function_end = + static_cast(xpti::trace_point_type_t::function_end); +constexpr uint16_t trace_function_with_args_begin = + static_cast(xpti::trace_point_type_t::function_with_args_begin); +constexpr uint16_t trace_function_with_args_end = + static_cast(xpti::trace_point_type_t::function_with_args_end); constexpr uint16_t trace_offload_alloc_construct = static_cast(xpti::trace_point_type_t::offload_alloc_construct); constexpr uint16_t trace_offload_alloc_associate =