From 5cf2b04f6a5973a6f556595ba07a004571fcd010 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Wed, 17 Feb 2016 19:45:07 +0000 Subject: [PATCH] Initial implementation of scope guard logging for including in crash logs. This adds a mechanism to create scope guards with an associated std::function which is called to output diagnostic info in the event of a crash. Add a macro to make it easy to efficiently capture variables on the stack and output a formatted message. Requires C++11, #ifdefed out for legacy compilers. --- source.list | 3 ++ src/crashlog.cpp | 9 ++++++ src/scope_info.cpp | 72 ++++++++++++++++++++++++++++++++++++++++++++++ src/scope_info.h | 58 +++++++++++++++++++++++++++++++++++++ src/stdafx.h | 6 ++++ 5 files changed, 148 insertions(+) create mode 100644 src/scope_info.cpp create mode 100644 src/scope_info.h diff --git a/source.list b/source.list index 2277a22139..456a71d374 100644 --- a/source.list +++ b/source.list @@ -1194,3 +1194,6 @@ thread/thread.h #else thread/thread_none.cpp #end + +scope_info.cpp +scope_info.h diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 19a5898623..a0ec4d26fb 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -28,6 +28,8 @@ #include "network/network.h" #include "language.h" #include "fontcache.h" +#include "scope_info.h" +#include "thread/thread.h" #include "ai/ai_info.hpp" #include "game/game.hpp" @@ -327,6 +329,13 @@ char *CrashLog::FillCrashLog(char *buffer, const char *last) const buffer += seprintf(buffer, last, "In game date: %i-%02i-%02i (%i)\n\n", ymd.year, ymd.month + 1, ymd.day, _date_fract); buffer = this->LogError(buffer, last, CrashLog::message); + +#ifdef USE_SCOPE_INFO + if (IsMainThread()) { + buffer += WriteScopeLog(buffer, last); + } +#endif + buffer = this->LogOpenTTDVersion(buffer, last); buffer = this->LogRegisters(buffer, last); buffer = this->LogStacktrace(buffer, last); diff --git a/src/scope_info.cpp b/src/scope_info.cpp new file mode 100644 index 0000000000..7e5aa1cc23 --- /dev/null +++ b/src/scope_info.cpp @@ -0,0 +1,72 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file scope_info.cpp Scope info debug functions. */ + +#include "stdafx.h" +#include "scope_info.h" +#include "string_func.h" +#include "strings_func.h" +#include "company_base.h" +#include "vehicle_base.h" +#include "table/strings.h" + +#include "safeguards.h" + +#ifdef USE_SCOPE_INFO + +std::vector> _scope_stack; + +int WriteScopeLog(char *buf, const char *last) +{ + char *b = buf; + if (!_scope_stack.empty()) { + b += seprintf(b, last, "Within context:"); + int depth = 0; + for (auto it = _scope_stack.rbegin(); it != _scope_stack.rend(); ++it, depth++) { + b += seprintf(b, last, "\n %2d: ", depth); + b += (*it)(b, last); + } + b += seprintf(b, last, "\n\n"); + } + return b - buf; +} + +// helper functions +char *DumpCompanyInfo(int company_id) +{ + char buf[256]; + char *b = buf + seprintf(buf, lastof(buf), "%d (", company_id); + SetDParam(0, company_id); + b = GetString(b, STR_COMPANY_NAME, lastof(buf)); + b += seprintf(b, lastof(buf), ")"); + return stredup(buf, lastof(buf)); +} + +char *DumpVehicleInfo(const Vehicle *v) +{ + char buf[256]; + char *b = buf; + if (v) { + b += seprintf(b, lastof(buf), "veh: %u: (", v->index); + SetDParam(0, v->index); + b = GetString(b, STR_VEHICLE_NAME, lastof(buf)); + if (v->First() && v->First() != v) { + b += seprintf(b, lastof(buf), "), front: %u: (", v->First()->index); + SetDParam(0, v->First()->index); + b = GetString(b, STR_VEHICLE_NAME, lastof(buf)); + } + b += seprintf(b, lastof(buf), ")"); + } else { + b += seprintf(b, lastof(buf), "veh: NULL"); + } + return stredup(buf, lastof(buf)); +} + +#endif diff --git a/src/scope_info.h b/src/scope_info.h new file mode 100644 index 0000000000..cb6cb18dd4 --- /dev/null +++ b/src/scope_info.h @@ -0,0 +1,58 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file scope_info.h Scope info debug functions. */ + +#ifndef SCOPE_INFO_H +#define SCOPE_INFO_H + +#ifdef USE_SCOPE_INFO +#include +#include + +struct Vehicle; + +extern std::vector> _scope_stack; + +struct scope_info_func_obj { + scope_info_func_obj(std::function func) + { + _scope_stack.emplace_back(std::move(func)); + } + + scope_info_func_obj(const scope_info_func_obj ©src) = delete; + + ~scope_info_func_obj() + { + _scope_stack.pop_back(); + } +}; + +int WriteScopeLog(char *buf, const char *last); + +#define SCOPE_INFO_PASTE(a, b) a ## b + +/** + * This creates a lambda in the current scope with the specified capture which outputs the given args as a format string. + * This lambda is then captured by reference in a std::function which is pushed onto the scope stack + * The scope stack is popped at the end of the scope + */ +#define SCOPE_INFO_FMT(capture, ...) auto SCOPE_INFO_PASTE(_sc_lm_, __LINE__) = capture (char *buf, const char *last) { return seprintf(buf, last, __VA_ARGS__); }; scope_info_func_obj SCOPE_INFO_PASTE(_sc_obj_, __LINE__) ([&](char *buf, const char *last) -> int { return SCOPE_INFO_PASTE(_sc_lm_, __LINE__) (buf, last); }); + +// helper functions +char *DumpCompanyInfo(int company_id); +char *DumpVehicleInfo(const Vehicle *v); + +#else /* USE_SCOPE_INFO */ + +#define SCOPE_INFO_FMT(...) + +#endif /* USE_SCOPE_INFO */ + +#endif /* SCOPE_INFO_H */ diff --git a/src/stdafx.h b/src/stdafx.h index 78401402e5..cb99d244a8 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -520,4 +520,10 @@ static inline void free(const void *ptr) #define IGNORE_UNINITIALIZED_WARNING_STOP #endif +#if !defined(DISABLE_SCOPE_INFO) && (__cplusplus >= 201103L || defined(__STDCXX_VERSION__) || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__)) +#define USE_SCOPE_INFO +#endif + +#define SINGLE_ARG(...) __VA_ARGS__ + #endif /* STDAFX_H */