Make most command-line tools return useful error codes on well-known exceptions.

This commit is contained in:
Daniel Patterson
2017-05-26 21:16:20 -07:00
committed by Patrick Niklaus
parent 03e83ec6a0
commit 3d77714c36
18 changed files with 229 additions and 39 deletions
+37
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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));
}
}
+70 -1
View File
@@ -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;
}
};
}
}
+1 -1
View File
@@ -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