Skip to content

Commit 9bae58d

Browse files
authored
Merge branch 'develop' into modernizecore
2 parents 1edf08d + 413d126 commit 9bae58d

File tree

9 files changed

+271
-10
lines changed

9 files changed

+271
-10
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

6364
## Misc Improvements
6465

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.
@@ -2383,6 +2388,11 @@ void Core::onStateChange(color_ostream &out, state_change_event event)
23832388

23842389
int Core::Shutdown ( void )
23852390
{
2391+
#ifdef LINUX_BUILD
2392+
extern void dfhack_crashlog_shutdown();
2393+
dfhack_crashlog_shutdown();
2394+
#endif
2395+
23862396
if(errorstate)
23872397
return true;
23882398
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+
}

library/include/DataDefs.h

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ distribution.
3131
#include <string>
3232
#include <type_traits>
3333
#include <utility>
34+
#include <unordered_map>
3435
#include <vector>
36+
#include <functional>
3537

3638
#include "BitArray.h"
3739
#include "Export.h"
@@ -136,8 +138,8 @@ namespace DFHack
136138

137139
class DFHACK_EXPORT compound_identity : public constructed_identity {
138140
static std::list<const compound_identity*>* list;
139-
static std::map<const compound_identity*, const compound_identity*>* parent_map;
140-
static std::map<const compound_identity*, std::vector<const compound_identity*>>* children_map;
141+
static std::unordered_map<const compound_identity*, const compound_identity*>* parent_map;
142+
static std::unordered_map<const compound_identity*, std::vector<const compound_identity*>>* children_map;
141143
static std::vector<const compound_identity*>* top_scope;
142144

143145
const char *dfhack_name;
@@ -204,7 +206,7 @@ namespace DFHack
204206
class DFHACK_EXPORT enum_identity : public compound_identity {
205207
public:
206208
struct ComplexData {
207-
std::map<int64_t, size_t> value_index_map;
209+
std::unordered_map<int64_t, size_t> value_index_map;
208210
std::vector<int64_t> index_value_map;
209211
ComplexData(std::initializer_list<int64_t> values);
210212
size_t size() const {
@@ -289,8 +291,8 @@ namespace DFHack
289291
};
290292

291293
class DFHACK_EXPORT struct_identity : public compound_identity {
292-
static std::map<const struct_identity*, const struct_identity*>* parent_map;
293-
static std::map<const struct_identity*, std::vector<const struct_identity*>>* children_map;
294+
static std::unordered_map<const struct_identity*, const struct_identity*>* parent_map;
295+
static std::unordered_map<const struct_identity*, std::vector<const struct_identity*>>* children_map;
294296

295297
const struct_field_info *fields;
296298

@@ -355,6 +357,30 @@ namespace DFHack
355357
virtual void build_metatable(lua_State *state) const;
356358
};
357359

360+
namespace
361+
{
362+
template<typename ... Bases>
363+
struct overload : Bases ...
364+
{
365+
using is_transparent = void;
366+
using Bases::operator() ...;
367+
};
368+
369+
struct char_pointer_hash
370+
{
371+
auto operator()(const char* ptr) const noexcept
372+
{
373+
return std::hash<std::string_view>{}(ptr);
374+
}
375+
};
376+
377+
using transparent_string_hash = overload<
378+
std::hash<std::string>,
379+
std::hash<std::string_view>,
380+
char_pointer_hash
381+
>;
382+
}
383+
358384
#ifdef _MSC_VER
359385
using virtual_ptr = void*;
360386
#else
@@ -367,13 +393,13 @@ namespace DFHack
367393
class DFHACK_EXPORT virtual_identity : public struct_identity {
368394
public:
369395
using interpose_t = VMethodInterposeLinkBase*;
370-
using interpose_list_t = std::map<int, interpose_t>;
396+
using interpose_list_t = std::unordered_map<int, interpose_t>;
371397

372398
private:
373-
static std::map<const std::string, const virtual_identity*, std::less<>> *name_lookup;
374-
static std::map<void*, const virtual_identity*>* known;
375-
static std::map<const virtual_identity*, void*>* vtable_ptr_map;
376-
static std::map<const virtual_identity*, interpose_list_t>* interpose_list_map;
399+
static std::unordered_map<std::string, const virtual_identity*, transparent_string_hash, std::equal_to<>> *name_lookup;
400+
static std::unordered_map<void*, const virtual_identity*>* known;
401+
static std::unordered_map<const virtual_identity*, void*>* vtable_ptr_map;
402+
static std::unordered_map<const virtual_identity*, interpose_list_t>* interpose_list_map;
377403

378404
const char *original_name;
379405

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)