Begin work on #8 - Add exception class with backtrace support
Some checks failed
buildbot/mdml-cgi-linux-podman-builder Build done.
buildbot/mdml-cgi-darwin-macos-builder Build done.
buildbot/mdml-cgi-freebsd-jail-builder Build done.

Additionally:
- Separate src and header files
This commit is contained in:
S David 2023-09-12 22:13:27 -04:00
parent 23226526e3
commit add045c405
11 changed files with 208 additions and 75 deletions

2
.gitignore vendored
View File

@ -52,3 +52,5 @@ debug/*
release/*
.cache/*
.DS_Store
*debug/

View File

@ -112,9 +112,11 @@ set_target_properties(
CHECK_INCLUDE_FILE("execinfo.h" HAVE_EXECINFO_H)
if (HAVE_EXECINFO_H)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_EXECINFO_H")
add_definitions(-DHAVE_EXECINFO_H=1)
endif()
include_directories(${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/include)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/artifacts/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/artifacts/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/artifacts/bin)

27
include/debuginfo.hpp Normal file
View File

@ -0,0 +1,27 @@
/* debuginfo.h
* Copyright © 2020-2023 Saul D. Beniquez
* License: Mozilla Public License v. 2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <iostream>
#include <sstream>
#ifdef QW_DEBUG // #region
#include <iostream>
#define debugprint(msg) std::cout << "#*!* " << msg << std::endl
#else
#define debugprint(msg) ;
#endif // #endregion
std::string generate_stacktrace(unsigned short framesToRemove = 1);
void print_cmdline(int argc, const char* argv[]);
// clang-format off
// vim: set foldmethod=marker foldmarker=#region,#endregion textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :

43
include/exception.hpp Normal file
View File

@ -0,0 +1,43 @@
/* exceptions.hpp
* Copyright © 2020 Saul D. Beniquez
* License: Mozilla Public License v. 2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "debuginfo.hpp"
#include <algorithm>
#include <exception>
#include <optional>
#include <sstream>
#include <stdexcept>
#include <string>
#define NOT_IMPLEMENTED std::runtime_error("Unimplemented Method");
#define THROW_EXCEPTION(message) \
throw mdml::exception(message, generate_stacktrace(2))
namespace mdml {
class exception : public std::exception {
public:
exception(
const std::optional<std::exception> innerException = std::nullopt,
const std::optional<std::string> backtrace = std::nullopt
);
const char* what() const noexcept override;
const char* getStacktrace() const noexcept;
private:
std::optional<std::string> stacktrace;
std::optional<std::exception> innerException;
};
}
// clang-format off
// vim: set foldmethod=marker foldmarker=@{,@} textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :

View File

@ -1,5 +1,5 @@
set(LIBMDML_SOURCES libmdml.cpp)
set(LIBMDML_SOURCES libmdml.cpp exception.cpp debuginfo.cpp)
add_library(mdml SHARED ${LIBMDML_SOURCES})

54
src/debuginfo.cpp Normal file
View File

@ -0,0 +1,54 @@
/* debuginfo.h
* Copyright © 2020-2023 Saul D. Beniquez
* License: Mozilla Public License v. 2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "debuginfo.hpp"
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#else
#include <boost/stacktrace.hpp>
#endif
#include <iostream>
#include <sstream>
#include <string>
std::string
generate_stacktrace(unsigned short framesToRemove)
{
std::stringstream buffer;
#ifdef HAVE_EXECINFO_H
void* callstack[128];
int i, frames = backtrace(callstack, 128);
char** strs = backtrace_symbols(callstack, frames);
for (i = framesToRemove; i < frames; ++i) {
buffer << strs[i] << std::endl;
}
free(strs);
#else
buffer << boost::stacktrace::stacktrace() << std::flush;
#endif
return buffer.str();
}
void
print_cmdline(int argc, const char* argv[])
{
int i;
std::cout << "Command-line received" << std::endl;
for (i = 0; i < argc; ++i)
std::cout << argv[i] << " ";
std::cout << std::endl;
}
// clang-format off
// vim: set foldmethod=marker foldmarker=#region,#endregion textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :

54
src/exception.cpp Normal file
View File

@ -0,0 +1,54 @@
/* exceptions.hpp
* Copyright © 2020 Saul D. Beniquez
* License: Mozilla Public License v. 2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "debuginfo.hpp"
#include "exception.hpp"
#include <algorithm>
#include <exception>
#include <optional>
#include <sstream>
#include <stdexcept>
#include <string>
#define NOT_IMPLEMENTED std::runtime_error("Unimplemented Method");
using mdml::exception;
exception::exception(
const std::optional<std::exception> innerException,
const std::optional<std::string> backtrace
)
: std::exception(), stacktrace(std::nullopt), innerException(std::nullopt)
{
if (!backtrace.has_value()) {
this->stacktrace = generate_stacktrace(2);
} else {
this->stacktrace = backtrace;
}
if (innerException.has_value()) {
this->innerException = *(innerException);
}
}
const char*
exception::what() const noexcept
{
return innerException->what();
}
const char*
exception::getStacktrace() const noexcept
{
return stacktrace->c_str();
}
// clang-format off
// vim: set foldmethod=marker foldmarker=@{,@} textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :

View File

@ -1,62 +0,0 @@
/* exceptions.hpp
* Copyright © 2020 Saul D. Beniquez
* License: Mozilla Public License v. 2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <exception>
#include <stdexcept>
#include <string>
#define NOT_IMPLEMENTED std::runtime_error("Unimplemented Method");
namespace mdml {
class exception : public std::exception {
public:
exception(
const std::exception& innerException, const std::string& backtrace
)
: std::exception(innerException)
, innerException(innerException)
, stacktrace(backtrace)
{
}
const char* what() override { return innerException.what(); }
const char* getStacktrace() const noexcept { return stacktrace.c_str(); }
private:
inline std::string generate_stacktrace()
{
std::stringstream buffer;
#ifdef HAVE_EXECINFO_H
void* callstack[128];
int i, frames = backtrace(callstack, 128);
char** strs = backtrace_symbols(callstack, frames);
for (i = 0; i < frames; ++i) {
buffer << strs[i] << std::endl;
}
free(strs);
#else
buffer << boost::stacktrace::stacktrace() << std::flush;
#endif
return buffer.str();
}
std::string stacktrace;
std::exception innerException;
};
}
// Macro to throw an exception with a generated stack trace
#define THROW_EXCEPTION(message) \
throw QWException(message, generate_stacktrace())
// clang-format off
// vim: set foldmethod=marker foldmarker=#region,#endregion textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :

View File

@ -1,4 +1,4 @@
/* libmdml.cpp
/* libmdml.hpp
* Copyright © 2023 Saul D. Beniquez
* License: Mozilla Public License v. 2.0
*
@ -7,10 +7,9 @@
* obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <iostream>
void print_hello() {
std::cout << "Hello World" << std::endl;
void
some_function()
{
return;
}

View File

@ -7,16 +7,30 @@
* obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "exception.hpp"
#include <catch2/catch_test_macros.hpp>
#include <iostream>
#define TEST(testname) TEST_CASE(testname, TEST_SUITE_NAME)
namespace {
const char* TEST_SUITE_NAME = "mdml::exceptions";
const char* TEST_SUITE_NAME = "mdml::exceptions";
// @todo: move this to a runtime-test.cpp file
TEST("Test the test framework")
{
REQUIRE_THROWS(
[]() { throw std::runtime_error("Error Message"); }(),
"Expected Error Message"
);
try {
throw mdml::exception(std::runtime_error("NOT IMPLEMENTED"));
} catch (mdml::exception& e) {
std::cout << e.what() << std::endl;
}
} // #endregion
// @todo: move this to a runtime-test.cpp file
TEST("Test the test framework")
{
REQUIRE( true == true );
} // #endregion
}