Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions sycl/doc/design/SYCLInstrumentationUsingXPTI.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,22 +221,32 @@ trace point that includes an event, a trace point type and a notification.
can attached a per-instance user data during this notification call that
*must* be guaranteed to be valid for the duration of the notification call.

- To support performance and debug streams, subscribing to the stream **"sycl.debug"**
allows the default streams to contain additional metadata when keeping overheads
to a minimum is not important
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
to a minimum is not important
to a minimum is not important.


This document will outline the protocol for the streams of data being generated
by the SYCL runtime.

## SYCL Stream `"ur.call"` Notification Signatures
## SYCL Stream `"sycl.debug"` Notification Signatures

| Trace Point Type | Parameter Description | Metadata |
| :--------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- |
| `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 `ur.call` 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 |
| `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 `ur.call` 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 |
The "sycl.debug" stream is a dummy stream, when subscribed to, indicates to the SYCL
runtime that additional metadata can be propagated for each SYCL event. Many toolchains
like to keep the overheads low when subscribing to the data and this provides a mechanism
to get more data when keeping overheads low is not important.

## SYCL Stream `"ur.call.debug"` Notification Signatures
## SYCL Stream `"ur.call"` Notification Signatures

| Trace Point Type | Parameter Description | Metadata |
| :------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- |
| `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 `ur.call.debug` layer.</li> <li> **event**: `nullptr` if code location is not available or event ID with code location data.</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 |
| `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 `ur.call.debug` layer.</li> <li> **event**: `nullptr` if code location is not available or event ID with code location data.</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 |
| `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 `ur.call` layer.</li> <li> **event**: `nullptr` if code location is not available or event ID with code location data.</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 |
| `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 `ur.call` layer.</li> <li> **event**: `nullptr` if code location is not available or event ID with code location data.</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 Stream `"ur.call.debug"` Notification Signatures

The `"ur.call.debug"` stream emits the same notifications as the `"ur.call"` stream, but with additional metadata describing the source code location of each traced function call. This enables tools to correlate traced events with their origin in the application's source code for enhanced debugging and analysis.

If a tool subscribes to both `"ur.call"` and `"ur.call.debug"`, only notifications from `"ur.call.debug"` will be delivered to avoid duplication.

## SYCL Stream `"sycl"` Notification Signatures

