Skip to content

Commit 0871519

Browse files
committed
Initial implementation for a linux crashlog
1 parent 004b4dd commit 0871519

3 files changed

Lines changed: 92 additions & 0 deletions

File tree

library/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ endif()
136136

137137
set(MAIN_SOURCES_LINUX
138138
${CONSOLE_SOURCES}
139+
Crashlog.cpp
139140
)
140141

141142
set(MAIN_SOURCES_DARWIN

library/Core.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,6 +1540,11 @@ bool Core::InitMainThread() {
15401540

15411541
Filesystem::init();
15421542

1543+
#ifdef LINUX_BUILD
1544+
extern void dfhack_crashlog_init();
1545+
dfhack_crashlog_init();
1546+
#endif
1547+
15431548
// Re-route stdout and stderr again - DF seems to set up stdout and
15441549
// stderr.txt on Windows as of 0.43.05. Also, log before switching files to
15451550
// make it obvious what's going on if someone checks the *.txt files.
@@ -2375,6 +2380,11 @@ void Core::onStateChange(color_ostream &out, state_change_event event)
23752380

23762381
int Core::Shutdown ( void )
23772382
{
2383+
#ifdef LINUX_BUILD
2384+
extern void dfhack_crashlog_shutdown();
2385+
dfhack_crashlog_shutdown();
2386+
#endif
2387+
23782388
if(errorstate)
23792389
return true;
23802390
errorstate = 1;

library/Crashlog.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#include "DFHackVersion.h"
2+
#include <csignal>
3+
#include <thread>
4+
#include <semaphore>
5+
#include <filesystem>
6+
#include <fstream>
7+
8+
#include <execinfo.h>
9+
10+
const int BT_ENTRY_MAX = 25;
11+
int bt_entries = 0;
12+
void* bt[BT_ENTRY_MAX];
13+
int crash_signal = 0;
14+
15+
std::binary_semaphore crashlog_ready{0};
16+
std::binary_semaphore crashlog_complete{0};
17+
18+
std::thread crashlog_thread;
19+
volatile bool shutdown = false;
20+
21+
extern "C" void dfhack_crashlog_handle_signal(int sig) {
22+
crash_signal = sig;
23+
bt_entries = backtrace(bt, BT_ENTRY_MAX);
24+
25+
// Signal saving of crashlog and wait for completion
26+
crashlog_ready.release();
27+
crashlog_complete.acquire();
28+
std::quick_exit(1);
29+
}
30+
31+
void dfhack_save_crashlog() {
32+
char** backtrace_strings = backtrace_symbols(bt, bt_entries);
33+
if (!backtrace_strings) {
34+
// Something has gone terribly wrong
35+
return;
36+
}
37+
std::filesystem::path crashlog_path = "./crash.txt";
38+
std::ofstream crashlog(crashlog_path);
39+
40+
crashlog << "Dwarf Fortress has crashed!" << "\n";
41+
crashlog << "DwarfFortress Version " << DFHack::Version::df_version() << "\n";
42+
crashlog << "DFHack Version " << DFHack::Version::dfhack_version() << "\n\n";
43+
44+
for (int i = 0; i < bt_entries; i++) {
45+
crashlog << i << "> " << backtrace_strings[i] << "\n";
46+
}
47+
48+
free(backtrace_strings);
49+
}
50+
51+
void dfhack_crashlog_thread() {
52+
// Wait for crash or shutdown signal
53+
crashlog_ready.acquire();
54+
if (shutdown)
55+
return;
56+
57+
dfhack_save_crashlog();
58+
crashlog_complete.release();
59+
std::quick_exit(1);
60+
}
61+
62+
const int desired_signals[3] = {SIGSEGV,SIGILL,SIGABRT};
63+
namespace DFHack {
64+
void dfhack_crashlog_init() {
65+
for (int signal : desired_signals) {
66+
std::signal(signal, dfhack_crashlog_handle_signal);
67+
}
68+
69+
// Ensure the library is initialized to avoid AsyncSignal-Unsafe init during crash
70+
int _ = backtrace(bt, 1);
71+
72+
crashlog_thread = std::thread(dfhack_crashlog_thread);
73+
}
74+
75+
void dfhack_crashlog_shutdown() {
76+
shutdown = true;
77+
crashlog_ready.release();
78+
crashlog_thread.join();
79+
return;
80+
}
81+
}

0 commit comments

Comments
 (0)