From 3da18ee02bd44215d07d2d3bc6b2686cd95bb13a Mon Sep 17 00:00:00 2001 From: Sandu Liviu Catalin Date: Tue, 10 Nov 2015 14:42:52 +0200 Subject: [PATCH] Initial implementation of the internal debugging system. --- cbp/Module.cbp | 2 + source/Base/Shared.cpp | 78 ++++++++++ source/Base/Shared.hpp | 17 ++- source/Core.cpp | 4 + source/Debug.cpp | 335 +++++++++++++++++++++++++++++++++++++++++ source/Debug.hpp | 223 +++++++++++++++++++++++++++ 6 files changed, 658 insertions(+), 1 deletion(-) create mode 100644 source/Debug.cpp create mode 100644 source/Debug.hpp diff --git a/cbp/Module.cbp b/cbp/Module.cbp index 169c26dd..5626dd81 100644 --- a/cbp/Module.cbp +++ b/cbp/Module.cbp @@ -158,6 +158,8 @@ + + diff --git a/source/Base/Shared.cpp b/source/Base/Shared.cpp index c15ee4d9..d7d86796 100644 --- a/source/Base/Shared.cpp +++ b/source/Base/Shared.cpp @@ -1,6 +1,7 @@ #include "Base/Shared.hpp" #include "Register.hpp" #include "Logger.hpp" +#include "Debug.hpp" #include "Core.hpp" // ------------------------------------------------------------------------------------------------ @@ -254,6 +255,83 @@ void LogFtl(const char * fmt, ...) va_end(args); } +// ------------------------------------------------------------------------------------------------ +void DbgWrn(const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + _Dbg->Wrn(NULL, NULL, fmt, args); + va_end(args); +} + +void DbgErr(const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + _Dbg->Wrn(NULL, NULL, fmt, args); + va_end(args); +} + +void DbgFtl(const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + _Dbg->Wrn(NULL, NULL, fmt, args); + va_end(args); +} + +// ------------------------------------------------------------------------------------------------ +void DbgWrn(const char * func, const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + _Dbg->Wrn(NULL, func, fmt, args); + va_end(args); +} + +void DbgErr(const char * func, const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + _Dbg->Wrn(NULL, func, fmt, args); + va_end(args); +} + +void DbgFtl(const char * func, const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + _Dbg->Wrn(NULL, func, fmt, args); + va_end(args); +} + +// ------------------------------------------------------------------------------------------------ +void DbgWrn(const char * type, const char * func, const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + _Dbg->Wrn(type, func, fmt, args); + va_end(args); +} + +void DbgErr(const char * type, const char * func, const char * fmt, ...) +{ + _Dbg->SetInf(type, func); + va_list args; + va_start(args, fmt); + _Dbg->Wrn(type, func, fmt, args); + va_end(args); +} + +void DbgFtl(const char * type, const char * func, const char * fmt, ...) +{ + _Dbg->SetInf(type, func); + va_list args; + va_start(args, fmt); + _Dbg->Wrn(type, func, fmt, args); + va_end(args); +} + // ------------------------------------------------------------------------------------------------ const SQChar * ToStringF(const char * fmt, ...) { diff --git a/source/Base/Shared.hpp b/source/Base/Shared.hpp index 8bbc5c13..e03fa0cd 100644 --- a/source/Base/Shared.hpp +++ b/source/Base/Shared.hpp @@ -84,7 +84,7 @@ template<> inline Float64 Clamp(const Float64 val, const Float64 min, const Floa } /* ------------------------------------------------------------------------------------------------ - * Simple functions to quickly forward logging messages without including the logging system + * Simple functions to quickly forward logging messages without including the logging system. */ void LogDbg(const char * fmt, ...); void LogMsg(const char * fmt, ...); @@ -94,6 +94,21 @@ void LogWrn(const char * fmt, ...); void LogErr(const char * fmt, ...); void LogFtl(const char * fmt, ...); +/* ------------------------------------------------------------------------------------------------ + * Simple functions to quickly forward debugging messages without including the debugging system. +*/ +void DbgWrn(const char * fmt, ...); +void DbgErr(const char * fmt, ...); +void DbgFtl(const char * fmt, ...); + +void DbgWrn(const char * func, const char * fmt, ...); +void DbgErr(const char * func, const char * fmt, ...); +void DbgFtl(const char * func, const char * fmt, ...); + +void DbgWrn(const char * type, const char * func, const char * fmt, ...); +void DbgErr(const char * type, const char * func, const char * fmt, ...); +void DbgFtl(const char * type, const char * func, const char * fmt, ...); + /* ------------------------------------------------------------------------------------------------ * ... */ diff --git a/source/Core.cpp b/source/Core.cpp index dc7cd6e7..3d487ff6 100644 --- a/source/Core.cpp +++ b/source/Core.cpp @@ -1,4 +1,5 @@ #include "Core.hpp" +#include "Debug.hpp" #include "Logger.hpp" #include "Entity.hpp" #include "Register.hpp" @@ -391,6 +392,7 @@ bool Core::CreateVM() DefaultVM::Set(m_VM); ErrorHandling::Enable(true); m_Scripts.clear(); + _Dbg->SetVM(m_VM); } LogDbg("Registering the standard libraries"); @@ -439,6 +441,8 @@ void Core::DestroyVM() m_VM = nullptr; // Close the Virtual Machine sq_close(sq_vm); + // Remove it from the debugger as well + _Dbg->SetVM(NULL); } } diff --git a/source/Debug.cpp b/source/Debug.cpp new file mode 100644 index 00000000..3e4b3e5a --- /dev/null +++ b/source/Debug.cpp @@ -0,0 +1,335 @@ +#include "Debug.hpp" +#include "Logger.hpp" +#include "Register.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +const Debug::Pointer _Dbg = Debug::Inst(); + +// ------------------------------------------------------------------------------------------------ +Debug::Debug() + : m_VM(NULL) + , m_Opts(PRINT_ALL | TRACE_ALL) + , m_Type() + , m_Func() + , m_RootName(_SC("root")) + , m_UnknownType(_SC("unknown")) + , m_UnknownFunc(_SC("unknown")) +{ + +} + +// ------------------------------------------------------------------------------------------------ +Debug::~Debug() +{ + +} + +// ------------------------------------------------------------------------------------------------ +void Debug::_Finalizer(Debug * ptr) +{ + delete ptr; /* Assuming 'delete' checks for NULL */ +} + +// ------------------------------------------------------------------------------------------------ +Debug::Pointer Debug::Inst() +{ + if (!_Dbg) + { + return Pointer(new Debug(), &Debug::_Finalizer); + } + + return Pointer(nullptr, &Debug::_Finalizer); +} + +// ------------------------------------------------------------------------------------------------ +HSQUIRRELVM Debug::GetVM() +{ + return m_VM; +} + +// ------------------------------------------------------------------------------------------------ +void Debug::SetVM(HSQUIRRELVM vm) +{ + m_VM = vm; +} + +// ------------------------------------------------------------------------------------------------ +void Debug::PrintTrace(SQInt32 lvl, SQInt32 end) +{ + SQMOD_UNUSED_VAR(lvl); + SQMOD_UNUSED_VAR(end); +} + +// ------------------------------------------------------------------------------------------------ +void Debug::PrintCallstack(SQInt32 lvl, SQInt32 end) +{ + SQMOD_UNUSED_VAR(lvl); + SQMOD_UNUSED_VAR(end); +} + +// ------------------------------------------------------------------------------------------------ +void Debug::SetInf(const char * type, const char * func) +{ + if (type != NULL) + { + m_Type.assign(type); + } + else + { + m_Type.assign(m_UnknownType); + } + + if (func != NULL) + { + m_Func.assign(func); + } + else + { + m_Func.assign(m_UnknownFunc); + } +} + +// ------------------------------------------------------------------------------------------------ +void Debug::Wrn(const char * type, const char * func, const char * fmt, va_list args) +{ + // Store the information about the source + SetInf(type, func); + // Are warning messages enabled? + if (!(m_Opts & WRN_PRINT)) + { + return; + } + // Send the message + _Log->Send(Logger::LEVEL_WRN, false, fmt, args); + // Is trace enabled for warning messages? + if (!(m_Opts & WRN_TRACE)) + { + return; + } + // Do a traceback of the function call + InternalTrace(); +} + +// ------------------------------------------------------------------------------------------------ +void Debug::Err(const char * type, const char * func, const char * fmt, va_list args) +{ + // Store the information about the source + SetInf(type, func); + // Are warning messages enabled? + if (!(m_Opts & ERR_PRINT)) + { + return; + } + // Send the message + _Log->Send(Logger::LEVEL_ERR, false, fmt, args); + // Is trace enabled for warning messages? + if (!(m_Opts & ERR_TRACE)) + { + return; + } + // Do a traceback of the function call + InternalTrace(); +} + +// ------------------------------------------------------------------------------------------------ +void Debug::Ftl(const char * type, const char * func, const char * fmt, va_list args) +{ + // Store the information about the source + SetInf(type, func); + // Are warning messages enabled? + if (!(m_Opts & FTL_PRINT)) + { + return; + } + // Send the message + _Log->Send(Logger::LEVEL_FTL, false, fmt, args); + // Is trace enabled for warning messages? + if (!(m_Opts & FTL_TRACE)) + { + return; + } + // Do a traceback of the function call + InternalTrace(); +} + +// ------------------------------------------------------------------------------------------------ +void Debug::Wrn(const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + Wrn(NULL, NULL, fmt, args); + va_end(args); +} + +// ------------------------------------------------------------------------------------------------ +void Debug::Err(const char * fmt, ...) +{ + SetInf(NULL, NULL); + va_list args; + va_start(args, fmt); + Err(NULL, NULL, fmt, args); + va_end(args); +} + +// ------------------------------------------------------------------------------------------------ +void Debug::Ftl(const char * fmt, ...) +{ + SetInf(NULL, NULL); + va_list args; + va_start(args, fmt); + Ftl(NULL, NULL, fmt, args); + va_end(args); +} + +// ------------------------------------------------------------------------------------------------ +void Debug::Wrn(const char * func, const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + Wrn(NULL, func, fmt, args); + va_end(args); +} + +// ------------------------------------------------------------------------------------------------ +void Debug::Err(const char * func, const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + Err(NULL, func, fmt, args); + va_end(args); +} + +// ------------------------------------------------------------------------------------------------ +void Debug::Ftl(const char * func, const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + Ftl(NULL, func, fmt, args); + va_end(args); +} + +// ------------------------------------------------------------------------------------------------ +void Debug::Wrn(const char * type, const char * func, const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + Wrn(type, func, fmt, args); + va_end(args); +} + +// ------------------------------------------------------------------------------------------------ +void Debug::Err(const char * type, const char * func, const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + Err(type, func, fmt, args); + va_end(args); +} + +// ------------------------------------------------------------------------------------------------ +void Debug::Ftl(const char * type, const char * func, const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + Ftl(type, func, fmt, args); + va_end(args); +} + +// ------------------------------------------------------------------------------------------------ +void Debug::InternalTrace() +{ + // Structure that will contain informations about the queried stack level + SQStackInfos si; + // Output the base stack information before the actual trace + if (SQ_SUCCEEDED(sq_stackinfos(m_VM, 1, &si))) + { + if (m_Func.at(0) == '@') + { + _Log->SInf(" > 1 [ %s:%d > %s::%s ]", si.source ? si.source : _SC("unknown"), + si.line, m_Type.c_str(), m_Func.c_str()+1); + } + else + { + _Log->SInf(" > 1 [ %s:%d > %s::%s(...) ]", si.source ? si.source : _SC("unknown"), + si.line, m_Type.c_str(), m_Func.c_str()); + } + } + // Keep outputting traceback information for the rest of the function calls + for (SQInt32 level = 2; SQ_SUCCEEDED(sq_stackinfos(m_VM, level, &si)); ++level) + { + _Log->SInf(" > %d [ %s:%d > %s::%s(...) ]", level, si.source ? si.source : _SC("unknown"), + si.line, EnvName(level).c_str(), si.funcname ? si.funcname : _SC("unknown")); + } +} + +// ------------------------------------------------------------------------------------------------ +String Debug::EnvName(SQInt32 level) +{ + // Obtain the top of the stack + const SQInteger top = sq_gettop(m_VM); + // Name of the retrieved local + const SQChar * name = NULL; + // Attempt to find the this environment + for (SQInt32 seq = 0; (name = sq_getlocal(m_VM, level, seq)); ++seq) + { + // Is this the `this` environment? + if (strcmp(name, "this") == 0) + { + // Found it! + break; + } + // Pop this useless local from the stack + sq_pop(m_VM, 1); + } + // Have we found anything? + if (name == NULL) + { + // Unable to find the `this` environment + return m_UnknownType; + } + // Push the root table on the stack + sq_pushroottable(m_VM); + // See if the `this` environment in the current level is the root table + if (sq_cmp(m_VM) == 0) + { + // Pop everything pushed onto the stack + sq_pop(m_VM, sq_gettop(m_VM) - top); + // Return the name specified for the root table + return m_RootName; + } + // Attempt to manually call the _typeof metamethod on the `this` environment + else if (SQ_SUCCEEDED(sq_typeof(m_VM, -2))) + { + // Treat the value returned by the the _typeof metamethod as a string + Var< String > str(m_VM, -1); + // Pop everything pushed onto the stack + sq_pop(m_VM, sq_gettop(m_VM) - top); + // If it was a valid string then return it as the name + if (!str.value.empty()) + { + return str.value; + } + } + // Just pop everything pushed onto the stack + else + { + sq_pop(m_VM, sq_gettop(m_VM) - top); + } + // At this point the `this` environment is not recognized + return m_UnknownType; +} + +// ================================================================================================ +bool Register_Dbg(HSQUIRRELVM vm) +{ + SQMOD_UNUSED_VAR(vm); + return true; +} + +} // Namespace:: SqMod diff --git a/source/Debug.hpp b/source/Debug.hpp new file mode 100644 index 00000000..08ab0d77 --- /dev/null +++ b/source/Debug.hpp @@ -0,0 +1,223 @@ +#ifndef _DEBUG_HPP_ +#define _DEBUG_HPP_ + +// ------------------------------------------------------------------------------------------------ +#include "Common.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Class meant to help with the debugging process by offering tools for extracting information. +*/ +class Debug +{ + /* -------------------------------------------------------------------------------------------- + * Allow only the smart pointer to delete this class instance as soon as it's not needed. + */ + friend class std::unique_ptr< Debug, void(*)(Debug *) >; + +protected: + + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + Debug(); + + /* -------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + Debug(const Debug & o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Move constructor (disabled). + */ + Debug(Debug && o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~Debug(); + + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + Debug & operator = (const Debug & o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Move assignment operator (disabled). + */ + Debug & operator = (Debug && o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Called by the smart pointer to delete the instance of this class. + */ + static void _Finalizer(Debug * ptr); + +public: + + // -------------------------------------------------------------------------------------------- + typedef std::unique_ptr< Debug, void(*)(Debug *) > Pointer; + + /* -------------------------------------------------------------------------------------------- + * Creates an instance of this type if one doesn't already exist and returns it. + */ + static Pointer Inst(); + + /* -------------------------------------------------------------------------------------------- + * ... + */ + static constexpr Uint8 WRN_PRINT = (1 << 0); + static constexpr Uint8 ERR_PRINT = (1 << 1); + static constexpr Uint8 FTL_PRINT = (1 << 2); + static constexpr Uint8 PRINT_ALL = (WRN_PRINT | ERR_PRINT | FTL_PRINT); + + /* -------------------------------------------------------------------------------------------- + * ... + */ + static constexpr Uint8 WRN_TRACE = (1 << 3); + static constexpr Uint8 ERR_TRACE = (1 << 4); + static constexpr Uint8 FTL_TRACE = (1 << 5); + static constexpr Uint8 TRACE_ALL = (WRN_TRACE | ERR_TRACE | FTL_TRACE); + + /* -------------------------------------------------------------------------------------------- + * ... + */ + HSQUIRRELVM GetVM(); + + /* -------------------------------------------------------------------------------------------- + * ... + */ + void SetVM(HSQUIRRELVM vm); + + /* -------------------------------------------------------------------------------------------- + * ... + */ + void PrintTrace(SQInt32 lvl, SQInt32 end); + + /* -------------------------------------------------------------------------------------------- + * ... + */ + void PrintCallstack(SQInt32 lvl, SQInt32 end); + + /* -------------------------------------------------------------------------------------------- + * ... + */ + void SetInf(const char * type, const char * func); + + /* -------------------------------------------------------------------------------------------- + * Throws a warning message. + */ + void Wrn(const char * type, const char * func, const char * fmt, va_list args); + + /* -------------------------------------------------------------------------------------------- + * Throws a error message. + */ + void Err(const char * type, const char * func, const char * fmt, va_list args); + + /* -------------------------------------------------------------------------------------------- + * Throws a fatal message. + */ + void Ftl(const char * type, const char * func, const char * fmt, va_list args); + + /* -------------------------------------------------------------------------------------------- + * Throws a warning message. + */ + void Wrn(const char * fmt, ...); + + /* -------------------------------------------------------------------------------------------- + * Throws a error message. + */ + void Err(const char * fmt, ...); + + /* -------------------------------------------------------------------------------------------- + * Throws a fatal message. + */ + void Ftl(const char * fmt, ...); + + /* -------------------------------------------------------------------------------------------- + * Throws a warning message. + */ + void Wrn(const char * func, const char * fmt, ...); + + /* -------------------------------------------------------------------------------------------- + * Throws a error message. + */ + void Err(const char * func, const char * fmt, ...); + + /* -------------------------------------------------------------------------------------------- + * Throws a fatal message. + */ + void Ftl(const char * func, const char * fmt, ...); + + /* -------------------------------------------------------------------------------------------- + * Throws a warning message. + */ + void Wrn(const char * type, const char * func, const char * fmt, ...); + + /* -------------------------------------------------------------------------------------------- + * Throws a error message. + */ + void Err(const char * type, const char * func, const char * fmt, ...); + + /* -------------------------------------------------------------------------------------------- + * Throws a fatal message. + */ + void Ftl(const char * type, const char * func, const char * fmt, ...); + +protected: + + /* -------------------------------------------------------------------------------------------- + * Internal function used to output a traceback of function calls when issues occur. + */ + void InternalTrace(); + + /* -------------------------------------------------------------------------------------------- + * ... + */ + String EnvName(SQInt32 level); + +private: + + /* -------------------------------------------------------------------------------------------- + * ... + */ + HSQUIRRELVM m_VM; + + /* -------------------------------------------------------------------------------------------- + * ... + */ + Uint8 m_Opts; + + /* -------------------------------------------------------------------------------------------- + * ... + */ + String m_Type; + + /* -------------------------------------------------------------------------------------------- + * ... + */ + String m_Func; + + /* -------------------------------------------------------------------------------------------- + * ... + */ + String m_RootName; + + /* -------------------------------------------------------------------------------------------- + * ... + */ + String m_UnknownType; + + /* -------------------------------------------------------------------------------------------- + * ... + */ + String m_UnknownFunc; +}; + +// ------------------------------------------------------------------------------------------------ +extern const Debug::Pointer _Dbg; + +} // Namespace:: SqMod + +#endif // _DEBUG_HPP_