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