From 6c546a21457fc7c5f1c32dc0d3903d464607e45c Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 26 Aug 2018 04:50:47 +0100 Subject: [PATCH 1/5] Add include for scope header file --- src/scope.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/scope.h b/src/scope.h index d341b50b1f..b546813ac3 100644 --- a/src/scope.h +++ b/src/scope.h @@ -12,6 +12,8 @@ #ifndef SCOPE_H #define SCOPE_H +#include + template class scope_exit_obj { T f; From dde88887ae8d04c1b5600cb1a00542d4dbd3a19b Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 25 Aug 2018 23:39:10 +0100 Subject: [PATCH 2/5] Add method to get name of current thread --- src/thread/thread.h | 8 ++++++++ src/thread/thread_morphos.cpp | 2 ++ src/thread/thread_none.cpp | 2 ++ src/thread/thread_os2.cpp | 2 ++ src/thread/thread_pthread.cpp | 15 +++++++++++++++ src/thread/thread_win32.cpp | 30 ++++++++++++++++++++++++++++++ 6 files changed, 59 insertions(+) diff --git a/src/thread/thread.h b/src/thread/thread.h index dddd26f63f..28c8b59988 100644 --- a/src/thread/thread.h +++ b/src/thread/thread.h @@ -133,4 +133,12 @@ void SetSelfAsMainThread(); */ bool IsMainThread(); +/** + * Get the name of the current thread, if any. + * @param str The start of the buffer. + * @param last The last char of the buffer. + * @return Number of chars written to str. + */ +int GetThreadName(char *str, const char *last); + #endif /* THREAD_H */ diff --git a/src/thread/thread_morphos.cpp b/src/thread/thread_morphos.cpp index 9b2485bffe..322a2ab534 100644 --- a/src/thread/thread_morphos.cpp +++ b/src/thread/thread_morphos.cpp @@ -199,3 +199,5 @@ private: void SetSelfAsMainThread() { } bool IsMainThread() { return false; } + +int GetThreadName(char *str, const char *last) { return 0; } diff --git a/src/thread/thread_none.cpp b/src/thread/thread_none.cpp index b68b372f77..30f1adeb6a 100644 --- a/src/thread/thread_none.cpp +++ b/src/thread/thread_none.cpp @@ -37,3 +37,5 @@ public: void SetSelfAsMainThread() { } bool IsMainThread() { return true; } + +int GetThreadName(char *str, const char *last) { return 0; } diff --git a/src/thread/thread_os2.cpp b/src/thread/thread_os2.cpp index 6cb182bae7..c666bbced7 100644 --- a/src/thread/thread_os2.cpp +++ b/src/thread/thread_os2.cpp @@ -149,3 +149,5 @@ public: void SetSelfAsMainThread() { } bool IsMainThread() { return false; } + +int GetThreadName(char *str, const char *last) { return 0; } diff --git a/src/thread/thread_pthread.cpp b/src/thread/thread_pthread.cpp index 103465c0a7..f00ae0bb8f 100644 --- a/src/thread/thread_pthread.cpp +++ b/src/thread/thread_pthread.cpp @@ -11,6 +11,7 @@ #include "../stdafx.h" #include "thread.h" +#include "../string_func.h" #include #include @@ -201,3 +202,17 @@ bool IsMainThread() { return main_thread == pthread_self(); } + +int GetThreadName(char *str, const char *last) +{ +#if defined(__GLIBC__) +#if __GLIBC_PREREQ(2, 12) + char buffer[16]; + int result = pthread_getname_np(pthread_self(), buffer, sizeof(buffer)); + if (result == 0) { + return seprintf(str, last, "%s", buffer); + } +#endif +#endif + return 0; +} diff --git a/src/thread/thread_win32.cpp b/src/thread/thread_win32.cpp index 446c1bafc7..c7e13a775b 100644 --- a/src/thread/thread_win32.cpp +++ b/src/thread/thread_win32.cpp @@ -13,13 +13,19 @@ #include "thread.h" #include "../debug.h" #include "../core/alloc_func.hpp" +#include "../scope.h" +#include "../string_func.h" #include #include #include #include "../os/windows/win32.h" +#include +#include #include "../safeguards.h" +static void Win32SetThreadName(uint id, const char *name); + /** * Win32 thread version for ThreadObject. */ @@ -46,6 +52,7 @@ public: { this->thread = (HANDLE)_beginthreadex(NULL, 0, &stThreadProc, this, CREATE_SUSPENDED, &this->id); if (this->thread == NULL) return; + Win32SetThreadName(this->id, name); ResumeThread(this->thread); } @@ -177,3 +184,26 @@ bool IsMainThread() { return main_thread_id == GetCurrentThreadId(); } + +static std::map _thread_name_map; +static ThreadMutex_Win32 _thread_name_map_mutex; + +static void Win32SetThreadName(uint id, const char *name) +{ + _thread_name_map_mutex.BeginCritical(); + _thread_name_map[id] = name; + _thread_name_map_mutex.EndCritical(); +} + +int GetThreadName(char *str, const char *last) +{ + _thread_name_map_mutex.BeginCritical(); + auto guard = scope_guard([&]() { + _thread_name_map_mutex.EndCritical(); + }); + auto iter = _thread_name_map.find(GetCurrentThreadId()); + if (iter != _thread_name_map.end()) { + return seprintf(str, last, "%s", iter->second.c_str()); + } + return 0; +} From 79644493c9badda9383b6a3899aea14bf83d0043 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 26 Aug 2018 22:13:20 +0100 Subject: [PATCH 3/5] Add method to get whether current thread is non-main --- src/thread/thread.h | 5 +++++ src/thread/thread_morphos.cpp | 1 + src/thread/thread_none.cpp | 1 + src/thread/thread_os2.cpp | 2 ++ src/thread/thread_pthread.cpp | 5 +++++ src/thread/thread_win32.cpp | 5 +++++ 6 files changed, 19 insertions(+) diff --git a/src/thread/thread.h b/src/thread/thread.h index 28c8b59988..ecfd655fb6 100644 --- a/src/thread/thread.h +++ b/src/thread/thread.h @@ -133,6 +133,11 @@ void SetSelfAsMainThread(); */ bool IsMainThread(); +/** + * @return true if the current thread definitely a "non-main" thread. If in doubt returns false. + */ +bool IsNonMainThread(); + /** * Get the name of the current thread, if any. * @param str The start of the buffer. diff --git a/src/thread/thread_morphos.cpp b/src/thread/thread_morphos.cpp index 322a2ab534..259b0a56b1 100644 --- a/src/thread/thread_morphos.cpp +++ b/src/thread/thread_morphos.cpp @@ -199,5 +199,6 @@ private: void SetSelfAsMainThread() { } bool IsMainThread() { return false; } +bool IsNonMainThread() { return false; } int GetThreadName(char *str, const char *last) { return 0; } diff --git a/src/thread/thread_none.cpp b/src/thread/thread_none.cpp index 30f1adeb6a..175cf32015 100644 --- a/src/thread/thread_none.cpp +++ b/src/thread/thread_none.cpp @@ -37,5 +37,6 @@ public: void SetSelfAsMainThread() { } bool IsMainThread() { return true; } +bool IsNonMainThread() { return false; } int GetThreadName(char *str, const char *last) { return 0; } diff --git a/src/thread/thread_os2.cpp b/src/thread/thread_os2.cpp index c666bbced7..41ae19ab38 100644 --- a/src/thread/thread_os2.cpp +++ b/src/thread/thread_os2.cpp @@ -150,4 +150,6 @@ void SetSelfAsMainThread() { } bool IsMainThread() { return false; } +bool IsNonMainThread() { return false; } + int GetThreadName(char *str, const char *last) { return 0; } diff --git a/src/thread/thread_pthread.cpp b/src/thread/thread_pthread.cpp index f00ae0bb8f..a6aeb521f4 100644 --- a/src/thread/thread_pthread.cpp +++ b/src/thread/thread_pthread.cpp @@ -203,6 +203,11 @@ bool IsMainThread() return main_thread == pthread_self(); } +bool IsNonMainThread() +{ + return main_thread != pthread_self(); +} + int GetThreadName(char *str, const char *last) { #if defined(__GLIBC__) diff --git a/src/thread/thread_win32.cpp b/src/thread/thread_win32.cpp index c7e13a775b..c7831040b0 100644 --- a/src/thread/thread_win32.cpp +++ b/src/thread/thread_win32.cpp @@ -185,6 +185,11 @@ bool IsMainThread() return main_thread_id == GetCurrentThreadId(); } +bool IsNonMainThread() +{ + return main_thread_id != GetCurrentThreadId(); +} + static std::map _thread_name_map; static ThreadMutex_Win32 _thread_name_map_mutex; From 5d61b27faa5e0ff31f2d328bd84351774ccfe67a Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 25 Aug 2018 23:39:47 +0100 Subject: [PATCH 4/5] Add note to crashlog if crash is in non-main thread --- src/crashlog.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 76f0d3c9b5..5d0b3f6067 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -398,6 +398,12 @@ char *CrashLog::FillCrashLog(char *buffer, const char *last) const } #endif + if (IsNonMainThread()) { + buffer += seprintf(buffer, last, "Non-main thread ("); + buffer += GetThreadName(buffer, last); + buffer += seprintf(buffer, last, ")\n\n"); + } + buffer = this->LogOpenTTDVersion(buffer, last); buffer = this->LogStacktrace(buffer, last); buffer = this->LogRegisters(buffer, last); From 743432e7f0d5a6e5688e794f28abfddc10fbb798 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 26 Aug 2018 22:15:21 +0100 Subject: [PATCH 5/5] Try to do non-main thread crash save/screenshot in main thread --- src/crashlog.cpp | 40 +++++++++++++++++++++++++++++++++++++++- src/crashlog.h | 5 +++++ src/openttd.cpp | 2 ++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 5d0b3f6067..32e0f21517 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -78,6 +78,7 @@ /* static */ const char *CrashLog::message = NULL; /* static */ char *CrashLog::gamelog_buffer = NULL; /* static */ const char *CrashLog::gamelog_last = NULL; +/* static */ const CrashLog *CrashLog::main_thread_pending_crashlog = NULL; char *CrashLog::LogCompiler(char *buffer, const char *last) const { @@ -534,8 +535,33 @@ bool CrashLog::MakeCrashLog() const printf("Crash dump written to %s. Please add this file to any bug reports.\n\n", filename); } + if (IsNonMainThread()) { + printf("Asking main thread to write crash savegame and screenshot...\n\n"); + CrashLog::main_thread_pending_crashlog = this; + _exit_game = true; + CSleep(60000); + if (!CrashLog::main_thread_pending_crashlog) return ret; + printf("Main thread did not write crash savegame and screenshot within 60s, trying it from this thread...\n\n"); + } + CrashLog::main_thread_pending_crashlog = nullptr; + bret = CrashLog::MakeCrashSavegameAndScreenshot(); + if (!bret) ret = false; + + return ret; +} + +/** + * Makes a crash dump and crash savegame. It uses DEBUG to write + * information like paths to the console. + * @return true when everything is made successfully. + */ +bool CrashLog::MakeCrashSavegameAndScreenshot() const +{ + char filename[MAX_PATH]; + bool ret = true; + printf("Writing crash savegame...\n"); - bret = this->WriteSavegame(filename, lastof(filename)); + bool bret = this->WriteSavegame(filename, lastof(filename)); if (bret) { printf("Crash savegame written to %s. Please add this file and the last (auto)save to any bug reports.\n\n", filename); } else { @@ -555,6 +581,18 @@ bool CrashLog::MakeCrashLog() const return ret; } +/* static */ void CrashLog::MainThreadExitCheckPendingCrashlog() +{ + const CrashLog *cl = CrashLog::main_thread_pending_crashlog; + if (cl) { + CrashLog::main_thread_pending_crashlog = nullptr; + cl->MakeCrashSavegameAndScreenshot(); + + CrashLog::AfterCrashLogCleanup(); + abort(); + } +} + /** * Sets a message for the error message handler. * @param message The error message of the error. diff --git a/src/crashlog.h b/src/crashlog.h index 8948a8acb4..72077d2bbf 100644 --- a/src/crashlog.h +++ b/src/crashlog.h @@ -127,6 +127,7 @@ public: bool WriteScreenshot(char *filename, const char *filename_last) const; bool MakeCrashLog() const; + bool MakeCrashSavegameAndScreenshot() const; /** * Initialiser for crash logs; do the appropriate things so crashes are @@ -141,6 +142,10 @@ public: inline const char *GetMessage() const { return this->message; } static const char *GetAbortCrashlogReason(); + + static const CrashLog *main_thread_pending_crashlog; + + static void MainThreadExitCheckPendingCrashlog(); }; #endif /* CRASHLOG_H */ diff --git a/src/openttd.cpp b/src/openttd.cpp index 43c22fdffa..a9b9f5fbe1 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -890,6 +890,8 @@ int openttd_main(int argc, char *argv[]) VideoDriver::GetInstance()->MainLoop(); + CrashLog::MainThreadExitCheckPendingCrashlog(); + WaitTillSaved(); /* only save config if we have to */