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