Skip to content

Commit b28ba4a

Browse files
authored
[XPTI][INFRA] Refactored API implementations to improve performance (#19160)
As a part of the performance study to improve SYCL runtime performance, it was documented that XPTI when enabled by a tool or collector was having higher than usual performance impact. This is the second PR to address the specific xptiMakeEvent* API and current implementation is ~30% faster than previous implementation. The last PR in this series will change the instrumentation in SYCL to address the large performance impact caused by mixing debug and performance streams in the runtime today. - Also fixes issues in XPTI samples due to changes in PR#18786 --------- Signed-off-by: Vasanth Tovinkere <[email protected]>
1 parent dc2fc8d commit b28ba4a

File tree

9 files changed

+599
-141
lines changed

9 files changed

+599
-141
lines changed

xpti/include/xpti/xpti_data_types.h

Lines changed: 122 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ enum class payload_flag_t {
358358
//
359359
using trace_point_t = uint16_t;
360360
using event_type_t = uint16_t;
361-
using string_id_t = int32_t;
361+
using string_id_t = uint32_t;
362362
using object_id_t = int32_t;
363363

364364
using safe_flag_t = std::atomic<bool>;
@@ -455,60 +455,105 @@ struct payload_t {
455455

456456
payload_t() = default;
457457

458-
// If the address of the kernel/function name is provided, we mark it as
459-
// valid since we can potentially reconstruct the name and the source file
460-
// information during post-processing step of symbol resolution; this
461-
// indicates a partial but valid payload.
458+
///
459+
/// @brief Constructs a payload_t with only a code pointer.
460+
///
461+
/// @details
462+
/// Initializes the payload with the provided code pointer address. All other
463+
/// fields (name, source file, line number, column number) are set to invalid
464+
/// or null values. If a valid code pointer is provided, the corresponding
465+
/// flag is set to indicate its availability.
466+
///
467+
/// If the address of the kernel/function name is provided, we mark it as
468+
/// valid since we can potentially reconstruct the name and the source file
469+
/// information during post-processing step of symbol resolution; this
470+
/// indicates a partial but valid payload.
471+
///
472+
/// @param codeptr Pointer to the code location associated with this payload.
473+
///
462474
payload_t(const void *codeptr) {
463-
code_ptr_va = codeptr;
464-
name = nullptr; ///< Invalid name string pointer
465-
source_file = nullptr; ///< Invalid source file string pointer
466-
line_no = invalid_id<>; ///< Invalid line number
467-
column_no = invalid_id<>; ///< Invalid column number
475+
code_ptr_va = codeptr; ///< Override the default initialization
476+
// If the incoming code ptr is null, we ensure that the flags are set
477+
// correctly
468478
if (codeptr) {
469479
flags = (uint64_t)payload_flag_t::CodePointerAvailable;
470480
}
471481
}
472482

473-
// If neither an address or the fully identifyable source file name and
474-
// location are not available, we take in the name of the
475-
// function/task/user-defined name as input and create a hash from it. We
476-
// mark it as valid since we can display the name in a timeline view, but
477-
// the payload is considered to be a partial but valid payload.
483+
///
484+
/// @brief Constructs a payload_t with only a function or kernel name.
485+
///
486+
/// @details
487+
/// Initializes the payload with the provided function or kernel name.
488+
/// If neither an address or the fully identifyable source file name and
489+
/// location are not available, we take in the name of the
490+
/// function/task/user-defined name as input and create a hash from it. We
491+
/// mark it as valid since we can display the name in a timeline view, but
492+
/// the payload is considered to be a partial but valid payload.
493+
///
494+
/// @param func_name Name of the function, kernel, or user-defined entity.
495+
///
478496
payload_t(const char *func_name) {
479-
code_ptr_va = nullptr;
480-
name = func_name; ///< Invalid name string pointer
481-
source_file = nullptr; ///< Invalid source file string pointer
497+
name = func_name; ///< Override the default initialization
498+
// If the incoming name is null, we ensure that the flags are set correctly
482499
if (func_name) {
483500
flags = (uint64_t)(payload_flag_t::NameAvailable);
484501
}
485502
}
486503

504+
///
505+
/// @brief Constructs a payload_t with a function or kernel name and code
506+
/// pointer.
507+
///
508+
/// @details
509+
/// Initializes the payload with the provided function or kernel name and code
510+
/// pointer. The source file is set to null. Flags are set to indicate which
511+
/// fields are available. The payload is considered partial, but valid.
512+
///
513+
/// @param func_name Name of the function, kernel, or user-defined entity.
514+
/// @param codeptr Pointer to the code location associated with this payload.
515+
///
487516
payload_t(const char *func_name, const void *codeptr) {
488-
code_ptr_va = codeptr;
489-
name = func_name; ///< Invalid name string pointer
490-
source_file = nullptr; ///< Invalid source file string pointer
517+
code_ptr_va = codeptr; ///< Override the default initialization
518+
name = func_name; ///< Override the default initialization
519+
// If the incoming name is null, we ensure that the flags are set correctly
491520
if (func_name) {
492521
flags = (uint64_t)(payload_flag_t::NameAvailable);
493522
}
523+
// If the incoming code ptr is null, we ensure that the flags are set
524+
// correctly
494525
if (codeptr) {
495526
flags |= (uint64_t)payload_flag_t::CodePointerAvailable;
496527
}
497528
}
498529

499-
// When the end user opts out of preserving the code location information and
500-
// the KernelInfo is not available from the given entry point, we will rely
501-
// on dynamic backtrace as a possibility. In this case, we send in the
502-
// caller/callee information as a string in the form "caller->callee" that
503-
// will be used to generate the unique ID.
530+
///
531+
/// @brief Constructs a payload_t with a name, stack trace, and code pointer.
532+
///
533+
/// @details
534+
/// Used when code location information is not preserved and dynamic backtrace
535+
/// is used. Initializes the payload with the provided kernel name,
536+
/// caller-callee stack trace, and code pointer. Sets flags to indicate which
537+
/// fields are available.
538+
///
539+
/// When the end user opts out of preserving the code location information and
540+
/// the KernelInfo is not available from the given entry point, we will rely
541+
/// on dynamic backtrace as a possibility. In this case, we send in the
542+
/// caller/callee information as a string in the form "caller->callee" that
543+
/// will be used to generate the unique ID.
544+
///
545+
/// @param kname Name of the kernel or function.
546+
/// @param caller_callee String representing the caller->callee relationship.
547+
/// @param codeptr Pointer to the code location associated with this payload.
548+
///
504549
payload_t(const char *kname, const char *caller_callee, const void *codeptr) {
505550
if (codeptr) {
506-
code_ptr_va = codeptr;
551+
code_ptr_va = codeptr; ///< Override the default initialization
507552
flags |= (uint64_t)payload_flag_t::CodePointerAvailable;
508553
}
509-
/// Capture the rest of the parameters
554+
// If the incoming name is null, we ensure that the flags are set correctly
510555
if (kname) {
511-
name = kname;
556+
name = kname; ///< Override the default initialization
512557
flags |= (uint64_t)payload_flag_t::NameAvailable;
513558
}
514559
if (caller_callee) {
@@ -517,34 +562,69 @@ struct payload_t {
517562
}
518563
}
519564

520-
// We need the payload to contain at the very least, the code pointer
521-
// information of the kernel or function. In the full payload case, we will
522-
// also have the function name and source file name along with the line and
523-
// column number of the trace point that forms the payload.
565+
///
566+
/// @brief Constructs a fully populated payload_t.
567+
///
568+
/// @details
569+
/// Initializes the payload with the provided kernel name, source file, line
570+
/// number, column number, and optionally a code pointer. Sets flags to
571+
/// indicate which fields are available. This would constitute a fully
572+
/// populated payload.
573+
///
574+
/// @param kname Name of the kernel or function.
575+
/// @param sf Source file name.
576+
/// @param line Line number in the source file.
577+
/// @param col Column number in the source file.
578+
/// @param codeptr (Optional) Pointer to the code location associated with
579+
/// this payload.
580+
///
524581
payload_t(const char *kname, const char *sf, int line, int col,
525582
const void *codeptr = nullptr) {
526-
code_ptr_va = codeptr;
527-
/// Capture the rest of the parameters
528-
name = kname;
529-
source_file = sf;
530-
line_no = static_cast<uint32_t>(line);
531-
column_no = static_cast<uint32_t>(col);
583+
code_ptr_va = codeptr; ///< Override the default initialization
584+
name = kname; ///< Override the default initialization
585+
source_file = sf; ///< Override the default initialization
586+
line_no =
587+
static_cast<uint32_t>(line); ///< Override the default initialization
588+
column_no =
589+
static_cast<uint32_t>(col); ///< Override the default initialization
590+
// If the incoming name is null, we ensure that the flags are set correctly
532591
if (kname && kname[0] != '\0') {
533592
flags = (uint64_t)payload_flag_t::NameAvailable;
534593
}
594+
// If the incoming source file name is null, we ensure that the flags are
595+
// set correctly
535596
if (sf && sf[0] != '\0') {
536597
flags |= (uint64_t)payload_flag_t::SourceFileAvailable |
537598
(uint64_t)payload_flag_t::LineInfoAvailable |
538599
(uint64_t)payload_flag_t::ColumnInfoAvailable;
539600
}
601+
// If the incoming code ptr is null, we ensure that the flags are set
602+
// correctly
540603
if (codeptr) {
541604
flags |= (uint64_t)payload_flag_t::CodePointerAvailable;
542605
}
543606
}
544607

545-
int32_t name_sid() const { return (int32_t)(uid.p2 & 0x00000000ffffffff); }
546-
int32_t stacktrace_sid() const { return (int32_t)(uid.p2 >> 32); }
547-
int32_t source_file_sid() const { return (int32_t)(uid.p1 >> 32); }
608+
/// @brief Retrieves the string ID (SID) for the name.
609+
/// @details Extracts the lower 32 bits of the uid.p2 member, which represents
610+
/// the xpti::string_id_t in legacy implementations or XPTI_USE_STRICT_HASH
611+
/// mode and contains the FNV1a hash value associated with the name.
612+
/// @return The 32-bit name string ID.
613+
uint32_t name_sid() const { return (uint32_t)(uid.p2 & 0x00000000ffffffff); }
614+
615+
/// @brief Retrieves the string ID (SID) for the stack trace.
616+
/// @details Extracts the upper 32 bits of the uid.p2 member, which represents
617+
/// the xpti::string_id_t in legacy implementations or XPTI_USE_STRICT_HASH
618+
/// mode and contains the FNV1a hash value associated with the stack trace.
619+
/// @return The 32-bit stack trace string ID.
620+
uint32_t stacktrace_sid() const { return (uint32_t)(uid.p2 >> 32); }
621+
622+
/// @brief Retrieves the string ID (SID) for the source file.
623+
/// @details Extracts the upper 32 bits of the uid.p1 member, which represents
624+
/// the xpti::string_id_t in legacy implementations or XPTI_USE_STRICT_HASH
625+
/// mode and contains the FNV1a hash value associated with the source file.
626+
/// @return The 32-bit source file string ID.
627+
uint32_t source_file_sid() const { return (uint32_t)(uid.p1 >> 32); }
548628
};
549629

550630
/// A data structure that holds information about an API function call and its

xpti/include/xpti/xpti_trace_framework.h

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,11 @@ XPTI_EXPORT_API const xpti_trace_event_t *xptiLookupEvent(uint64_t uid);
696696
/// @param column_no A uint32_t value representing the column number on the
697697
/// specified line where the tracepoint is located. This provides the most
698698
/// precise location of the tracepoint.
699+
/// @param code_ptr_va A void pointer representing the virtual address of the
700+
/// code where the tracepoint is located. This can be used to associate the
701+
/// tracepoint with a specific code location in memory, which is particularly
702+
/// useful for debugging and performance analysis. If not provided, it defaults
703+
/// to nullptr, indicating that the code pointer is not specified.
699704
///
700705
/// @return A pointer to the created `xpti_tracepoint_t` structure, which
701706
/// represents the tracepoint. If the tracepoint cannot be created, the function
@@ -704,10 +709,10 @@ XPTI_EXPORT_API const xpti_trace_event_t *xptiLookupEvent(uint64_t uid);
704709
/// @note In order to preserve ABI compatibility, an interface pointer to
705710
/// `xpti_tracepoint_t` is returned.
706711

707-
XPTI_EXPORT_API xpti_tracepoint_t *xptiCreateTracepoint(const char *func_name,
708-
const char *file_name,
709-
uint32_t line_no,
710-
uint32_t column_no);
712+
XPTI_EXPORT_API xpti_tracepoint_t *
713+
xptiCreateTracepoint(const char *func_name, const char *file_name,
714+
uint32_t line_no, uint32_t column_no,
715+
void *code_ptr_va = nullptr);
711716

712717
/// @brief Deletes a tracepoint object.
713718
///
@@ -916,12 +921,16 @@ XPTI_EXPORT_API void xptiUnsetTracepointScopeData();
916921
/// @param columnNo The column number in the source file where the trace point
917922
/// is defined. If column information is not available, this can
918923
/// be set to 0.
924+
/// @param codePtrVa The code pointer value associated with the trace point.
925+
/// If code pointer information is not available, this can
926+
/// be set to nullptr.
919927
/// @return Returns a pointer to the registered `xpti_tracepoint_t` structure if
920928
/// the registration is successful; otherwise, returns NULL. The
921929
/// returned pointer should not be freed by the caller.
922930
XPTI_EXPORT_API const xpti_tracepoint_t *
923931
xptiRegisterTracepointScope(const char *funcName, const char *fileName,
924-
uint32_t lineNo, uint32_t columnNo);
932+
uint32_t lineNo, uint32_t columnNo,
933+
void *codePtrVa = nullptr);
925934

926935
/// @brief Retrieves the default stream ID.
927936
/// @details This function is used to get the default stream ID that is
@@ -1042,7 +1051,8 @@ typedef xpti::result_t (*xpti_make_key_from_payload_t)(xpti::payload_t *,
10421051
xpti::uid128_t *);
10431052
typedef const xpti_tracepoint_t *(*xpti_get_trace_point_scope_data_t)();
10441053
typedef const xpti_tracepoint_t *(*xpti_register_tracepoint_scope_t)(
1045-
const char *func, const char *file, uint32_t line, uint32_t col);
1054+
const char *func, const char *file, uint32_t line, uint32_t col,
1055+
void *code_ptr_va);
10461056
typedef xpti::result_t (*xpti_set_trace_point_scope_data_t)(
10471057
xpti_tracepoint_t *);
10481058
typedef void (*xpti_unset_trace_point_scope_data_t)();
@@ -1059,6 +1069,6 @@ typedef const xpti_payload_t *(*xpti_lookup_payload_t)(uint64_t);
10591069
typedef const xpti_trace_event_t *(*xpti_lookup_event_t)(uint64_t);
10601070
typedef xpti_tracepoint_t *(*xpti_create_tracepoint_t)(const char *,
10611071
const char *, uint32_t,
1062-
uint32_t);
1072+
uint32_t, void *);
10631073
typedef xpti::result_t (*xpti_delete_tracepoint_t)(xpti_tracepoint_t *);
10641074
}

