Handout
This commit is contained in:
14
debug/assert.cc
Normal file
14
debug/assert.cc
Normal 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
75
debug/assert.h
Normal 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
44
debug/copystream.h
Normal 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
27
debug/kernelpanic.h
Normal 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
4
debug/nullstream.cc
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "nullstream.h"
|
||||
|
||||
// Instance
|
||||
NullStream nullstream;
|
||||
45
debug/nullstream.h
Normal file
45
debug/nullstream.h
Normal 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
93
debug/output.h
Normal 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];
|
||||
Reference in New Issue
Block a user