Commit c6c918d7 authored by Jonas Jelten's avatar Jonas Jelten 🥕 Committed by Tobias Lasser
Browse files

add an error class with backtrace capturing for more useful exceptions

parent 8542a42d
Pipeline #461576 passed with stages
in 18 minutes and 51 seconds
.idea
.vscode
# cmake-generated
cmake-build-*
cmake-build-debug
cmake-build-release
build/
bin/
/compile_commands.json
# git
*.orig
# clion
.idea
# vscode
.vscode/*
*.code-workspace
# vim
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
Session.vim
Sessionx.vim
.netrwhist
[._]*.un~
# emacs
*~
\#*\#
.\#*
.dir-locals.el
# clangd
.cache
# misc
.ignore
todo.org
todo
......@@ -90,6 +90,7 @@ if(ELSA_MASTER_PROJECT)
# Adding quickvec
if(ELSA_CUDA_VECTOR)
message(STATUS "CUDA support requested...")
include(CheckLanguage)
check_language(CUDA)
......@@ -109,13 +110,10 @@ if(ELSA_MASTER_PROJECT)
set(ELSA_BUILD_WITH_QUICKVEC TRUE)
add_definitions(-DELSA_CUDA_VECTOR)
else()
message(STATUS "CUDA compiler not found or not compiling with clang!")
message(STATUS "Compiling without a DataHandlerGPU")
message(STATUS "The default container type will be CPU")
set(ELSA_BUILD_WITH_QUICKVEC FALSE)
set(ELSA_CUDA_VECTOR OFF)
message(FATAL_ERROR "CUDA_VECTOR library quickvec could not be added.")
endif()
else()
message(FATAL_ERROR "CUDA_VECTOR language support not found")
endif()
endif()
......
......@@ -21,6 +21,7 @@ set(MODULE_HEADERS
Descriptors/RandomBlocksDescriptor.h
DataContainer.h
DataContainerIterator.h
Error.h
Handlers/DataHandler.h
Handlers/DataHandlerCPU.h
Handlers/DataHandlerMapCPU.h
......@@ -41,6 +42,7 @@ set(MODULE_SOURCES
Descriptors/DetectorDescriptor.cpp
Descriptors/PartitionDescriptor.cpp
DataContainer.cpp
Error.cpp
Handlers/DataHandlerCPU.cpp
Handlers/DataHandlerMapCPU.cpp
LinearOperator.cpp
......@@ -65,6 +67,8 @@ add_library(elsa::${ELSA_MODULE_NAME} ALIAS ${ELSA_MODULE_TARGET_NAME})
target_link_libraries(${ELSA_MODULE_TARGET_NAME} PUBLIC Eigen3::Eigen)
target_link_libraries(${ELSA_MODULE_TARGET_NAME} PRIVATE ${CMAKE_DL_LIBS})
# use OpenMP is available
find_package(OpenMP)
if(OpenMP_CXX_FOUND)
......
......@@ -2,8 +2,8 @@
#include "DataHandlerCPU.h"
#include "DataHandlerMapCPU.h"
#include "BlockDescriptor.h"
#include "Error.h"
#include <stdexcept>
#include <utility>
namespace elsa
......@@ -27,7 +27,7 @@ namespace elsa
_dataHandlerType{handlerType}
{
if (_dataHandler->getSize() != data.size())
throw std::invalid_argument("DataContainer: initialization vector has invalid size");
throw InvalidArgumentError("DataContainer: initialization vector has invalid size");
for (index_t i = 0; i < _dataHandler->getSize(); ++i)
(*_dataHandler)[i] = data[i];
......@@ -247,7 +247,7 @@ namespace elsa
return std::make_unique<DataHandlerGPU<data_t>>(std::forward<Args>(args)...);
#endif
default:
throw std::invalid_argument("DataContainer: unknown handler type");
throw InvalidArgumentError("DataContainer: unknown handler type");
}
}
......@@ -284,10 +284,10 @@ namespace elsa
{
const auto blockDesc = dynamic_cast<const BlockDescriptor*>(_dataDescriptor.get());
if (!blockDesc)
throw std::logic_error("DataContainer: cannot get block from not-blocked container");
throw LogicError("DataContainer: cannot get block from not-blocked container");
if (i >= blockDesc->getNumberOfBlocks() || i < 0)
throw std::invalid_argument("DataContainer: block index out of bounds");
throw InvalidArgumentError("DataContainer: block index out of bounds");
index_t startIndex = blockDesc->getOffsetOfBlock(i);
const auto& ithDesc = blockDesc->getDescriptorOfBlock(i);
......@@ -307,10 +307,10 @@ namespace elsa
{
const auto blockDesc = dynamic_cast<const BlockDescriptor*>(_dataDescriptor.get());
if (!blockDesc)
throw std::logic_error("DataContainer: cannot get block from not-blocked container");
throw LogicError("DataContainer: cannot get block from not-blocked container");
if (i >= blockDesc->getNumberOfBlocks() || i < 0)
throw std::invalid_argument("DataContainer: block index out of bounds");
throw InvalidArgumentError("DataContainer: block index out of bounds");
index_t startIndex = blockDesc->getOffsetOfBlock(i);
const auto& ithDesc = blockDesc->getDescriptorOfBlock(i);
......@@ -331,7 +331,7 @@ namespace elsa
DataContainer<data_t> DataContainer<data_t>::viewAs(const DataDescriptor& dataDescriptor)
{
if (dataDescriptor.getNumberOfCoefficients() != getSize())
throw std::invalid_argument("DataContainer: view must have same size as container");
throw InvalidArgumentError("DataContainer: view must have same size as container");
DataHandlerType newHandlerType = (_dataHandlerType == DataHandlerType::CPU
|| _dataHandlerType == DataHandlerType::MAP_CPU)
......@@ -347,7 +347,7 @@ namespace elsa
DataContainer<data_t>::viewAs(const DataDescriptor& dataDescriptor) const
{
if (dataDescriptor.getNumberOfCoefficients() != getSize())
throw std::invalid_argument("DataContainer: view must have same size as container");
throw InvalidArgumentError("DataContainer: view must have same size as container");
DataHandlerType newHandlerType = (_dataHandlerType == DataHandlerType::CPU
|| _dataHandlerType == DataHandlerType::MAP_CPU)
......@@ -443,7 +443,7 @@ namespace elsa
{
if (_dataHandlerType == DataHandlerType::CPU
|| _dataHandlerType == DataHandlerType::MAP_CPU) {
throw std::logic_error(
throw LogicError(
"DataContainer: cannot load data to CPU with already CPU based container");
}
......@@ -461,7 +461,7 @@ namespace elsa
{
if (_dataHandlerType == DataHandlerType::GPU
|| _dataHandlerType == DataHandlerType::MAP_GPU) {
throw std::logic_error(
throw LogicError(
"DataContainer: cannot load data to GPU with already GPU based container");
}
......
......@@ -6,6 +6,7 @@
#include "DataHandlerCPU.h"
#include "DataHandlerMapCPU.h"
#include "DataContainerIterator.h"
#include "Error.h"
#include "Expression.h"
#ifdef ELSA_CUDA_VECTOR
......@@ -129,7 +130,7 @@ namespace elsa
handler->accessData().eval(source.template eval<true>());
#endif
} else {
throw std::logic_error("Unknown handler type");
throw LogicError("Unknown handler type");
}
return *this;
......@@ -213,7 +214,7 @@ namespace elsa
return temp.sum();
#endif
} else {
throw std::logic_error("Unknown handler type");
throw LogicError("Unknown handler type");
}
}
......
#include "DataDescriptor.h"
#include <stdexcept>
#include <algorithm>
#include "Error.h"
namespace elsa
{
DataDescriptor::DataDescriptor(IndexVector_t numberOfCoefficientsPerDimension)
......@@ -13,7 +14,7 @@ namespace elsa
{
// sanity checks
if ((numberOfCoefficientsPerDimension.array() <= 0).any())
throw std::invalid_argument(
throw InvalidArgumentError(
"DataDescriptor: non-positive number of coefficients not allowed");
// set the origin at center
......@@ -36,13 +37,13 @@ namespace elsa
{
// sanity checks
if ((numberOfCoefficientsPerDimension.array() <= 0).any())
throw std::invalid_argument(
throw InvalidArgumentError(
"DataDescriptor: non-positive number of coefficients not allowed");
if (numberOfCoefficientsPerDimension.size() != spacingPerDimension.size())
throw std::invalid_argument("DataDescriptor: mismatch between "
"numberOfCoefficientsPerDimension and spacingPerDimension");
throw InvalidArgumentError("DataDescriptor: mismatch between "
"numberOfCoefficientsPerDimension and spacingPerDimension");
if ((spacingPerDimension.array() < 0).any())
throw std::invalid_argument("DataDescriptor: non-positive spacing not allowed");
throw InvalidArgumentError("DataDescriptor: non-positive spacing not allowed");
// set the origin at center
_locationOfOrigin = static_cast<real_t>(0.5)
......@@ -77,7 +78,7 @@ namespace elsa
{
// sanity check
if (coordinate.size() != _productOfCoefficientsPerDimension.size())
throw std::invalid_argument(
throw InvalidArgumentError(
"DataDescriptor: mismatch of coordinate and descriptor size");
return _productOfCoefficientsPerDimension.cwiseProduct(coordinate).sum();
......@@ -87,7 +88,7 @@ namespace elsa
{
// sanity check
if (index < 0 || index >= getNumberOfCoefficients())
throw std::invalid_argument("DataDescriptor: invalid index");
throw InvalidArgumentError("DataDescriptor: invalid index");
IndexVector_t coordinate(_numberOfDimensions);
......
......@@ -37,7 +37,7 @@ namespace elsa
* \param[in] numberOfCoefficientsPerDimension vector containing the number of coefficients
* per dimension, (dimension is set implicitly from the size of the vector)
*
* \throw std::invalid_argument if any number of coefficients is non-positive
* \throw InvalidArgumentError if any number of coefficients is non-positive
*/
explicit DataDescriptor(IndexVector_t numberOfCoefficientsPerDimension);
......@@ -48,7 +48,7 @@ namespace elsa
* per dimension, (dimension is set implicitly from the size of the vector)
* \param[in] spacingPerDimension vector containing the spacing per dimension
*
* \throw std::invalid_argument if any number of coefficients is non-positive,
* \throw InvalidArgumentError if any number of coefficients is non-positive,
* or sizes of numberOfCoefficientsPerDimension and spacingPerDimension do not match
*/
explicit DataDescriptor(IndexVector_t numberOfCoefficientsPerDimension,
......
#include "DescriptorUtils.h"
#include "DataDescriptor.h"
#include "VolumeDescriptor.h"
#include "Error.h"
namespace elsa
{
std::unique_ptr<DataDescriptor> bestCommon(const std::vector<const DataDescriptor*>& descList)
{
if (descList.empty())
throw std::invalid_argument("DataDescriptor::bestCommon: descriptor list empty");
throw InvalidArgumentError("DataDescriptor::bestCommon: descriptor list empty");
const auto& firstDesc = *descList[0];
auto coeffs = firstDesc.getNumberOfCoefficientsPerDimension();
......@@ -44,9 +45,8 @@ namespace elsa
});
if (!allSameSize)
throw std::invalid_argument(
"DataDescriptor::bestCommon: descriptor sizes do not match");
throw InvalidArgumentError("DataDescriptor::bestCommon: descriptor sizes do not match");
return std::make_unique<VolumeDescriptor>(IndexVector_t::Constant(1, size));
}
} // namespace elsa
\ No newline at end of file
} // namespace elsa
......@@ -12,7 +12,7 @@ namespace elsa
*
* \return std::unique_ptr<DataDescriptor> the best common descriptor
*
* \throw std::invalid_argument if the vector is empty or the descriptors in the vector
* \throw InvalidArgumentError if the vector is empty or the descriptors in the vector
* don't all have the same size
*
* If all descriptors are equal, a clone of the first descriptor in the list is returned.
......
#include "IdenticalBlocksDescriptor.h"
#include "VolumeDescriptor.h"
#include "Error.h"
namespace elsa
{
......@@ -17,7 +18,7 @@ namespace elsa
const DataDescriptor& IdenticalBlocksDescriptor::getDescriptorOfBlock(index_t i) const
{
if (i < 0 || i >= _numberOfBlocks)
throw std::invalid_argument("BlockDescriptor: index i is out of bounds");
throw InvalidArgumentError("BlockDescriptor: index i is out of bounds");
return *_blockDescriptor;
}
......@@ -25,7 +26,7 @@ namespace elsa
index_t IdenticalBlocksDescriptor::getOffsetOfBlock(index_t i) const
{
if (i < 0 || i >= _numberOfBlocks)
throw std::invalid_argument("BlockDescriptor: index i is out of bounds");
throw InvalidArgumentError("BlockDescriptor: index i is out of bounds");
return i * _blockDescriptor->getNumberOfCoefficients();
}
......@@ -53,7 +54,7 @@ namespace elsa
const DataDescriptor& dataDescriptor)
{
if (numberOfBlocks < 1)
throw std::invalid_argument(
throw InvalidArgumentError(
"IdenticalBlockDescriptor: number of blocks has to be positive");
auto numberOfCoeffs = dataDescriptor.getNumberOfCoefficientsPerDimension();
......
......@@ -30,7 +30,7 @@ namespace elsa
* times
* along a new dimension
*
* \throw std::invalid_argument if numberOfBlocks is non-positive
* \throw InvalidArgumentError if numberOfBlocks is non-positive
*/
IdenticalBlocksDescriptor(index_t numberOfBlocks, const DataDescriptor& dataDescriptor);
......
#include "PartitionDescriptor.h"
#include "Error.h"
#include <unordered_map>
#include <type_traits>
......@@ -13,13 +14,13 @@ namespace elsa
_blockOffsets(numberOfBlocks)
{
if (numberOfBlocks < 2)
throw std::invalid_argument(
throw InvalidArgumentError(
"PartitionDescriptor: number of blocks must be greater than one");
index_t lastDimSize = _numberOfCoefficientsPerDimension[_numberOfDimensions - 1];
if (numberOfBlocks > lastDimSize)
throw std::invalid_argument(
throw InvalidArgumentError(
"PartitionDescriptor: number of blocks too large for given descriptor");
index_t rest = lastDimSize % numberOfBlocks;
......@@ -48,16 +49,16 @@ namespace elsa
_blockOffsets(slicesInBlock.size())
{
if (slicesInBlock.size() < 2)
throw std::invalid_argument(
throw InvalidArgumentError(
"PartitionDescriptor: number of blocks must be greater than one");
if ((slicesInBlock.array() <= 0).any())
throw std::invalid_argument(
throw InvalidArgumentError(
"PartitionDescriptor: non-positive number of coefficients not allowed");
if (slicesInBlock.sum() != _numberOfCoefficientsPerDimension[_numberOfDimensions - 1])
throw std::invalid_argument("PartitionDescriptor: cumulative size of partitioned "
"descriptor does not match size of original descriptor");
throw InvalidArgumentError("PartitionDescriptor: cumulative size of partitioned "
"descriptor does not match size of original descriptor");
std::unordered_map<index_t, index_t> sizeToIndex;
_blockOffsets[0] = 0;
......@@ -93,7 +94,7 @@ namespace elsa
const DataDescriptor& PartitionDescriptor::getDescriptorOfBlock(index_t i) const
{
if (i < 0 || i >= getNumberOfBlocks())
throw std::invalid_argument("BlockDescriptor: index i is out of bounds");
throw InvalidArgumentError("BlockDescriptor: index i is out of bounds");
auto index = std::make_unsigned_t<long>(_indexMap[i]);
return *_blockDescriptors[index];
......@@ -102,7 +103,7 @@ namespace elsa
index_t PartitionDescriptor::getOffsetOfBlock(index_t i) const
{
if (i < 0 || i >= getNumberOfBlocks())
throw std::invalid_argument("BlockDescriptor: index i is out of bounds");
throw InvalidArgumentError("BlockDescriptor: index i is out of bounds");
return _blockOffsets[i];
}
......
......@@ -28,7 +28,7 @@ namespace elsa
* \param[in] dataDescriptor the descriptor to be partitioned
* \param[in] numberOfBlocks the number of blocks
*
* \throw std::invalid_argument if numberOfBlocks is less than 2 or greater than the number
* \throw InvalidArgumentError if numberOfBlocks is less than 2 or greater than the number
* of coefficients in the last dimension
*
* If the given descriptor has a size of \f$ N \f$ in its last dimension, when dividing it
......@@ -48,7 +48,7 @@ namespace elsa
* \param[in] dataDescriptor the descriptor to be partitioned
* \param[in] slicesInBlock the number of slices in each block
*
* \throw std::invalid_argument if slicesInBlock does not specify a valid partition scheme
* \throw InvalidArgumentError if slicesInBlock does not specify a valid partition scheme
* for the given descriptor
*
* Note: if the passed in DataDescriptor is a block descriptor, the block information
......
#include "RandomBlocksDescriptor.h"
#include "VolumeDescriptor.h"
#include "Error.h"
namespace elsa
{
......@@ -40,7 +41,7 @@ namespace elsa
const DataDescriptor& RandomBlocksDescriptor::getDescriptorOfBlock(index_t i) const
{
if (i < 0 || i >= _blockOffsets.size())
throw std::invalid_argument("BlockDescriptor: index i is out of bounds");
throw InvalidArgumentError("BlockDescriptor: index i is out of bounds");
return *_blockDescriptors[static_cast<std::size_t>(i)];
}
......@@ -48,7 +49,7 @@ namespace elsa
index_t RandomBlocksDescriptor::getOffsetOfBlock(index_t i) const
{
if (i < 0 || i >= _blockOffsets.size())
throw std::invalid_argument("BlockDescriptor: index i is out of bounds");
throw InvalidArgumentError("BlockDescriptor: index i is out of bounds");
return _blockOffsets[i];
}
......@@ -80,7 +81,7 @@ namespace elsa
const std::vector<std::unique_ptr<DataDescriptor>>& blockDescriptors)
{
if (blockDescriptors.empty())
throw std::invalid_argument(
throw InvalidArgumentError(
"RandomBlockDescriptor: list of block descriptors cannot be empty");
index_t size = 0;
......
......@@ -30,7 +30,7 @@ namespace elsa
*
* \param[in] blockDescriptors the list of descriptors of each block
*
* \throw std::invalid_argument if the list is empty
* \throw InvalidArgumentError if the list is empty
*/
RandomBlocksDescriptor(
const std::vector<std::unique_ptr<DataDescriptor>>& blockDescriptors);
......@@ -40,7 +40,7 @@ namespace elsa
*
* \param[in] blockDescriptors the list of descriptors of each block
*
* \throw std::invalid_argument if the list is empty
* \throw InvalidArgumentError if the list is empty
*/
RandomBlocksDescriptor(std::vector<std::unique_ptr<DataDescriptor>>&& blockDescriptors);
......
#include "VolumeDescriptor.h"
#include <stdexcept>
#include <algorithm>
namespace elsa
......
......@@ -32,7 +32,7 @@ namespace elsa
* \param[in] numberOfCoefficientsPerDimension vector containing the number of coefficients
* per dimension, (dimension is set implicitly from the size of the vector)
*
* \throw std::invalid_argument if any number of coefficients is non-positive
* \throw InvalidArgumentError if any number of coefficients is non-positive
*/
explicit VolumeDescriptor(IndexVector_t numberOfCoefficientsPerDimension);
......@@ -43,7 +43,7 @@ namespace elsa
* \param[in] numberOfCoefficientsPerDimension initializer list containing the number of
* coefficients per dimension (dimension is set implicitly from the size of the list)
*
* \throw std::invalid_argument if any number of coefficients is non-positive
* \throw InvalidArgumentError if any number of coefficients is non-positive
*/
explicit VolumeDescriptor(std::initializer_list<index_t> numberOfCoefficientsPerDimension);
......@@ -54,7 +54,7 @@ namespace elsa
* per dimension, (dimension is set implicitly from the size of the vector)
* \param[in] spacingPerDimension vector containing the spacing per dimension
*
* \throw std::invalid_argument if any number of coefficients or spacing is non-positive,
* \throw InvalidArgumentError if any number of coefficients or spacing is non-positive,
* or sizes of numberOfCoefficientsPerDimension and spacingPerDimension do not match
*/
explicit VolumeDescriptor(IndexVector_t numberOfCoefficientsPerDimension,
......@@ -68,7 +68,7 @@ namespace elsa
* coefficients per dimension (dimension is set implicitly from the size of the list)
* \param[in] spacingPerDimension initializer list containing the spacing per dimension
*
* \throw std::invalid_argument if any number of coefficients or spacing is non-positive,
* \throw InvalidArgumentError if any number of coefficients or spacing is non-positive,
* or sizes of numberOfCoefficientsPerDimension and spacingPerDimension do not match
*/
explicit VolumeDescriptor(std::initializer_list<index_t> numberOfCoefficientsPerDimension,
......
#include "Error.h"
#include <cxxabi.h>
#include <dlfcn.h>
#include <execinfo.h>
#include <memory>
#include <sstream>
namespace elsa::detail
{
/**
* Demangles a symbol name.
*
* On failure, the mangled symbol name is returned.
*/
std::string symbolDemangle(const char* symbol)
{
int status;
char* buf = abi::__cxa_demangle(symbol, nullptr, nullptr, &status);
if (status != 0) {
return symbol;
} else {
std::string result{buf};
::free(buf);
return result;
}
}
/**
* Convert a pointer address to string.
*/
std::string addrToString(const void* addr)
{
std::ostringstream out;
out << "[" << addr << "]";
return out.str();
}
/**
* Return the demangled symbol name for a given code address.
*/
std::string symbolName(const void* addr, bool require_exact_addr = true,
bool no_pure_addrs = false)
{
Dl_info addr_info;
if (::dladdr(addr, &addr_info) == 0) {
// dladdr has... failed.
return no_pure_addrs ? "" : addrToString(addr);
} else {
size_t symbol_offset =
reinterpret_cast<size_t>(addr) - reinterpret_cast<size_t>(addr_info.dli_saddr);
if (addr_info.dli_sname == nullptr or (symbol_offset != 0 and require_exact_addr)) {
return no_pure_addrs ? "" : addrToString(addr);
}
if (symbol_offset == 0) {
// this is our symbol name.
return symbolDemangle(addr_info.dli_sname);
} else {
std::ostringstream out;
out << symbolDemangle(addr_info.dli_sname) << "+0x" << std::hex << symbol_offset
<< std::dec;
return out.str();
}
}
}
} // namespace elsa::detail
namespace elsa
{
void Backtrace::analyze()
{
std::vector<void*> buffer{32};
// increase buffer size until it's enough
while (true) {