## Handling Errors and Debugging
## Learning Objectives
* Learn about how SYCL handles errors
* Learn about the difference between synchronous and asynchronous exceptions
* Learn how to handle exceptions and retrieve further information
* Learn about the host device and how to use it
## SYCL exceptions
* In SYCL errors are handled by throwing exceptions.
* It is crucial that these errors are handled, otherwise your application could fail
in unpredictable ways.
* In SYCL there are two kinds of error:
* Synchronous errors (thrown in user thread).
* Asynchronous errors (thrown by the SYCL scheduler).
#### SYCL exceptions

#### Handling errors
#include <sycl/sycl.hpp>
using namespace sycl;
int main() {
queue q;
/* Synchronous code */
q.single_task([=]() {
/* Asynchronous code */
});
/* Synchronous code */
q.wait();
}
* Code on the device runs asynchronously
* If errors are not
handled, the application can fail:
* A default async handler that
will call `std::terminate` when an asynchronous error is thrown.
#include <sycl/sycl.hpp>
using namespace sycl;
int main() {
try {
queue q;
int* shared = malloc_shared<int>(1024, q);
q.parallel_for(1024, [=](id<1> i) {
shared[i] = i;
});
q.wait_and_throw();
free(shared, q);
} catch (...) { /* handle errors */ }
}
* Synchronous errors are typically thrown by SYCL API functions.
* In order to handle all SYCL errors you must wrap everything in a
try-catch block.
#include <sycl/sycl.hpp>
using namespace sycl;
int main() {
try {
queue q(async_handler{});
int* shared = malloc_shared<int>(1024, q);
q.parallel_for(1024, [=](id<1> i) {
shared[i] = i;
});
q.wait_and_throw();
free(shared, q);
} catch (...) { /* handle errors */ }
}
* Asynchronous errors that may have occurred will be thrown
after a command has been submitted to a `queue`.
* To handle these errors you must provide an async handler when constructing the
queue object.
* Then you must also call the `throw_asynchronous` or
`wait_and_throw` member functions of the `queue` class.
* This will
pass the exceptions to the async handler in the user thread so they
can be thrown.
#include<sycl/sycl.hpp>
using namespace sycl;
int main() {
try {
queue q([=](exception_list eL) {
for (auto e : eL) { std::rethrow_exception(e);
}
});
int* shared = malloc_shared<int>(1024, q);
q.parallel_for(1024, [=](id<1> i) {
shared[i] = i;
});
q.wait_and_throw();
free(shared, q);
} catch (...) { /* handle errors */ }
}
* The async handler is a C++ lambda or function object that takes as a parameter an `exception_list`
* The exception_list class is a wrapper around a list of `exception_ptrs` which can be iterated over
* The exception_ptrs can be rethrown by passing them to `std::rethrow_exception`
#include<sycl/sycl.hpp>
#include<iostream>
using namespace sycl;
int main() {
try {
queue q([=](exception_list eL) {
for (auto e : eL) { std::rethrow_exception(e); }
});
/* ... */
q.wait_and_throw();
} catch (const exception& e) {
std::cout << "Exception caught: " << e.what()
<< std::endl;
}
}
* Once rethrown and caught, a SYCL exception can provide information
about the error
* The `what` member function will return a string with more details
#include<sycl/sycl.hpp>
#include<iostream>
using namespace sycl;
int main() {
try {
queue q([=](exception_list eL) {
for (auto e : eL) { std::rethrow_exception(e); }
});
/* ... */
q.wait_and_throw();
} catch (const exception& e) {
std::cout << "Exception caught: " << e.what();
}
}
* SYCL 2020 provides the `error_category_for` templated free function
that allows checking for the category of the exception depending on
the backend used.
#include<sycl/sycl.hpp>
using namespace sycl;
int main() {
queue q([=](exception_list eL) {
for (auto e : eL) { std::rethrow_exception(e); }
});
try {
/* ... */
q.wait_and_throw();
} catch (const sycl::exception& e) {
if (e.has_context()) {
if (e.get_context() == q.get_context()) {
/* handle error */
}
}
}
}
* The `has_context` exception member function will tell you if there is a SYCL
context associated with the error
* If that returns true then the
`get_context` member function will return the associated SYCL
context object
* SYCL 2020 only has a single `sycl::exception` type which provides
different error codes
* E.g. `errc::runtime`, `errc::kernel`
## Debugging SYCL Kernel Functions
* SYCL 2020 only guarantees that a device will always be available,
and users can query the `host_debuggable` device aspect to check
whether they can use the same functionality as the SYCL 1.2.1 host
device
#include<sycl/sycl.hpp>
using namespace sycl;
int main() {
try {
queue q(cpu_selector_v, async_handler{});
int* shared = malloc_shared<int>(1024, q);
q.parallel_for(1024, [=](id<1> i) {
shared[i] = i;
});
q.wait_and_throw();
free(shared, q);
} catch (...) { /* handle errors */ }
}
* Any SYCL application can be debugged on the host device by
switching the queue for a host queue
* Replacing the device selector
for the `aspect_selector` will ensure that the queue submits all
work to the device with the requested aspects, in this case a host
debuggable device
#### Exercise
Code_Exercises/Handling_Errors/source
Add error handling to a SYCL application for both synchronous and
asynchronous errors.