|
10 | 10 |
|
11 | 11 | #include <cstddef> |
12 | 12 | #include <utility> |
| 13 | +#include <cstddef> |
| 14 | +#include <cstdint> |
| 15 | +#include <iostream> |
| 16 | +#include <sstream> |
| 17 | +#include <string> |
| 18 | +#include <string_view> |
| 19 | +#include <array> |
| 20 | +#include <tuple> |
| 21 | +#include <utility> |
| 22 | +#include <algorithm> |
| 23 | +#include <stdexcept> |
| 24 | +#include <type_traits> |
13 | 25 |
|
| 26 | +#include "camp/config.hpp" |
14 | 27 | #include "camp/defines.hpp" |
15 | 28 |
|
16 | 29 | namespace camp |
@@ -228,6 +241,211 @@ CAMP_HOST_DEVICE void safe_swap(T& t1, T& t2) |
228 | 241 | using std::swap; |
229 | 242 | swap(t1, t2); |
230 | 243 | } |
| 244 | + |
| 245 | + |
| 246 | +namespace experimental |
| 247 | +{ |
| 248 | + |
| 249 | +//! Printing helper class to add printability to types defined outside of camp. |
| 250 | +// |
| 251 | +// Specialize to customize printing or add printing for a type. This avoids |
| 252 | +// conflicts if an operator<< is later added for the type in question. |
| 253 | +// Write specializations for non-const reference and const-reference. |
| 254 | +template < typename T > |
| 255 | +struct StreamInsertHelper |
| 256 | +{ |
| 257 | + static_assert(std::is_lvalue_reference_v<T>); |
| 258 | + |
| 259 | + T m_val; |
| 260 | + |
| 261 | + std::ostream& operator()(std::ostream& str) const |
| 262 | + { |
| 263 | + return str << m_val; |
| 264 | + } |
| 265 | +}; |
| 266 | + |
| 267 | +// deduction guide for StreamInsertHelper |
| 268 | +template<typename T> |
| 269 | +StreamInsertHelper(T& val) -> StreamInsertHelper<T&>; |
| 270 | +template<typename T> |
| 271 | +StreamInsertHelper(T const& val) -> StreamInsertHelper<T const&>; |
| 272 | +template<typename T> |
| 273 | +StreamInsertHelper(T&& val) -> StreamInsertHelper<T const&>; |
| 274 | +template<typename T> |
| 275 | +StreamInsertHelper(T const&& val) -> StreamInsertHelper<T const&>; |
| 276 | + |
| 277 | +// Allow printing of StreamInsertHelper using its call operator |
| 278 | +template < typename T > |
| 279 | +inline std::ostream& operator<<(std::ostream& str, StreamInsertHelper<T> const& si) |
| 280 | +{ |
| 281 | + return si(str); |
| 282 | +} |
| 283 | + |
| 284 | + |
| 285 | +#ifdef CAMP_ENABLE_CUDA |
| 286 | + |
| 287 | +//! Get the argument names for the given cuda API function name. |
| 288 | +// |
| 289 | +// Returns a space separated string of the arguments to the given function. |
| 290 | +// Returns an empty string if func is unknown. |
| 291 | +constexpr std::string_view cuda_get_api_arg_names(std::string_view func) |
| 292 | +{ |
| 293 | + using storage_type = std::pair<const char*, const char*>; |
| 294 | + constexpr storage_type known_functions[] { |
| 295 | + {"cudaDeviceSynchronize", ""}, |
| 296 | + {"cudaGetDevice", "device"}, |
| 297 | + {"cudaGetDeviceProperties", "prop device"}, |
| 298 | + {"cudaSetDevice", "device"}, |
| 299 | + {"cudaStreamCreate", "pStream"}, |
| 300 | + {"cudaStreamSynchronize", "stream"}, |
| 301 | + {"cudaStreamWaitEvent", "stream event flags"}, |
| 302 | + {"cudaEventCreateWithFlags", "event flags"}, |
| 303 | + {"cudaEventSynchronize", "event"}, |
| 304 | + {"cudaEventQuery", "event"}, |
| 305 | + {"cudaEventRecord", "event stream"}, |
| 306 | + {"cudaHostAlloc", "pHost size flags"}, |
| 307 | + {"cudaMallocHost", "ptr size"}, |
| 308 | + {"cudaMalloc", "devPtr size"}, |
| 309 | + {"cudaMallocManaged", "devPtr size flags"}, |
| 310 | + {"cudaHostFree", "ptr"}, |
| 311 | + {"cudaFree", "devPtr"}, |
| 312 | + {"cudaMemset", "devPtr value count"}, |
| 313 | + {"cudaMemcpy", "dst src count kind"}, |
| 314 | + {"cudaMemsetAsync", "devPtr value count stream"}, |
| 315 | + {"cudaMemcpyAsync", "dst src count kind stream"}, |
| 316 | + {"cudaLaunchKernel", "func gridDim blockDim args sharedMem stream"}, |
| 317 | + {"cudaPeekAtLastError", ""}, |
| 318 | + {"cudaGetLastError", ""}, |
| 319 | + {"cudaFuncGetAttributes", "attr func"}, |
| 320 | + {"cudaOccupancyMaxPotentialBlockSize", "minGridSize blockSize func dynamicSMemSize blockSizeLimit"}, |
| 321 | + {"cudaOccupancyMaxActiveBlocksPerMultiprocessor", "numBlocks func blockSize dynamicSMemSize"} |
| 322 | + }; |
| 323 | + for (auto [api_name, api_args] : known_functions) { |
| 324 | + if (func == api_name) { |
| 325 | + return api_args; |
| 326 | + } |
| 327 | + } |
| 328 | + return ""; |
| 329 | +} |
| 330 | + |
| 331 | +#endif //#ifdef CAMP_ENABLE_CUDA |
| 332 | + |
| 333 | +#ifdef CAMP_ENABLE_HIP |
| 334 | + |
| 335 | +//! Get the argument names for the given hip API function name. |
| 336 | +// |
| 337 | +// Returns a space separated string of the arguments to the given function. |
| 338 | +// Returns an empty string if func is unknown. |
| 339 | +constexpr std::string_view hip_get_api_arg_names(std::string_view func) |
| 340 | +{ |
| 341 | + using storage_type = std::pair<const char*, const char*>; |
| 342 | + constexpr storage_type known_functions[] { |
| 343 | + {"hipDeviceSynchronize", ""}, |
| 344 | + {"hipGetDevice", "device"}, |
| 345 | + {"hipGetDeviceProperties", "prop device"}, |
| 346 | + {"hipGetDevicePropertiesR0600", "prop device"}, |
| 347 | + {"hipSetDevice", "device"}, |
| 348 | + {"hipStreamCreate", "pStream"}, |
| 349 | + {"hipStreamSynchronize", "stream"}, |
| 350 | + {"hipStreamWaitEvent", "stream event flags"}, |
| 351 | + {"hipEventCreateWithFlags", "event flags"}, |
| 352 | + {"hipEventSynchronize", "event"}, |
| 353 | + {"hipEventQuery", "event"}, |
| 354 | + {"hipEventRecord", "event stream"}, |
| 355 | + {"hipHostMalloc", "pHost size flags"}, |
| 356 | + {"hipMalloc", "devPtr size"}, |
| 357 | + {"hipMallocManaged", "devPtr size flags"}, |
| 358 | + {"hipHostFree", "ptr"}, |
| 359 | + {"hipFree", "devPtr"}, |
| 360 | + {"hipMemset", "devPtr value count"}, |
| 361 | + {"hipMemcpy", "dst src count kind"}, |
| 362 | + {"hipMemsetAsync", "devPtr value count stream"}, |
| 363 | + {"hipMemcpyAsync", "dst src count kind stream"}, |
| 364 | + {"hipLaunchKernel", "func gridDim blockDim args sharedMem stream"}, |
| 365 | + {"hipPeekAtLastError", ""}, |
| 366 | + {"hipGetLastError", ""}, |
| 367 | + {"hipFuncGetAttributes", "attr func"}, |
| 368 | + {"hipOccupancyMaxPotentialBlockSize", "minGridSize blockSize func dynamicSMemSize blockSizeLimit"}, |
| 369 | + {"hipOccupancyMaxActiveBlocksPerMultiprocessor", "numBlocks func blockSize dynamicSMemSize"} |
| 370 | + }; |
| 371 | + for (auto [api_name, api_args] : known_functions) { |
| 372 | + if (func == api_name) { |
| 373 | + return api_args; |
| 374 | + } |
| 375 | + } |
| 376 | + return ""; |
| 377 | +} |
| 378 | + |
| 379 | +#endif //#ifdef CAMP_ENABLE_HIP |
| 380 | + |
| 381 | + |
| 382 | +//! Generate a string for the given args |
| 383 | +// |
| 384 | +// This function generates a string for the given argument names and arguments. |
| 385 | +// Uses StreamInsertHelper to stringify the types in args. |
| 386 | +template < typename Tuple, std::size_t ...Is > |
| 387 | +std::ostream& insertArgsString(std::ostream& str, |
| 388 | + std::string_view arg_names, |
| 389 | + Tuple&& args_tuple, |
| 390 | + std::index_sequence<Is...>) |
| 391 | +{ |
| 392 | + auto insert_arg = [&, first=true](auto&& arg) mutable { |
| 393 | + if (first) { |
| 394 | + first = false; |
| 395 | + } else { |
| 396 | + str << ", "; |
| 397 | + } |
| 398 | + if (!arg_names.empty()) { |
| 399 | + auto arg_name_size = arg_names.find(' '); |
| 400 | + str << arg_names.substr(0, arg_name_size) << "="; |
| 401 | + if (arg_name_size < arg_names.size()) { |
| 402 | + ++arg_name_size; // skip space |
| 403 | + } |
| 404 | + arg_names.remove_prefix(arg_name_size); |
| 405 | + } |
| 406 | + str << ::camp::experimental::StreamInsertHelper{arg}; |
| 407 | + }; |
| 408 | + ::camp::sink(insert_arg); // suppress unused warning from nvcc |
| 409 | + using std::get; |
| 410 | + (..., insert_arg(get<Is>(std::forward<Tuple>(args_tuple)))); |
| 411 | + return str; |
| 412 | +} |
| 413 | + |
| 414 | +} // namespace experimental |
| 415 | + |
| 416 | + |
| 417 | +/// Report hip errors by throwing an exception or printing to cerr |
| 418 | +/// |
| 419 | +/// This function generates an error message by getting a string for the given |
| 420 | +/// hip error code, function, argument names, arguments, and source location |
| 421 | +/// information. Uses StreamInsertHelper to stringify the types in args. |
| 422 | +/// |
| 423 | +/// This function throws an exception if abort is true otherwise prints to cerr. |
| 424 | +template < typename Tuple > |
| 425 | +void reportError(std::string_view error_name, |
| 426 | + std::string_view error_description, |
| 427 | + std::string_view func_name, |
| 428 | + std::string_view arg_names, |
| 429 | + Tuple&& args_tuple, |
| 430 | + std::string_view file, |
| 431 | + int line, |
| 432 | + bool abort = true) |
| 433 | +{ |
| 434 | + std::ostringstream str; |
| 435 | + str << error_name << " error: " << error_description; |
| 436 | + str << " " << func_name << "("; |
| 437 | + ::camp::experimental::insertArgsString( |
| 438 | + str, arg_names, |
| 439 | + std::forward<Tuple>(args_tuple), |
| 440 | + std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{}); |
| 441 | + str << ") " << file << ":" << line; |
| 442 | + if (abort) { |
| 443 | + ::camp::throw_re(str.str()); |
| 444 | + } else { |
| 445 | + std::cerr << str.str(); |
| 446 | + } |
| 447 | +} |
| 448 | + |
231 | 449 | } // namespace camp |
232 | 450 |
|
233 | 451 | #endif /* CAMP_HELPERS_HPP */ |
0 commit comments