Make most command-line tools return useful error codes on well-known exceptions.
This commit is contained in:
committed by
Patrick Niklaus
parent
03e83ec6a0
commit
3d77714c36
@@ -0,0 +1,37 @@
|
||||
#ifndef OSRM_ERRORCODES_HPP
|
||||
#define OSRM_ERRORCODES_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
|
||||
/**
|
||||
* Various error codes that can be returned by OSRM internal functions.
|
||||
* Note: often, these translate into return codes from `int main()` functions.
|
||||
* Thus, do not change the order - if adding new codes, append them to the
|
||||
* end, so the code values do not change for users that are checking for
|
||||
* certain values.
|
||||
*/
|
||||
enum ErrorCode
|
||||
{
|
||||
InvalidFingerprint = 2, // Start at 2 to avoid colliding with POSIX EXIT_FAILURE
|
||||
IncompatibleFileVersion,
|
||||
FileOpenError,
|
||||
FileReadError,
|
||||
FileWriteError,
|
||||
FileIOError,
|
||||
UnexpectedEndOfFile,
|
||||
IncompatibleDataset,
|
||||
UnknownAlgorithm
|
||||
#ifndef NDEBUG
|
||||
// Leave this at the end. In debug mode, we assert that the size of
|
||||
// this enum matches the number of messages we have documented, and __ENDMARKER__
|
||||
// is used as the "count" value.
|
||||
,
|
||||
__ENDMARKER__
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif // OSRM_ERRORCODES_HPP
|
||||
@@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
namespace osrm
|
||||
{
|
||||
using util::exception;
|
||||
using util::RuntimeError;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+34
-21
@@ -1,6 +1,8 @@
|
||||
#ifndef OSRM_STORAGE_IO_HPP_
|
||||
#define OSRM_STORAGE_IO_HPP_
|
||||
|
||||
#include "osrm/error_codes.hpp"
|
||||
|
||||
#include "util/exception.hpp"
|
||||
#include "util/exception_utils.hpp"
|
||||
#include "util/fingerprint.hpp"
|
||||
@@ -10,6 +12,8 @@
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/iostreams/seek.hpp>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <cstring>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
@@ -49,12 +53,16 @@ class FileReader
|
||||
: filepath(filepath_), fingerprint(flag)
|
||||
{
|
||||
input_stream.open(filepath, std::ios::binary);
|
||||
|
||||
// Note: filepath.string() is wrapped in std::string() because it can
|
||||
// return char * on some platforms, which makes the + operator not work
|
||||
if (!input_stream)
|
||||
throw util::exception("Error opening " + filepath.string());
|
||||
throw util::RuntimeError(
|
||||
filepath.string(), ErrorCode::FileOpenError, SOURCE_REF, std::strerror(errno));
|
||||
|
||||
if (flag == VerifyFingerprint && !ReadAndCheckFingerprint())
|
||||
{
|
||||
throw util::exception("Fingerprint mismatch in " + filepath_.string() + SOURCE_REF);
|
||||
throw util::RuntimeError(filepath.string(), ErrorCode::InvalidFingerprint, SOURCE_REF);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +74,11 @@ class FileReader
|
||||
|
||||
if (file_size == boost::filesystem::ifstream::pos_type(-1))
|
||||
{
|
||||
throw util::exception("File size for " + filepath.string() + " failed " + SOURCE_REF);
|
||||
throw util::RuntimeError("Unable to determine file size for " +
|
||||
std::string(filepath.string()),
|
||||
ErrorCode::FileIOError,
|
||||
SOURCE_REF,
|
||||
std::strerror(errno));
|
||||
}
|
||||
|
||||
// restore the current position
|
||||
@@ -101,10 +113,11 @@ class FileReader
|
||||
{
|
||||
if (result.eof())
|
||||
{
|
||||
throw util::exception("Error reading from " + filepath.string() +
|
||||
": Unexpected end of file " + SOURCE_REF);
|
||||
throw util::RuntimeError(
|
||||
filepath.string(), ErrorCode::UnexpectedEndOfFile, SOURCE_REF);
|
||||
}
|
||||
throw util::exception("Error reading from " + filepath.string() + " " + SOURCE_REF);
|
||||
throw util::RuntimeError(
|
||||
filepath.string(), ErrorCode::FileReadError, SOURCE_REF, std::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,23 +158,19 @@ class FileReader
|
||||
|
||||
if (!loaded_fingerprint.IsValid())
|
||||
{
|
||||
util::Log(logERROR) << "Fingerprint magic number or checksum is invalid in "
|
||||
<< filepath.string();
|
||||
return false;
|
||||
throw util::RuntimeError(filepath.string(), ErrorCode::InvalidFingerprint, SOURCE_REF);
|
||||
}
|
||||
|
||||
if (!expected_fingerprint.IsDataCompatible(loaded_fingerprint))
|
||||
{
|
||||
util::Log(logERROR) << filepath.string()
|
||||
<< " is not compatible with this version of OSRM";
|
||||
|
||||
util::Log(logERROR) << "It was prepared with OSRM "
|
||||
<< loaded_fingerprint.GetMajorVersion() << "."
|
||||
<< loaded_fingerprint.GetMinorVersion() << "."
|
||||
<< loaded_fingerprint.GetPatchVersion() << " but you are running "
|
||||
<< OSRM_VERSION;
|
||||
util::Log(logERROR) << "Data is only compatible between minor releases.";
|
||||
return false;
|
||||
const std::string fileversion =
|
||||
std::to_string(loaded_fingerprint.GetMajorVersion()) + "." +
|
||||
std::to_string(loaded_fingerprint.GetMinorVersion()) + "." +
|
||||
std::to_string(loaded_fingerprint.GetPatchVersion());
|
||||
throw util::RuntimeError(std::string(filepath.string()) + " prepared with OSRM " +
|
||||
fileversion + " but this is " + OSRM_VERSION,
|
||||
ErrorCode::IncompatibleFileVersion,
|
||||
SOURCE_REF);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -192,7 +201,10 @@ class FileWriter
|
||||
{
|
||||
output_stream.open(filepath, std::ios::binary);
|
||||
if (!output_stream)
|
||||
throw util::exception("Error opening " + filepath.string());
|
||||
{
|
||||
throw util::RuntimeError(
|
||||
filepath.string(), ErrorCode::FileOpenError, SOURCE_REF, std::strerror(errno));
|
||||
}
|
||||
|
||||
if (flag == GenerateFingerprint)
|
||||
{
|
||||
@@ -216,7 +228,8 @@ class FileWriter
|
||||
|
||||
if (!result)
|
||||
{
|
||||
throw util::exception("Error writing to " + filepath.string());
|
||||
throw util::RuntimeError(
|
||||
filepath.string(), ErrorCode::FileWriteError, SOURCE_REF, std::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,10 +28,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#ifndef OSRM_EXCEPTION_HPP
|
||||
#define OSRM_EXCEPTION_HPP
|
||||
|
||||
#include <array>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "osrm/error_codes.hpp"
|
||||
#include <boost/format.hpp>
|
||||
|
||||
namespace osrm
|
||||
@@ -39,7 +42,7 @@ namespace osrm
|
||||
namespace util
|
||||
{
|
||||
|
||||
class exception final : public std::exception
|
||||
class exception : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit exception(const char *message) : message(message) {}
|
||||
@@ -54,6 +57,72 @@ class exception final : public std::exception
|
||||
virtual void anchor() const;
|
||||
const std::string message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates a class of error that occurred that was caused by some kind of
|
||||
* external input (common examples are out of disk space, file permission errors,
|
||||
* user supplied bad data, etc).
|
||||
*/
|
||||
|
||||
constexpr const std::array<const char *, 11> ErrorDescriptions = {{
|
||||
"", // Dummy - ErrorCode values start at 2
|
||||
"", // Dummy - ErrorCode values start at 2
|
||||
"Fingerprint did not match the expected value", // InvalidFingerprint
|
||||
"File is incompatible with this version of OSRM", // IncompatibleFileVersion
|
||||
"Problem opening file", // FileOpenError
|
||||
"Problem reading from file", // FileReadError
|
||||
"Problem writing to file", // FileWriteError
|
||||
"I/O error occurred", // FileIOError
|
||||
"Unexpected end of file", // UnexpectedEndOfFile
|
||||
"The dataset you are trying to load is not " // IncompatibleDataset
|
||||
"compatible with the routing algorithm you want to use." // ...continued...
|
||||
"Incompatible algorithm" // IncompatibleAlgorithm
|
||||
}};
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Check that we have messages for every enum
|
||||
static_assert(ErrorDescriptions.size() == ErrorCode::__ENDMARKER__,
|
||||
"ErrorCode list and ErrorDescription lists are different sizes");
|
||||
#endif
|
||||
|
||||
class RuntimeError : public exception
|
||||
{
|
||||
using Base = exception;
|
||||
using Base::Base;
|
||||
|
||||
public:
|
||||
explicit RuntimeError(const std::string &message,
|
||||
const ErrorCode code_,
|
||||
const std::string &sourceref,
|
||||
const char *possiblecause = nullptr)
|
||||
: Base(BuildMessage(message, code_, sourceref, possiblecause)), code(code_)
|
||||
{
|
||||
}
|
||||
|
||||
ErrorCode GetCode() const { return code; }
|
||||
|
||||
private:
|
||||
// This function exists to 'anchor' the class, and stop the compiler from
|
||||
// copying vtable and RTTI info into every object file that includes
|
||||
// this header. (Caught by -Wweak-vtables under Clang.)
|
||||
virtual void anchor() const;
|
||||
const ErrorCode code;
|
||||
|
||||
static std::string BuildMessage(const std::string &message,
|
||||
const ErrorCode code_,
|
||||
const std::string &sourceref,
|
||||
const char *possiblecause = nullptr)
|
||||
{
|
||||
std::string result;
|
||||
result += ErrorDescriptions[code_];
|
||||
result += ": " + std::string(message);
|
||||
result += possiblecause != nullptr
|
||||
? (std::string(" (possible cause: \"") + possiblecause + "\")")
|
||||
: "";
|
||||
result += " (at " + sourceref + ")";
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
#define OSRM_SOURCE_FILE_ PROJECT_RELATIVE_PATH_(__FILE__)
|
||||
|
||||
// This is the macro to use
|
||||
#define SOURCE_REF std::string(" (at ") + OSRM_SOURCE_FILE_ + ":" + std::to_string(__LINE__) + ")"
|
||||
#define SOURCE_REF OSRM_SOURCE_FILE_ + ":" + std::to_string(__LINE__)
|
||||
|
||||
#endif // SOURCE_MACROS_HPP
|
||||
|
||||
Reference in New Issue
Block a user