This commit is contained in:
Niklas Gollenstede
2025-04-14 11:20:52 +02:00
commit 5a2e32aaeb
126 changed files with 16742 additions and 0 deletions

14
debug/assert.cc Normal file
View File

@@ -0,0 +1,14 @@
#include "assert.h"
[[noreturn]] void assertion_failed(const char* exp, const char* func,
const char* file, int line) {
(void)exp;
(void)func;
(void)file;
(void)line;
// TODO: Print error message (in debug window)
// TODO: Then stop the current core permanently
// Use appropriate method from class Core to do so.
while (true) {
} // wait forever so we can mark this as [[noreturn]]
}

75
debug/assert.h Normal file
View File

@@ -0,0 +1,75 @@
// vim: set noet ts=4 sw=4:
/*! \file
* \brief Contains several macros usable for making assertions
*
* Depending on the type of assertion (either static or at runtime), a failing
* assertion will trigger an error. For static assertion, this error will be
* shown at compile time and abort compilation. Runtime assertions will trigger
* a message containing details about the error occurred and will make the CPU
* die.
*/
/*!
* \defgroup debug Debugging functions
*/
#pragma once
#include "../types.h"
#ifndef STRINGIFY
/*! \def STRINGIFY(S)
* \brief Converts a macro parameter into a string
* \ingroup debug
* \param S Expression to be converted
* \return stringified version of S
*/
#define STRINGIFY(S) #S
#endif
/*! \def assert_size(TYPE, SIZE)
* \brief Statically ensure (at compile time) that a data type (or variable)
* has the expected size.
*
* \ingroup debug
* \param TYPE The type to be checked
* \param SIZE Expected size in bytes
*/
#define assert_size(TYPE, SIZE) \
static_assert(sizeof(TYPE) == (SIZE), "Wrong size for " STRINGIFY(TYPE))
/*! \def assert(EXP)
* \brief Ensure (at execution time) an expression evaluates to `true`, print
* an error message and stop the CPU otherwise.
*
* \ingroup debug
* \param EXP The expression to be checked
*/
#ifdef NDEBUG
#define assert(EXP) ((void)0)
#else
#define assert(EXP) \
do { \
if (__builtin_expect(!(EXP), 0)) { \
assertion_failed(STRINGIFY(EXP), __func__, __FILE__, __LINE__); \
} \
} while (false)
/*! \brief Handles a failed assertion
*
* This function will print a message containing further information about the
* failed assertion and stops the current CPU permanently.
*
* \note This function should never be called directly, but only via the macro
* `assert`.
*
* \todo(11) Implement Remainder of Method (output & CPU stopping)
*
* \param exp Expression that did not hold
* \param func Name of the function in which the assertion failed
* \param file Name of the file in which the assertion failed
* \param line Line in which the assertion failed
*/
[[noreturn]] void assertion_failed(const char* exp, const char* func,
const char* file, int line);
#endif

44
debug/copystream.h Normal file
View File

@@ -0,0 +1,44 @@
/*! \file
* \brief \ref CopyStream duplicates \ref OutputStream "output streams"
*/
#pragma once
#include "../object/outputstream.h"
#include "../types.h"
/*! \brief Duplicate all data passed by the stream operator to two \ref
* OutputStream "output streams"
* \ingroup io
*
* Can be used as replacement for any \ref OutputStream -- for example,
* forwarding the \ref DBG output simultaneously to screen (\ref TextStream) and
* serial console (\ref SerialStream).
*
*/
class CopyStream : public OutputStream {
/*! \brief First recipient
*/
OutputStream* first;
/*! \brief Second recipient
*/
OutputStream* second;
public:
/*! \brief Constructor
*
* \param first First recipient for output passed to this object
* \param second Second recipient for output passed to this object
*/
CopyStream(OutputStream* first, OutputStream* second)
: first(first), second(second) {}
/*! \brief Redirect the buffer to both streams and flush them, too.
*/
void flush() override {
buffer[pos] = '\0'; // make sure buffer will only be printed until pos.
*first << buffer << ::flush;
*second << buffer << ::flush;
pos = 0;
}
};

27
debug/kernelpanic.h Normal file
View File

@@ -0,0 +1,27 @@
// vim: set noet ts=4 sw=4:
/*! \file
* \brief Macro to print an error message and stop the current core.
*/
#pragma once
#include "../types.h"
/*! \def kernelpanic
* \brief Print an error message in the debug window and \ref Core::die "stop
* the current core"
*
* \param MSG error message
* \ingroup debug
*/
#define kernelpanic(MSG) \
do { \
DBG << "PANIC: '" << (MSG) << "' in " << __func__ << " @ " << __FILE__ \
<< ":" << __LINE__ << ") - CPU stopped." << endl; \
Core::die(); \
} while (0)
// The includes are intentionally placed at the end, so the macro can be used
// inside those included files as well.
#include "../arch/core.h"
#include "./output.h"

