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