Skip to content

Commit 789d138

Browse files
author
Alexander Batashev
authored
[SYCL] Introduce XPTI-based tooling for SYCL applications (#5389)
# sycl-trace Tracing tool that prints SYCL PI calls, analog to SYCL_PI_TRACE env variable. **Limitations**: - No support for plugin load diagnostics - No support for device selector diagnostics - No support for graph tracing # sycl-prof Dumps profiling information from SYCL runtime and saves it to Chrome-compatible JSON file **Limitations**: - No support for "summary" mode. # sycl-sanitize A CLI tool to facilitate development of DPC++ applications. Currently sycl-sanitizer is capable of the following diagnostics: - USM pointer memory leaks. Report is generated at the end of program execution and contains location of leaked pointer malloc call. - Construction of SYCL buffers from device or shared USM pointer. The tool will abort execution of application if it attempts to create a buffer from device or shared USM pointer. - Construction of SYCL buffer from host USM pointer of smaller size, than buffer range. The application will be terminated. **Common limitations to all tools**: - All tools are only available on Linux
1 parent 4f3d7e1 commit 789d138

File tree

15 files changed

+787
-24
lines changed

15 files changed

+787
-24
lines changed

sycl/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,16 @@ set( SYCL_TOOLCHAIN_DEPLOY_COMPONENTS
337337
${XPTIFW_LIBS}
338338
)
339339

340+
if (TARGET sycl-prof)
341+
list(APPEND SYCL_TOOLCHAIN_DEPLOY_COMPONENTS sycl-prof)
342+
endif()
343+
if (TARGET sycl-sanitize)
344+
list(APPEND SYCL_TOOLCHAIN_DEPLOY_COMPONENTS sycl-sanitize)
345+
endif()
346+
if (TARGET sycl-trace)
347+
list(APPEND SYCL_TOOLCHAIN_DEPLOY_COMPONENTS sycl-trace)
348+
endif()
349+
340350
if(OpenCL_INSTALL_KHRONOS_ICD_LOADER AND TARGET OpenCL-ICD)
341351
list(APPEND SYCL_TOOLCHAIN_DEPLOY_COMPONENTS OpenCL-ICD)
342352
endif()

sycl/tools/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
add_subdirectory(sycl-ls)
22

33
if (SYCL_ENABLE_XPTI_TRACING)
4-
add_subdirectory(pi-trace)
4+
if (UNIX)
5+
add_subdirectory(sycl-prof)
6+
add_subdirectory(sycl-trace)
7+
add_subdirectory(sycl-sanitize)
8+
endif()
59
endif()
610

711
# TODO: move each tool in its own sub-directory

sycl/tools/pi-trace/CMakeLists.txt

Lines changed: 0 additions & 14 deletions
This file was deleted.

sycl/tools/sycl-prof/CMakeLists.txt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
add_executable(sycl-prof
2+
main.cpp
3+
)
4+
5+
target_include_directories(sycl-prof PRIVATE
6+
"${CMAKE_CURRENT_SOURCE_DIR}/../xpti_helpers/"
7+
)
8+
9+
target_link_libraries(sycl-prof PRIVATE
10+
LLVMSupport
11+
)
12+
13+
target_compile_options(sycl-prof PRIVATE -fno-exceptions -fno-rtti)
14+
15+
add_library(sycl_profiler_collector SHARED collector.cpp)
16+
target_compile_definitions(sycl_profiler_collector PRIVATE XPTI_CALLBACK_API_EXPORTS)
17+
target_link_libraries(sycl_profiler_collector PRIVATE xptifw)
18+
if (TARGET OpenCL-Headers)
19+
target_link_libraries(sycl_profiler_collector PRIVATE OpenCL-Headers)
20+
endif()
21+
target_include_directories(sycl_profiler_collector PRIVATE
22+
"${sycl_inc_dir}"
23+
"${sycl_src_dir}"
24+
)
25+
26+
target_compile_options(sycl_profiler_collector PRIVATE -g)
27+
28+
add_dependencies(sycl-prof sycl_profiler_collector)
29+
add_dependencies(sycl-toolchain sycl-prof)
30+
31+
include(GNUInstallDirs)
32+
install(TARGETS sycl-prof sycl_profiler_collector
33+
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sycl-prof
34+
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sycl-prof
35+
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sycl-prof
36+
)

sycl/tools/sycl-prof/collector.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//==-------------- collector.cpp -------------------------------------------==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "writer.hpp"
10+
#include "xpti/xpti_data_types.h"
11+
12+
#include <cstdint>
13+
#include <xpti/xpti_trace_framework.h>
14+
15+
#include <chrono>
16+
#include <memory>
17+
#include <stdio.h>
18+
#include <thread>
19+
#include <unistd.h>
20+
21+
namespace chrono = std::chrono;
22+
23+
Writer *GWriter = nullptr;
24+
25+
struct Measurements {
26+
size_t TID;
27+
size_t PID;
28+
size_t TimeStamp;
29+
};
30+
31+
unsigned long process_id() { return static_cast<unsigned long>(getpid()); }
32+
33+
static Measurements measure() {
34+
size_t TID = std::hash<std::thread::id>{}(std::this_thread::get_id());
35+
size_t PID = process_id();
36+
auto Now = chrono::high_resolution_clock::now();
37+
size_t TS = chrono::time_point_cast<chrono::nanoseconds>(Now)
38+
.time_since_epoch()
39+
.count();
40+
41+
return Measurements{TID, PID, TS};
42+
}
43+
44+
XPTI_CALLBACK_API void piBeginEndCallback(uint16_t TraceType,
45+
xpti::trace_event_data_t *,
46+
xpti::trace_event_data_t *,
47+
uint64_t /*Instance*/,
48+
const void *UserData);
49+
XPTI_CALLBACK_API void taskBeginEndCallback(uint16_t TraceType,
50+
xpti::trace_event_data_t *,
51+
xpti::trace_event_data_t *,
52+
uint64_t /*Instance*/,
53+
const void *UserData);
54+
XPTI_CALLBACK_API void waitBeginEndCallback(uint16_t TraceType,
55+
xpti::trace_event_data_t *,
56+
xpti::trace_event_data_t *,
57+
uint64_t /*Instance*/,
58+
const void *UserData);
59+
60+
XPTI_CALLBACK_API void xptiTraceInit(unsigned int /*major_version*/,
61+
unsigned int /*minor_version*/,
62+
const char * /*version_str*/,
63+
const char *StreamName) {
64+
if (GWriter == nullptr) {
65+
GWriter = new JSONWriter(std::getenv("SYCL_PROF_OUT_FILE"));
66+
GWriter->init();
67+
}
68+
69+
if (std::string_view(StreamName) == "sycl.pi") {
70+
uint8_t StreamID = xptiRegisterStream(StreamName);
71+
xptiRegisterCallback(StreamID, xpti::trace_function_begin,
72+
piBeginEndCallback);
73+
xptiRegisterCallback(StreamID, xpti::trace_function_end,
74+
piBeginEndCallback);
75+
} else if (std::string_view(StreamName) == "sycl") {
76+
uint8_t StreamID = xptiRegisterStream(StreamName);
77+
xptiRegisterCallback(StreamID, xpti::trace_task_begin,
78+
taskBeginEndCallback);
79+
xptiRegisterCallback(StreamID, xpti::trace_task_end, taskBeginEndCallback);
80+
xptiRegisterCallback(StreamID, xpti::trace_wait_begin,
81+
waitBeginEndCallback);
82+
xptiRegisterCallback(StreamID, xpti::trace_wait_end, waitBeginEndCallback);
83+
xptiRegisterCallback(StreamID, xpti::trace_barrier_begin,
84+
waitBeginEndCallback);
85+
xptiRegisterCallback(StreamID, xpti::trace_barrier_end,
86+
waitBeginEndCallback);
87+
}
88+
}
89+
90+
XPTI_CALLBACK_API void xptiTraceFinish(const char *) { GWriter->finalize(); }
91+
92+
XPTI_CALLBACK_API void piBeginEndCallback(uint16_t TraceType,
93+
xpti::trace_event_data_t *,
94+
xpti::trace_event_data_t *,
95+
uint64_t /*Instance*/,
96+
const void *UserData) {
97+
auto [TID, PID, TS] = measure();
98+
if (TraceType == xpti::trace_function_begin) {
99+
GWriter->writeBegin(static_cast<const char *>(UserData), "Plugin", PID, TID,
100+
TS);
101+
} else {
102+
GWriter->writeEnd(static_cast<const char *>(UserData), "Plugin", PID, TID,
103+
TS);
104+
}
105+
}
106+
107+
XPTI_CALLBACK_API void taskBeginEndCallback(uint16_t TraceType,
108+
xpti::trace_event_data_t *,
109+
xpti::trace_event_data_t *Event,
110+
uint64_t /*Instance*/,
111+
const void *) {
112+
std::string_view Name = "unknown";
113+
114+
xpti::metadata_t *Metadata = xptiQueryMetadata(Event);
115+
for (auto &Item : *Metadata) {
116+
std::string_view Key{xptiLookupString(Item.first)};
117+
if (Key == "kernel_name" || Key == "memory_object") {
118+
Name = xptiLookupString(Item.second);
119+
}
120+
}
121+
122+
auto [TID, PID, TS] = measure();
123+
if (TraceType == xpti::trace_task_begin) {
124+
GWriter->writeBegin(Name, "SYCL", PID, TID, TS);
125+
} else {
126+
GWriter->writeEnd(Name, "SYCL", PID, TID, TS);
127+
}
128+
}
129+
130+
XPTI_CALLBACK_API void waitBeginEndCallback(uint16_t TraceType,
131+
xpti::trace_event_data_t *,
132+
xpti::trace_event_data_t *,
133+
uint64_t /*Instance*/,
134+
const void *UserData) {
135+
auto [TID, PID, TS] = measure();
136+
if (TraceType == xpti::trace_wait_begin ||
137+
TraceType == xpti::trace_barrier_begin) {
138+
GWriter->writeBegin(static_cast<const char *>(UserData), "SYCL", PID, TID,
139+
TS);
140+
} else {
141+
GWriter->writeEnd(static_cast<const char *>(UserData), "SYCL", PID, TID,
142+
TS);
143+
}
144+
}

sycl/tools/sycl-prof/main.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//==------------ main.cpp - SYCL Profiler Tool -----------------------------==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "launch.hpp"
10+
#include "llvm/Support/CommandLine.h"
11+
12+
#include <iostream>
13+
#include <string>
14+
15+
using namespace llvm;
16+
17+
enum OutputFormatKind { JSON };
18+
19+
int main(int argc, char **argv, char *env[]) {
20+
cl::opt<OutputFormatKind> OutputFormat(
21+
"format", cl::desc("Set profiler output format:"),
22+
cl::values(
23+
// TODO performance summary
24+
clEnumValN(JSON, "json",
25+
"JSON file, compatible with chrome://tracing")));
26+
cl::opt<std::string> OutputFilename("o", cl::desc("Specify output filename"),
27+
cl::value_desc("filename"), cl::Required);
28+
cl::opt<std::string> TargetExecutable(
29+
cl::Positional, cl::desc("<target executable>"), cl::Required);
30+
cl::list<std::string> Argv(cl::ConsumeAfter,
31+
cl::desc("<program arguments>..."));
32+
33+
cl::ParseCommandLineOptions(argc, argv);
34+
35+
std::vector<std::string> NewEnv;
36+
37+
{
38+
size_t I = 0;
39+
while (env[I] != nullptr)
40+
NewEnv.emplace_back(env[I++]);
41+
}
42+
43+
std::string ProfOutFile = "SYCL_PROF_OUT_FILE=" + OutputFilename;
44+
NewEnv.push_back(ProfOutFile);
45+
NewEnv.push_back("XPTI_FRAMEWORK_DISPATCHER=libxptifw.so");
46+
NewEnv.push_back("XPTI_SUBSCRIBERS=libsycl_profiler_collector.so");
47+
NewEnv.push_back("XPTI_TRACE_ENABLE=1");
48+
49+
std::vector<std::string> Args;
50+
51+
Args.push_back(TargetExecutable);
52+
std::copy(Argv.begin(), Argv.end(), std::back_inserter(Args));
53+
54+
int Err = launch(TargetExecutable, Args, NewEnv);
55+
56+
if (Err) {
57+
std::cerr << "Failed to launch target application. Error code " << Err
58+
<< "\n";
59+
return Err;
60+
}
61+
62+
return 0;
63+
}

sycl/tools/sycl-prof/writer.hpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//==----------------- writer.hpp -------------------------------------------==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#pragma once
10+
11+
#include <fstream>
12+
#include <mutex>
13+
#include <string_view>
14+
15+
class Writer {
16+
public:
17+
virtual void init() = 0;
18+
virtual void finalize() = 0;
19+
virtual void writeBegin(std::string_view Name, std::string_view Category,
20+
size_t PID, size_t TID, size_t TimeStamp) = 0;
21+
virtual void writeEnd(std::string_view Name, std::string_view Category,
22+
size_t PID, size_t TID, size_t TimeStamp) = 0;
23+
virtual ~Writer() = default;
24+
};
25+
26+
class JSONWriter : public Writer {
27+
public:
28+
explicit JSONWriter(const std::string &OutPath) : MOutFile(OutPath) {}
29+
30+
void init() final {
31+
std::lock_guard _{MWriteMutex};
32+
33+
MOutFile << "{\n";
34+
MOutFile << " \"traceEvents\": [\n";
35+
}
36+
37+
void writeBegin(std::string_view Name, std::string_view Category, size_t PID,
38+
size_t TID, size_t TimeStamp) override {
39+
std::lock_guard _{MWriteMutex};
40+
41+
if (!MOutFile.is_open())
42+
return;
43+
44+
MOutFile << "{\"name\": \"" << Name << "\", ";
45+
MOutFile << "\"cat\": \"" << Category << "\", ";
46+
MOutFile << "\"ph\": \"B\", ";
47+
MOutFile << "\"pid\": \"" << PID << "\", ";
48+
MOutFile << "\"tid\": \"" << TID << "\", ";
49+
MOutFile << "\"ts\": \"" << TimeStamp << "\"},";
50+
MOutFile << std::endl;
51+
}
52+
53+
void writeEnd(std::string_view Name, std::string_view Category, size_t PID,
54+
size_t TID, size_t TimeStamp) override {
55+
std::lock_guard _{MWriteMutex};
56+
57+
if (!MOutFile.is_open())
58+
return;
59+
60+
MOutFile << "{\"name\": \"" << Name << "\", ";
61+
MOutFile << "\"cat\": \"" << Category << "\", ";
62+
MOutFile << "\"ph\": \"E\", ";
63+
MOutFile << "\"pid\": \"" << PID << "\", ";
64+
MOutFile << "\"tid\": \"" << TID << "\", ";
65+
MOutFile << "\"ts\": \"" << TimeStamp << "\"},";
66+
MOutFile << std::endl;
67+
}
68+
69+
void finalize() final {
70+
std::lock_guard _{MWriteMutex};
71+
72+
if (!MOutFile.is_open())
73+
return;
74+
75+
MOutFile << "],\n";
76+
MOutFile << "\"displayTimeUnit\":\"ns\"\n}\n";
77+
MOutFile.close();
78+
}
79+
80+
~JSONWriter() { finalize(); }
81+
82+
private:
83+
std::mutex MWriteMutex;
84+
std::ofstream MOutFile;
85+
};

0 commit comments

Comments
 (0)