xpti/include/xpti/xpti_trace_framework.hpp

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ typedef void *xpti_plugin_function_t;
5656
#endif
5757

5858
namespace xpti {
59+
constexpr const char *g_unknown_function = "<unknown-function>";
60+
constexpr const char *g_unknown_file = "<unknown-file>";
5961
namespace utils {
6062
/// @class StringHelper
6163
/// @brief A helper class for string manipulations.
@@ -575,12 +577,28 @@ inline std::string readMetadata(const metadata_t::value_type &MD) {
575577
inline bool is_valid_payload(const xpti::payload_t *Payload) {
576578
if (!Payload)
577579
return false;
578-
else
579-
return (Payload->flags != 0) &&
580-
((Payload->flags &
581-
static_cast<uint64_t>(payload_flag_t::SourceFileAvailable) ||
582-
(Payload->flags &
583-
static_cast<uint64_t>(payload_flag_t::NameAvailable))));
580+
else {
581+
bool isValid = false;
582+
bool hasSourceFile =
583+
(Payload->flags &
584+
static_cast<uint64_t>(payload_flag_t::SourceFileAvailable)) &&
585+
Payload->source_file;
586+
bool hasName = (Payload->flags &
587+
static_cast<uint64_t>(payload_flag_t::NameAvailable)) &&
588+
Payload->name;
589+
bool hasCodePtrVa =
590+
(Payload->flags &
591+
static_cast<uint64_t>(payload_flag_t::CodePointerAvailable)) &&
592+
Payload->code_ptr_va;
593+
bool hasLineInfo =
594+
(Payload->flags &
595+
static_cast<uint64_t>(payload_flag_t::LineInfoAvailable)) &&
596+
Payload->line_no != xpti::invalid_id<uint32_t>;
597+
// We ignore checking for column info as it may not always be available
598+
isValid = ((hasSourceFile && hasLineInfo) || hasName || hasCodePtrVa);
599+
600+
return isValid;
601+
}
584602
}
585603

586604
/// @brief Generates a default payload object with unknown details.
@@ -595,11 +613,11 @@ inline bool is_valid_payload(const xpti::payload_t *Payload) {
595613
///
596614
/// @return A `xpti::payload_t` object with its members set to represent an
597615
/// unknown or unspecified payload. This includes setting the function name and
598-
/// file name to "unknown", line and column numbers to 0, and the module handle
599-
/// to nullptr.
616+
/// file name to "<unknown-function>" and "<unknown-file>" respectively, line
617+
/// and column numbers to 0, and the module handle to nullptr.
600618
///
601619
inline xpti::payload_t unknown_payload() {
602-
xpti::payload_t Payload("unknown", "unknown-file", 0, 0, nullptr);
620+
xpti::payload_t Payload(g_unknown_function, g_unknown_file, 0, 0, nullptr);
603621
return Payload;
604622
}
605623

@@ -951,7 +969,8 @@ class tracepoint_scope_t {
951969
/// @note MSFT compiler 2019/2022 support __builtin_FUNCTION() macro
952970
///
953971
tracepoint_scope_t(const char *fileName, const char *funcName, int line,
954-
int column, bool selfNotify = false,
972+
int column, void *codePtrVa = nullptr,
973+
bool selfNotify = false,
955974
const char *callerFuncName = __builtin_FUNCTION())
956975
: MTop(false), MSelfNotify(selfNotify), MCallerFuncName(callerFuncName) {
957976
if (!xptiTraceEnabled())
@@ -962,9 +981,9 @@ class tracepoint_scope_t {
962981
if (!MData) {
963982
if (funcName && fileName)
964983
init(funcName, fileName, static_cast<uint32_t>(line),
965-
static_cast<uint32_t>(column));
984+
static_cast<uint32_t>(column), codePtrVa);
966985
else
967-
init(callerFuncName, nullptr, 0u, 0u);
986+
init(callerFuncName, nullptr, 0u, 0u, codePtrVa);
968987
} else {
969988
MTraceEvent = MData->event_ref();
970989
}
@@ -1014,11 +1033,11 @@ class tracepoint_scope_t {
10141033
/// tracepoint data.
10151034
///
10161035
void init(const char *FuncName, const char *FileName, uint32_t LineNo,
1017-
uint32_t ColumnNo) {
1036+
uint32_t ColumnNo, void *CodePtrVa) {
10181037
// Register the payload and prepare the tracepoint data. The function
10191038
// returns a UID, associated payload and trace event
1020-
MData = const_cast<xpti_tracepoint_t *>(
1021-
xptiRegisterTracepointScope(FuncName, FileName, LineNo, ColumnNo));
1039+
MData = const_cast<xpti_tracepoint_t *>(xptiRegisterTracepointScope(
1040+
FuncName, FileName, LineNo, ColumnNo, CodePtrVa));
10221041
if (MData) {
10231042
// Set the tracepoint scope with the prepared data so all nested functions
10241043
// will have access to it; this call also sets the Universal ID separately

0 commit comments

Comments
 (0)