Skip to content

Commit fc9cf52

Browse files
smaslov-intelAlexander Batashev
andauthored
[SYCL][XPTI] Add support for tracing Level Zero API calls (#6023)
Add support for tracing Level Zero API calls to Level Zero plugin. Also modify sycl-trace tool to be able to display both PI and L0 calls simultaneously. Co-authored-by: Alexander Batashev <[email protected]>
1 parent e06d1b5 commit fc9cf52

14 files changed

+1033
-67
lines changed

sycl/doc/design/SYCLInstrumentationUsingXPTI.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,3 +300,23 @@ All trace point types in bold provide semantic information about the graph, node
300300
| `mem_alloc_end` | <div style="text-align: left"><li>**trace_type**: `xpti::trace_point_type_t::mem_alloc_end` that marks the end of memory allocation process</li> <li> **parent**: Event ID created for all functions in the `oneapi.level_zero.experimental.mem_alloc` layer.</li> <li> **event**: `nullptr` - since the stream of data just captures functions being called.</li> <li> **instance**: Unique ID to allow the correlation of the `mem_alloc_begin` event with the `mem_alloc_end` event. This value is guaranteed to be the same value received by the trace event for the corresponding `mem_alloc_begin`.</li> <li> **user_data**: A pointer to `mem_alloc_data_t` object, that includes memory object ID (if any), allocated pointer, allocation size, and guard zone size (if any). </li></div> | None |
301301
| `mem_release_begin` | <div style="text-align: left"><li>**trace_type**: `xpti::trace_point_type_t::mem_release_begin` that marks the beginning of memory allocation process</li> <li> **parent**: Event ID created for all functions in the `oneapi.level_zero.experimental.mem_alloc` layer.</li> <li> **event**: `nullptr` - since the stream of data just captures functions being called.</li> <li> **instance**: Unique ID to allow the correlation of the `mem_release_begin` event with the `mem_release_end` event. </li> <li> **user_data**: A pointer to `mem_alloc_data_t` object, that includes memory object ID (if any) and released pointer. </li></div> | None |
302302
| `mem_release_end` | <div style="text-align: left"><li>**trace_type**: `xpti::trace_point_type_t::mem_release_end` that marks the end of memory allocation process</li> <li> **parent**: Event ID created for all functions in the `oneapi.level_zero.experimental.mem_alloc` layer.</li> <li> **event**: `nullptr` - since the stream of data just captures functions being called.</li> <li> **instance**: Unique ID to allow the correlation of the `mem_release_begin` event with the `mem_release_end` event. This value is guaranteed to be the same value received by the trace event for the corresponding `mem_release_begin`.</li> <li> **user_data**: A pointer to `mem_alloc_data_t` object, that includes memory object ID (if any) and released pointer. </li></div> | None |
303+
304+
## SYCL Stream `"sycl.experimental.level_zero.call"` Notification Signatures
305+
306+
This stream transfers events about Level Zero API calls made by SYCL
307+
application.
308+
309+
| Trace Point Type | Parameter Description | Metadata |
310+
| :--------------: | :-------------------- | :------- |
311+
| `function_begin` | <div style="text-align: left"><li>**trace_type**: `xpti::trace_point_type_t::function_begin` that marks the beginning of a function</li> <li> **parent**: Event ID created for all functions in the `sycl.pi` layer.</li> <li> **event**: `nullptr` - since the stream of data just captures functions being called.</li> <li> **instance**: Unique ID to allow the correlation of the `function_begin` event with the `function_end` event. </li> <li> **user_data**: Name of the function being called sent in as `const char *` </li></div> | None |
312+
| `function_end` | <div style="text-align: left"><li>**trace_type**: `xpti::trace_point_type_t::function_end` that marks the beginning of a function</li> <li> **parent**: Event ID created for all functions in the `sycl.pi` layer.</li> <li> **event**: `nullptr` - since the stream of data just captures functions being called.</li> <li> **instance**: Unique ID to allow the correlation of the `function_begin` event with the `function_end` event. This value is guaranteed to be the same value received by the trace event for the corresponding `function_begin` </li> <li> **user_data**: Name of the function being called sent in as `const char *` </li></div> | None |
313+
314+
## SYCL Stream `"sycl.experimental.level_zero.debug"` Notification Signatures
315+
316+
This stream transfers events about Level Zero API calls and their function
317+
arguments made by SYCL application.
318+
319+
| Trace Point Type | Parameter Description | Metadata |
320+
| :------------------------: | :-------------------- | :------- |
321+
| `function_with_args_begin` | <div style="text-align: left"><li>**trace_type**: `xpti::trace_point_type_t::function_with_args_begin` that marks the beginning of a function</li> <li> **parent**: Event ID created for all functions in the `sycl.pi.debug` layer.</li> <li> **event**: `nullptr` - since the stream of data just captures functions being called.</li> <li> **instance**: Unique ID to allow the correlation of the `function_with_args_begin` event with the `function_with_args_end` event. </li> <li> **user_data**: A pointer to `function_with_args_t` object, that includes function ID, name, and arguments. </li></div> | None |
322+
| `function_with_args_end` | <div style="text-align: left"><li>**trace_type**: `xpti::trace_point_type_t::function_with_args_end` that marks the beginning of a function</li> <li> **parent**: Event ID created for all functions in the `sycl.pi.debug` layer.</li> <li> **event**: `nullptr` - since the stream of data just captures functions being called.</li> <li> **instance**: Unique ID to allow the correlation of the `function_with_args_begin` event with the `function_with_args_end` event. This value is guaranteed to be the same value received by the trace event for the corresponding `function_with_args_begin` </li> <li> **user_data**: A pointer to `function_with_args_t` object, that includes function ID, name, arguments, and return value. </li></div> | None |

sycl/plugins/level_zero/CMakeLists.txt

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,20 +99,46 @@ target_include_directories(LevelZeroLoader-Headers
9999
INTERFACE "${LEVEL_ZERO_INCLUDE_DIR}"
100100
)
101101

102-
find_package(Threads REQUIRED)
102+
if (SYCL_ENABLE_XPTI_TRACING)
103+
set(XPTI_PROXY_SRC "${CMAKE_SOURCE_DIR}/../xpti/src/xpti_proxy.cpp")
104+
endif()
105+
103106
add_sycl_plugin(level_zero
104107
SOURCES
105108
"${sycl_inc_dir}/CL/sycl/detail/pi.h"
106109
"${CMAKE_CURRENT_SOURCE_DIR}/pi_level_zero.cpp"
107110
"${CMAKE_CURRENT_SOURCE_DIR}/pi_level_zero.hpp"
108111
"${CMAKE_CURRENT_SOURCE_DIR}/usm_allocator.cpp"
109112
"${CMAKE_CURRENT_SOURCE_DIR}/usm_allocator.hpp"
113+
"${CMAKE_CURRENT_SOURCE_DIR}/tracing.cpp"
114+
${XPTI_PROXY_SRC}
110115
LIBRARIES
111116
"${LEVEL_ZERO_LOADER}"
112117
Threads::Threads
113118
)
114119

120+
find_package(Python3 REQUIRED)
121+
122+
add_custom_target(ze-api
123+
COMMAND ${Python3_EXECUTABLE}
124+
${CMAKE_CURRENT_SOURCE_DIR}/ze_api_generator.py
125+
${LEVEL_ZERO_INCLUDE_DIR}/level_zero/ze_api.h
126+
BYPRODUCTS
127+
${CMAKE_CURRENT_BINARY_DIR}/ze_api.def
128+
)
129+
target_include_directories(pi_level_zero PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
130+
add_dependencies(pi_level_zero ze-api)
131+
132+
if (SYCL_ENABLE_XPTI_TRACING)
133+
target_compile_definitions(pi_level_zero PRIVATE
134+
XPTI_ENABLE_INSTRUMENTATION
135+
XPTI_STATIC_LIBRARY
136+
)
137+
target_include_directories(pi_level_zero PRIVATE "${CMAKE_SOURCE_DIR}/../xpti/include")
138+
target_link_libraries(pi_level_zero PRIVATE ${CMAKE_DL_LIBS})
139+
endif()
140+
115141
if (TARGET level-zero-loader)
142+
add_dependencies(ze-api level-zero-loader)
116143
add_dependencies(pi_level_zero level-zero-loader)
117144
endif()
118-

sycl/plugins/level_zero/pi_level_zero.cpp

100644100755
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ static pi_result EventCreate(pi_context Context, pi_queue Queue,
3636
bool HostVisible, pi_event *RetEvent);
3737
}
3838

39+
// Defined in tracing.cpp
40+
void enableZeTracing();
41+
void disableZeTracing();
42+
3943
namespace {
4044

4145
// Controls Level Zero calls serialization to w/a Level Zero driver being not MT
@@ -7942,6 +7946,7 @@ pi_result piPluginInit(pi_plugin *PluginInit) {
79427946
(PluginInit->PiFunctionTable).api = (decltype(&::api))(&api);
79437947
#include <CL/sycl/detail/pi.def>
79447948

7949+
enableZeTracing();
79457950
return PI_SUCCESS;
79467951
}
79477952

@@ -8048,6 +8053,8 @@ pi_result piTearDown(void *PluginParameter) {
80488053
}
80498054
if (LeakFound)
80508055
return PI_INVALID_MEM_OBJECT;
8056+
8057+
disableZeTracing();
80518058
return PI_SUCCESS;
80528059
}
80538060

sycl/plugins/level_zero/tracing.cpp

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
//===-------------- tracing.cpp - Level-Zero Host API Tracing --------------==//
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 "xpti/xpti_data_types.h"
10+
#include <exception>
11+
#include <level_zero/layers/zel_tracing_api.h>
12+
#include <level_zero/ze_api.h>
13+
#include <xpti/xpti_trace_framework.h>
14+
15+
#include <iostream>
16+
17+
constexpr auto ZE_CALL_STREAM_NAME = "sycl.experimental.level_zero.call";
18+
constexpr auto ZE_DEBUG_STREAM_NAME = "sycl.experimental.level_zero.debug";
19+
20+
thread_local uint64_t CallCorrelationID = 0;
21+
thread_local uint64_t DebugCorrelationID = 0;
22+
23+
constexpr auto GVerStr = "0.1";
24+
constexpr int GMajVer = 0;
25+
constexpr int GMinVer = 1;
26+
27+
#ifdef XPTI_ENABLE_INSTRUMENTATION
28+
static xpti_td *GCallEvent = nullptr;
29+
static xpti_td *GDebugEvent = nullptr;
30+
#endif // XPTI_ENABLE_INSTRUMENTATION
31+
32+
enum class ZEApiKind {
33+
#define _ZE_API(call, domain, cb, params_type) call,
34+
#include "ze_api.def"
35+
#undef _ZE_API
36+
};
37+
38+
void enableZeTracing() {
39+
#ifdef XPTI_ENABLE_INSTRUMENTATION
40+
if (!xptiTraceEnabled())
41+
return;
42+
43+
xptiRegisterStream(ZE_CALL_STREAM_NAME);
44+
xptiInitialize(ZE_CALL_STREAM_NAME, GMajVer, GMinVer, GVerStr);
45+
xptiRegisterStream(ZE_DEBUG_STREAM_NAME);
46+
xptiInitialize(ZE_DEBUG_STREAM_NAME, GMajVer, GMinVer, GVerStr);
47+
48+
uint64_t Dummy;
49+
xpti::payload_t ZePayload("Level Zero Plugin Layer");
50+
GCallEvent =
51+
xptiMakeEvent("Level Zero Plugin Layer", &ZePayload,
52+
xpti::trace_algorithm_event, xpti_at::active, &Dummy);
53+
54+
xpti::payload_t ZeDebugPayload("Level Zero Plugin Debug Layer");
55+
GDebugEvent =
56+
xptiMakeEvent("Level Zero Plugin Debug Layer", &ZeDebugPayload,
57+
xpti::trace_algorithm_event, xpti_at::active, &Dummy);
58+
59+
ze_result_t Status = zeInit(0);
60+
if (Status != ZE_RESULT_SUCCESS) {
61+
// Most likey there are no Level Zero devices.
62+
return;
63+
}
64+
65+
int Foo = 0;
66+
zel_tracer_desc_t TracerDesc = {ZEL_STRUCTURE_TYPE_TRACER_EXP_DESC, nullptr,
67+
&Foo};
68+
zel_tracer_handle_t Tracer = nullptr;
69+
70+
Status = zelTracerCreate(&TracerDesc, &Tracer);
71+
72+
if (Status != ZE_RESULT_SUCCESS || Tracer == nullptr) {
73+
std::cerr << "[WARNING] Failed to create Level Zero tracer: " << Status
74+
<< "\n";
75+
return;
76+
}
77+
78+
zel_core_callbacks_t Prologue = {};
79+
zel_core_callbacks_t Epilogue = {};
80+
81+
#define _ZE_API(call, domain, cb, params_type) \
82+
Prologue.domain.cb = [](params_type *Params, ze_result_t, void *, void **) { \
83+
if (xptiTraceEnabled()) { \
84+
uint8_t CallStreamID = xptiRegisterStream(ZE_CALL_STREAM_NAME); \
85+
uint8_t DebugStreamID = xptiRegisterStream(ZE_DEBUG_STREAM_NAME); \
86+
CallCorrelationID = xptiGetUniqueId(); \
87+
DebugCorrelationID = xptiGetUniqueId(); \
88+
const char *FuncName = #call; \
89+
xptiNotifySubscribers( \
90+
CallStreamID, (uint16_t)xpti::trace_point_type_t::function_begin, \
91+
GCallEvent, nullptr, CallCorrelationID, FuncName); \
92+
uint32_t FuncID = static_cast<uint32_t>(ZEApiKind::call); \
93+
xpti::function_with_args_t Payload{FuncID, FuncName, Params, nullptr, \
94+
nullptr}; \
95+
xptiNotifySubscribers( \
96+
DebugStreamID, \
97+
(uint16_t)xpti::trace_point_type_t::function_with_args_begin, \
98+
GDebugEvent, nullptr, DebugCorrelationID, &Payload); \
99+
} \
100+
}; \
101+
Epilogue.domain.cb = [](params_type *Params, ze_result_t Result, void *, \
102+
void **) { \
103+
if (xptiTraceEnabled()) { \
104+
uint8_t CallStreamID = xptiRegisterStream(ZE_CALL_STREAM_NAME); \
105+
uint8_t DebugStreamID = xptiRegisterStream(ZE_DEBUG_STREAM_NAME); \
106+
const char *FuncName = #call; \
107+
xptiNotifySubscribers(CallStreamID, \
108+
(uint16_t)xpti::trace_point_type_t::function_end, \
109+
GCallEvent, nullptr, CallCorrelationID, FuncName); \
110+
uint32_t FuncID = static_cast<uint32_t>(ZEApiKind::call); \
111+
xpti::function_with_args_t Payload{FuncID, FuncName, Params, &Result, \
112+
nullptr}; \
113+
xptiNotifySubscribers( \
114+
DebugStreamID, \
115+
(uint16_t)xpti::trace_point_type_t::function_with_args_end, \
116+
GDebugEvent, nullptr, DebugCorrelationID, &Payload); \
117+
} \
118+
};
119+
120+
#include "ze_api.def"
121+
122+
#undef _ZE_API
123+
124+
Status = zelTracerSetPrologues(Tracer, &Prologue);
125+
if (Status != ZE_RESULT_SUCCESS) {
126+
std::cerr << "Failed to enable Level Zero tracing\n";
127+
std::terminate();
128+
}
129+
Status = zelTracerSetEpilogues(Tracer, &Epilogue);
130+
if (Status != ZE_RESULT_SUCCESS) {
131+
std::cerr << "Failed to enable Level Zero tracing\n";
132+
std::terminate();
133+
}
134+
135+
Status = zelTracerSetEnabled(Tracer, true);
136+
if (Status != ZE_RESULT_SUCCESS) {
137+
std::cerr << "Failed to enable Level Zero tracing\n";
138+
std::terminate();
139+
}
140+
#endif // XPTI_ENABLE_INSTRUMENTATION
141+
}
142+
143+
void disableZeTracing() {
144+
#ifdef XPTI_ENABLE_INSTRUMENTATION
145+
if (!xptiTraceEnabled())
146+
return;
147+
148+
xptiFinalize(ZE_CALL_STREAM_NAME);
149+
xptiFinalize(ZE_DEBUG_STREAM_NAME);
150+
#endif // XPTI_ENABLE_INSTRUMENTATION
151+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import re
2+
import sys
3+
4+
def camel_to_snake(src):
5+
return re.sub(r'(?<!^)(?=[A-Z])', '_', src).lower()
6+
7+
def snake_to_camel(src):
8+
temp = src.split('_')
9+
return ''.join(x.title() for x in temp)
10+
11+
12+
def extract_ze_apis(header):
13+
"""
14+
Emit file with contents of
15+
_ZE_API(api_name, api_domain, cb, param_type)
16+
"""
17+
api = open("ze_api.def", "w")
18+
19+
matches = re.finditer(r'typedef struct _ze_([_a-z]+)_callbacks_t\n\{\n([a-zA-Z_;\s\n]+)\n\} ze_([_a-z]+)_callbacks_t;', header)
20+
21+
for match in matches:
22+
api_domain = snake_to_camel(match.group(1))
23+
for l in match.group(2).splitlines():
24+
parts = l.split()
25+
api_match = re.match(r'ze_pfn([a-zA-Z]+)Cb_t', parts[0])
26+
api_name_tail = api_match.group(1)
27+
api_name = 'ze' + api_name_tail
28+
29+
param_type = 'ze_' + camel_to_snake(api_name_tail) + '_params_t'
30+
31+
cb = 'pfn' + api_name_tail.replace(api_domain, '') + 'Cb'
32+
33+
api.write("_ZE_API({}, {}, {}, {})\n".format(api_name, api_domain, cb, param_type))
34+
35+
api.close()
36+
37+
if __name__ == "__main__":
38+
with open(sys.argv[1], 'r') as f:
39+
header = f.read()
40+
extract_ze_apis(header)

sycl/tools/sycl-prof/collector.cpp

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ static Measurements measure() {
4242
return Measurements{TID, PID, TS};
4343
}
4444

45-
XPTI_CALLBACK_API void piBeginEndCallback(uint16_t TraceType,
46-
xpti::trace_event_data_t *,
47-
xpti::trace_event_data_t *,
48-
uint64_t /*Instance*/,
49-
const void *UserData);
45+
XPTI_CALLBACK_API void apiBeginEndCallback(uint16_t TraceType,
46+
xpti::trace_event_data_t *,
47+
xpti::trace_event_data_t *,
48+
uint64_t /*Instance*/,
49+
const void *UserData);
5050
XPTI_CALLBACK_API void taskBeginEndCallback(uint16_t TraceType,
5151
xpti::trace_event_data_t *,
5252
xpti::trace_event_data_t *,
@@ -71,13 +71,14 @@ XPTI_CALLBACK_API void xptiTraceInit(unsigned int /*major_version*/,
7171
GWriter->init();
7272
}
7373

74-
if (std::string_view(StreamName) == "sycl.pi") {
74+
std::string_view NameView{StreamName};
75+
if (NameView == "sycl.pi") {
7576
uint8_t StreamID = xptiRegisterStream(StreamName);
7677
xptiRegisterCallback(StreamID, xpti::trace_function_begin,
77-
piBeginEndCallback);
78+
apiBeginEndCallback);
7879
xptiRegisterCallback(StreamID, xpti::trace_function_end,
79-
piBeginEndCallback);
80-
} else if (std::string_view(StreamName) == "sycl") {
80+
apiBeginEndCallback);
81+
} else if (NameView == "sycl") {
8182
uint8_t StreamID = xptiRegisterStream(StreamName);
8283
xptiRegisterCallback(StreamID, xpti::trace_task_begin,
8384
taskBeginEndCallback);
@@ -89,23 +90,28 @@ XPTI_CALLBACK_API void xptiTraceInit(unsigned int /*major_version*/,
8990
waitBeginEndCallback);
9091
xptiRegisterCallback(StreamID, xpti::trace_barrier_end,
9192
waitBeginEndCallback);
93+
} else if (NameView == "sycl.experimental.level_zero.call") {
94+
uint8_t StreamID = xptiRegisterStream(StreamName);
95+
xptiRegisterCallback(StreamID, xpti::trace_function_begin,
96+
apiBeginEndCallback);
97+
xptiRegisterCallback(StreamID, xpti::trace_function_end,
98+
apiBeginEndCallback);
9299
}
93100
}
94101

95102
XPTI_CALLBACK_API void xptiTraceFinish(const char *) { GWriter->finalize(); }
96103

97-
XPTI_CALLBACK_API void piBeginEndCallback(uint16_t TraceType,
98-
xpti::trace_event_data_t *,
99-
xpti::trace_event_data_t *,
100-
uint64_t /*Instance*/,
101-
const void *UserData) {
104+
XPTI_CALLBACK_API void apiBeginEndCallback(uint16_t TraceType,
105+
xpti::trace_event_data_t *,
106+
xpti::trace_event_data_t *,
107+
uint64_t /*Instance*/,
108+
const void *UserData) {
102109
auto [TID, PID, TS] = measure();
103110
if (TraceType == xpti::trace_function_begin) {
104-
GWriter->writeBegin(static_cast<const char *>(UserData), "Plugin", PID, TID,
111+
GWriter->writeBegin(static_cast<const char *>(UserData), "API", PID, TID,
105112
TS);
106113
} else {
107-
GWriter->writeEnd(static_cast<const char *>(UserData), "Plugin", PID, TID,
108-
TS);
114+
GWriter->writeEnd(static_cast<const char *>(UserData), "API", PID, TID, TS);
109115
}
110116
}
111117

sycl/tools/sycl-prof/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ int main(int argc, char **argv, char *env[]) {
4545
NewEnv.push_back("XPTI_FRAMEWORK_DISPATCHER=libxptifw.so");
4646
NewEnv.push_back("XPTI_SUBSCRIBERS=libsycl_profiler_collector.so");
4747
NewEnv.push_back("XPTI_TRACE_ENABLE=1");
48+
NewEnv.push_back("ZE_ENABLE_TRACING_LAYER=1");
4849

4950
std::vector<std::string> Args;
5051

0 commit comments

Comments
 (0)