Expand Down
2 changes: 1 addition & 1 deletion sycl/source/detail/global_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ void GlobalHandler::TraceEventXPTI(const char *Message) {
xpti::framework::tracepoint_scope_t TP(
CodeLocation.fileName(), CodeLocation.functionName(),
CodeLocation.lineNumber(), CodeLocation.columnNumber(), nullptr);

// Notify the subscriber with a diagnostic message when an exception occurs.
TP.stream(detail::GSYCLStreamID)
.traceType(xpti::trace_point_type_t::diagnostics)
.parentEvent(GSYCLCallEvent)
Expand Down
132 changes: 65 additions & 67 deletions sycl/source/detail/queue_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ event queue_impl::memset(void *Ptr, int Value, size_t Count,
.traceType(xpti::trace_point_type_t::node_create)
.parentEvent(detail::GSYCLGraphEvent);

// This information is necessary for memset, so we will not guard it by debug
// stream check.
TP.addMetadata([&](auto TEvent) {
xpti::addMetadata(TEvent, "sycl_device",
reinterpret_cast<size_t>(MDevice.getHandleRef()));
Expand Down Expand Up @@ -223,6 +225,7 @@ event queue_impl::memcpy(void *Dest, const void *Src, size_t Count,
.traceType(xpti::trace_point_type_t::node_create)
.parentEvent(GSYCLGraphEvent);
const char *UserData = "memory_transfer_node::memcpy";
// We will include this metadata information as it is required for memcpy.
TP.addMetadata([&](auto TEvent) {
xpti::addMetadata(TEvent, "sycl_device",
reinterpret_cast<size_t>(MDevice.getHandleRef()));
Expand Down Expand Up @@ -524,33 +527,32 @@ void *queue_impl::instrumentationProlog(const detail::code_location &CodeLoc,
if (!xptiCheckTraceEnabled(StreamID, NotificationTraceType))
return TraceEvent;

xpti::payload_t Payload;
bool HasSourceInfo = false;
xpti_tracepoint_t *Event;
// We try to create a unique string for the wait() call by combining it with
// the queue address
xpti::utils::StringHelper NG;
Name = NG.nameWithAddress<queue_impl *>("queue.wait", this);

if (CodeLoc.fileName()) {
// We have source code location information
Payload =
xpti::payload_t(Name.c_str(), CodeLoc.fileName(), CodeLoc.lineNumber(),
CodeLoc.columnNumber(), (void *)this);
HasSourceInfo = true;
} else {
// We have no location information, so we'll use the address of the queue
Payload = xpti::payload_t(Name.c_str(), (void *)this);
}
bool HasSourceInfo = CodeLoc.fileName() != nullptr;
// wait() calls could be at different user-code locations; We create a new
// event based on the code location info and if this has been seen before, a
// previously created event will be returned.
uint64_t QWaitInstanceNo = 0;
xpti::trace_event_data_t *WaitEvent =
xptiMakeEvent(Name.c_str(), &Payload, xpti::trace_graph_event,
xpti_at::active, &QWaitInstanceNo);
IId = QWaitInstanceNo;
if (WaitEvent) {
xpti::addMetadata(WaitEvent, "sycl_device_type", queueDeviceToString(this));
if (HasSourceInfo) {
Event = xptiCreateTracepoint(CodeLoc.functionName(), CodeLoc.fileName(),
CodeLoc.lineNumber(), CodeLoc.columnNumber(),
(void *)this);
} else {
Event = xptiCreateTracepoint(Name.c_str(), nullptr, 0, 0, (void *)this);
}

IId = xptiGetUniqueId();
auto WaitEvent = Event->event_ref();
// We will allow the device type to be set
xpti::addMetadata(WaitEvent, "sycl_device_type", queueDeviceToString(this));
// We limit the amount of metadata that is added to streams if the
// "sycl.debug" stream is not subscribed to. This improves the performance
// when this data is not required by the tool or the collector.
if (xptiCheckTraceEnabled(detail::GSYCLDebugStreamID)) {
if (HasSourceInfo) {
xpti::addMetadata(WaitEvent, "sym_function_name", CodeLoc.functionName());
xpti::addMetadata(WaitEvent, "sym_source_file_name", CodeLoc.fileName());
Expand All @@ -560,11 +562,11 @@ void *queue_impl::instrumentationProlog(const detail::code_location &CodeLoc,
WaitEvent, "sym_column_no",
static_cast<xpti::object_id_t>((CodeLoc.columnNumber())));
}
xptiNotifySubscribers(StreamID, xpti::trace_wait_begin, nullptr, WaitEvent,
QWaitInstanceNo,
static_cast<const void *>(Name.c_str()));
TraceEvent = (void *)WaitEvent;
}
xptiNotifySubscribers(StreamID, xpti::trace_wait_begin, nullptr, WaitEvent,
IId, static_cast<const void *>(Name.c_str()));
TraceEvent = (void *)WaitEvent;

return TraceEvent;
}

Expand All @@ -587,13 +589,11 @@ void queue_impl::instrumentationEpilog(void *TelemetryEvent, std::string &Name,
void queue_impl::wait(const detail::code_location &CodeLoc) {
(void)CodeLoc;
#ifdef XPTI_ENABLE_INSTRUMENTATION
const bool xptiEnabled = xptiCheckTraceEnabled(GSYCLStreamID);
// xptiCheckTraceEnabled() is being performed in instrumentationProlog().
void *TelemetryEvent = nullptr;
uint64_t IId;
std::string Name;
if (xptiEnabled) {
TelemetryEvent = instrumentationProlog(CodeLoc, Name, GSYCLStreamID, IId);
}
TelemetryEvent = instrumentationProlog(CodeLoc, Name, GSYCLStreamID, IId);
#endif

if (!MGraph.expired()) {
Expand Down Expand Up @@ -673,51 +673,49 @@ void queue_impl::wait(const detail::code_location &CodeLoc) {
}

#ifdef XPTI_ENABLE_INSTRUMENTATION
if (xptiEnabled) {
instrumentationEpilog(TelemetryEvent, Name, GSYCLStreamID, IId);
}
// There is an early return in instrumentationEpilog() if no subscribers are
// subscribing to queue.wait().
instrumentationEpilog(TelemetryEvent, Name, GSYCLStreamID, IId);
#endif
}

void queue_impl::constructorNotification() {
#if XPTI_ENABLE_INSTRUMENTATION
if (xptiTraceEnabled()) {
constexpr uint16_t NotificationTraceType =
static_cast<uint16_t>(xpti::trace_point_type_t::queue_create);
if (xptiCheckTraceEnabled(detail::GSYCLStreamID, NotificationTraceType)) {
xpti::utils::StringHelper SH;
std::string AddrStr = SH.addressAsString<size_t>(MQueueID);
std::string QueueName = SH.nameWithAddressString("queue", AddrStr);
// Create a payload for the queue create event as we do not get code
// location for the queue create event
xpti::payload_t QPayload(QueueName.c_str());
MInstanceID = xptiGetUniqueId();
uint64_t RetInstanceNo;
xpti_td *TEvent =
xptiMakeEvent("queue_create", &QPayload,
(uint16_t)xpti::trace_event_type_t::algorithm,
xpti_at::active, &RetInstanceNo);
// Cache the trace event, stream id and instance IDs for the destructor
MTraceEvent = (void *)TEvent;

xpti::addMetadata(TEvent, "sycl_context",
reinterpret_cast<size_t>(MContext->getHandleRef()));
xpti::addMetadata(TEvent, "sycl_device_name",
MDevice.get_info<info::device::name>());
xpti::addMetadata(TEvent, "sycl_device",
reinterpret_cast<size_t>(MDevice.getHandleRef()));
xpti::addMetadata(TEvent, "is_inorder", MIsInorder);
xpti::addMetadata(TEvent, "queue_id", MQueueID);
xpti::addMetadata(TEvent, "queue_handle",
reinterpret_cast<size_t>(getHandleRef()));
// Also publish to TLS before notification
xpti::framework::stash_tuple(XPTI_QUEUE_INSTANCE_ID_KEY, MQueueID);
xptiNotifySubscribers(detail::GSYCLStreamID,
(uint16_t)xpti::trace_point_type_t::queue_create,
nullptr, TEvent, MInstanceID,
static_cast<const void *>("queue_create"));
}
}
// If there are no subscribers to queue_create, return immediately.
constexpr uint16_t NotificationTraceType =
static_cast<uint16_t>(xpti::trace_point_type_t::queue_create);
if (!xptiCheckTraceEnabled(detail::GSYCLStreamID, NotificationTraceType))
return;
// We do not have CodeLoc for the queue constructor, so we will have to create
// a queue name with the queue ID to create an event; this step can be avoided
// by using CodeLoc.
xpti::utils::StringHelper SH;
std::string AddrStr = SH.addressAsString<size_t>(MQueueID);
std::string QueueName = SH.nameWithAddressString("queue", AddrStr);

xpti_tracepoint_t *Event =
xptiCreateTracepoint(QueueName.c_str(), nullptr, 0, 0, (void *)this);
MInstanceID = xptiGetUniqueId();
xpti_td *TEvent = Event->event_ref();
// Cache the trace event, stream id and instance IDs for the destructor.
MTraceEvent = (void *)TEvent;
// We will allow the queue metadata to be set as this is performed
// infrequently.
xpti::addMetadata(TEvent, "sycl_context",
reinterpret_cast<size_t>(MContext->getHandleRef()));
xpti::addMetadata(TEvent, "sycl_device_name",
MDevice.get_info<info::device::name>());
xpti::addMetadata(TEvent, "sycl_device",
reinterpret_cast<size_t>(MDevice.getHandleRef()));
xpti::addMetadata(TEvent, "is_inorder", MIsInorder);
xpti::addMetadata(TEvent, "queue_id", MQueueID);
xpti::addMetadata(TEvent, "queue_handle",
reinterpret_cast<size_t>(getHandleRef()));
// Also publish to TLS before notification.
xpti::framework::stash_tuple(XPTI_QUEUE_INSTANCE_ID_KEY, MQueueID);
xptiNotifySubscribers(
detail::GSYCLStreamID, (uint16_t)xpti::trace_point_type_t::queue_create,
nullptr, TEvent, MInstanceID, static_cast<const void *>("queue_create"));
#endif
}

Expand Down
Loading