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 {
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 #endif
32 
33 template <PiApiKind Kind, size_t Idx, typename... Args>
35 
36 template <PiApiKind Kind> struct PiApiArgTuple;
37 
38 #define _PI_API(api) \
39  template <> struct PiApiArgTuple<PiApiKind::api> { \
40  using type = typename function_traits<decltype(api)>::args_type; \
41  };
42 
43 #include <sycl/detail/pi.def>
44 #undef _PI_API
45 
46 template <PiApiKind Kind, size_t Idx, typename T>
47 struct array_fill_helper<Kind, Idx, T> {
48  static void fill(unsigned char *Dst, T &&Arg) {
49  using ArgsTuple = typename PiApiArgTuple<Kind>::type;
50  // C-style cast is required here.
51  auto RealArg = (std::tuple_element_t<Idx, ArgsTuple>)(Arg);
52  *(std::remove_cv_t<std::tuple_element_t<Idx, ArgsTuple>> *)Dst = RealArg;
53  }
54 };
55 
56 template <PiApiKind Kind, size_t Idx, typename T, typename... Args>
57 struct array_fill_helper<Kind, Idx, T, Args...> {
58  static void fill(unsigned char *Dst, const T &&Arg, Args &&...Rest) {
59  using ArgsTuple = typename PiApiArgTuple<Kind>::type;
60  // C-style cast is required here.
61  auto RealArg = (std::tuple_element_t<Idx, ArgsTuple>)(Arg);
62  *(std::remove_cv_t<std::tuple_element_t<Idx, ArgsTuple>> *)Dst = RealArg;
64  Dst + sizeof(decltype(RealArg)), std::forward<Args>(Rest)...);
65  }
66 };
67 
68 template <typename... Ts>
69 constexpr size_t totalSize(const std::tuple<Ts...> &) {
70  return (sizeof(Ts) + ...);
71 }
72 
73 template <PiApiKind Kind, typename... ArgsT>
74 auto packCallArguments(ArgsT &&...Args) {
75  using ArgsTuple = typename PiApiArgTuple<Kind>::type;
76 
77  constexpr size_t TotalSize = totalSize(ArgsTuple{});
78 
79  std::array<unsigned char, TotalSize> ArgsData;
81  std::forward<ArgsT>(Args)...);
82 
83  return ArgsData;
84 }
85 
90 class plugin {
91 public:
92  plugin() = delete;
93  plugin(const std::shared_ptr<RT::PiPlugin> &Plugin, backend UseBackend,
94  void *LibraryHandle)
95  : MPlugin(Plugin), MBackend(UseBackend), MLibraryHandle(LibraryHandle),
96  TracingMutex(std::make_shared<std::mutex>()),
97  MPluginMutex(std::make_shared<std::mutex>()) {}
98 
99  plugin &operator=(const plugin &) = default;
100  plugin(const plugin &) = default;
101  plugin &operator=(plugin &&other) noexcept = default;
102  plugin(plugin &&other) noexcept = default;
103 
104  ~plugin() = default;
105 
106  const RT::PiPlugin &getPiPlugin() const { return *MPlugin; }
107  RT::PiPlugin &getPiPlugin() { return *MPlugin; }
108  const std::shared_ptr<RT::PiPlugin> &getPiPluginPtr() const {
109  return MPlugin;
110  }
111 
115  template <typename Exception = sycl::runtime_error>
117  char *message = nullptr;
118  if (pi_result == PI_ERROR_PLUGIN_SPECIFIC_ERROR) {
119  pi_result = call_nocheck<PiApiKind::piPluginGetLastError>(&message);
120 
121  // If the warning level is greater then 2 emit the message
123  std::clog << message << std::endl;
124 
125  // If it is a warning do not throw code
126  if (pi_result == PI_SUCCESS)
127  return;
128  }
129  __SYCL_CHECK_OCL_CODE_THROW(pi_result, Exception, message);
130  }
131 
133  template <sycl::errc errc> void checkPiResult(RT::PiResult pi_result) const {
134  if (pi_result == PI_ERROR_PLUGIN_SPECIFIC_ERROR) {
135  char *message = nullptr;
136  pi_result = call_nocheck<PiApiKind::piPluginGetLastError>(&message);
137 
138  // If the warning level is greater then 2 emit the message
140  std::clog << message << std::endl;
141 
142  // If it is a warning do not throw code
143  if (pi_result == PI_SUCCESS)
144  return;
145  }
147  }
148 
149  void reportPiError(RT::PiResult pi_result, const char *context) const {
150  if (pi_result != PI_SUCCESS) {
151  throw sycl::runtime_error(std::string(context) +
152  " API failed with error: " +
154  pi_result);
155  }
156  }
157 
168 
169  template <PiApiKind PiApiOffset, typename... ArgsT>
170  RT::PiResult call_nocheck(ArgsT... Args) const {
171  RT::PiFuncInfo<PiApiOffset> PiCallInfo;
172 #ifdef XPTI_ENABLE_INSTRUMENTATION
173  // Emit a function_begin trace for the PI API before the call is executed.
174  // If arguments need to be captured, then a data structure can be sent in
175  // the per_instance_user_data field.
176  const char *PIFnName = PiCallInfo.getFuncName();
177  uint64_t CorrelationID = pi::emitFunctionBeginTrace(PIFnName);
178  uint64_t CorrelationIDWithArgs = 0;
179  unsigned char *ArgsDataPtr = nullptr;
180  // TODO check if stream is observed when corresponding API is present.
181  if (xptiTraceEnabled()) {
182  auto ArgsData =
183  packCallArguments<PiApiOffset>(std::forward<ArgsT>(Args)...);
184  ArgsDataPtr = ArgsData.data();
185  CorrelationIDWithArgs = pi::emitFunctionWithArgsBeginTrace(
186  static_cast<uint32_t>(PiApiOffset), PIFnName, ArgsDataPtr, *MPlugin);
187  }
188 #endif
189  RT::PiResult R;
191  std::lock_guard<std::mutex> Guard(*TracingMutex);
192  const char *FnName = PiCallInfo.getFuncName();
193  std::cout << "---> " << FnName << "(" << std::endl;
194  RT::printArgs(Args...);
195  R = PiCallInfo.getFuncPtr(*MPlugin)(Args...);
196  std::cout << ") ---> ";
197  RT::printArgs(R);
198  RT::printOuts(Args...);
199  std::cout << std::endl;
200  } else {
201  R = PiCallInfo.getFuncPtr(*MPlugin)(Args...);
202  }
203 #ifdef XPTI_ENABLE_INSTRUMENTATION
204  // Close the function begin with a call to function end
205  pi::emitFunctionEndTrace(CorrelationID, PIFnName);
206  pi::emitFunctionWithArgsEndTrace(CorrelationIDWithArgs,
207  static_cast<uint32_t>(PiApiOffset),
208  PIFnName, ArgsDataPtr, R, *MPlugin);
209 #endif
210  return R;
211  }
212 
216  template <PiApiKind PiApiOffset, typename... ArgsT>
217  void call(ArgsT... Args) const {
218  RT::PiResult Err = call_nocheck<PiApiOffset>(Args...);
219  checkPiResult(Err);
220  }
221 
223  template <sycl::errc errc, PiApiKind PiApiOffset, typename... ArgsT>
224  void call(ArgsT... Args) const {
225  RT::PiResult Err = call_nocheck<PiApiOffset>(Args...);
226  checkPiResult<errc>(Err);
227  }
228 
229  backend getBackend(void) const { return MBackend; }
230  void *getLibraryHandle() const { return MLibraryHandle; }
231  void *getLibraryHandle() { return MLibraryHandle; }
232  int unload() { return RT::unloadPlugin(MLibraryHandle); }
233 
234  // return the index of PiPlatforms.
235  // If not found, add it and return its index.
236  // The function is expected to be called in a thread safe manner.
238  auto It = std::find(PiPlatforms.begin(), PiPlatforms.end(), Platform);
239  if (It != PiPlatforms.end())
240  return It - PiPlatforms.begin();
241 
242  PiPlatforms.push_back(Platform);
243  LastDeviceIds.push_back(0);
244  return PiPlatforms.size() - 1;
245  }
246 
247  // Device ids are consecutive across platforms within a plugin.
248  // We need to return the same starting index for the given platform.
249  // So, instead of returing the last device id of the given platform,
250  // return the last device id of the predecessor platform.
251  // The function is expected to be called in a thread safe manner.
253  int PlatformId = getPlatformId(Platform);
254  if (PlatformId == 0)
255  return 0;
256  return LastDeviceIds[PlatformId - 1];
257  }
258 
259  // set the id of the last device for the given platform
260  // The function is expected to be called in a thread safe manner.
261  void setLastDeviceId(RT::PiPlatform Platform, int Id) {
262  int PlatformId = getPlatformId(Platform);
263  LastDeviceIds[PlatformId] = Id;
264  }
265 
266  // Adjust the id of the last device for the given platform.
267  // Involved when there is no device on that platform at all.
268  // The function is expected to be called in a thread safe manner.
270  int PlatformId = getPlatformId(Platform);
271  if (PlatformId > 0 &&
272  LastDeviceIds[PlatformId] < LastDeviceIds[PlatformId - 1])
273  LastDeviceIds[PlatformId] = LastDeviceIds[PlatformId - 1];
274  }
275 
277  auto It = std::find(PiPlatforms.begin(), PiPlatforms.end(), Platform);
278  return It != PiPlatforms.end();
279  }
280 
281  std::shared_ptr<std::mutex> getPluginMutex() { return MPluginMutex; }
282 
283 private:
284  std::shared_ptr<RT::PiPlugin> MPlugin;
285  backend MBackend;
286  void *MLibraryHandle; // the handle returned from dlopen
287  std::shared_ptr<std::mutex> TracingMutex;
288  // Mutex to guard PiPlatforms and LastDeviceIds.
289  // Note that this is a temporary solution until we implement the global
290  // Device/Platform cache later.
291  std::shared_ptr<std::mutex> MPluginMutex;
292  // vector of PiPlatforms that belong to this plugin
293  std::vector<RT::PiPlatform> PiPlatforms;
294  // represents the unique ids of the last device of each platform
295  // index of this vector corresponds to the index in PiPlatforms vector.
296  std::vector<int> LastDeviceIds;
297 }; // class plugin
298 } // namespace detail
299 } // __SYCL_INLINE_VER_NAMESPACE(_V1)
300 } // namespace sycl
sycl::_V1::detail::codeToString
static std::string codeToString(pi_int32 code)
Definition: common.hpp:184
sycl::_V1::detail::plugin::plugin
plugin(const std::shared_ptr< RT::PiPlugin > &Plugin, backend UseBackend, void *LibraryHandle)
Definition: plugin.hpp:93
sycl::_V1::detail::PiApiKind
PiApiKind
Definition: pi.hpp:42
sycl::_V1::backend
backend
Definition: backend_types.hpp:21
T
type_traits.hpp
sycl::_V1::detail::plugin::unload
int unload()
Definition: plugin.hpp:232
config.hpp
stl.hpp
sycl::_V1::detail::plugin::checkPiResult
void checkPiResult(RT::PiResult pi_result) const
Checks return value from PI calls.
Definition: plugin.hpp:116
__SYCL_INLINE_VER_NAMESPACE
#define __SYCL_INLINE_VER_NAMESPACE(X)
Definition: defines_elementary.hpp:11
_pi_result
_pi_result
Definition: pi.h:135
sycl::_V1::detail::SYCLConfig
Definition: config.hpp:110
sycl::_V1::detail::array_fill_helper< Kind, Idx, T >::fill
static void fill(unsigned char *Dst, T &&Arg)
Definition: plugin.hpp:48
sycl::_V1::detail::plugin::getPiPluginPtr
const std::shared_ptr< RT::PiPlugin > & getPiPluginPtr() const
Definition: plugin.hpp:108
sycl
---— Error handling, matching OpenCL plugin semantics.
Definition: access.hpp:14
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:144
sycl::_V1::detail::plugin::getBackend
backend getBackend(void) const
Definition: plugin.hpp:229
sycl::_V1::detail::pi::PiPlatform
::pi_platform PiPlatform
Definition: pi.hpp:123
sycl::_V1::detail::plugin::getLibraryHandle
void * getLibraryHandle() const
Definition: plugin.hpp:230
pi.hpp
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:372
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:85
sycl::_V1::detail::pi::printOuts
void printOuts(void)
Definition: plugin_printers.hpp:171
sycl::_V1::detail::plugin
The plugin class provides a unified interface to the underlying low-level runtimes for the device-agn...
Definition: plugin.hpp:90
sycl::_V1::detail::PiApiArgTuple
Definition: plugin.hpp:36
sycl::_V1::detail::plugin::reportPiError
void reportPiError(RT::PiResult pi_result, const char *context) const
Definition: plugin.hpp:149
sycl::_V1::detail::pi::PiResult
::pi_result PiResult
Definition: pi.hpp:122
sycl::_V1::ext::oneapi::experimental::operator=
annotated_arg & operator=(annotated_arg &)=default
__SYCL_CHECK_CODE_THROW_VIA_ERRC
#define __SYCL_CHECK_CODE_THROW_VIA_ERRC(X, ERRC)
Definition: common.hpp:272
sycl::_V1::detail::plugin::adjustLastDeviceId
void adjustLastDeviceId(RT::PiPlatform Platform)
Definition: plugin.hpp:269
sycl::_V1::detail::pi::PI_TRACE_CALLS
@ PI_TRACE_CALLS
Definition: pi.hpp:57
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:58
sycl::_V1::errc
errc
Definition: exception.hpp:28
sycl::_V1::detail::plugin::call_nocheck
RT::PiResult call_nocheck(ArgsT... Args) const
Calls the PiApi, traces the call, and returns the result.
Definition: plugin.hpp:170
backend_types.hpp
sycl::_V1::detail::plugin::getPlatformId
int getPlatformId(RT::PiPlatform Platform)
Definition: plugin.hpp:237
sycl::_V1::detail::plugin::call
void call(ArgsT... Args) const
Calls the API, traces the call, checks the result.
Definition: plugin.hpp:217
sycl::_V1::detail::pi::printArgs
void printArgs(void)
Definition: plugin_printers.hpp:122
std
Definition: accessor.hpp:3201
sycl::_V1::detail::totalSize
constexpr size_t totalSize(const std::tuple< Ts... > &)
Definition: plugin.hpp:69
sycl::_V1::detail::plugin::getPiPlugin
RT::PiPlugin & getPiPlugin()
Definition: plugin.hpp:107
sycl::_V1::detail::plugin::call
void call(ArgsT... Args) const
Definition: plugin.hpp:224
sycl::_V1::detail::plugin::getStartingDeviceId
int getStartingDeviceId(RT::PiPlatform Platform)
Definition: plugin.hpp:252
sycl::_V1::detail::plugin::setLastDeviceId
void setLastDeviceId(RT::PiPlatform Platform, int Id)
Definition: plugin.hpp:261
sycl::_V1::detail::plugin::getPluginMutex
std::shared_ptr< std::mutex > getPluginMutex()
Definition: plugin.hpp:281
sycl::_V1::detail::array_fill_helper
Definition: plugin.hpp:34
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:129
sycl::_V1::detail::plugin::getLibraryHandle
void * getLibraryHandle()
Definition: plugin.hpp:231
sycl::_V1::detail::pi::trace
bool trace(TraceLevel level)
Definition: pi.cpp:400
sycl::_V1::detail::plugin::containsPiPlatform
bool containsPiPlatform(RT::PiPlatform Platform)
Definition: plugin.hpp:276
std::cout
__SYCL_EXTERN_STREAM_ATTRS ostream cout
Linked to standard output.
sycl::_V1::detail::packCallArguments
auto packCallArguments(ArgsT &&...Args)
Definition: plugin.hpp:74
__SYCL_CHECK_OCL_CODE_THROW
#define __SYCL_CHECK_OCL_CODE_THROW(X, EXC, STR)
Definition: common.hpp:268
sycl::_V1::detail::plugin::getPiPlugin
const RT::PiPlugin & getPiPlugin() const
Definition: plugin.hpp:106
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:164
sycl::_V1::detail::pi::PiPlugin
::pi_plugin PiPlugin
Definition: pi.hpp:121
sycl::_V1::context
The context class represents a SYCL context on which kernel functions may be executed.
Definition: context.hpp:41