Skip to content

Commit c91b3b8

Browse files
author
Alexander Batashev
authored
[SYCL][XPTI] Revisit resource management strategy (intel#4494)
Global objects are typically destroyed on process tear down, but the order of the destructor calls is undefined. Since SYCL applications can potentially call SYCL APIs from global context, XPTI and all of its resources have to outlive user application. This patch refactors XPTI proxy library and framework to allocate global objects on heap and manages their lifetime based on communications from traced application. Each user of XPTI (e.g. SYCL runtime) has to call xptiFrameworkInitialize() once prior to any other XPTI API. When application is done collecting trace information, it must close streams and then call xptiFrameworkFinalize(). The XPTI framework will maintain a reference counter, and will only free resources and unload libraries, when the counter hits 0. This will allow the subscribers to survive past DllMain call or global shared library destructor.
1 parent 75eda57 commit c91b3b8

File tree

6 files changed

+205
-65
lines changed

6 files changed

+205
-65
lines changed

sycl/source/detail/pi.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,8 @@ static void initializePlugins(std::vector<plugin> &Plugins) {
435435
}
436436

437437
#ifdef XPTI_ENABLE_INSTRUMENTATION
438+
GlobalHandler::instance().getXPTIRegistry().initializeFrameworkOnce();
439+
438440
if (!(xptiTraceEnabled() && !XPTIInitDone))
439441
return;
440442
// Not sure this is the best place to initialize the framework; SYCL runtime

sycl/source/detail/xpti_registry.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#pragma once
1010

11+
#include <mutex>
1112
#include <string>
1213
#include <unordered_set>
1314

@@ -31,6 +32,12 @@ inline constexpr const char *SYCL_PIDEBUGCALL_STREAM_NAME = "sycl.pi.debug";
3132

3233
class XPTIRegistry {
3334
public:
35+
void initializeFrameworkOnce() {
36+
#ifdef XPTI_ENABLE_INSTRUMENTATION
37+
std::call_once(MInitialized, [] { xptiFrameworkInitialize(); });
38+
#endif
39+
}
40+
3441
/// Notifies XPTI subscribers about new stream.
3542
///
3643
/// \param StreamName is a name of newly initialized stream.
@@ -50,11 +57,13 @@ class XPTIRegistry {
5057
for (const auto &StreamName : MActiveStreams) {
5158
xptiFinalize(StreamName.c_str());
5259
}
60+
xptiFrameworkFinalize();
5361
#endif // XPTI_ENABLE_INSTRUMENTATION
5462
}
5563

5664
private:
5765
std::unordered_set<std::string> MActiveStreams;
66+
std::once_flag MInitialized;
5867
};
5968
} // namespace detail
6069
} // namespace sycl

xpti/include/xpti/xpti_trace_framework.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,20 @@
3535

3636
extern "C" {
3737

38+
/// @brief Initializes XPTI framework.
39+
/// @details Initialize XPTI framework resources. Each user of XPTI must call
40+
/// this function prior to any other XPTI API call. It is framework's
41+
/// responsibility to ensure that resources are initialized once. Each call to
42+
/// this function must have corresponding call to xptiFrameworkFinalize() to
43+
/// ensure resources are freed.
44+
XPTI_EXPORT_API void xptiFrameworkInitialize();
45+
46+
/// @brief Deinitializes XPTI framework.
47+
/// @details Call to this function decrements framework's internal reference
48+
/// counter. Once its value is equal to zero, XPTI framework can release
49+
/// resources and unload subscribers.
50+
XPTI_EXPORT_API void xptiFrameworkFinalize();
51+
3852
/// @brief Initialization function that is called when a new stream is generated
3953
/// @details When a runtime or application that uses XPTI instrumentation API
4054
/// starts to generate a new stream, a call to xptiInitialize() must be made to
@@ -414,6 +428,8 @@ XPTI_EXPORT_API void xptiReset();
414428
/// The proxy/stub library does not implement this function.
415429
XPTI_EXPORT_API void xptiForceSetTraceEnabled(bool yesOrNo);
416430

431+
typedef xpti::result_t (*xpti_framework_initialize_t)();
432+
typedef xpti::result_t (*xpti_framework_finalize_t)();
417433
typedef xpti::result_t (*xpti_initialize_t)(const char *, uint32_t, uint32_t,
418434
const char *);
419435
typedef void (*xpti_finalize_t)(const char *);

xpti/include/xpti/xpti_trace_framework.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//
88
#pragma once
99

10+
#include <atomic>
1011
#include <cstdint>
1112
#include <memory>
1213
#include <sstream>
@@ -273,6 +274,20 @@ class PlatformHelper {
273274
return false;
274275
}
275276
};
277+
278+
/// This is an implementation of a SpinLock synchronization primitive, that has
279+
/// trivial constructor and destructor.
280+
class SpinLock {
281+
public:
282+
void lock() {
283+
while (MLock.test_and_set(std::memory_order_acquire))
284+
std::this_thread::yield();
285+
}
286+
void unlock() { MLock.clear(std::memory_order_release); }
287+
288+
private:
289+
std::atomic_flag MLock = ATOMIC_FLAG_INIT;
290+
};
276291
} // namespace utils
277292

278293
namespace framework {

0 commit comments

Comments
 (0)