1 /**
2 * @file
3 * @brief Assertions and crashing.
4 **/
5
6 #include "AppHdr.h"
7
8 #include <cerrno>
9 #include <csignal>
10
11 #include "abyss.h"
12 #include "chardump.h"
13 #include "coordit.h"
14 #include "crash.h"
15 #include "dbg-scan.h"
16 #include "dbg-util.h"
17 #include "delay.h"
18 #include "directn.h"
19 #include "dlua.h"
20 #include "env.h"
21 #include "files.h"
22 #include "hiscores.h"
23 #include "initfile.h"
24 #include "item-name.h"
25 #include "jobs.h"
26 #include "mapmark.h"
27 #include "message.h"
28 #include "misc.h"
29 #include "mutation.h"
30 #include "religion.h"
31 #include "skills.h"
32 #include "spl-util.h"
33 #include "state.h"
34 #include "stringutil.h"
35 #include "tiles-build-specific.h"
36 #include "travel.h"
37 #include "version.h"
38 #include "view.h"
39 #
40 #if defined(TARGET_OS_WINDOWS) || defined(TARGET_COMPILER_MINGW)
41 #define NOCOMM /* Comm driver APIs and definitions */
42 #define NOLOGERROR /* LogError() and related definitions */
43 #define NOPROFILER /* Profiler APIs */
44 #define NOLFILEIO /* _l* file I/O routines */
45 #define NOOPENFILE /* OpenFile and related definitions */
46 #define NORESOURCE /* Resource management */
47 #define NOATOM /* Atom management */
48 #define NOLANGUAGE /* Character test routines */
49 #define NOLSTRING /* lstr* string management routines */
50 #define NODBCS /* Double-byte character set routines */
51 #define NOKEYBOARDINFO /* Keyboard driver routines */
52 #define NOCOLOR /* COLOR_* colour values */
53 #define NODRAWTEXT /* DrawText() and related definitions */
54 #define NOSCALABLEFONT /* Truetype scalable font support */
55 #define NOMETAFILE /* Metafile support */
56 #define NOSYSTEMPARAMSINFO /* SystemParametersInfo() and SPI_* definitions */
57 #define NODEFERWINDOWPOS /* DeferWindowPos and related definitions */
58 #define NOKEYSTATES /* MK_* message key state flags */
59 #define NOWH /* SetWindowsHook and related WH_* definitions */
60 #define NOCLIPBOARD /* Clipboard APIs and definitions */
61 #define NOICONS /* IDI_* icon IDs */
62 #define NOMDI /* MDI support */
63 #define NOCTLMGR /* Control management and controls */
64 #define NOHELP /* Help support */
65 #define WIN32_LEAN_AND_MEAN /* No cryptography etc */
66 #define NONLS /* All NLS defines and routines */
67 #define NOSERVICE /* All Service Controller routines, SERVICE_ equates, etc. */
68 #define NOKANJI /* Kanji support stuff. */
69 #define NOMCX /* Modem Configuration Extensions */
70 #include <windows.h>
71 #undef max
72
73 #ifdef USE_TILE_LOCAL
74 #ifdef TARGET_COMPILER_VC
75 # include <SDL_syswm.h>
76 #else
77 # include <SDL2/SDL_syswm.h>
78 #endif
79 #endif
80 #endif
81
82 #ifdef __ANDROID__
83 # include <android/log.h>
84 #endif
85
86 static string _assert_msg;
87
_dump_compilation_info(FILE * file)88 static void _dump_compilation_info(FILE* file)
89 {
90 fprintf(file, "Compilation info:\n");
91 fprintf(file, "<<<<<<<<<<<\n");
92 fprintf(file, "%s", compilation_info);
93 fprintf(file, ">>>>>>>>>>>\n\n");
94 }
95
96 extern abyss_state abyssal_state;
97
_dump_level_info(FILE * file)98 static void _dump_level_info(FILE* file)
99 {
100 fprintf(file, "Place info:\n");
101
102 fprintf(file, "branch = %d, depth = %d\n\n",
103 (int)you.where_are_you, you.depth);
104
105 string place = level_id::current().describe();
106
107 fprintf(file, "Level id: %s\n", place.c_str());
108 if (player_in_branch(BRANCH_ABYSS))
109 {
110 fprintf(file, "Abyssal state:\n"
111 " major_coord = (%d,%d)\n"
112 " seed = 0x%" PRIx32 "\n"
113 " depth = %" PRIu32 "\n"
114 " phase = %g\n"
115 " destroy_all_terrain = %d\n"
116 " level = (%d : %d)\n",
117 abyssal_state.major_coord.x, abyssal_state.major_coord.y,
118 abyssal_state.seed, abyssal_state.depth, abyssal_state.phase,
119 abyssal_state.destroy_all_terrain,
120 abyssal_state.level.branch, abyssal_state.level.depth);
121 }
122
123 debug_dump_levgen();
124 }
125
_dump_player(FILE * file)126 static void _dump_player(FILE *file)
127 {
128 // Only dump player info during arena mode if the player is likely
129 // the cause of the crash.
130 if ((crawl_state.game_is_arena() || crawl_state.arena_suspended)
131 && !in_bounds(you.pos()) && you.hp > 0 && you.hp_max > 0
132 && you.strength() > 0 && you.intel() > 0 && you.dex() > 0)
133 {
134 // Arena mode can change behaviour of the rest of the code and/or lead
135 // to asserts.
136 crawl_state.type = GAME_TYPE_NORMAL;
137 crawl_state.arena_suspended = false;
138 return;
139 }
140
141 // Arena mode can change behaviour of the rest of the code and/or lead
142 // to asserts.
143 crawl_state.arena_suspended = false;
144
145 fprintf(file, "Player:\n");
146 fprintf(file, "{{{{{{{{{{{\n");
147
148 fprintf(file, "Name: [%s]\n", you.your_name.c_str());
149 fprintf(file, "Species: %s\n", species::name(you.species).c_str());
150 fprintf(file, "Job: %s\n\n", get_job_name(you.char_class));
151
152 fprintf(file, "HP: %d/%d; mods: %d/%d\n", you.hp, you.hp_max,
153 you.hp_max_adj_temp, you.hp_max_adj_perm);
154 fprintf(file, "MP: %d/%d; mod: %d\n",
155 you.magic_points, you.max_magic_points,
156 you.mp_max_adj);
157 fprintf(file, "Stats: %d (%d) %d (%d) %d (%d)\n",
158 you.strength(false), you.max_strength(),
159 you.intel(false), you.max_intel(),
160 you.dex(false), you.max_dex());
161 fprintf(file, "Position: %s, god: %s (%d), turn_is_over: %d, "
162 "banished: %d\n",
163 debug_coord_str(you.pos()).c_str(),
164 god_name(you.religion).c_str(), (int) you.religion,
165 (int) you.turn_is_over, (int) you.banished);
166
167 if (in_bounds(you.pos()))
168 {
169 fprintf(file, "Standing on/in/over feature: %s\n",
170 raw_feature_description(you.pos()).c_str());
171 }
172
173 debug_dump_constriction(&you);
174 fprintf(file, "\n");
175
176 if (you.running.runmode != RMODE_NOT_RUNNING)
177 {
178 fprintf(file, "Runrest:\n");
179 fprintf(file, " mode: %d\n", you.running.runmode);
180 fprintf(file, " mp: %d\n", you.running.mp);
181 fprintf(file, " hp: %d\n", you.running.hp);
182 fprintf(file, " pos: %s\n\n",
183 debug_coord_str(you.running.pos).c_str());
184 }
185
186 if (!you.delay_queue.empty())
187 {
188 fprintf(file, "Delayed (%u):\n",
189 (unsigned int)you.delay_queue.size());
190 for (const auto &delay : you.delay_queue)
191 {
192 fprintf(file, " type: %s", delay->name());
193 fprintf(file, "\n");
194 fprintf(file, " duration: %d\n", delay->duration);
195 }
196 fprintf(file, "\n");
197 }
198
199 fprintf(file, "Skills (mode: %s)\n", you.auto_training ? "auto" : "manual");
200 fprintf(file, "Name | can_currently_train | train | training |"
201 " level | points | progress\n");
202 for (size_t i = 0; i < NUM_SKILLS; ++i)
203 {
204 const skill_type sk = skill_type(i);
205 if (is_useless_skill(sk))
206 continue;
207
208 int needed_min = 0, needed_max = 0;
209 if (sk >= 0 && you.skills[sk] <= 27)
210 needed_min = skill_exp_needed(you.skills[sk], sk);
211 if (sk >= 0 && you.skills[sk] < 27)
212 needed_max = skill_exp_needed(you.skills[sk] + 1, sk);
213
214 fprintf(file, "%-16s| %c | %u | %3u | %2d | %6d | %d/%d\n",
215 skill_name(sk),
216 you.can_currently_train[sk] ? 'X' : ' ',
217 you.train[sk],
218 you.training[sk],
219 you.skills[sk],
220 you.skill_points[sk],
221 you.skill_points[sk] - needed_min,
222 max(needed_max - needed_min, 0));
223 }
224 fprintf(file, "\n");
225
226 fprintf(file, "Spell bugs:\n");
227 for (size_t i = 0; i < you.spells.size(); ++i)
228 {
229 const spell_type spell = you.spells[i];
230
231 if (spell == SPELL_NO_SPELL)
232 continue;
233
234 if (!is_valid_spell(spell))
235 {
236 fprintf(file, " spell slot #%d: invalid spell #%d\n",
237 (int)i, (int)spell);
238 continue;
239 }
240
241 const spell_flags flags = get_spell_flags(spell);
242
243 if (flags & spflag::monster)
244 {
245 fprintf(file, " spell slot #%d: monster only spell %s\n",
246 (int)i, spell_title(spell));
247 }
248 else if (flags & spflag::testing)
249 fprintf(file, " spell slot #%d: testing spell %s\n",
250 (int)i, spell_title(spell));
251 else if (count_bits(get_spell_disciplines(spell)) == 0)
252 fprintf(file, " spell slot #%d: school-less spell %s\n",
253 (int)i, spell_title(spell));
254 }
255 fprintf(file, "\n");
256
257 fprintf(file, "Durations:\n");
258 for (int i = 0; i < NUM_DURATIONS; ++i)
259 if (you.duration[i] != 0)
260 fprintf(file, " #%d: %d\n", i, you.duration[i]);
261
262 fprintf(file, "\n");
263
264 fprintf(file, "Attributes:\n");
265 for (int i = 0; i < NUM_ATTRIBUTES; ++i)
266 if (you.attribute[i] != 0)
267 fprintf(file, " #%d: %d\n", i, you.attribute[i]);
268
269 fprintf(file, "\n");
270
271 fprintf(file, "Mutations:\n");
272 for (int i = 0; i < NUM_MUTATIONS; ++i)
273 {
274 mutation_type mut = static_cast<mutation_type>(i);
275 int normal = you.mutation[i];
276 int innate = you.innate_mutation[i];
277 int temp = you.temp_mutation[i];
278
279 // Normally innate and temp imply normal, but a crash handler should
280 // expect the spanish^Wunexpected.
281 if (!normal && !innate && !temp)
282 continue;
283
284 if (const char* name = mutation_name(mut))
285 fprintf(file, " %s: %d", name, normal);
286 else
287 fprintf(file, " unknown #%d: %d", i, normal);
288
289 if (innate)
290 {
291 if (innate == normal)
292 fprintf(file, " (innate)");
293 else
294 fprintf(file, " (%d innate)", innate);
295 }
296
297 if (temp)
298 {
299 if (temp == normal)
300 fprintf(file, " (temporary)");
301 else
302 fprintf(file, " (%d temporary)", temp);
303 }
304
305 fprintf(file, "\n");
306 }
307
308 fprintf(file, "\n");
309
310 fprintf(file, "Inventory bugs:\n");
311 for (int i = 0; i < ENDOFPACK; ++i)
312 {
313 item_def &item(you.inv[i]);
314
315 if (item.base_type == OBJ_UNASSIGNED && item.quantity != 0)
316 {
317 fprintf(file, " slot #%d: unassigned item has quant %d\n",
318 i, item.quantity);
319 continue;
320 }
321 else if (item.base_type != OBJ_UNASSIGNED && item.quantity < 1)
322 {
323 const int orig_quant = item.quantity;
324 item.quantity = 1;
325
326 fprintf(file, " slot #%d: otherwise valid item '%s' has "
327 "invalid quantity %d\n",
328 i, item.name(DESC_PLAIN, false, true).c_str(),
329 orig_quant);
330 item.quantity = orig_quant;
331 continue;
332 }
333 else if (!item.defined())
334 continue;
335
336 const string name = item.name(DESC_PLAIN, false, true);
337
338 if (item.link != i)
339 {
340 fprintf(file, " slot #%d: item '%s' has invalid link %d\n",
341 i, name.c_str(), item.link);
342 }
343
344 if (item.slot < 0 || item.slot > 127)
345 {
346 fprintf(file, " slot #%d: item '%s' has invalid slot %d\n",
347 i, name.c_str(), item.slot);
348 }
349
350 if (!item.pos.equals(-1, -1))
351 {
352 fprintf(file, " slot #%d: item '%s' has invalid pos %s\n",
353 i, name.c_str(), debug_coord_str(item.pos).c_str());
354 }
355 }
356 fprintf(file, "\n");
357
358 fprintf(file, "Equipment:\n");
359 for (int i = EQ_FIRST_EQUIP; i < NUM_EQUIP; ++i)
360 {
361 int8_t eq = you.equip[i];
362
363 if (eq == -1)
364 continue;
365
366 fprintf(file, " eq slot #%d, inv slot #%d", i, (int) eq);
367 if (eq < 0 || eq >= ENDOFPACK)
368 {
369 fprintf(file, " <invalid>\n");
370 continue;
371 }
372 const bool unknown = !item_type_known(you.inv[eq]);
373 const bool melded = you.melded[i];
374 string suffix = "";
375 if (unknown || melded)
376 {
377 suffix = " (";
378 if (unknown)
379 {
380 suffix += "unknown";
381 if (melded)
382 suffix += ", ";
383 }
384 if (melded)
385 suffix += "melded";
386 suffix += ")";
387 }
388 fprintf(file, ": %s%s\n",
389 you.inv[eq].name(DESC_PLAIN, false, true).c_str(), suffix.c_str());
390 }
391 fprintf(file, "\n");
392
393 if (in_bounds(you.pos()) && monster_at(you.pos()))
394 {
395 fprintf(file, "Standing on same square as: ");
396 const unsigned short midx = env.mgrid(you.pos());
397
398 if (invalid_monster_index(midx))
399 fprintf(file, "invalid monster index %d\n", (int) midx);
400 else
401 {
402 const monster* mon = &env.mons[midx];
403 fprintf(file, "%s:\n", debug_mon_str(mon).c_str());
404 debug_dump_mon(mon, true);
405 }
406 fprintf(file, "\n");
407 }
408
409 fprintf(file, "}}}}}}}}}}}\n\n");
410 }
411
_debug_marker_scan()412 static void _debug_marker_scan()
413 {
414 vector<map_marker*> markers = env.markers.get_all();
415
416 for (unsigned int i = 0; i < markers.size(); ++i)
417 {
418 map_marker* marker = markers[i];
419
420 if (marker == nullptr)
421 {
422 mprf(MSGCH_ERROR, "Marker #%d is nullptr", i);
423 continue;
424 }
425
426 map_marker_type type = marker->get_type();
427
428 if (type < MAT_FEATURE || type >= NUM_MAP_MARKER_TYPES)
429 {
430 mprf(MSGCH_ERROR, "Marker #%d at (%d, %d) has invalid type %d",
431 i, marker->pos.x, marker->pos.y, (int) type);
432 }
433
434 if (!in_bounds(marker->pos))
435 {
436 mprf(MSGCH_ERROR, "Marker #%d, type %d at (%d, %d) out of bounds",
437 i, (int) type, marker->pos.x, marker->pos.y);
438 continue;
439 }
440
441 vector<map_marker*> at_pos = env.markers.get_markers_at(marker->pos);
442 if (find(begin(at_pos), end(at_pos), marker) == end(at_pos))
443 {
444 mprf(MSGCH_ERROR, "Marker #%d, type %d at (%d, %d) unlinked",
445 i, (int) type, marker->pos.x, marker->pos.y);
446 }
447 }
448
449 const coord_def start(MAPGEN_BORDER, MAPGEN_BORDER);
450 const coord_def end(GXM - MAPGEN_BORDER - 2, GYM - MAPGEN_BORDER - 2);
451 for (rectangle_iterator ri(start, end); ri; ++ri)
452 {
453 vector<map_marker*> at_pos = env.markers.get_markers_at(*ri);
454
455 for (unsigned int i = 0; i < at_pos.size(); ++i)
456 {
457 map_marker *marker = at_pos[i];
458
459 if (marker == nullptr)
460 {
461 mprf(MSGCH_ERROR, "Marker #%d at (%d, %d) nullptr",
462 i, ri->x, ri->y);
463 continue;
464 }
465 if (marker->pos != *ri)
466 {
467 mprf(MSGCH_ERROR, "Marker #%d, type %d at (%d, %d) "
468 "thinks it's at (%d, %d)",
469 i, (int) marker->get_type(), ri->x, ri->y,
470 marker->pos.x, marker->pos.y);
471
472 if (!in_bounds(marker->pos))
473 mprf(MSGCH_ERROR, "Further, it thinks it's out of bounds.");
474 }
475 }
476 }
477 } // _debug_marker_scan()
478
_debug_dump_markers()479 static void _debug_dump_markers()
480 {
481 vector<map_marker*> markers = env.markers.get_all();
482
483 for (unsigned int i = 0; i < markers.size(); ++i)
484 {
485 map_marker* marker = markers[i];
486
487 if (marker == nullptr || marker->get_type() == MAT_LUA_MARKER)
488 continue;
489
490 mprf(MSGCH_DIAGNOSTICS, "Marker #%d, type %d at (%d, %d): %s",
491 i, marker->get_type(),
492 marker->pos.x, marker->pos.y,
493 marker->debug_describe().c_str());
494 }
495 }
496
_debug_dump_lua_markers(FILE * file)497 static void _debug_dump_lua_markers(FILE *file)
498 {
499 vector<map_marker*> markers = env.markers.get_all();
500
501 for (unsigned int i = 0; i < markers.size(); ++i)
502 {
503 map_marker* marker = markers[i];
504
505 if (marker == nullptr || marker->get_type() != MAT_LUA_MARKER)
506 continue;
507
508 map_lua_marker* lua_marker = dynamic_cast<map_lua_marker*>(marker);
509
510 string result = lua_marker->debug_to_string();
511
512 if (!result.empty() && result[result.size() - 1] == '\n')
513 result = result.substr(0, result.size() - 1);
514
515 fprintf(file, "Lua marker %u at (%d, %d):\n",
516 i, marker->pos.x, marker->pos.y);
517 fprintf(file, "{{{{\n");
518 fprintf(file, "%s", result.c_str());
519 fprintf(file, "}}}}\n");
520 }
521 }
522
_debug_dump_lua_persist(FILE * file)523 static void _debug_dump_lua_persist(FILE* file)
524 {
525 lua_stack_cleaner cln(dlua);
526
527 string result;
528 if (!dlua.callfn("persist_to_string", 0, 1))
529 {
530 result = make_stringf("error (persist_to_string): %s",
531 dlua.error.c_str());
532 }
533 else if (lua_isstring(dlua, -1))
534 result = lua_tostring(dlua, -1);
535 else
536 result = "persist_to_string() returned nothing";
537
538 fprintf(file, "%s", result.c_str());
539 }
540
_dump_ver_stuff(FILE * file)541 static void _dump_ver_stuff(FILE* file)
542 {
543 fprintf(file, "Version: %s %s\n", CRAWL, Version::Long);
544 #if defined(UNIX)
545 fprintf(file, "Platform: unix");
546 # if defined(TARGET_OS_MACOSX)
547 fprintf(file, " (OS X)");
548 # endif
549 fprintf(file, "\n");
550 #elif defined(TARGET_OS_WINDOWS)
551 fprintf(file, "Platform: Windows\n");
552 #elif defined(TARGET_OS_DOS)
553 fprintf(file, "Platform: DOS\n");
554 #endif // UNIX
555
556 fprintf(file, "Bits: %d\n", (int)sizeof(void*)*8);
557 fprintf(file, "Game mode: %s\n",
558 gametype_to_str(crawl_state.type).c_str());
559
560 #if defined(USE_TILE_LOCAL)
561 fprintf(file, "Tiles: yes\n\n");
562 #elif defined(USE_TILE_WEB)
563 fprintf(file, "Tiles: online\n\n");
564 #else
565 fprintf(file, "Tiles: no\n\n");
566 #endif
567 if (you.fully_seeded)
568 {
569 fprintf(file, "Seed: %" PRIu64 ", deterministic pregen: %d\n",
570 crawl_state.seed, (int) you.deterministic_levelgen);
571 }
572 if (Version::history_size() > 1)
573 fprintf(file, "Version history:\n%s\n\n", Version::history().c_str());
574 }
575
_dump_command_line(FILE * file)576 static void _dump_command_line(FILE *file)
577 {
578 fprintf(file, "Command line:");
579 for (const string& str : crawl_state.command_line_arguments)
580 fprintf(file, " %s", str.c_str());
581 if (crawl_state.command_line_arguments.empty())
582 fprintf(file, " (unknown)");
583 fprintf(file, "\n\n");
584 }
585
586 // Dump any game options that could affect stability.
_dump_options(FILE * file)587 static void _dump_options(FILE *file)
588 {
589 fprintf(file, "RC options:\n");
590 fprintf(file, "restart_after_game = %s\n",
591 maybe_to_string(Options.restart_after_game).c_str());
592 fprintf(file, "\n\n");
593 }
594
595 // Defined in end.cc. Not a part of crawl_state, since that's a
596 // global C++ instance which is free'd by exit() hooks when exit()
597 // is called, and we don't want to reference free'd memory.
598 extern bool CrawlIsExiting;
599
do_crash_dump()600 void do_crash_dump()
601 {
602 if (CrawlIsExiting)
603 {
604 // We crashed during exit() callbacks, so it's likely that
605 // any global C++ instances we could reference would be
606 // free'd and invalid, plus their content likely wouldn't help
607 // tracking it down anyway. Thus, just do the bare bones
608 // info to stderr and quit.
609 fprintf(stderr, "Crashed while calling exit()!!!!\n");
610
611 _dump_ver_stuff(stderr);
612
613 fprintf(stderr, "%s\n\n", crash_signal_info().c_str());
614 write_stack_trace(stderr);
615 call_gdb(stderr);
616
617 return;
618 }
619
620 // Want same time for file name and crash milestone.
621 const time_t t = time(nullptr);
622
623 string dir = (!Options.morgue_dir.empty() ? Options.morgue_dir :
624 !SysEnv.crawl_dir.empty() ? SysEnv.crawl_dir
625 : "");
626
627 if (!dir.empty() && dir[dir.length() - 1] != FILE_SEPARATOR)
628 dir += FILE_SEPARATOR;
629
630 char name[180] = {};
631
632 snprintf(name, sizeof(name), "%scrash-%s-%s.txt", dir.c_str(),
633 you.your_name.c_str(), make_file_time(t).c_str());
634
635 const string signal_info = crash_signal_info();
636 const string cause_msg = _assert_msg.empty() ? signal_info : _assert_msg;
637
638 if (!crawl_state.test && !cause_msg.empty())
639 fprintf(stderr, "\n%s", cause_msg.c_str());
640 // This message is parsed by the WebTiles server.
641 fprintf(stderr,
642 "\n\nWe crashed! This is likely due to a bug in Crawl. "
643 "\nPlease submit a bug report at https://github.com/crawl/crawl/issues or at"
644 "\nhttps://crawl.develz.org/mantis/ and include:"
645 "\n- The crash report: %s"
646 "\n- Your save file: %s"
647 "\n- A description of what you were doing when this crash occurred.\n\n",
648 name, get_savedir_filename(you.your_name).c_str());
649 errno = 0;
650 FILE* file = crawl_state.test ? stderr : freopen(name, "a+", stderr);
651
652 // The errno values are only relevant when the function in
653 // question has returned a value indicating (possible) failure, so
654 // only freak out if freopen() returned nullptr!
655 if (!file)
656 {
657 fprintf(stdout, "\nUnable to open file '%s' for writing: %s\n",
658 name, strerror(errno));
659 file = stdout;
660 }
661
662 // Unbuffer the file, since if we recursively crash buffered lines
663 // won't make it to the file.
664 setvbuf(file, nullptr, _IONBF, 0);
665
666 set_msg_dump_file(file);
667
668 if (!cause_msg.empty())
669 fprintf(file, "%s\n\n", cause_msg.c_str());
670
671 _dump_ver_stuff(file);
672
673 _dump_command_line(file);
674
675 _dump_options(file);
676
677 // First get the immediate cause of the crash and the stack trace,
678 // since that's most important and later attempts to get more information
679 // might themselves cause crashes.
680 if (!signal_info.empty())
681 fprintf(file, "%s\n\n", signal_info.c_str());
682 write_stack_trace(file);
683 fprintf(file, "\n");
684
685 call_gdb(file);
686 fprintf(file, "\n");
687
688 // Next information on how the binary was compiled
689 _dump_compilation_info(file);
690
691 // Next information about the level the player is on, plus level
692 // generation info if the crash happened during level generation.
693 _dump_level_info(file);
694
695 // Dumping information on marker inconsistancy is unlikely to crash,
696 // as is dumping the descriptions of non-Lua markers.
697 fprintf(file, "Markers:\n");
698 fprintf(file, "<<<<<<<<<<<<<<<<<<<<<<\n");
699 _debug_marker_scan();
700 _debug_dump_markers();
701 fprintf(file, ">>>>>>>>>>>>>>>>>>>>>>\n");
702
703 // Dumping current messages is unlikely to crash.
704 if (file != stdout)
705 {
706 fprintf(file, "\nMessages:\n");
707 fprintf(file, "<<<<<<<<<<<<<<<<<<<<<<\n");
708 string messages = get_last_messages(NUM_STORED_MESSAGES, true);
709 fprintf(file, "%s", messages.c_str());
710 fprintf(file, ">>>>>>>>>>>>>>>>>>>>>>\n");
711 }
712
713 // Dumping the player state and crawl state is next least likely to cause
714 // another crash, so do that next.
715 fprintf(file, "\nVersion history:\n%s\n", Version::history().c_str());
716 crawl_state.dump();
717 _dump_player(file);
718
719 // Next item and monster scans. Any messages will be sent straight to
720 // the file because of set_msg_dump_file()
721 #ifdef DEBUG_ITEM_SCAN
722 if (crawl_state.crash_debug_scans_safe)
723 debug_item_scan();
724 else
725 fprintf(file, "\nCrashed while loading a save; skipping debug_item_scan.\n");
726 #endif
727 #ifdef DEBUG_MONS_SCAN
728 if (crawl_state.crash_debug_scans_safe)
729 debug_mons_scan();
730 else
731 fprintf(file, "\nCrashed while loading a save; skipping debug_mons_scan.\n");
732 #endif
733
734 // Dump Webtiles message buffer.
735 #ifdef USE_TILE_WEB
736 tiles.dump();
737 #endif
738
739 // Now a screenshot
740 if (crawl_state.generating_level)
741 {
742 fprintf(file, "\nMap:\n");
743 dump_map(file, true);
744 }
745 else
746 {
747 fprintf(file, "\nScreenshot:\n");
748 fprintf(file, "%s\n", screenshot().c_str());
749 }
750
751 // If anything has screwed up the Lua runtime stacks then trying to
752 // print those stacks will likely crash, so do this after the others.
753 fprintf(file, "clua stack:\n");
754 clua.print_stack();
755
756 fprintf(file, "dlua stack:\n");
757 dlua.print_stack();
758
759 // Lastly try to dump the Lua persistent data and the contents of the Lua
760 // markers, since actually running Lua code has the greatest chance of
761 // crashing.
762 fprintf(file, "Lua persistent data:\n");
763 fprintf(file, "<<<<<<<<<<<<<<<<<<<<<<\n");
764 _debug_dump_lua_persist(file);
765 fprintf(file, ">>>>>>>>>>>>>>>>>>>>>>\n\n");
766 fprintf(file, "Lua marker contents:\n");
767 fprintf(file, "<<<<<<<<<<<<<<<<<<<<<<\n");
768 _debug_dump_lua_markers(file);
769 fprintf(file, ">>>>>>>>>>>>>>>>>>>>>>\n");
770
771 set_msg_dump_file(nullptr);
772
773 mark_milestone("crash", cause_msg, "", t);
774
775 if (file != stderr)
776 fclose(file);
777 }
778
779 ///////////////////////////////////////////////////////////////////////
780 ///////////////////////////////////////////////////////////////////////
781
782 // Assertions and such
783
_BreakStrToDebugger(const char * mesg,bool assert)784 NORETURN static void _BreakStrToDebugger(const char *mesg, bool assert)
785 {
786 UNUSED(assert);
787 // FIXME: this needs a way to get the SDL_window in windowmanager-sdl.cc
788 #if 0
789 #if defined(USE_TILE_LOCAL) && defined(TARGET_OS_WINDOWS)
790 SDL_SysWMinfo SysInfo;
791 SDL_VERSION(&SysInfo.version);
792 if (SDL_GetWindowWMInfo(window, &SysInfo) > 0)
793 {
794 MessageBoxW(window, OUTW(mesg),
795 assert ? L"Assertion failed!" : L"Error",
796 MB_OK|MB_ICONERROR);
797 }
798 // Print the message to STDERR in addition to the above message box,
799 // so it's in the message history if we call Crawl from a shell.
800 #endif
801 #endif
802 fprintf(stderr, "%s\n", mesg);
803
804 #if defined(TARGET_OS_WINDOWS)
805 OutputDebugString(mesg);
806 if (IsDebuggerPresent())
807 DebugBreak();
808 #endif
809
810 // MSVCRT's abort() give's a funny message ...
811 raise(SIGABRT);
812 abort();
813 }
814
815 #ifdef ASSERTS
AssertFailed(const char * expr,const char * file,int line,const char * text,...)816 NORETURN void AssertFailed(const char *expr, const char *file, int line,
817 const char *text, ...)
818 {
819 char mesg[512];
820 va_list args;
821
822 const char *fileName = file + strlen(file); // strip off path
823
824 while (fileName > file && fileName[-1] != '\\')
825 --fileName;
826
827 snprintf(mesg, sizeof(mesg), "ASSERT(%s) in '%s' at line %d failed.",
828 expr, fileName, line);
829
830 _assert_msg = mesg;
831
832 // Compose additional information that was passed
833 if (text)
834 {
835 // Write the args into the format specified by text
836 char detail[512];
837 va_start(args, text);
838 vsnprintf(detail, sizeof(detail), text, args);
839 va_end(args);
840 // Build the final result
841 char final_mesg[1026];
842 snprintf(final_mesg, sizeof(final_mesg), "%s (%s)", mesg, detail);
843 _assert_msg = final_mesg;
844 _BreakStrToDebugger(final_mesg, true);
845 }
846 else
847 {
848 _assert_msg = mesg;
849 _BreakStrToDebugger(mesg, true);
850 }
851 }
852 #endif
853
854 #undef die
die(const char * file,int line,const char * format,...)855 NORETURN void die(const char *file, int line, const char *format, ...)
856 {
857 char tmp[2048] = {};
858 char mesg[2071] = {};
859
860 va_list args;
861
862 va_start(args, format);
863 vsnprintf(tmp, sizeof(tmp), format, args);
864 va_end(args);
865
866 snprintf(mesg, sizeof(mesg), "ERROR in '%s' at line %d: %s",
867 file, line, tmp);
868
869 _assert_msg = mesg;
870
871 _BreakStrToDebugger(mesg, false);
872 }
873
die_noline(const char * format,...)874 NORETURN void die_noline(const char *format, ...)
875 {
876 char tmp[2048] = {};
877 char mesg[2055] = {};
878
879 va_list args;
880
881 va_start(args, format);
882 vsnprintf(tmp, sizeof(tmp), format, args);
883 va_end(args);
884
885 snprintf(mesg, sizeof(mesg), "ERROR: %s", tmp);
886
887 #ifdef __ANDROID__
888 __android_log_print(ANDROID_LOG_INFO, "Crawl", "%s", mesg);
889 #endif
890
891 _assert_msg = mesg;
892
893 _BreakStrToDebugger(mesg, false);
894 }
895