DPC++ Runtime
Runtime libraries for oneAPI DPC++
plugin.hpp
Go to the documentation of this file.
1 //==------------------------- plugin.hpp - SYCL platform -------------------==//
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 #include <detail/config.hpp>
12 #include <memory>
13 #include <mutex>
14 #include <sycl/backend_types.hpp>
15 #include <sycl/detail/common.hpp>
16 #include <sycl/detail/pi.hpp>
18 #include <sycl/stl.hpp>
19 
20 #ifdef XPTI_ENABLE_INSTRUMENTATION
21 // Include the headers necessary for emitting traces using the trace framework
22 #include "xpti/xpti_trace_framework.h"
23 #endif
24 
25 namespace sycl {
26 inline namespace _V1 {
27 namespace detail {
28 #ifdef XPTI_ENABLE_INSTRUMENTATION
29 extern xpti::trace_event_data_t *GPICallEvent;
30 extern xpti::trace_event_data_t *GPIArgCallEvent;
31 extern uint8_t PiCallStreamID;
32 extern uint8_t PiDebugCallStreamID;
33 #endif
34 
35 template <PiApiKind Kind, size_t Idx, typename... Args>
37 
38 template <PiApiKind Kind> struct PiApiArgTuple;
39 
40 #define _PI_API(api) \
41  template <> struct PiApiArgTuple<PiApiKind::api> { \
42  using type = typename function_traits<decltype(api)>::args_type; \
43  };
44 
45 #include <sycl/detail/pi.def>
46 #undef _PI_API
47 
48 template <PiApiKind Kind, size_t Idx, typename T>
49 struct array_fill_helper<Kind, Idx, T> {
50  static void fill(unsigned char *Dst, T &&Arg) {
51  using ArgsTuple = typename PiApiArgTuple<Kind>::type;
52  // C-style cast is required here.
53  auto RealArg = (std::tuple_element_t<Idx, ArgsTuple>)(Arg);
54  *(std::remove_cv_t<std::tuple_element_t<Idx, ArgsTuple>> *)Dst = RealArg;
55  }
56 };
57 
58 template <PiApiKind Kind, size_t Idx, typename T, typename... Args>
59 struct array_fill_helper<Kind, Idx, T, Args...> {
60  static void fill(unsigned char *Dst, const T &&Arg, Args &&...Rest) {
61  using ArgsTuple = typename PiApiArgTuple<Kind>::type;
62  // C-style cast is required here.
63  auto RealArg = (std::tuple_element_t<Idx, ArgsTuple>)(Arg);
64  *(std::remove_cv_t<std::tuple_element_t<Idx, ArgsTuple>> *)Dst = RealArg;
66  Dst + sizeof(decltype(RealArg)), std::forward<Args>(Rest)...);
67  }
68 };
69 
70 template <typename... Ts>
71 constexpr size_t totalSize(const std::tuple<Ts...> &) {
72  return (sizeof(Ts) + ...);
73 }
74 
75 template <PiApiKind Kind, typename... ArgsT>
76 auto packCallArguments(ArgsT &&...Args) {
77  using ArgsTuple = typename PiApiArgTuple<Kind>::type;
78 
79  constexpr size_t TotalSize = totalSize(ArgsTuple{});
80 
81  std::array<unsigned char, TotalSize> ArgsData;
83  std::forward<ArgsT>(Args)...);
84 
85  return ArgsData;
86 }
87 
92 class plugin {
93 public:
94  plugin() = delete;
95  plugin(const std::shared_ptr<sycl::detail::pi::PiPlugin> &Plugin,
96  backend UseBackend, void *LibraryHandle)
97  : MPlugin(Plugin), MBackend(UseBackend), MLibraryHandle(LibraryHandle),
98  TracingMutex(std::make_shared<std::mutex>()),
99  MPluginMutex(std::make_shared<std::mutex>()) {}
100 
101  // Disallow accidental copies of plugins
102  plugin &operator=(const plugin &) = delete;
103  plugin(const plugin &) = delete;
104  plugin &operator=(plugin &&other) noexcept = delete;
105  plugin(plugin &&other) noexcept = delete;
106 
107  ~plugin() = default;
108 
109  const sycl::detail::pi::PiPlugin &getPiPlugin() const { return *MPlugin; }
110  sycl::detail::pi::PiPlugin &getPiPlugin() { return *MPlugin; }
111  const std::shared_ptr<sycl::detail::pi::PiPlugin> &getPiPluginPtr() const {
112  return MPlugin;
113  }
114 
118  template <typename Exception = sycl::runtime_error>
120  char *message = nullptr;
121  if (pi_result == PI_ERROR_PLUGIN_SPECIFIC_ERROR) {
122  pi_result = call_nocheck<PiApiKind::piPluginGetLastError>(&message);
123 
124  // If the warning level is greater then 2 emit the message
126  std::clog << message << std::endl;
127 
128  // If it is a warning do not throw code
129  if (pi_result == PI_SUCCESS)
130  return;
131  }
132  __SYCL_CHECK_OCL_CODE_THROW(pi_result, Exception, message);
133  }
134 
136  template <sycl::errc errc>
138  if (pi_result == PI_ERROR_PLUGIN_SPECIFIC_ERROR) {
139  char *message = nullptr;
140  pi_result = call_nocheck<PiApiKind::piPluginGetLastError>(&message);
141 
142  // If the warning level is greater then 2 emit the message
144  std::clog << message << std::endl;
145 
146  // If it is a warning do not throw code
147  if (pi_result == PI_SUCCESS)
148  return;
149  }
151  }
152 
154  const char *context) const {
155  if (pi_result != PI_SUCCESS) {
156  throw sycl::runtime_error(std::string(context) +
157  " API failed with error: " +
159  pi_result);
160  }
161  }
162 
173 
174  template <PiApiKind PiApiOffset, typename... ArgsT>
177 #ifdef XPTI_ENABLE_INSTRUMENTATION
178  bool CorrelationIDAvailable = false, CorrelationIDWithArgsAvailable = false;
179  // Emit a function_begin trace for the PI API before the call is executed.
180  // If arguments need to be captured, then a data structure can be sent in
181  // the per_instance_user_data field.
182  const char *PIFnName = PiCallInfo.getFuncName();
183  uint64_t CorrelationIDWithArgs = 0, CorrelationID = 0;
184 
185  if (xptiCheckTraceEnabled(
186  PiCallStreamID,
187  (uint16_t)xpti::trace_point_type_t::function_begin)) {
188  CorrelationID = pi::emitFunctionBeginTrace(PIFnName);
189  CorrelationIDAvailable = true;
190  }
191  unsigned char *ArgsDataPtr = nullptr;
192  // If subscribers are listening to Pi debug call stream, only then prepare
193  // the data for the notifications and emit notifications. Even though the
194  // function emitFunctionWithArgsBeginTrace() checks for the trqace typoe
195  // using xptiTraceCheckEnabled(), we add a guard here before we prepare the
196  // data for the notification, as it comes with a cost
197  if (xptiCheckTraceEnabled(
198  PiDebugCallStreamID,
199  (uint16_t)xpti::trace_point_type_t::function_with_args_begin)) {
200  using PackCallArgumentsTy = decltype(packCallArguments<PiApiOffset>(
201  std::forward<ArgsT>(Args)...));
202  auto ArgsData =
203  xptiTraceEnabled()
204  ? packCallArguments<PiApiOffset>(std::forward<ArgsT>(Args)...)
205  : PackCallArgumentsTy{};
206  // TODO check if stream is observed when corresponding API is present.
207  ArgsDataPtr = ArgsData.data();
208  CorrelationIDWithArgs = pi::emitFunctionWithArgsBeginTrace(
209  static_cast<uint32_t>(PiApiOffset), PIFnName, ArgsDataPtr, *MPlugin);
210  CorrelationIDWithArgsAvailable = true;
211  }
212 #endif
213  sycl::detail::pi::PiResult R = PI_SUCCESS;
215  std::lock_guard<std::mutex> Guard(*TracingMutex);
216  const char *FnName = PiCallInfo.getFuncName();
217  std::cout << "---> " << FnName << "(" << std::endl;
219  if (!pluginReleased) {
220  R = PiCallInfo.getFuncPtr(*MPlugin)(Args...);
221  std::cout << ") ---> ";
224  std::cout << std::endl;
225  } else {
226  std::cout << ") ---> ";
227  std::cout << "API Called After Plugin Teardown, Functon Call ignored.";
228  std::cout << std::endl;
229  }
230  } else {
231  if (!pluginReleased) {
232  R = PiCallInfo.getFuncPtr(*MPlugin)(Args...);
233  }
234  }
235 #ifdef XPTI_ENABLE_INSTRUMENTATION
236  // Close the function begin with a call to function end; we do not need to
237  // check th xptiTraceCheckEnbled() here as it is performed within the
238  // function
239  if (CorrelationIDAvailable) {
240  // Only send function_end notification if function_begin is subscribed to
241  pi::emitFunctionEndTrace(CorrelationID, PIFnName);
242  }
243  if (CorrelationIDWithArgsAvailable) {
244  pi::emitFunctionWithArgsEndTrace(CorrelationIDWithArgs,
245  static_cast<uint32_t>(PiApiOffset),
246  PIFnName, ArgsDataPtr, R, *MPlugin);
247  }
248 #endif
249  return R;
250  }
251 
255  template <PiApiKind PiApiOffset, typename... ArgsT>
256  void call(ArgsT... Args) const {
257  sycl::detail::pi::PiResult Err = call_nocheck<PiApiOffset>(Args...);
258  checkPiResult(Err);
259  }
260 
262  template <sycl::errc errc, PiApiKind PiApiOffset, typename... ArgsT>
263  void call(ArgsT... Args) const {
264  sycl::detail::pi::PiResult Err = call_nocheck<PiApiOffset>(Args...);
265  checkPiResult<errc>(Err);
266  }
267 
271  bool hasBackend(backend Backend) const { return Backend == MBackend; }
272 
273  void *getLibraryHandle() const { return MLibraryHandle; }
274  void *getLibraryHandle() { return MLibraryHandle; }
275  int unload() {
276  this->pluginReleased = true;
277  return sycl::detail::pi::unloadPlugin(MLibraryHandle);
278  }
279 
280  // return the index of PiPlatforms.
281  // If not found, add it and return its index.
282  // The function is expected to be called in a thread safe manner.
284  auto It = std::find(PiPlatforms.begin(), PiPlatforms.end(), Platform);
285  if (It != PiPlatforms.end())
286  return It - PiPlatforms.begin();
287 
288  PiPlatforms.push_back(Platform);
289  LastDeviceIds.push_back(0);
290  return PiPlatforms.size() - 1;
291  }
292 
293  // Device ids are consecutive across platforms within a plugin.
294  // We need to return the same starting index for the given platform.
295  // So, instead of returing the last device id of the given platform,
296  // return the last device id of the predecessor platform.
297  // The function is expected to be called in a thread safe manner.
299  int PlatformId = getPlatformId(Platform);
300  if (PlatformId == 0)
301  return 0;
302  return LastDeviceIds[PlatformId - 1];
303  }
304 
305  // set the id of the last device for the given platform
306  // The function is expected to be called in a thread safe manner.
308  int PlatformId = getPlatformId(Platform);
309  LastDeviceIds[PlatformId] = Id;
310  }
311 
312  // Adjust the id of the last device for the given platform.
313  // Involved when there is no device on that platform at all.
314  // The function is expected to be called in a thread safe manner.
316  int PlatformId = getPlatformId(Platform);
317  if (PlatformId > 0 &&
318  LastDeviceIds[PlatformId] < LastDeviceIds[PlatformId - 1])
319  LastDeviceIds[PlatformId] = LastDeviceIds[PlatformId - 1];
320  }
321 
323  auto It = std::find(PiPlatforms.begin(), PiPlatforms.end(), Platform);
324  return It != PiPlatforms.end();
325  }
326 
327  std::shared_ptr<std::mutex> getPluginMutex() { return MPluginMutex; }
328  bool pluginReleased = false;
329 
330 private:
331  std::shared_ptr<sycl::detail::pi::PiPlugin> MPlugin;
332  backend MBackend;
333  void *MLibraryHandle; // the handle returned from dlopen
334  std::shared_ptr<std::mutex> TracingMutex;
335  // Mutex to guard PiPlatforms and LastDeviceIds.
336  // Note that this is a temporary solution until we implement the global
337  // Device/Platform cache later.
338  std::shared_ptr<std::mutex> MPluginMutex;
339  // vector of PiPlatforms that belong to this plugin
340  std::vector<sycl::detail::pi::PiPlatform> PiPlatforms;
341  // represents the unique ids of the last device of each platform
342  // index of this vector corresponds to the index in PiPlatforms vector.
343  std::vector<int> LastDeviceIds;
344 }; // class plugin
345 
346 using PluginPtr = std::shared_ptr<plugin>;
347 
348 } // namespace detail
349 } // namespace _V1
350 } // namespace sycl
sycl::_V1::detail::plugin::getPiPlugin
const sycl::detail::pi::PiPlugin & getPiPlugin() const
Definition: plugin.hpp:109
sycl::_V1::detail::plugin::pluginReleased
bool pluginReleased
Definition: plugin.hpp:328
sycl::_V1::detail::plugin::getPiPlugin
sycl::detail::pi::PiPlugin & getPiPlugin()
Definition: plugin.hpp:110
sycl::_V1::detail::PiApiKind
PiApiKind
Definition: pi.hpp:43
sycl::_V1::backend
backend
Definition: backend_types.hpp:18
type_traits.hpp
sycl::_V1::detail::plugin::hasBackend
bool hasBackend(backend Backend) const
Tells if this plugin can serve specified backend.
Definition: plugin.hpp:271
sycl::_V1::detail::plugin::unload
int unload()
Definition: plugin.hpp:275
config.hpp
stl.hpp
sycl::_V1::detail::pi::PiFuncInfo
Definition: pi.hpp:215
_pi_plugin
Definition: pi.h:2703
_pi_result
_pi_result
Definition: pi.h:205
sycl::_V1::detail::SYCLConfig
Definition: config.hpp:111
sycl::_V1::detail::array_fill_helper< Kind, Idx, T >::fill
static void fill(unsigned char *Dst, T &&Arg)
Definition: plugin.hpp:50
sycl::_V1::detail::plugin::containsPiPlatform
bool containsPiPlatform(sycl::detail::pi::PiPlatform Platform)
Definition: plugin.hpp:322
sycl
Definition: access.hpp:18
sycl::_V1::detail::pi::emitFunctionWithArgsBeginTrace
uint64_t emitFunctionWithArgsBeginTrace(uint32_t FuncID, const char *FName, unsigned char *ArgsData, pi_plugin Plugin)
Notifies XPTI subscribers about PI function calls and packs call arguments.
Definition: pi.cpp:143
noexcept
_Abi const simd< _Tp, _Abi > & noexcept
Definition: simd.hpp:1324
_pi_platform
Definition: pi_cuda.hpp:44
sycl::_V1::detail::plugin::getLibraryHandle
void * getLibraryHandle() const
Definition: plugin.hpp:273
pi.hpp
syclcompat::detail::fill
static sycl::event fill(sycl::queue q, void *dev_ptr, const T &pattern, size_t count)
Set pattern to the first count elements of type T starting from dev_ptr.
Definition: memory.hpp:192
std::clog
__SYCL_EXTERN_STREAM_ATTRS ostream clog
Linked to standard error (buffered)
sycl::_V1::detail::pi::unloadPlugin
int unloadPlugin(void *Library)
Definition: pi.cpp:395
plugin_printers.hpp
sycl::_V1::detail::pi::emitFunctionBeginTrace
uint64_t emitFunctionBeginTrace(const char *FName)
Emits an XPTI trace before a PI API call is made.
Definition: pi.cpp:82
sycl::_V1::detail::pi::printOuts
void printOuts(void)
Definition: plugin_printers.hpp:171
sycl::_V1::detail::plugin::adjustLastDeviceId
void adjustLastDeviceId(sycl::detail::pi::PiPlatform Platform)
Definition: plugin.hpp:315
sycl::_V1::detail::plugin
The plugin class provides a unified interface to the underlying low-level runtimes for the device-agn...
Definition: plugin.hpp:92
sycl::_V1::detail::PiApiArgTuple
Definition: plugin.hpp:38
__SYCL_CHECK_CODE_THROW_VIA_ERRC
#define __SYCL_CHECK_CODE_THROW_VIA_ERRC(X, ERRC)
Definition: common.hpp:241
sycl::_V1::detail::pi::PI_TRACE_CALLS
@ PI_TRACE_CALLS
Definition: pi.hpp:59
sycl::_V1::detail::plugin::~plugin
~plugin()=default
common.hpp
sycl::_V1::detail::array_fill_helper< Kind, Idx, T, Args... >::fill
static void fill(unsigned char *Dst, const T &&Arg, Args &&...Rest)
Definition: plugin.hpp:60
sycl::_V1::detail::codeToString
std::string codeToString(pi_int32 code)
Definition: common.hpp:153
sycl::_V1::errc
errc
Definition: exception.hpp:32
sycl::_V1::detail::plugin::operator=
plugin & operator=(const plugin &)=delete
sycl::_V1::detail::plugin::setLastDeviceId
void setLastDeviceId(sycl::detail::pi::PiPlatform Platform, int Id)
Definition: plugin.hpp:307
sycl::_V1::detail::plugin::getStartingDeviceId
int getStartingDeviceId(sycl::detail::pi::PiPlatform Platform)
Definition: plugin.hpp:298
sycl::_V1::detail::plugin::getPiPluginPtr
const std::shared_ptr< sycl::detail::pi::PiPlugin > & getPiPluginPtr() const
Definition: plugin.hpp:111
sycl::_V1::detail::plugin::call_nocheck
sycl::detail::pi::PiResult call_nocheck(ArgsT... Args) const
Calls the PiApi, traces the call, and returns the result.
Definition: plugin.hpp:175
backend_types.hpp
sycl::_V1::detail::plugin::call
void call(ArgsT... Args) const
Calls the API, traces the call, checks the result.
Definition: plugin.hpp:256
sycl::_V1::detail::pi::printArgs
void printArgs(void)
Definition: plugin_printers.hpp:122
std
Definition: accessor.hpp:4171
sycl::_V1::detail::totalSize
constexpr size_t totalSize(const std::tuple< Ts... > &)
Definition: plugin.hpp:71
sycl::_V1::detail::plugin::checkPiResult
void checkPiResult(sycl::detail::pi::PiResult pi_result) const
Checks return value from PI calls.
Definition: plugin.hpp:119
sycl::_V1::detail::plugin::call
void call(ArgsT... Args) const
Definition: plugin.hpp:263
sycl::_V1::detail::plugin::getPluginMutex
std::shared_ptr< std::mutex > getPluginMutex()
Definition: plugin.hpp:327
sycl::_V1::detail::array_fill_helper
Definition: plugin.hpp:36
sycl::_V1::detail::plugin::plugin
plugin()=delete
sycl::_V1::detail::pi::emitFunctionEndTrace
void emitFunctionEndTrace(uint64_t CorrelationID, const char *FName)
Emits an XPTI trace after the PI API call has been made.
Definition: pi.cpp:127
sycl::_V1::detail::plugin::reportPiError
void reportPiError(sycl::detail::pi::PiResult pi_result, const char *context) const
Definition: plugin.hpp:153
sycl::_V1::detail::plugin::getLibraryHandle
void * getLibraryHandle()
Definition: plugin.hpp:274
sycl::_V1::detail::pi::trace
bool trace(TraceLevel level)
Definition: pi.cpp:423
std::cout
__SYCL_EXTERN_STREAM_ATTRS ostream cout
Linked to standard output.
sycl::_V1::detail::packCallArguments
auto packCallArguments(ArgsT &&...Args)
Definition: plugin.hpp:76
sycl::_V1::detail::plugin::getPlatformId
int getPlatformId(sycl::detail::pi::PiPlatform Platform)
Definition: plugin.hpp:283
__SYCL_CHECK_OCL_CODE_THROW
#define __SYCL_CHECK_OCL_CODE_THROW(X, EXC, STR)
Definition: common.hpp:237
sycl::_V1::detail::pi::emitFunctionWithArgsEndTrace
void emitFunctionWithArgsEndTrace(uint64_t CorrelationID, uint32_t FuncID, const char *FName, unsigned char *ArgsData, pi_result Result, pi_plugin Plugin)
Notifies XPTI subscribers about PI function call result.
Definition: pi.cpp:175
sycl::_V1::detail::plugin::plugin
plugin(const std::shared_ptr< sycl::detail::pi::PiPlugin > &Plugin, backend UseBackend, void *LibraryHandle)
Definition: plugin.hpp:95
sycl::_V1::context
The context class represents a SYCL context on which kernel functions may be executed.
Definition: context.hpp:51
sycl::_V1::detail::PluginPtr
std::shared_ptr< plugin > PluginPtr
Definition: pi.hpp:48