4
debug/nullstream.cc Normal file
View File

@@ -0,0 +1,4 @@
#include "nullstream.h"
// Instance
NullStream nullstream;

45
debug/nullstream.h Normal file
View File

@@ -0,0 +1,45 @@
/*! \file
* \brief \ref NullStream is a stream discarding everything
*/
#pragma once
#include "../object/outputstream.h"
#include "../types.h"
/*! \brief Ignore all data passed by the stream operator
* \ingroup io
*
* Can be used instead of the \ref OutputStream if (for debugging reasons) all
* output should be ignored, e.g. for \ref DBG_VERBOSE
*
* By using template programming, a single generic methods is sufficient
* (which simply discard everything).
*/
class NullStream {
/*! \brief Check if type is supported by output stream
*/
template <typename T>
auto check(T v, OutputStream* p = nullptr) -> decltype(*p << v, void()) {}
public:
/*! \brief Empty default constructor
*/
NullStream() {}
/*! \brief Generic stream operator for any data type
*
* Uses template meta programming for a generic & short solution
*
* \tparam T Type of data to ignore
* \param value data to be ignore
* \return Reference to the \ref NullStream object allowing concatenation of
* operators
*/
template <typename T>
NullStream& operator<<(T value) {
check(value);
return *this;
}
};
extern NullStream nullstream;

93
debug/output.h Normal file
View File

@@ -0,0 +1,93 @@
// vim: set noet ts=4 sw=4:
/*! \file
* \brief Debug macros enabling debug output on a separate window for each
* core.
*/
#pragma once
#include "../types.h"
/*! \def DBG_VERBOSE
* \brief An output stream, which is only displayed in the debug window in
* verbose mode
*
* \note If a serial console has been implemented, the output can be redirected
* to the serial stream instead (by changing the macro) -- this makes the
* (usually) very large output more readable (since it allows scrolling
* back)
*/
#ifdef VERBOSE
// If VERBOSE is defined, forward everything to \ref DBG
#define DBG_VERBOSE DBG
#else
// Otherwise sent everything to the NullStream (which will simply discard
// everything)
#define DBG_VERBOSE nullstream
// in this case we have to include the null stream
#include "./nullstream.h"
#endif
/*! \def DBG
* \brief An output stream, which is displayed in the debug window of the core
* it was executed on
*
* In single core (\OOStuBS) this is just an alias to the debug window object
* `dout`.
*/
/*! However, on a multi core system a debug window for each core is
* required, therefore `dout` has to be an \ref TextStream object array with the
* core ID as array index -- the selection is done via Core::getID()
*
* \warning In case of a very unfavorable scheduling, it is theoretically
* possible that the debug output in a multi core system is displayed
* on the wrong (previous) core.
*/
#define DBG nullstream
#include "../arch/core.h"
#include "../device/textstream.h"
/*! \brief Debug window for the CGA screen
*
* Debug output using \ref DBG like
* `DBG << "var = " << var << endl`
* should be displayed in window dedicated to the core it is executed on.
*
* While this is quite easy on single core systems like \OOStuBS -- they only
* require a single \ref TextStream object called `dout` -- multi core systems
* like \MPStuBS need an object array with one window per core.
* In the latter case direct list initialization can be used:
*
* \code{.cpp}
* TextStream dout[Core::MAX]{
* {0, 40, 17, 21}, // Debug window for core 0, like TextStream(0, 40, 17,
* 21) {40, 80, 17, 21}, // Debug window for core 1, like TextStream(40, 80,
* 17, 21)
* //...
* };
* \endcode
*
* The debug windows in should be located right below the normal output window
* without any overlap and should be able to display at least 3 lines.
* In \MPStuBS, two windows can be placed side-by-side, having 40 columns each.
*
* \todo(11) Define `dout`
*/
extern TextStream dout[Core::MAX];
/*! \brief Debug window with copy function to serial
*
* Provide an additional layer to also ouput debug prints to serial.
* While this is a simple CopyStream pointer in the single core case, it is
* an array in the multi core case, which consists of three TextStreams and
* one CopyStream.
* For that, construction is done like:
*
* \code{.cpp}
* OutputStream* copyout[Core::MAX]{&dout[0], &dout[1], ...}
* \endcode
*
* \todo(11) Define `copyout`
*/
extern OutputStream* copyout[Core::MAX];