Skip to content

Commit 22e317f

Browse files
authored
Merge branch 'develop' into feat/53_general_updates
2 parents 1df6c49 + b4d6e82 commit 22e317f

File tree

8 files changed

+235
-0
lines changed

8 files changed

+235
-0
lines changed

docs/changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Template for new versions:
5959
## New Features
6060

6161
## Fixes
62+
- `buildingplan`: Bolt throwers will no longer be constructed using populated bins.
6263
- `suspendmanager`: treat reinforced walls as a blocking construction and buildable platform
6364

6465
## Misc Improvements

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: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
#include "DFHackVersion.h"
2+
#include <csignal>
3+
#include <iomanip>
4+
#include <filesystem>
5+
#include <fstream>
6+
#include <thread>
7+
8+
#include <sys/eventfd.h>
9+
#include <execinfo.h>
10+
#include <unistd.h>
11+
12+
const int BT_ENTRY_MAX = 25;
13+
struct CrashInfo {
14+
int backtrace_entries = 0;
15+
void* backtrace[BT_ENTRY_MAX];
16+
int signal = 0;
17+
};
18+
19+
CrashInfo crash_info;
20+
21+
std::atomic<bool> crashed = false;
22+
std::atomic<bool> crashlog_ready = false;
23+
std::atomic<bool> shutdown = false;
24+
25+
// Use eventfd for async-signal safe waiting
26+
int crashlog_complete = -1;
27+
28+
void flag_set(std::atomic_bool &atom) {
29+
atom.store(true);
30+
atom.notify_all();
31+
}
32+
void flag_wait(std::atomic_bool &atom) {
33+
atom.wait(false);
34+
}
35+
36+
void signal_crashlog_complete() {
37+
if (crashlog_complete == -1)
38+
return;
39+
uint64_t v = 1;
40+
[[maybe_unused]] auto _ = write(crashlog_complete, &v, sizeof(v));
41+
}
42+
43+
std::thread crashlog_thread;
44+
45+
extern "C" void dfhack_crashlog_handle_signal(int sig) {
46+
if (shutdown.load() || crashed.exchange(true) || crashlog_ready.load()) {
47+
// Ensure the signal handler doesn't try to write a crashlog
48+
// whilst the crashlog thread is unavailable.
49+
std::quick_exit(1);
50+
}
51+
crash_info.signal = sig;
52+
crash_info.backtrace_entries = backtrace(crash_info.backtrace, BT_ENTRY_MAX);
53+
54+
// Signal saving of crashlog
55+
flag_set(crashlog_ready);
56+
// Wait for completion via eventfd read, if fd isn't valid, bail
57+
if (crashlog_complete != -1) {
58+
[[maybe_unused]] uint64_t v;
59+
[[maybe_unused]] auto _ = read(crashlog_complete, &v, sizeof(v));
60+
}
61+
std::quick_exit(1);
62+
}
63+
64+
void dfhack_crashlog_handle_terminate() {
65+
dfhack_crashlog_handle_signal(0);
66+
}
67+
68+
std::string signal_name(int sig) {
69+
switch (sig) {
70+
case SIGINT:
71+
return "SIGINT";
72+
case SIGILL:
73+
return "SIGILL";
74+
case SIGABRT:
75+
return "SIGABRT";
76+
case SIGFPE:
77+
return "SIGFPE";
78+
case SIGSEGV:
79+
return "SIGSEGV";
80+
case SIGTERM:
81+
return "SIGTERM";
82+
}
83+
return "";
84+
}
85+
86+
std::filesystem::path get_crashlog_path() {
87+
std::time_t time = std::time(nullptr);
88+
std::tm* tm = std::localtime(&time);
89+
90+
std::string timestamp = "unknown";
91+
if (tm) {
92+
char stamp[64];
93+
std::size_t out = strftime(&stamp[0], 63, "%Y-%m-%d-%H-%M-%S", tm);
94+
if (out != 0)
95+
timestamp = stamp;
96+
}
97+
98+
std::filesystem::path dir = "crashlog";
99+
std::error_code err;
100+
std::filesystem::create_directories(dir, err);
101+
102+
std::filesystem::path log_path = dir / ("crash_" + timestamp + ".txt");
103+
return log_path;
104+
}
105+
106+
void dfhack_save_crashlog() {
107+
char** backtrace_strings = backtrace_symbols(crash_info.backtrace, crash_info.backtrace_entries);
108+
if (!backtrace_strings) {
109+
// Allocation failed, give up
110+
return;
111+
}
112+
try {
113+
std::filesystem::path crashlog_path = get_crashlog_path();
114+
std::ofstream crashlog(crashlog_path);
115+
116+
crashlog << "Dwarf Fortress Linux has crashed!" << "\n";
117+
crashlog << "Dwarf Fortress Version " << DFHack::Version::df_version() << "\n";
118+
crashlog << "DFHack Version " << DFHack::Version::dfhack_version() << "\n\n";
119+
120+
std::string signal = signal_name(crash_info.signal);
121+
if (!signal.empty()) {
122+
crashlog << "Signal " << signal << "\n";
123+
}
124+
125+
for (int i = 0; i < crash_info.backtrace_entries; i++) {
126+
crashlog << i << "> " << backtrace_strings[i] << "\n";
127+
}
128+
} catch (...) {}
129+
130+
free(backtrace_strings);
131+
}
132+
133+
void dfhack_crashlog_thread() {
134+
// Wait for activation signal
135+
flag_wait(crashlog_ready);
136+
if (shutdown.load()) // Shutting down gracefully, end thread.
137+
return;
138+
139+
dfhack_save_crashlog();
140+
signal_crashlog_complete();
141+
std::quick_exit(1);
142+
}
143+
144+
std::terminate_handler term_handler = nullptr;
145+
146+
const int desired_signals[3] = {SIGSEGV,SIGILL,SIGABRT};
147+
namespace DFHack {
148+
void dfhack_crashlog_init() {
149+
// Initialize eventfd flag
150+
crashlog_complete = eventfd(0, EFD_CLOEXEC);
151+
152+
crashlog_thread = std::thread(dfhack_crashlog_thread);
153+
154+
for (int signal : desired_signals) {
155+
std::signal(signal, dfhack_crashlog_handle_signal);
156+
}
157+
term_handler = std::set_terminate(dfhack_crashlog_handle_terminate);
158+
159+
// https://sourceware.org/glibc/manual/latest/html_mono/libc.html#index-backtrace-1
160+
// backtrace is AsyncSignal-Unsafe due to dynamic loading of libgcc_s
161+
// Using it here ensures it is loaded before use in the signal handler.
162+
[[maybe_unused]] int _ = backtrace(crash_info.backtrace, 1);
163+
}
164+
165+
void dfhack_crashlog_shutdown() {
166+
shutdown.exchange(true);
167+
for (int signal : desired_signals) {
168+
std::signal(signal, SIG_DFL);
169+
}
170+
std::set_terminate(term_handler);
171+
172+
// Shutdown the crashlog thread.
173+
flag_set(crashlog_ready);
174+
crashlog_thread.join();
175+
176+
// If the signal handler is somehow running whilst here, let it terminate
177+
signal_crashlog_complete();
178+
if (crashlog_complete != -1)
179+
close(crashlog_complete); // Close fd
180+
return;
181+
}
182+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!*.h
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#pragma once
2+
3+
#include <functional>
4+
#include <bit>
5+
#include "df/labor_kitchen_interface_food_key.h"
6+
7+
namespace std
8+
{
9+
template<>
10+
struct hash<df::labor_kitchen_interface_food_key>
11+
{
12+
auto operator()(const df::labor_kitchen_interface_food_key& a) const -> size_t
13+
{
14+
struct thing
15+
{
16+
int16_t t;
17+
int16_t st;
18+
int32_t x;
19+
} thing{
20+
.t = a.type,
21+
.st = a.subtype,
22+
.x = static_cast<int32_t>(a.mat) ^ (static_cast<int32_t>(a.matg))
23+
};
24+
25+
return hash<int64_t>()(std::bit_cast<int64_t>(thing));
26+
}
27+
};
28+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
bool operator<(const df::labor_kitchen_interface_food_key &b) const {
2+
if (type<b.type) return true;
3+
if (subtype<b.subtype) return true;
4+
if (mat<b.mat) return true;
5+
if (matg<b.matg) return true;
6+
return false;
7+
}
8+
9+
bool operator==(const df::labor_kitchen_interface_food_key &b) const {
10+
return type==b.type&&subtype==b.subtype&&mat==b.mat&&matg==b.matg;
11+
}

library/lua/dfhack/buildings.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ local siegeengine_input = {
358358
vector_id=df.job_item_vector_id.BOLT_THROWER_PARTS,
359359
},
360360
{
361+
flags1={ empty=true },
361362
item_type=df.item_type.BIN,
362363
vector_id=df.job_item_vector_id.BIN,
363364
},

0 commit comments

Comments
 (0)