Program Listing for File FLAMEGPUDeviceException.cu
↰ Return to documentation for file (src/flamegpu/exception/FLAMEGPUDeviceException.cu
)
#include <cstdio>
#include <string>
#include "flamegpu/exception/FLAMEGPUDeviceException.cuh"
#include "flamegpu/simulation/detail/CUDAErrorChecking.cuh"
#include "flamegpu/detail/cuda.cuh"
#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS
namespace flamegpu {
namespace exception {
DeviceExceptionManager::DeviceExceptionManager()
: d_buffer()
, hd_buffer() {
memset (&d_buffer, 0, sizeof(d_buffer));
memset (&hd_buffer, 0, sizeof(hd_buffer));
}
DeviceExceptionManager::~DeviceExceptionManager() {
for (auto &i : d_buffer) {
gpuErrchk(flamegpu::detail::cuda::cudaFree(i));
}
}
DeviceExceptionBuffer *DeviceExceptionManager::getDevicePtr(const unsigned int streamId, const cudaStream_t stream) {
if (streamId >= detail::CUDAScanCompaction::MAX_STREAMS) {
THROW exception::OutOfBoundsException("Stream id %u is out of bounds, %u >= %u, "
"in FLAMEGPUDeviceException::getDevicePtr()\n", streamId, streamId, detail::CUDAScanCompaction::MAX_STREAMS);
}
// It may be better to move this (and the memsets) out to a separate up-front reset call in the future.
if (!d_buffer[streamId]) {
gpuErrchk(cudaMalloc(&d_buffer[streamId], sizeof(DeviceExceptionBuffer)));
}
// @todo - We might need a sync here in some cases? Tests all pass without it.
// gpuErrchk(cudaDeviceSynchronize());
// Memset and return buffer
gpuErrchk(cudaMemsetAsync(d_buffer[streamId], 0, sizeof(DeviceExceptionBuffer), stream));
memset(&hd_buffer[streamId], 0, sizeof(DeviceExceptionBuffer));
return d_buffer[streamId];
}
void DeviceExceptionManager::checkError(const std::string &function, const unsigned int streamId, const cudaStream_t stream) {
if (streamId >= detail::CUDAScanCompaction::MAX_STREAMS) {
THROW exception::OutOfBoundsException("Stream id %u is out of bounds, %u >= %u, "
"in FLAMEGPUDeviceException::checkError()\n", streamId, streamId, detail::CUDAScanCompaction::MAX_STREAMS);
}
if (d_buffer[streamId]) {
// Grab buffer from device
gpuErrchk(cudaMemcpyAsync(&hd_buffer[streamId], d_buffer[streamId], sizeof(DeviceExceptionBuffer), cudaMemcpyDeviceToHost, stream));
gpuErrchk(cudaStreamSynchronize(stream));
// If there is a reported error count
if (hd_buffer[streamId].error_count) {
std::string location_string = getLocationString(hd_buffer[streamId]);
std::string error_string = getErrorString(hd_buffer[streamId]);
throw exception::DeviceError(
"Device function '%s' reported %u errors.\nFirst error:\n%s:\n%s",
function.c_str(), hd_buffer[streamId].error_count, location_string.c_str(), error_string.c_str());
}
} else {
THROW exception::OutOfBoundsException("FLAMEGPUDeviceExceptionBuffer for stream %u has not been allocated, "
"in FLAMEGPUDeviceException::checkError()\n", streamId, streamId, detail::CUDAScanCompaction::MAX_STREAMS);
}
}
std::string DeviceExceptionManager::getLocationString(const DeviceExceptionBuffer &b) {
char buff[DeviceExceptionBuffer::OUT_STRING_LEN];
snprintf(buff, DeviceExceptionBuffer::OUT_STRING_LEN, "%s(%u)[%u,%u,%u][%u,%u,%u]",
b.file_path, b.line_no,
b.block_id[0], b.block_id[1], b.block_id[2],
b.thread_id[0], b.thread_id[1], b.thread_id[2]);
return buff;
}
std::string DeviceExceptionManager::getErrorString(const DeviceExceptionBuffer &b) {
char temp_buffer[DeviceExceptionBuffer::FORMAT_BUFF_LEN];
char out_buffer[DeviceExceptionBuffer::OUT_STRING_LEN];
memset(out_buffer, 0, DeviceExceptionBuffer::FORMAT_BUFF_LEN);
// Progress through b.format_string
unsigned int format_buffer_index = 0;
// Progress through out_buffer
unsigned int out_index = 0;
// Progress through b.format_args_sizes
unsigned int arg_no = 0;
// Progress through b.format_args
unsigned int arg_offset = 0;
// Whilst there is still work to be done, we are still in range of format string and all other structures used
while (b.format_string[format_buffer_index] != '\0' &&
format_buffer_index < DeviceExceptionBuffer::FORMAT_BUFF_LEN &&
out_index < DeviceExceptionBuffer::FORMAT_BUFF_LEN &&
arg_no < DeviceExceptionBuffer::MAX_ARGS) {
// If we find the start of a sub format string
if (b.format_string[format_buffer_index] == '%') {
// Find the next sub format start, or end of entire format string
unsigned int format_end = format_buffer_index + 1;
char format_type = '\0';
while (b.format_string[format_end] != '%' &&
b.format_string[format_end] != '\0' &&
format_end < DeviceExceptionBuffer::FORMAT_BUFF_LEN) {
// Detect the format type, we will use this later
if (format_type == '\0') {
switch (b.format_string[format_end]) {
// This is every format specifier supported by the printf family of functions
case 'd':
case 'i':
case 'u':
case 'o':
case 'x':
case 'X':
case 'f':
case 'e':
case 'g':
case 'G':
case 'a':
case 'A':
case 'c':
case 's':
case 'p':
case 'n':
format_type = b.format_string[format_end];
break;
}
}
++format_end;
}
// Sub format string bounds have been found
// Copy the sub format string into a temporary buffer
memset(temp_buffer, 0, DeviceExceptionBuffer::FORMAT_BUFF_LEN);
memcpy(temp_buffer, b.format_string + format_buffer_index, format_end - format_buffer_index);
// Now send this substring to the formatter to process
// Cast it to the correct type first
// (This assumes snprintf never returns negative)
switch (format_type) {
case 'd':
case 'i': {
// Signed integer
if (b.format_args_sizes[arg_no] == 4) {
out_index += snprintf(out_buffer + out_index, DeviceExceptionBuffer::OUT_STRING_LEN - out_index, temp_buffer, *reinterpret_cast<const int32_t*>(b.format_args+arg_offset));
} else {
out_index += snprintf(out_buffer + out_index, DeviceExceptionBuffer::OUT_STRING_LEN - out_index, temp_buffer, *reinterpret_cast<const int64_t*>(b.format_args+arg_offset));
}
break;
}
case 'u':
case 'o':
case 'x':
case 'X': {
// Unsigned integer
if (b.format_args_sizes[arg_no] == 4) {
out_index += snprintf(out_buffer + out_index, DeviceExceptionBuffer::OUT_STRING_LEN - out_index, temp_buffer, *reinterpret_cast<const uint32_t*>(b.format_args+arg_offset));
} else {
out_index += snprintf(out_buffer + out_index, DeviceExceptionBuffer::OUT_STRING_LEN - out_index, temp_buffer, *reinterpret_cast<const uint64_t*>(b.format_args+arg_offset));
}
break;
}
case 'f':
case 'e':
case 'g':
case 'G':
case 'a':
case 'A': {
// Floating point
if (b.format_args_sizes[arg_no] == 4) {
out_index += snprintf(out_buffer + out_index, DeviceExceptionBuffer::OUT_STRING_LEN - out_index, temp_buffer, *reinterpret_cast<const float*>(b.format_args+arg_offset));
} else {
out_index += snprintf(out_buffer + out_index, DeviceExceptionBuffer::OUT_STRING_LEN - out_index, temp_buffer, *reinterpret_cast<const double*>(b.format_args+arg_offset));
}
break;
}
case 'c': {
// Char
out_index += snprintf(out_buffer + out_index, DeviceExceptionBuffer::OUT_STRING_LEN - out_index, temp_buffer, *reinterpret_cast<const char*>(b.format_args+arg_offset));
break;
}
case 's': {
// Char string
out_index += snprintf(out_buffer + out_index, DeviceExceptionBuffer::OUT_STRING_LEN - out_index, temp_buffer, reinterpret_cast<const char*>(b.format_args+arg_offset));
break;
}
case 'p': {
// Pointer
out_index += snprintf(out_buffer + out_index, DeviceExceptionBuffer::OUT_STRING_LEN - out_index, temp_buffer, reinterpret_cast<const void*>(b.format_args+arg_offset));
break;
}
case 'n': {
// No of chars written (signed pointer to have value written back to)
if (b.format_args_sizes[arg_no] == 4) {
out_index += snprintf(out_buffer + out_index, DeviceExceptionBuffer::OUT_STRING_LEN - out_index, temp_buffer, reinterpret_cast<const int32_t*>(b.format_args+arg_offset));
} else {
out_index += snprintf(out_buffer + out_index, DeviceExceptionBuffer::OUT_STRING_LEN - out_index, temp_buffer, reinterpret_cast<const int64_t*>(b.format_args+arg_offset));
}
break;
}
}
// Update arg counters
arg_offset += b.format_args_sizes[arg_no];
++arg_no;
// Update pointer into main format string and continue loop
format_buffer_index = format_end;
} else {
// Copy the single char
// This will only happen until we hit first sub format string
out_buffer[out_index] = b.format_string[format_buffer_index];
++out_index;
++format_buffer_index;
}
}
return out_buffer;
}
} // namespace exception
} // namespace flamegpu
#endif // FLAMEGPU_SEATBELTS are off