crashlog.cpp

Go to the documentation of this file.
00001 /* $Id: crashlog.cpp 18672 2009-12-31 00:09:29Z rubidium $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * 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.
00006  * 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.
00007  * 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/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "crashlog.h"
00014 #include "gamelog.h"
00015 #include "date_func.h"
00016 #include "map_func.h"
00017 #include "rev.h"
00018 #include "strings_func.h"
00019 #include "blitter/factory.hpp"
00020 #include "base_media_base.h"
00021 #include "music/music_driver.hpp"
00022 #include "sound/sound_driver.hpp"
00023 #include "video/video_driver.hpp"
00024 #include "saveload/saveload.h"
00025 #include "screenshot.h"
00026 #include "gfx_func.h"
00027 
00028 #include <squirrel.h>
00029 #include "ai/ai_info.hpp"
00030 #include "company_base.h"
00031 
00032 #include <time.h>
00033 
00034 /* static */ const char *CrashLog::message = NULL;
00035 /* static */ char *CrashLog::gamelog_buffer = NULL;
00036 /* static */ const char *CrashLog::gamelog_last = NULL;
00037 
00038 /* virtual */ char *CrashLog::LogRegisters(char *buffer, const char *last) const
00039 {
00040   /* Stub implementation; not all OSes support this. */
00041   return buffer;
00042 }
00043 
00044 /* virtual */ char *CrashLog::LogModules(char *buffer, const char *last) const
00045 {
00046   /* Stub implementation; not all OSes support this. */
00047   return buffer;
00048 }
00049 
00050 char *CrashLog::LogOpenTTDVersion(char *buffer, const char *last) const
00051 {
00052   return buffer + seprintf(buffer, last,
00053       "OpenTTD version:\n"
00054       " Version:    %s (%d)\n"
00055       " NewGRF ver: %08x\n"
00056       " Bits:       %d\n"
00057       " Endian:     %s\n"
00058       " Dedicated:  %s\n"
00059       " Build date: %s\n\n",
00060       _openttd_revision,
00061       _openttd_revision_modified,
00062       _openttd_newgrf_version,
00063 #ifdef _SQ64
00064       64,
00065 #else
00066       32,
00067 #endif
00068 #if (TTD_ENDIAN == TTD_LITTLE_ENDIAN)
00069       "little",
00070 #else
00071       "big",
00072 #endif
00073 #ifdef DEDICATED
00074       "yes",
00075 #else
00076       "no",
00077 #endif
00078       _openttd_build_date
00079   );
00080 }
00081 
00082 char *CrashLog::LogConfiguration(char *buffer, const char *last) const
00083 {
00084   buffer += seprintf(buffer, last,
00085       "Configuration:\n"
00086       " Blitter:      %s\n"
00087       " Graphics set: %s\n"
00088       " Language:     %s\n"
00089       " Music driver: %s\n"
00090       " Music set:    %s\n"
00091       " Sound driver: %s\n"
00092       " Sound set:    %s\n"
00093       " Video driver: %s\n\n",
00094       BlitterFactoryBase::GetCurrentBlitter() == NULL ? "none" : BlitterFactoryBase::GetCurrentBlitter()->GetName(),
00095       BaseGraphics::GetUsedSet() == NULL ? "none" : BaseGraphics::GetUsedSet()->name,
00096       StrEmpty(_dynlang.curr_file) ? "none" : _dynlang.curr_file,
00097       _music_driver == NULL ? "none" : _music_driver->GetName(),
00098       BaseMusic::GetUsedSet() == NULL ? "none" : BaseMusic::GetUsedSet()->name,
00099       _sound_driver == NULL ? "none" : _sound_driver->GetName(),
00100       BaseSounds::GetUsedSet() == NULL ? "none" : BaseSounds::GetUsedSet()->name,
00101       _video_driver == NULL ? "none" : _video_driver->GetName()
00102   );
00103 
00104   buffer += seprintf(buffer, last, "AI Configuration:\n");
00105   const Company *c;
00106   FOR_ALL_COMPANIES(c) {
00107     if (c->ai_info == NULL) {
00108       buffer += seprintf(buffer, last, " %2i: Human\n", (int)c->index);
00109     } else {
00110       buffer += seprintf(buffer, last, " %2i: %s (v%d)\n", (int)c->index, c->ai_info->GetName(), c->ai_info->GetVersion());
00111     }
00112   }
00113   buffer += seprintf(buffer, last, "\n");
00114 
00115   return buffer;
00116 }
00117 
00118 /* Include these here so it's close to where it's actually used. */
00119 #ifdef WITH_ALLEGRO
00120 # include <allegro.h>
00121 #endif /* WITH_ALLEGRO */
00122 #ifdef WITH_FONTCONFIG
00123 # include <fontconfig/fontconfig.h>
00124 #endif /* WITH_FONTCONFIG */
00125 #ifdef WITH_PNG
00126   /* pngconf.h, included by png.h doesn't like something in the
00127    * freetype headers. As such it's not alphabetically sorted. */
00128 # include <png.h>
00129 #endif /* WITH_PNG */
00130 #ifdef WITH_FREETYPE
00131 # include <ft2build.h>
00132 # include FT_FREETYPE_H
00133 #endif /* WITH_FREETYPE */
00134 #ifdef WITH_ICU
00135 # include <unicode/uversion.h>
00136 #endif /* WITH_ICU */
00137 #ifdef WITH_SDL
00138 # include "sdl.h"
00139 # include <SDL.h>
00140 #endif /* WITH_SDL */
00141 
00142 char *CrashLog::LogLibraries(char *buffer, const char *last) const
00143 {
00144   buffer += seprintf(buffer, last, "Libraries:\n");
00145 
00146 #ifdef WITH_ALLEGRO
00147   buffer += seprintf(buffer, last, " Allegro:    %s\n", allegro_id);
00148 #endif /* WITH_ALLEGRO */
00149 
00150 #ifdef WITH_FONTCONFIG
00151   int version = FcGetVersion();
00152   buffer += seprintf(buffer, last, " FontConfig: %d.%d.%d\n", version / 10000, (version / 100) % 100, version % 100);
00153 #endif /* WITH_FONTCONFIG */
00154 
00155 #ifdef WITH_FREETYPE
00156   FT_Library library;
00157   int major, minor, patch;
00158   FT_Init_FreeType(&library);
00159   FT_Library_Version(library, &major, &minor, &patch);
00160   FT_Done_FreeType(library);
00161   buffer += seprintf(buffer, last, " FreeType:   %d.%d.%d\n", major, minor, patch);
00162 #endif /* WITH_FREETYPE */
00163 
00164 #ifdef WITH_ICU
00165   /* 4 times 0-255, separated by dots (.) and a trailing '\0' */
00166   char buf[4 * 3 + 3 + 1];
00167   UVersionInfo ver;
00168   u_getVersion(ver);
00169   u_versionToString(ver, buf);
00170   buffer += seprintf(buffer, last, " ICU:        %s\n", buf);
00171 #endif /* WITH_ICU */
00172 
00173 #ifdef WITH_PNG
00174   buffer += seprintf(buffer, last, " PNG:        %s\n", png_get_libpng_ver(NULL));
00175 #endif /* WITH_PNG */
00176 
00177 #ifdef WITH_SDL
00178 #ifdef DYNAMICALLY_LOADED_SDL
00179   if (SDL_CALL SDL_Linked_Version != NULL) {
00180 #else
00181   {
00182 #endif
00183     const SDL_version *v = SDL_CALL SDL_Linked_Version();
00184     buffer += seprintf(buffer, last, " SDL:        %d.%d.%d\n", v->major, v->minor, v->patch);
00185   }
00186 #endif /* WITH_SDL */
00187 
00188   buffer += seprintf(buffer, last, "\n");
00189   return buffer;
00190 }
00191 
00192 /* static */ void CrashLog::GamelogFillCrashLog(const char *s)
00193 {
00194   CrashLog::gamelog_buffer += seprintf(CrashLog::gamelog_buffer, CrashLog::gamelog_last, "%s\n", s);
00195 }
00196 
00197 char *CrashLog::LogGamelog(char *buffer, const char *last) const
00198 {
00199   CrashLog::gamelog_buffer = buffer;
00200   CrashLog::gamelog_last = last;
00201   GamelogPrint(&CrashLog::GamelogFillCrashLog);
00202   return CrashLog::gamelog_buffer + seprintf(CrashLog::gamelog_buffer, last, "\n");
00203 }
00204 
00205 char *CrashLog::FillCrashLog(char *buffer, const char *last) const
00206 {
00207   time_t cur_time = time(NULL);
00208   buffer += seprintf(buffer, last, "*** OpenTTD Crash Report ***\n\n");
00209   buffer += seprintf(buffer, last, "Crash at: %s", asctime(gmtime(&cur_time)));
00210 
00211   YearMonthDay ymd;
00212   ConvertDateToYMD(_date, &ymd);
00213   buffer += seprintf(buffer, last, "In game date: %i-%02i-%02i (%i)\n\n", ymd.year, ymd.month + 1, ymd.day, _date_fract);
00214 
00215   buffer = this->LogError(buffer, last, CrashLog::message);
00216   buffer = this->LogOpenTTDVersion(buffer, last);
00217   buffer = this->LogRegisters(buffer, last);
00218   buffer = this->LogStacktrace(buffer, last);
00219   buffer = this->LogOSVersion(buffer, last);
00220   buffer = this->LogConfiguration(buffer, last);
00221   buffer = this->LogLibraries(buffer, last);
00222   buffer = this->LogModules(buffer, last);
00223   buffer = this->LogGamelog(buffer, last);
00224 
00225   buffer += seprintf(buffer, last, "*** End of OpenTTD Crash Report ***\n");
00226   return buffer;
00227 }
00228 
00229 bool CrashLog::WriteCrashLog(const char *buffer, char *filename, const char *filename_last) const
00230 {
00231   seprintf(filename, filename_last, "%scrash.log", _personal_dir);
00232 
00233   FILE *file = FioFOpenFile(filename, "w", NO_DIRECTORY);
00234   if (file == NULL) return false;
00235 
00236   size_t len = strlen(buffer);
00237   size_t written = fwrite(buffer, 1, len, file);
00238 
00239   FioFCloseFile(file);
00240   return len == written;
00241 }
00242 
00243 /* virtual */ int CrashLog::WriteCrashDump(char *filename, const char *filename_last) const
00244 {
00245   /* Stub implementation; not all OSes support this. */
00246   return 0;
00247 }
00248 
00249 bool CrashLog::WriteSavegame(char *filename, const char *filename_last) const
00250 {
00251   /* If the map array doesn't exist, saving will fail too. If the map got
00252    * initialised, there is a big chance the rest is initialised too. */
00253   if (_m == NULL) return false;
00254 
00255   try {
00256     GamelogEmergency();
00257 
00258     seprintf(filename, filename_last, "%scrash.sav", _personal_dir);
00259 
00260     /* Don't do a threaded saveload. */
00261     return SaveOrLoad(filename, SL_SAVE, NO_DIRECTORY, false) == SL_OK;
00262   } catch (...) {
00263     return false;
00264   }
00265 }
00266 
00267 bool CrashLog::WriteScreenshot(char *filename, const char *filename_last) const
00268 {
00269   /* Don't draw when we have invalid screen size */
00270   if (_screen.width < 1 || _screen.height < 1 || _screen.dst_ptr == NULL) return false;
00271 
00272   bool res = MakeScreenshot(SC_RAW, "crash");
00273   if (res) strecpy(filename, _full_screenshot_name, filename_last);
00274   return res;
00275 }
00276 
00277 bool CrashLog::MakeCrashLog() const
00278 {
00279   /* Don't keep looping logging crashes. */
00280   static bool crashlogged = false;
00281   if (crashlogged) return false;
00282   crashlogged = true;
00283 
00284   char filename[MAX_PATH];
00285   char buffer[65536];
00286   bool ret = true;
00287 
00288   printf("Crash encountered, generating crash log...\n");
00289   this->FillCrashLog(buffer, lastof(buffer));
00290   printf("%s\n", buffer);
00291   printf("Crash log generated.\n\n");
00292 
00293   printf("Writing crash log to disk...\n");
00294   bool bret = this->WriteCrashLog(buffer, filename, lastof(filename));
00295   if (bret) {
00296     printf("Crash log written to %s. Please add this file to any bug reports.\n\n", filename);
00297   } else {
00298     printf("Writing crash log failed. Please attach the output above to any bug reports.\n\n");
00299     ret = false;
00300   }
00301 
00302   /* Don't mention writing crash dumps because not all platforms support it. */
00303   int dret = this->WriteCrashDump(filename, lastof(filename));
00304   if (dret < 0) {
00305     printf("Writing crash dump failed.\n\n");
00306     ret = false;
00307   } else if (dret > 0) {
00308     printf("Crash dump written to %s. Please add this file to any bug reports.\n\n", filename);
00309   }
00310 
00311   printf("Writing crash savegame...\n");
00312   bret = this->WriteSavegame(filename, lastof(filename));
00313   if (bret) {
00314     printf("Crash savegame written to %s. Please add this file and the last (auto)save to any bug reports.\n\n", filename);
00315   } else {
00316     ret = false;
00317     printf("Writing crash savegame failed. Please attach the last (auto)save to any bug reports.\n\n");
00318   }
00319 
00320   printf("Writing crash screenshot...\n");
00321   bret = this->WriteScreenshot(filename, lastof(filename));
00322   if (bret) {
00323     printf("Crash screenshot written to %s. Please add this file to any bug reports.\n\n", filename);
00324   } else {
00325     ret = false;
00326     printf("Writing crash screenshot failed.\n\n");
00327   }
00328 
00329   return ret;
00330 }
00331 
00332 /* static */ void CrashLog::SetErrorMessage(const char *message)
00333 {
00334   CrashLog::message = message;
00335 }
00336 
00337 /* static */ void CrashLog::AfterCrashLogCleanup()
00338 {
00339   if (_music_driver != NULL) _music_driver->Stop();
00340   if (_sound_driver != NULL) _sound_driver->Stop();
00341   if (_video_driver != NULL) _video_driver->Stop();
00342 }

Generated on Tue Jan 5 21:02:53 2010 for OpenTTD by  doxygen 1.5.6