4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
10 /** @file crashlog.cpp Implementation of generic function to be called to log a crash */
16 #endif /* WITH_ALLEGRO */
17 #ifdef WITH_FONTCONFIG
18 # include <fontconfig/fontconfig.h>
19 #endif /* WITH_FONTCONFIG */
21 /* pngconf.h, included by png.h doesn't like something in the
22 * freetype headers. As such it's not alphabetically sorted. */
26 # include <ft2build.h>
27 # include FT_FREETYPE_H
28 #endif /* WITH_FREETYPE */
29 #if defined(WITH_ICU_LAYOUT) || defined(WITH_ICU_SORT)
30 # include <unicode/uversion.h>
31 #endif /* WITH_ICU_SORT || WITH_ICU_LAYOUT */
36 #include <lzo/lzo1x.h>
48 #include "date_func.h"
50 #include "strings_func.h"
51 #include "blitter/blitter.h"
52 #include "base_media_base.h"
53 #include "music/music_driver.hpp"
54 #include "sound/sound_driver.hpp"
55 #include "video/video_driver.hpp"
56 #include "saveload/saveload.h"
57 #include "screenshot.h"
59 #include "network/network.h"
61 #include "fontcache.h"
63 #include "ai/ai_info.hpp"
64 #include "game/game.hpp"
65 #include "game/game_info.hpp"
66 #include "company_base.h"
67 #include "company_func.h"
71 /* static */ const char *CrashLog::message
= NULL
;
74 * Writes compiler (and its version, if available) to the buffer.
75 * @param buffer The string where to write.
77 static void LogCompiler (stringb
*buffer
)
79 buffer
->append_fmt (" Compiler: "
82 #elif defined(__ICC) && defined(__GNUC__)
83 "ICC %d (GCC %d.%d.%d mode)", __ICC
, __GNUC__
, __GNUC_MINOR__
, __GNUC_PATCHLEVEL__
86 #elif defined(__GNUC__)
87 "GCC %d.%d.%d", __GNUC__
, __GNUC_MINOR__
, __GNUC_PATCHLEVEL__
88 #elif defined(__WATCOMC__)
89 "WatcomC %d", __WATCOMC__
94 #if defined(__VERSION__)
95 buffer
->append (" \"" __VERSION__
"\"\n\n");
97 buffer
->append ("\n\n");
101 /* virtual */ void CrashLog::LogRegisters (stringb
*buffer
) const
103 /* Stub implementation; not all OSes support this. */
106 /* virtual */ void CrashLog::LogModules (stringb
*buffer
) const
108 /* Stub implementation; not all OSes support this. */
112 * Writes version and compilation information to the buffer.
113 * @param buffer The string where to write.
115 static void LogVersion (stringb
*buffer
)
119 " Version: %s (%d)\n"
120 " NewGRF ver: %08x\n"
128 #if (TTD_ENDIAN == TTD_LITTLE_ENDIAN)
137 _openttd_revision
, _openttd_revision_modified
,
138 _openttd_newgrf_version
,
144 * Writes the (important) configuration settings to the buffer.
145 * E.g. graphics set, sound set, blitter and AIs.
146 * @param buffer The string where to write.
148 static void LogConfiguration (stringb
*buffer
)
153 " Graphics set: %s (%u)\n"
155 " Music driver: %s\n"
156 " Music set: %s (%u)\n"
158 " Sound driver: %s\n"
159 " Sound set: %s (%u)\n"
160 " Video driver: %s\n\n",
162 BaseGraphics::GetUsedSet() == NULL
? "none" : BaseGraphics::GetUsedSet()->get_name(),
163 BaseGraphics::GetUsedSet() == NULL
? UINT32_MAX
: BaseGraphics::GetUsedSet()->version
,
164 _current_language
== NULL
? "none" : _current_language
->file
,
165 MusicDriver::GetActiveDriver() == NULL
? "none" : MusicDriver::GetActiveDriverName(),
166 BaseMusic::GetUsedSet() == NULL
? "none" : BaseMusic::GetUsedSet()->get_name(),
167 BaseMusic::GetUsedSet() == NULL
? UINT32_MAX
: BaseMusic::GetUsedSet()->version
,
168 _networking
? (_network_server
? "server" : "client") : "no",
169 SoundDriver::GetActiveDriver() == NULL
? "none" : SoundDriver::GetActiveDriverName(),
170 BaseSounds::GetUsedSet() == NULL
? "none" : BaseSounds::GetUsedSet()->get_name(),
171 BaseSounds::GetUsedSet() == NULL
? UINT32_MAX
: BaseSounds::GetUsedSet()->version
,
172 VideoDriver::GetActiveDriver() == NULL
? "none" : VideoDriver::GetActiveDriverName()
181 FontCache::Get(FS_SMALL
)->GetFontName(),
182 FontCache::Get(FS_NORMAL
)->GetFontName(),
183 FontCache::Get(FS_LARGE
)->GetFontName(),
184 FontCache::Get(FS_MONO
)->GetFontName()
187 buffer
->append_fmt ("AI Configuration (local: %i):\n", (int)_local_company
);
189 FOR_ALL_COMPANIES(c
) {
190 if (c
->ai_info
== NULL
) {
191 buffer
->append_fmt (" %2i: Human\n", (int)c
->index
);
193 buffer
->append_fmt (" %2i: %s (v%d)\n", (int)c
->index
, c
->ai_info
->GetName(), c
->ai_info
->GetVersion());
197 if (Game::GetInfo() != NULL
) {
198 buffer
->append_fmt (" GS: %s (v%d)\n", Game::GetInfo()->GetName(), Game::GetInfo()->GetVersion());
200 buffer
->append ('\n');
204 * Writes information (versions) of the used libraries.
205 * @param buffer The string where to write.
207 static void LogLibraries (stringb
*buffer
)
209 buffer
->append ("Libraries:\n");
212 buffer
->append_fmt (" Allegro: %s\n", allegro_id
);
213 #endif /* WITH_ALLEGRO */
215 #ifdef WITH_FONTCONFIG
216 int version
= FcGetVersion();
217 buffer
->append_fmt (" FontConfig: %d.%d.%d\n", version
/ 10000, (version
/ 100) % 100, version
% 100);
218 #endif /* WITH_FONTCONFIG */
222 int major
, minor
, patch
;
223 FT_Init_FreeType(&library
);
224 FT_Library_Version(library
, &major
, &minor
, &patch
);
225 FT_Done_FreeType(library
);
226 buffer
->append_fmt (" FreeType: %d.%d.%d\n", major
, minor
, patch
);
227 #endif /* WITH_FREETYPE */
229 #if defined(WITH_ICU_LAYOUT) || defined(WITH_ICU_SORT)
230 /* 4 times 0-255, separated by dots (.) and a trailing '\0' */
231 char buf
[4 * 3 + 3 + 1];
234 u_versionToString(ver
, buf
);
236 buffer
->append_fmt (" ICU i18n: %s\n", buf
);
238 #ifdef WITH_ICU_LAYOUT
239 buffer
->append_fmt (" ICU lx: %s\n", buf
);
241 #endif /* WITH_ICU_SORT || WITH_ICU_LAYOUT */
244 buffer
->append_fmt (" LZMA: %s\n", lzma_version_string());
248 buffer
->append_fmt (" LZO: %s\n", lzo_version_string());
252 buffer
->append_fmt (" PNG: %s\n", png_get_libpng_ver(NULL
));
253 #endif /* WITH_PNG */
256 #ifdef DYNAMICALLY_LOADED_SDL
257 if (SDL_CALL SDL_Linked_Version
!= NULL
) {
261 const SDL_version
*v
= SDL_CALL
SDL_Linked_Version();
262 buffer
->append_fmt (" SDL: %d.%d.%d\n", v
->major
, v
->minor
, v
->patch
);
264 #endif /* WITH_SDL */
267 buffer
->append_fmt (" Zlib: %s\n", zlibVersion());
270 buffer
->append ('\n');
274 * Helper function for printing the gamelog.
275 * @param s the string to print.
276 * @param buffer the buffer where to print.
278 static void GamelogFillCrashLog (const char *s
, void *buffer
)
280 ((stringb
*)buffer
)->append_fmt ("%s\n", s
);
284 * Fill the crash log buffer with all data of a crash log.
285 * @param buffer The string where to write.
287 void CrashLog::FillCrashLog (stringb
*buffer
) const
289 time_t cur_time
= time(NULL
);
290 buffer
->append ("*** OpenTTD Crash Report ***\n\n");
291 buffer
->append_fmt ("Crash at: %s", asctime(gmtime(&cur_time
)));
294 ConvertDateToYMD(_date
, &ymd
);
295 buffer
->append_fmt ("In game date: %i-%02i-%02i (%i)\n\n", ymd
.year
, ymd
.month
+ 1, ymd
.day
, _date_fract
);
297 this->LogError(buffer
, CrashLog::message
);
299 this->LogRegisters(buffer
);
300 this->LogStacktrace(buffer
);
301 this->LogOSVersion(buffer
);
303 LogConfiguration(buffer
);
304 LogLibraries(buffer
);
305 this->LogModules(buffer
);
307 /* Write the gamelog data to the buffer. */
308 GamelogPrint (&GamelogFillCrashLog
, buffer
);
310 buffer
->append ("\n*** End of OpenTTD Crash Report ***\n");
314 * Write the crash log to a file.
315 * @note On success the filename will be filled with the full path of the
316 * crash log file. Make sure filename is at least \c MAX_PATH big.
317 * @param buffer The begin of the buffer to write to the disk.
318 * @param filename Output for the filename of the written file.
319 * @return true when the crash log was successfully written.
321 bool CrashLog::WriteCrashLog (const char *buffer
, stringb
*filename
) const
323 filename
->fmt ("%scrash.log", _personal_dir
);
325 FILE *file
= FioFOpenFile (filename
->c_str(), "w", NO_DIRECTORY
);
326 if (file
== NULL
) return false;
328 size_t len
= strlen(buffer
);
329 size_t written
= fwrite(buffer
, 1, len
, file
);
332 return len
== written
;
335 /* virtual */ int CrashLog::WriteCrashDump (stringb
*filename
) const
337 /* Stub implementation; not all OSes support this. */
342 * Write the (crash) savegame to a file.
343 * @note On success the filename will be filled with the full path of the
344 * crash save file. Make sure filename is at least \c MAX_PATH big.
345 * @param filename Output for the filename of the written file.
346 * @return true when the crash save was successfully made.
348 bool CrashLog::WriteSavegame (stringb
*filename
) const
350 /* If the map array doesn't exist, saving will fail too. If the map got
351 * initialised, there is a big chance the rest is initialised too. */
352 if (_mth
== NULL
) return false;
357 filename
->fmt ("%scrash.sav", _personal_dir
);
359 /* Don't do a threaded saveload. */
360 return SaveGame(filename
->c_str(), NO_DIRECTORY
, false);
367 * Write the (crash) screenshot to a file.
368 * @note On success the filename will be filled with the full path of the
369 * screenshot. Make sure filename is at least \c MAX_PATH big.
370 * @param filename Output for the filename of the written file.
371 * @return true when the crash screenshot was successfully made.
373 bool CrashLog::WriteScreenshot (stringb
*filename
) const
375 /* Don't draw when we have invalid screen size */
376 if (_screen_width
< 1 || _screen_height
< 1 || !_screen_surface
) return false;
378 bool res
= MakeScreenshot(SC_CRASHLOG
, "crash");
379 if (res
) filename
->copy (_full_screenshot_name
);
384 * Makes the crash log, writes it to a file and then subsequently tries
385 * to make a crash dump and crash savegame. It uses DEBUG to write
386 * information like paths to the console.
387 * @return true when everything is made successfully.
389 bool CrashLog::MakeCrashLog() const
391 /* Don't keep looping logging crashes. */
392 static bool crashlogged
= false;
393 if (crashlogged
) return false;
396 sstring
<MAX_PATH
> filename
;
397 sstring
<65536> buffer
;
400 printf("Crash encountered, generating crash log...\n");
401 this->FillCrashLog (&buffer
);
402 printf("%s\n", buffer
.c_str());
403 printf("Crash log generated.\n\n");
405 printf("Writing crash log to disk...\n");
406 bool bret
= this->WriteCrashLog (buffer
.c_str(), &filename
);
408 printf("Crash log written to %s. Please add this file to any bug reports.\n\n", filename
.c_str());
410 printf("Writing crash log failed. Please attach the output above to any bug reports.\n\n");
414 /* Don't mention writing crash dumps because not all platforms support it. */
415 int dret
= this->WriteCrashDump (&filename
);
417 printf("Writing crash dump failed.\n\n");
419 } else if (dret
> 0) {
420 printf("Crash dump written to %s. Please add this file to any bug reports.\n\n", filename
.c_str());
423 printf("Writing crash savegame...\n");
424 bret
= this->WriteSavegame (&filename
);
426 printf("Crash savegame written to %s. Please add this file and the last (auto)save to any bug reports.\n\n", filename
.c_str());
429 printf("Writing crash savegame failed. Please attach the last (auto)save to any bug reports.\n\n");
432 printf("Writing crash screenshot...\n");
433 bret
= this->WriteScreenshot (&filename
);
435 printf("Crash screenshot written to %s. Please add this file to any bug reports.\n\n", filename
.c_str());
438 printf("Writing crash screenshot failed.\n\n");
445 * Sets a message for the error message handler.
446 * @param message The error message of the error.
448 /* static */ void CrashLog::SetErrorMessage(const char *message
)
450 CrashLog::message
= message
;
454 * Try to close the sound/video stuff so it doesn't keep lingering around
455 * incorrect video states or so, e.g. keeping dpmi disabled.
457 /* static */ void CrashLog::AfterCrashLogCleanup()
459 if (MusicDriver::GetActiveDriver() != NULL
) MusicDriver::GetActiveDriver()->Stop();
460 if (SoundDriver::GetActiveDriver() != NULL
) SoundDriver::GetActiveDriver()->Stop();
461 if (VideoDriver::GetActiveDriver() != NULL
) VideoDriver::GetActiveDriver()->Stop();