1
0
Fork 0

Codechange: replace seprintf with fmt::format for filling the crash log data

pull/10853/head
Rubidium 2023-05-20 19:45:38 +02:00 committed by rubidium42
parent f4b0ac2bd4
commit 19304bd3d5
5 changed files with 234 additions and 315 deletions

View File

@ -76,58 +76,54 @@
/* static */ std::string CrashLog::message{ "<none>" }; /* static */ std::string CrashLog::message{ "<none>" };
char *CrashLog::LogCompiler(char *buffer, const char *last) const void CrashLog::LogCompiler(std::back_insert_iterator<std::string> &output_iterator) const
{ {
buffer += seprintf(buffer, last, " Compiler: " fmt::format_to(output_iterator, " Compiler: "
#if defined(_MSC_VER) #if defined(_MSC_VER)
"MSVC %d", _MSC_VER "MSVC {}", _MSC_VER
#elif defined(__ICC) && defined(__GNUC__) #elif defined(__ICC) && defined(__GNUC__)
"ICC %d (GCC %d.%d.%d mode)", __ICC, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ "ICC {} (GCC {}.{}.{} mode)", __ICC, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__
#elif defined(__ICC) #elif defined(__ICC)
"ICC %d", __ICC "ICC {}", __ICC
#elif defined(__GNUC__) #elif defined(__GNUC__)
"GCC %d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ "GCC {}.{}.{}", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__
#elif defined(__WATCOMC__) #elif defined(__WATCOMC__)
"WatcomC %d", __WATCOMC__ "WatcomC {}", __WATCOMC__
#else #else
"<unknown>" "<unknown>"
#endif #endif
); );
#if defined(__VERSION__) #if defined(__VERSION__)
return buffer + seprintf(buffer, last, " \"" __VERSION__ "\"\n\n"); fmt::format_to(output_iterator, " \"" __VERSION__ "\"\n\n");
#else #else
return buffer + seprintf(buffer, last, "\n\n"); fmt::format_to(output_iterator, "\n\n");
#endif #endif
} }
/* virtual */ char *CrashLog::LogRegisters(char *buffer, const char *last) const /* virtual */ void CrashLog::LogRegisters(std::back_insert_iterator<std::string> &output_iterator) const
{ {
/* Stub implementation; not all OSes support this. */ /* Stub implementation; not all OSes support this. */
return buffer;
} }
/* virtual */ char *CrashLog::LogModules(char *buffer, const char *last) const /* virtual */ void CrashLog::LogModules(std::back_insert_iterator<std::string> &output_iterator) const
{ {
/* Stub implementation; not all OSes support this. */ /* Stub implementation; not all OSes support this. */
return buffer;
} }
/** /**
* Writes OpenTTD's version to the buffer. * Writes OpenTTD's version to the buffer.
* @param buffer The begin where to write at. * @param output_iterator Iterator to write the output to.
* @param last The last position in the buffer to write to.
* @return the position of the \c '\0' character after the buffer.
*/ */
char *CrashLog::LogOpenTTDVersion(char *buffer, const char *last) const void CrashLog::LogOpenTTDVersion(std::back_insert_iterator<std::string> &output_iterator) const
{ {
return buffer + seprintf(buffer, last, fmt::format_to(output_iterator,
"OpenTTD version:\n" "OpenTTD version:\n"
" Version: %s (%d)\n" " Version: {} ({})\n"
" NewGRF ver: %08x\n" " NewGRF ver: {:08x}\n"
" Bits: %d\n" " Bits: {}\n"
" Endian: %s\n" " Endian: {}\n"
" Dedicated: %s\n" " Dedicated: {}\n"
" Build date: %s\n\n", " Build date: {}\n\n",
_openttd_revision, _openttd_revision,
_openttd_revision_modified, _openttd_revision_modified,
_openttd_newgrf_version, _openttd_newgrf_version,
@ -153,83 +149,77 @@ char *CrashLog::LogOpenTTDVersion(char *buffer, const char *last) const
/** /**
* Writes the (important) configuration settings to the buffer. * Writes the (important) configuration settings to the buffer.
* E.g. graphics set, sound set, blitter and AIs. * E.g. graphics set, sound set, blitter and AIs.
* @param buffer The begin where to write at. * @param output_iterator Iterator to write the output to.
* @param last The last position in the buffer to write to.
* @return the position of the \c '\0' character after the buffer.
*/ */
char *CrashLog::LogConfiguration(char *buffer, const char *last) const void CrashLog::LogConfiguration(std::back_insert_iterator<std::string> &output_iterator) const
{ {
buffer += seprintf(buffer, last, fmt::format_to(output_iterator,
"Configuration:\n" "Configuration:\n"
" Blitter: %s\n" " Blitter: {}\n"
" Graphics set: %s (%u)\n" " Graphics set: {} ({})\n"
" Language: %s\n" " Language: {}\n"
" Music driver: %s\n" " Music driver: {}\n"
" Music set: %s (%u)\n" " Music set: {} ({})\n"
" Network: %s\n" " Network: {}\n"
" Sound driver: %s\n" " Sound driver: {}\n"
" Sound set: %s (%u)\n" " Sound set: {} ({})\n"
" Video driver: %s\n\n", " Video driver: {}\n\n",
BlitterFactory::GetCurrentBlitter() == nullptr ? "none" : BlitterFactory::GetCurrentBlitter()->GetName(), BlitterFactory::GetCurrentBlitter() == nullptr ? "none" : BlitterFactory::GetCurrentBlitter()->GetName(),
BaseGraphics::GetUsedSet() == nullptr ? "none" : BaseGraphics::GetUsedSet()->name.c_str(), BaseGraphics::GetUsedSet() == nullptr ? "none" : BaseGraphics::GetUsedSet()->name,
BaseGraphics::GetUsedSet() == nullptr ? UINT32_MAX : BaseGraphics::GetUsedSet()->version, BaseGraphics::GetUsedSet() == nullptr ? UINT32_MAX : BaseGraphics::GetUsedSet()->version,
_current_language == nullptr ? "none" : _current_language->file, _current_language == nullptr ? "none" : _current_language->file,
MusicDriver::GetInstance() == nullptr ? "none" : MusicDriver::GetInstance()->GetName(), MusicDriver::GetInstance() == nullptr ? "none" : MusicDriver::GetInstance()->GetName(),
BaseMusic::GetUsedSet() == nullptr ? "none" : BaseMusic::GetUsedSet()->name.c_str(), BaseMusic::GetUsedSet() == nullptr ? "none" : BaseMusic::GetUsedSet()->name,
BaseMusic::GetUsedSet() == nullptr ? UINT32_MAX : BaseMusic::GetUsedSet()->version, BaseMusic::GetUsedSet() == nullptr ? UINT32_MAX : BaseMusic::GetUsedSet()->version,
_networking ? (_network_server ? "server" : "client") : "no", _networking ? (_network_server ? "server" : "client") : "no",
SoundDriver::GetInstance() == nullptr ? "none" : SoundDriver::GetInstance()->GetName(), SoundDriver::GetInstance() == nullptr ? "none" : SoundDriver::GetInstance()->GetName(),
BaseSounds::GetUsedSet() == nullptr ? "none" : BaseSounds::GetUsedSet()->name.c_str(), BaseSounds::GetUsedSet() == nullptr ? "none" : BaseSounds::GetUsedSet()->name,
BaseSounds::GetUsedSet() == nullptr ? UINT32_MAX : BaseSounds::GetUsedSet()->version, BaseSounds::GetUsedSet() == nullptr ? UINT32_MAX : BaseSounds::GetUsedSet()->version,
VideoDriver::GetInstance() == nullptr ? "none" : VideoDriver::GetInstance()->GetInfoString() VideoDriver::GetInstance() == nullptr ? "none" : VideoDriver::GetInstance()->GetInfoString()
); );
buffer += seprintf(buffer, last, fmt::format_to(output_iterator,
"Fonts:\n" "Fonts:\n"
" Small: %s\n" " Small: {}\n"
" Medium: %s\n" " Medium: {}\n"
" Large: %s\n" " Large: {}\n"
" Mono: %s\n\n", " Mono: {}\n\n",
FontCache::Get(FS_SMALL)->GetFontName(), FontCache::Get(FS_SMALL)->GetFontName(),
FontCache::Get(FS_NORMAL)->GetFontName(), FontCache::Get(FS_NORMAL)->GetFontName(),
FontCache::Get(FS_LARGE)->GetFontName(), FontCache::Get(FS_LARGE)->GetFontName(),
FontCache::Get(FS_MONO)->GetFontName() FontCache::Get(FS_MONO)->GetFontName()
); );
buffer += seprintf(buffer, last, "AI Configuration (local: %i) (current: %i):\n", (int)_local_company, (int)_current_company); fmt::format_to(output_iterator, "AI Configuration (local: {}) (current: {}):\n", _local_company, _current_company);
for (const Company *c : Company::Iterate()) { for (const Company *c : Company::Iterate()) {
if (c->ai_info == nullptr) { if (c->ai_info == nullptr) {
buffer += seprintf(buffer, last, " %2i: Human\n", (int)c->index); fmt::format_to(output_iterator, " {:2}: Human\n", c->index);
} else { } else {
buffer += seprintf(buffer, last, " %2i: %s (v%d)\n", (int)c->index, c->ai_info->GetName().c_str(), c->ai_info->GetVersion()); fmt::format_to(output_iterator, " {:2}: {} (v{})\n", (int)c->index, c->ai_info->GetName(), c->ai_info->GetVersion());
} }
} }
if (Game::GetInfo() != nullptr) { if (Game::GetInfo() != nullptr) {
buffer += seprintf(buffer, last, " GS: %s (v%d)\n", Game::GetInfo()->GetName().c_str(), Game::GetInfo()->GetVersion()); fmt::format_to(output_iterator, " GS: {} (v{})\n", Game::GetInfo()->GetName(), Game::GetInfo()->GetVersion());
} }
buffer += seprintf(buffer, last, "\n"); fmt::format_to(output_iterator, "\n");
return buffer;
} }
/** /**
* Writes information (versions) of the used libraries. * Writes information (versions) of the used libraries.
* @param buffer The begin where to write at. * @param output_iterator Iterator to write the output to.
* @param last The last position in the buffer to write to.
* @return the position of the \c '\0' character after the buffer.
*/ */
char *CrashLog::LogLibraries(char *buffer, const char *last) const void CrashLog::LogLibraries(std::back_insert_iterator<std::string> &output_iterator) const
{ {
buffer += seprintf(buffer, last, "Libraries:\n"); fmt::format_to(output_iterator, "Libraries:\n");
#ifdef WITH_ALLEGRO #ifdef WITH_ALLEGRO
buffer += seprintf(buffer, last, " Allegro: %s\n", allegro_id); fmt::format_to(output_iterator, " Allegro: {}\n", allegro_id);
#endif /* WITH_ALLEGRO */ #endif /* WITH_ALLEGRO */
#ifdef WITH_FONTCONFIG #ifdef WITH_FONTCONFIG
int version = FcGetVersion(); int version = FcGetVersion();
buffer += seprintf(buffer, last, " FontConfig: %d.%d.%d\n", version / 10000, (version / 100) % 100, version % 100); fmt::format_to(output_iterator, " FontConfig: {}.{}.{}\n", version / 10000, (version / 100) % 100, version % 100);
#endif /* WITH_FONTCONFIG */ #endif /* WITH_FONTCONFIG */
#ifdef WITH_FREETYPE #ifdef WITH_FREETYPE
@ -238,11 +228,11 @@ char *CrashLog::LogLibraries(char *buffer, const char *last) const
FT_Init_FreeType(&library); FT_Init_FreeType(&library);
FT_Library_Version(library, &major, &minor, &patch); FT_Library_Version(library, &major, &minor, &patch);
FT_Done_FreeType(library); FT_Done_FreeType(library);
buffer += seprintf(buffer, last, " FreeType: %d.%d.%d\n", major, minor, patch); fmt::format_to(output_iterator, " FreeType: {}.{}.{}\n", major, minor, patch);
#endif /* WITH_FREETYPE */ #endif /* WITH_FREETYPE */
#if defined(WITH_HARFBUZZ) #if defined(WITH_HARFBUZZ)
buffer += seprintf(buffer, last, " HarfBuzz: %s\n", hb_version_string()); fmt::format_to(output_iterator, " HarfBuzz: {}\n", hb_version_string());
#endif /* WITH_HARFBUZZ */ #endif /* WITH_HARFBUZZ */
#if defined(WITH_ICU_I18N) #if defined(WITH_ICU_I18N)
@ -251,82 +241,76 @@ char *CrashLog::LogLibraries(char *buffer, const char *last) const
UVersionInfo ver; UVersionInfo ver;
u_getVersion(ver); u_getVersion(ver);
u_versionToString(ver, buf); u_versionToString(ver, buf);
buffer += seprintf(buffer, last, " ICU i18n: %s\n", buf); fmt::format_to(output_iterator, " ICU i18n: {}\n", buf);
#endif /* WITH_ICU_I18N */ #endif /* WITH_ICU_I18N */
#ifdef WITH_LIBLZMA #ifdef WITH_LIBLZMA
buffer += seprintf(buffer, last, " LZMA: %s\n", lzma_version_string()); fmt::format_to(output_iterator, " LZMA: {}\n", lzma_version_string());
#endif #endif
#ifdef WITH_LZO #ifdef WITH_LZO
buffer += seprintf(buffer, last, " LZO: %s\n", lzo_version_string()); fmt::format_to(output_iterator, " LZO: {}\n", lzo_version_string());
#endif #endif
#ifdef WITH_PNG #ifdef WITH_PNG
buffer += seprintf(buffer, last, " PNG: %s\n", png_get_libpng_ver(nullptr)); fmt::format_to(output_iterator, " PNG: {}\n", png_get_libpng_ver(nullptr));
#endif /* WITH_PNG */ #endif /* WITH_PNG */
#ifdef WITH_SDL #ifdef WITH_SDL
const SDL_version *sdl_v = SDL_Linked_Version(); const SDL_version *sdl_v = SDL_Linked_Version();
buffer += seprintf(buffer, last, " SDL1: %d.%d.%d\n", sdl_v->major, sdl_v->minor, sdl_v->patch); fmt::format_to(output_iterator, " SDL1: {}.{}.{}\n", sdl_v->major, sdl_v->minor, sdl_v->patch);
#elif defined(WITH_SDL2) #elif defined(WITH_SDL2)
SDL_version sdl2_v; SDL_version sdl2_v;
SDL_GetVersion(&sdl2_v); SDL_GetVersion(&sdl2_v);
buffer += seprintf(buffer, last, " SDL2: %d.%d.%d\n", sdl2_v.major, sdl2_v.minor, sdl2_v.patch); fmt::format_to(output_iterator, " SDL2: {}.{}.{}\n", sdl2_v.major, sdl2_v.minor, sdl2_v.patch);
#endif #endif
#ifdef WITH_ZLIB #ifdef WITH_ZLIB
buffer += seprintf(buffer, last, " Zlib: %s\n", zlibVersion()); fmt::format_to(output_iterator, " Zlib: {}\n", zlibVersion());
#endif #endif
#ifdef WITH_CURL #ifdef WITH_CURL
auto *curl_v = curl_version_info(CURLVERSION_NOW); auto *curl_v = curl_version_info(CURLVERSION_NOW);
buffer += seprintf(buffer, last, " Curl: %s\n", curl_v->version); fmt::format_to(output_iterator, " Curl: {}\n", curl_v->version);
if (curl_v->ssl_version != nullptr) { if (curl_v->ssl_version != nullptr) {
buffer += seprintf(buffer, last, " Curl SSL: %s\n", curl_v->ssl_version); fmt::format_to(output_iterator, " Curl SSL: {}\n", curl_v->ssl_version);
} else { } else {
buffer += seprintf(buffer, last, " Curl SSL: none\n"); fmt::format_to(output_iterator, " Curl SSL: none\n");
} }
#endif #endif
buffer += seprintf(buffer, last, "\n"); fmt::format_to(output_iterator, "\n");
return buffer;
} }
/** /**
* Writes the gamelog data to the buffer. * Writes the gamelog data to the buffer.
* @param buffer The begin where to write at. * @param output_iterator Iterator to write the output to.
* @param last The last position in the buffer to write to.
* @return the position of the \c '\0' character after the buffer.
*/ */
char *CrashLog::LogGamelog(char *buffer, const char *last) const void CrashLog::LogGamelog(std::back_insert_iterator<std::string> &output_iterator) const
{ {
_gamelog.Print([&buffer, last](const std::string &s) { _gamelog.Print([&output_iterator](const std::string &s) {
buffer += seprintf(buffer, last, "%s\n", s.c_str()); fmt::format_to(output_iterator, "{}\n", s);
}); });
return buffer + seprintf(buffer, last, "\n"); fmt::format_to(output_iterator, "\n");
} }
/** /**
* Writes up to 32 recent news messages to the buffer, with the most recent first. * Writes up to 32 recent news messages to the buffer, with the most recent first.
* @param buffer The begin where to write at. * @param output_iterator Iterator to write the output to.
* @param last The last position in the buffer to write to.
* @return the position of the \c '\0' character after the buffer.
*/ */
char *CrashLog::LogRecentNews(char *buffer, const char *last) const void CrashLog::LogRecentNews(std::back_insert_iterator<std::string> &output_iterator) const
{ {
buffer += seprintf(buffer, last, "Recent news messages:\n"); fmt::format_to(output_iterator, "Recent news messages:\n");
int i = 0; int i = 0;
for (NewsItem *news = _latest_news; i < 32 && news != nullptr; news = news->prev, i++) { for (NewsItem *news = _latest_news; i < 32 && news != nullptr; news = news->prev, i++) {
TimerGameCalendar::YearMonthDay ymd; TimerGameCalendar::YearMonthDay ymd;
TimerGameCalendar::ConvertDateToYMD(news->date, &ymd); TimerGameCalendar::ConvertDateToYMD(news->date, &ymd);
buffer += seprintf(buffer, last, "(%i-%02i-%02i) StringID: %u, Type: %u, Ref1: %u, %u, Ref2: %u, %u\n", fmt::format_to(output_iterator, "({}-{:02}-{:02}) StringID: {}, Type: {}, Ref1: {}, {}, Ref2: {}, {}\n",
ymd.year, ymd.month + 1, ymd.day, news->string_id, news->type, ymd.year, ymd.month + 1, ymd.day, news->string_id, news->type,
news->reftype1, news->ref1, news->reftype2, news->ref2); news->reftype1, news->ref1, news->reftype2, news->ref2);
} }
buffer += seprintf(buffer, last, "\n"); fmt::format_to(output_iterator, "\n");
return buffer;
} }
/** /**
@ -349,34 +333,30 @@ int CrashLog::CreateFileName(char *filename, const char *filename_last, const ch
/** /**
* Fill the crash log buffer with all data of a crash log. * Fill the crash log buffer with all data of a crash log.
* @param buffer The begin where to write at. * @param output_iterator Iterator to write the output to.
* @param last The last position in the buffer to write to.
* @return the position of the \c '\0' character after the buffer.
*/ */
char *CrashLog::FillCrashLog(char *buffer, const char *last) const void CrashLog::FillCrashLog(std::back_insert_iterator<std::string> &output_iterator) const
{ {
buffer += seprintf(buffer, last, "*** OpenTTD Crash Report ***\n\n"); fmt::format_to(output_iterator, "*** OpenTTD Crash Report ***\n\n");
std::string temp = fmt::format("Crash at: {:%Y-%m-%d %H:%M:%S} (UTC)\n", fmt::gmtime(time(nullptr))); fmt::format_to(output_iterator, "Crash at: {:%Y-%m-%d %H:%M:%S} (UTC)\n", fmt::gmtime(time(nullptr)));
buffer = strecpy(buffer, temp.c_str(), last);
TimerGameCalendar::YearMonthDay ymd; TimerGameCalendar::YearMonthDay ymd;
TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date, &ymd); TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date, &ymd);
buffer += seprintf(buffer, last, "In game date: %i-%02i-%02i (%i)\n\n", ymd.year, ymd.month + 1, ymd.day, TimerGameCalendar::date_fract); fmt::format_to(output_iterator, "In game date: {}-{:02}-{:02} ({})\n\n", ymd.year, ymd.month + 1, ymd.day, TimerGameCalendar::date_fract);
buffer = this->LogError(buffer, last, CrashLog::message.c_str()); this->LogError(output_iterator, CrashLog::message);
buffer = this->LogOpenTTDVersion(buffer, last); this->LogOpenTTDVersion(output_iterator);
buffer = this->LogRegisters(buffer, last); this->LogRegisters(output_iterator);
buffer = this->LogStacktrace(buffer, last); this->LogStacktrace(output_iterator);
buffer = this->LogOSVersion(buffer, last); this->LogOSVersion(output_iterator);
buffer = this->LogCompiler(buffer, last); this->LogCompiler(output_iterator);
buffer = this->LogConfiguration(buffer, last); this->LogConfiguration(output_iterator);
buffer = this->LogLibraries(buffer, last); this->LogLibraries(output_iterator);
buffer = this->LogModules(buffer, last); this->LogModules(output_iterator);
buffer = this->LogGamelog(buffer, last); this->LogGamelog(output_iterator);
buffer = this->LogRecentNews(buffer, last); this->LogRecentNews(output_iterator);
buffer += seprintf(buffer, last, "*** End of OpenTTD Crash Report ***\n"); fmt::format_to(output_iterator, "*** End of OpenTTD Crash Report ***\n");
return buffer;
} }
/** /**
@ -475,16 +455,18 @@ bool CrashLog::MakeCrashLog() const
crashlogged = true; crashlogged = true;
char filename[MAX_PATH]; char filename[MAX_PATH];
char buffer[65536]; std::string buffer;
buffer.reserve(65536);
auto output_iterator = std::back_inserter(buffer);
bool ret = true; bool ret = true;
fmt::print("Crash encountered, generating crash log...\n"); fmt::print("Crash encountered, generating crash log...\n");
this->FillCrashLog(buffer, lastof(buffer)); this->FillCrashLog(output_iterator);
fmt::print("{}\n", buffer); fmt::print("{}\n", buffer);
fmt::print("Crash log generated.\n\n"); fmt::print("Crash log generated.\n\n");
fmt::print("Writing crash log to disk...\n"); fmt::print("Writing crash log to disk...\n");
bool bret = this->WriteCrashLog(buffer, filename, lastof(filename)); bool bret = this->WriteCrashLog(buffer.c_str(), filename, lastof(filename));
if (bret) { if (bret) {
fmt::print("Crash log written to {}. Please add this file to any bug reports.\n\n", filename); fmt::print("Crash log written to {}. Please add this file to any bug reports.\n\n", filename);
} else { } else {

View File

@ -20,62 +20,50 @@ private:
protected: protected:
/** /**
* Writes OS' version to the buffer. * Writes OS' version to the buffer.
* @param buffer The begin where to write at. * @param output_iterator Iterator to write the output to.
* @param last The last position in the buffer to write to.
* @return the position of the \c '\0' character after the buffer.
*/ */
virtual char *LogOSVersion(char *buffer, const char *last) const = 0; virtual void LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const = 0;
/** /**
* Writes compiler (and its version, if available) to the buffer. * Writes compiler (and its version, if available) to the buffer.
* @param buffer The begin where to write at. * @param output_iterator Iterator to write the output to.
* @param last The last position in the buffer to write to.
* @return the position of the \c '\0' character after the buffer.
*/ */
virtual char *LogCompiler(char *buffer, const char *last) const; virtual void LogCompiler(std::back_insert_iterator<std::string> &output_iterator) const;
/** /**
* Writes actually encountered error to the buffer. * Writes actually encountered error to the buffer.
* @param buffer The begin where to write at. * @param output_iterator Iterator to write the output to.
* @param last The last position in the buffer to write to.
* @param message Message passed to use for errors. * @param message Message passed to use for errors.
* @return the position of the \c '\0' character after the buffer.
*/ */
virtual char *LogError(char *buffer, const char *last, const char *message) const = 0; virtual void LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const = 0;
/** /**
* Writes the stack trace to the buffer, if there is information about it * Writes the stack trace to the buffer, if there is information about it
* available. * available.
* @param buffer The begin where to write at. * @param output_iterator Iterator to write the output to.
* @param last The last position in the buffer to write to.
* @return the position of the \c '\0' character after the buffer.
*/ */
virtual char *LogStacktrace(char *buffer, const char *last) const = 0; virtual void LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const = 0;
/** /**
* Writes information about the data in the registers, if there is * Writes information about the data in the registers, if there is
* information about it available. * information about it available.
* @param buffer The begin where to write at. * @param output_iterator Iterator to write the output to.
* @param last The last position in the buffer to write to.
* @return the position of the \c '\0' character after the buffer.
*/ */
virtual char *LogRegisters(char *buffer, const char *last) const; virtual void LogRegisters(std::back_insert_iterator<std::string> &output_iterator) const;
/** /**
* Writes the dynamically linked libraries/modules to the buffer, if there * Writes the dynamically linked libraries/modules to the buffer, if there
* is information about it available. * is information about it available.
* @param buffer The begin where to write at. * @param output_iterator Iterator to write the output to.
* @param last The last position in the buffer to write to.
* @return the position of the \c '\0' character after the buffer.
*/ */
virtual char *LogModules(char *buffer, const char *last) const; virtual void LogModules(std::back_insert_iterator<std::string> &output_iterator) const;
char *LogOpenTTDVersion(char *buffer, const char *last) const; void LogOpenTTDVersion(std::back_insert_iterator<std::string> &output_iterator) const;
char *LogConfiguration(char *buffer, const char *last) const; void LogConfiguration(std::back_insert_iterator<std::string> &output_iterator) const;
char *LogLibraries(char *buffer, const char *last) const; void LogLibraries(std::back_insert_iterator<std::string> &output_iterator) const;
char *LogGamelog(char *buffer, const char *last) const; void LogGamelog(std::back_insert_iterator<std::string> &output_iterator) const;
char *LogRecentNews(char *buffer, const char *list) const; void LogRecentNews(std::back_insert_iterator<std::string> &output_iterator) const;
int CreateFileName(char *filename, const char *filename_last, const char *ext, bool with_dir = true) const; int CreateFileName(char *filename, const char *filename_last, const char *ext, bool with_dir = true) const;
@ -83,7 +71,7 @@ public:
/** Stub destructor to silence some compilers. */ /** Stub destructor to silence some compilers. */
virtual ~CrashLog() = default; virtual ~CrashLog() = default;
char *FillCrashLog(char *buffer, const char *last) const; void FillCrashLog(std::back_insert_iterator<std::string> &output_iterator) const;
bool WriteCrashLog(const char *buffer, char *filename, const char *filename_last) const; bool WriteCrashLog(const char *buffer, char *filename, const char *filename_last) const;
/** /**

View File

@ -31,13 +31,6 @@
#define IS_ALIGNED(addr) (((uintptr_t)(addr) & 0xf) == 0) #define IS_ALIGNED(addr) (((uintptr_t)(addr) & 0xf) == 0)
#endif #endif
/* printf format specification for 32/64-bit addresses. */
#ifdef __LP64__
#define PRINTF_PTR "0x%016lx"
#else
#define PRINTF_PTR "0x%08lx"
#endif
#define MAX_STACK_FRAMES 64 #define MAX_STACK_FRAMES 64
/** /**
@ -51,20 +44,20 @@ class CrashLogOSX : public CrashLog {
char filename_save[MAX_PATH]; ///< Path of crash.sav char filename_save[MAX_PATH]; ///< Path of crash.sav
char filename_screenshot[MAX_PATH]; ///< Path of crash.(png|bmp|pcx) char filename_screenshot[MAX_PATH]; ///< Path of crash.(png|bmp|pcx)
char *LogOSVersion(char *buffer, const char *last) const override void LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const override
{ {
int ver_maj, ver_min, ver_bug; int ver_maj, ver_min, ver_bug;
GetMacOSVersion(&ver_maj, &ver_min, &ver_bug); GetMacOSVersion(&ver_maj, &ver_min, &ver_bug);
const NXArchInfo *arch = NXGetLocalArchInfo(); const NXArchInfo *arch = NXGetLocalArchInfo();
return buffer + seprintf(buffer, last, fmt::format_to(output_iterator,
"Operating system:\n" "Operating system:\n"
" Name: Mac OS X\n" " Name: Mac OS X\n"
" Release: %d.%d.%d\n" " Release: {}.{}.{}\n"
" Machine: %s\n" " Machine: {}\n"
" Min Ver: %d\n" " Min Ver: {}\n"
" Max Ver: %d\n", " Max Ver: {}\n",
ver_maj, ver_min, ver_bug, ver_maj, ver_min, ver_bug,
arch != nullptr ? arch->description : "unknown", arch != nullptr ? arch->description : "unknown",
MAC_OS_X_VERSION_MIN_REQUIRED, MAC_OS_X_VERSION_MIN_REQUIRED,
@ -72,25 +65,25 @@ class CrashLogOSX : public CrashLog {
); );
} }
char *LogError(char *buffer, const char *last, const char *message) const override void LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const override
{ {
return buffer + seprintf(buffer, last, fmt::format_to(output_iterator,
"Crash reason:\n" "Crash reason:\n"
" Signal: %s (%d)\n" " Signal: {} ({})\n"
" Message: %s\n\n", " Message: {}\n\n",
strsignal(this->signum), strsignal(this->signum),
this->signum, this->signum,
message message
); );
} }
char *LogStacktrace(char *buffer, const char *last) const override void LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const override
{ {
/* As backtrace() is only implemented in 10.5 or later, /* As backtrace() is only implemented in 10.5 or later,
* we're rolling our own here. Mostly based on * we're rolling our own here. Mostly based on
* http://stackoverflow.com/questions/289820/getting-the-current-stack-trace-on-mac-os-x * http://stackoverflow.com/questions/289820/getting-the-current-stack-trace-on-mac-os-x
* and some details looked up in the Darwin sources. */ * and some details looked up in the Darwin sources. */
buffer += seprintf(buffer, last, "\nStacktrace:\n"); fmt::format_to(output_iterator, "\nStacktrace:\n");
void **frame; void **frame;
#if defined(__ppc__) || defined(__ppc64__) #if defined(__ppc__) || defined(__ppc64__)
@ -110,7 +103,7 @@ class CrashLogOSX : public CrashLog {
if (ip == nullptr) break; if (ip == nullptr) break;
/* Print running index. */ /* Print running index. */
buffer += seprintf(buffer, last, " [%02d]", i); fmt::format_to(output_iterator, " [{:02}]", i);
Dl_info dli; Dl_info dli;
bool dl_valid = dladdr(ip, &dli) != 0; bool dl_valid = dladdr(ip, &dli) != 0;
@ -126,7 +119,7 @@ class CrashLogOSX : public CrashLog {
} }
} }
/* Print image name and IP. */ /* Print image name and IP. */
buffer += seprintf(buffer, last, " %-20s " PRINTF_PTR, fname, (uintptr_t)ip); fmt::format_to(output_iterator, " {:20s} {}", fname, (uintptr_t)ip);
/* Print function offset if information is available. */ /* Print function offset if information is available. */
if (dl_valid && dli.dli_sname != nullptr && dli.dli_saddr != nullptr) { if (dl_valid && dli.dli_sname != nullptr && dli.dli_saddr != nullptr) {
@ -135,11 +128,11 @@ class CrashLogOSX : public CrashLog {
char *func_name = abi::__cxa_demangle(dli.dli_sname, nullptr, 0, &status); char *func_name = abi::__cxa_demangle(dli.dli_sname, nullptr, 0, &status);
long int offset = (intptr_t)ip - (intptr_t)dli.dli_saddr; long int offset = (intptr_t)ip - (intptr_t)dli.dli_saddr;
buffer += seprintf(buffer, last, " (%s + %ld)", func_name != nullptr ? func_name : dli.dli_sname, offset); fmt::format_to(output_iterator, " ({} + {})", func_name != nullptr ? func_name : dli.dli_sname, offset);
free(func_name); free(func_name);
} }
buffer += seprintf(buffer, last, "\n"); fmt::format_to(output_iterator, "\n");
/* Get address of next stack frame. */ /* Get address of next stack frame. */
void **next = (void **)frame[0]; void **next = (void **)frame[0];
@ -148,7 +141,7 @@ class CrashLogOSX : public CrashLog {
frame = next; frame = next;
} }
return buffer + seprintf(buffer, last, "\n"); fmt::format_to(output_iterator, "\n");
} }
public: public:
@ -163,40 +156,6 @@ public:
filename_screenshot[0] = '\0'; filename_screenshot[0] = '\0';
} }
/** Generate the crash log. */
bool MakeCrashLog()
{
char buffer[65536];
bool ret = true;
fmt::print("Crash encountered, generating crash log...\n");
this->FillCrashLog(buffer, lastof(buffer));
fmt::print("{}\n", buffer);
fmt::print("Crash log generated.\n\n");
fmt::print("Writing crash log to disk...\n");
if (!this->WriteCrashLog(buffer, filename_log, lastof(filename_log))) {
filename_log[0] = '\0';
ret = false;
}
fmt::print("Writing crash savegame...\n");
if (!this->WriteSavegame(filename_save, lastof(filename_save))) {
filename_save[0] = '\0';
ret = false;
}
fmt::print("Writing crash screenshot...\n");
if (!this->WriteScreenshot(filename_screenshot, lastof(filename_screenshot))) {
filename_screenshot[0] = '\0';
ret = false;
}
this->SendSurvey();
return ret;
}
/** Show a dialog with the crash information. */ /** Show a dialog with the crash information. */
void DisplayCrashDialog() const void DisplayCrashDialog() const
{ {

View File

@ -38,19 +38,20 @@ class CrashLogUnix : public CrashLog {
/** Signal that has been thrown. */ /** Signal that has been thrown. */
int signum; int signum;
char *LogOSVersion(char *buffer, const char *last) const override void LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const override
{ {
struct utsname name; struct utsname name;
if (uname(&name) < 0) { if (uname(&name) < 0) {
return buffer + seprintf(buffer, last, "Could not get OS version: %s\n", strerror(errno)); fmt::format_to(output_iterator, "Could not get OS version: {}\n", strerror(errno));
return;
} }
return buffer + seprintf(buffer, last, fmt::format_to(output_iterator,
"Operating system:\n" "Operating system:\n"
" Name: %s\n" " Name: {}\n"
" Release: %s\n" " Release: {}\n"
" Version: %s\n" " Version: {}\n"
" Machine: %s\n", " Machine: {}\n",
name.sysname, name.sysname,
name.release, name.release,
name.version, name.version,
@ -58,12 +59,12 @@ class CrashLogUnix : public CrashLog {
); );
} }
char *LogError(char *buffer, const char *last, const char *message) const override void LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const override
{ {
return buffer + seprintf(buffer, last, fmt::format_to(output_iterator,
"Crash reason:\n" "Crash reason:\n"
" Signal: %s (%d)\n" " Signal: {} ({})\n"
" Message: %s\n\n", " Message: {}\n\n",
strsignal(this->signum), strsignal(this->signum),
this->signum, this->signum,
message message
@ -73,8 +74,7 @@ class CrashLogUnix : public CrashLog {
#if defined(SUNOS) #if defined(SUNOS)
/** Data needed while walking up the stack */ /** Data needed while walking up the stack */
struct StackWalkerParams { struct StackWalkerParams {
char **bufptr; ///< Buffer std::back_insert_iterator<std::string> *output_iterator; ///< Buffer
const char *last; ///< End of buffer
int counter; ///< We are at counter-th stack level int counter; ///< We are at counter-th stack level
}; };
@ -92,10 +92,10 @@ class CrashLogUnix : public CrashLog {
/* Resolve program counter to file and nearest symbol (if possible) */ /* Resolve program counter to file and nearest symbol (if possible) */
Dl_info dli; Dl_info dli;
if (dladdr((void *)pc, &dli) != 0) { if (dladdr((void *)pc, &dli) != 0) {
*wp->bufptr += seprintf(*wp->bufptr, wp->last, " [%02i] %s(%s+0x%x) [0x%x]\n", fmt::format_to(*wp->output_iterator, " [{:02}] {}({}+0x{:x}) [0x{:x}]\n",
wp->counter, dli.dli_fname, dli.dli_sname, (int)((byte *)pc - (byte *)dli.dli_saddr), (uint)pc); wp->counter, dli.dli_fname, dli.dli_sname, (int)((byte *)pc - (byte *)dli.dli_saddr), (uint)pc);
} else { } else {
*wp->bufptr += seprintf(*wp->bufptr, wp->last, " [%02i] [0x%x]\n", wp->counter, (uint)pc); fmt::format_to(*wp->output_iterator, " [{:02}] [0x{:x}]\n", wp->counter, (uint)pc);
} }
wp->counter++; wp->counter++;
@ -103,31 +103,31 @@ class CrashLogUnix : public CrashLog {
} }
#endif #endif
char *LogStacktrace(char *buffer, const char *last) const override void LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const override
{ {
buffer += seprintf(buffer, last, "Stacktrace:\n"); fmt::format_to(output_iterator, "Stacktrace:\n");
#if defined(__GLIBC__) #if defined(__GLIBC__)
void *trace[64]; void *trace[64];
int trace_size = backtrace(trace, lengthof(trace)); int trace_size = backtrace(trace, lengthof(trace));
char **messages = backtrace_symbols(trace, trace_size); char **messages = backtrace_symbols(trace, trace_size);
for (int i = 0; i < trace_size; i++) { for (int i = 0; i < trace_size; i++) {
buffer += seprintf(buffer, last, " [%02i] %s\n", i, messages[i]); fmt::format_to(output_iterator, " [{:02}] {}\n", i, messages[i]);
} }
free(messages); free(messages);
#elif defined(SUNOS) #elif defined(SUNOS)
ucontext_t uc; ucontext_t uc;
if (getcontext(&uc) != 0) { if (getcontext(&uc) != 0) {
buffer += seprintf(buffer, last, " getcontext() failed\n\n"); fmt::format_to(output_iterator, " getcontext() failed\n\n");
return buffer; return buffer;
} }
StackWalkerParams wp = { &buffer, last, 0 }; StackWalkerParams wp = { &output_iterator, 0 };
walkcontext(&uc, &CrashLogUnix::SunOSStackWalker, &wp); walkcontext(&uc, &CrashLogUnix::SunOSStackWalker, &wp);
#else #else
buffer += seprintf(buffer, last, " Not supported.\n"); fmt::format_to(output_iterator, " Not supported.\n");
#endif #endif
return buffer + seprintf(buffer, last, "\n"); fmt::format_to(output_iterator, "\n");
} }
public: public:
/** /**

View File

@ -25,13 +25,6 @@
#include "../../safeguards.h" #include "../../safeguards.h"
/* printf format specification for 32/64-bit addresses. */
#ifdef _M_AMD64
#define PRINTF_PTR "0x%016IX"
#else
#define PRINTF_PTR "0x%08X"
#endif
/** /**
* Windows implementation for the crash logger. * Windows implementation for the crash logger.
*/ */
@ -39,21 +32,21 @@ class CrashLogWindows : public CrashLog {
/** Information about the encountered exception */ /** Information about the encountered exception */
EXCEPTION_POINTERS *ep; EXCEPTION_POINTERS *ep;
char *LogOSVersion(char *buffer, const char *last) const override; void LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const override;
char *LogError(char *buffer, const char *last, const char *message) const override; void LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const override;
char *LogStacktrace(char *buffer, const char *last) const override; void LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const override;
char *LogRegisters(char *buffer, const char *last) const override; void LogRegisters(std::back_insert_iterator<std::string> &output_iterator) const override;
char *LogModules(char *buffer, const char *last) const override; void LogModules(std::back_insert_iterator<std::string> &output_iterator) const override;
public: public:
#if defined(_MSC_VER) #if defined(_MSC_VER)
int WriteCrashDump(char *filename, const char *filename_last) const override; int WriteCrashDump(char *filename, const char *filename_last) const override;
char *AppendDecodedStacktrace(char *buffer, const char *last) const; void AppendDecodedStacktrace(std::back_insert_iterator<std::string> &output_iterator) const;
#else #else
char *AppendDecodedStacktrace(char *buffer, const char *last) const { return buffer; } void AppendDecodedStacktrace(std::back_insert_iterator<std::string> &output_iterator) const {}
#endif /* _MSC_VER */ #endif /* _MSC_VER */
/** Buffer for the generated crash log */ /** Buffer for the generated crash log */
char crashlog[65536]; std::string crashlog;
/** Buffer for the filename of the crash log */ /** Buffer for the filename of the crash log */
char crashlog_filename[MAX_PATH]; char crashlog_filename[MAX_PATH];
/** Buffer for the filename of the crash dump */ /** Buffer for the filename of the crash dump */
@ -68,7 +61,7 @@ public:
CrashLogWindows(EXCEPTION_POINTERS *ep = nullptr) : CrashLogWindows(EXCEPTION_POINTERS *ep = nullptr) :
ep(ep) ep(ep)
{ {
this->crashlog[0] = '\0'; this->crashlog.reserve(65536);
this->crashlog_filename[0] = '\0'; this->crashlog_filename[0] = '\0';
this->crashdump_filename[0] = '\0'; this->crashdump_filename[0] = '\0';
this->screenshot_filename[0] = '\0'; this->screenshot_filename[0] = '\0';
@ -82,36 +75,32 @@ public:
/* static */ CrashLogWindows *CrashLogWindows::current = nullptr; /* static */ CrashLogWindows *CrashLogWindows::current = nullptr;
/* virtual */ char *CrashLogWindows::LogOSVersion(char *buffer, const char *last) const /* virtual */ void CrashLogWindows::LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const
{ {
_OSVERSIONINFOA os; _OSVERSIONINFOA os;
os.dwOSVersionInfoSize = sizeof(os); os.dwOSVersionInfoSize = sizeof(os);
GetVersionExA(&os); GetVersionExA(&os);
return buffer + seprintf(buffer, last, fmt::format_to(output_iterator,
"Operating system:\n" "Operating system:\n"
" Name: Windows\n" " Name: Windows\n"
" Release: %d.%d.%d (%s)\n", " Release: {}.{}.{} ({})\n",
(int)os.dwMajorVersion, os.dwMajorVersion,
(int)os.dwMinorVersion, os.dwMinorVersion,
(int)os.dwBuildNumber, os.dwBuildNumber,
os.szCSDVersion os.szCSDVersion
); );
} }
/* virtual */ char *CrashLogWindows::LogError(char *buffer, const char *last, const char *message) const /* virtual */ void CrashLogWindows::LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const
{ {
return buffer + seprintf(buffer, last, fmt::format_to(output_iterator,
"Crash reason:\n" "Crash reason:\n"
" Exception: %.8X\n" " Exception: {:08X}\n"
#ifdef _M_AMD64 " Location: {:X}\n"
" Location: %.16IX\n" " Message: {}\n\n",
#else ep->ExceptionRecord->ExceptionCode,
" Location: %.8X\n"
#endif
" Message: %s\n\n",
(int)ep->ExceptionRecord->ExceptionCode,
(size_t)ep->ExceptionRecord->ExceptionAddress, (size_t)ep->ExceptionRecord->ExceptionAddress,
message message
); );
@ -179,16 +168,16 @@ static void GetFileInfo(DebugFileInfo *dfi, const wchar_t *filename)
} }
static char *PrintModuleInfo(char *output, const char *last, HMODULE mod) static void PrintModuleInfo(std::back_insert_iterator<std::string> &output_iterator, HMODULE mod)
{ {
wchar_t buffer[MAX_PATH]; wchar_t buffer[MAX_PATH];
DebugFileInfo dfi; DebugFileInfo dfi;
GetModuleFileName(mod, buffer, MAX_PATH); GetModuleFileName(mod, buffer, MAX_PATH);
GetFileInfo(&dfi, buffer); GetFileInfo(&dfi, buffer);
output += seprintf(output, last, " %-20s handle: %p size: %d crc: %.8X date: %d-%.2d-%.2d %.2d:%.2d:%.2d\n", fmt::format_to(output_iterator, " {:20s} handle: {:X} size: {} crc: {:8X} date: {}-{:02}-{:02} {:02}:{:02}:{:02}\n",
FS2OTTD(buffer).c_str(), FS2OTTD(buffer),
mod, (size_t)mod,
dfi.size, dfi.size,
dfi.crc32, dfi.crc32,
dfi.file_time.wYear, dfi.file_time.wYear,
@ -198,14 +187,13 @@ static char *PrintModuleInfo(char *output, const char *last, HMODULE mod)
dfi.file_time.wMinute, dfi.file_time.wMinute,
dfi.file_time.wSecond dfi.file_time.wSecond
); );
return output;
} }
/* virtual */ char *CrashLogWindows::LogModules(char *output, const char *last) const /* virtual */ void CrashLogWindows::LogModules(std::back_insert_iterator<std::string> &output_iterator) const
{ {
MakeCRCTable(); MakeCRCTable();
output += seprintf(output, last, "Module information:\n"); fmt::format_to(output_iterator, "Module information:\n");
HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
if (proc != nullptr) { if (proc != nullptr) {
@ -216,24 +204,25 @@ static char *PrintModuleInfo(char *output, const char *last, HMODULE mod)
if (res) { if (res) {
size_t count = std::min<DWORD>(needed / sizeof(HMODULE), lengthof(modules)); size_t count = std::min<DWORD>(needed / sizeof(HMODULE), lengthof(modules));
for (size_t i = 0; i != count; i++) output = PrintModuleInfo(output, last, modules[i]); for (size_t i = 0; i != count; i++) PrintModuleInfo(output_iterator, modules[i]);
return output + seprintf(output, last, "\n"); fmt::format_to(output_iterator, "\n");
return;
} }
} }
output = PrintModuleInfo(output, last, nullptr); PrintModuleInfo(output_iterator, nullptr);
return output + seprintf(output, last, "\n"); fmt::format_to(output_iterator, "\n");
} }
/* virtual */ char *CrashLogWindows::LogRegisters(char *buffer, const char *last) const /* virtual */ void CrashLogWindows::LogRegisters(std::back_insert_iterator<std::string> &output_iterator) const
{ {
buffer += seprintf(buffer, last, "Registers:\n"); fmt::format_to(output_iterator, "Registers:\n");
#ifdef _M_AMD64 #ifdef _M_AMD64
buffer += seprintf(buffer, last, fmt::format_to(output_iterator,
" RAX: %.16I64X RBX: %.16I64X RCX: %.16I64X RDX: %.16I64X\n" " RAX: {:016X} RBX: {:016X} RCX: {:016X} RDX: {:016X}\n"
" RSI: %.16I64X RDI: %.16I64X RBP: %.16I64X RSP: %.16I64X\n" " RSI: {:016X} RDI: {:016X} RBP: {:016X} RSP: {:016X}\n"
" R8: %.16I64X R9: %.16I64X R10: %.16I64X R11: %.16I64X\n" " R8: {:016X} R9: {:016X} R10: {:016X} R11: {:016X}\n"
" R12: %.16I64X R13: %.16I64X R14: %.16I64X R15: %.16I64X\n" " R12: {:016X} R13: {:016X} R14: {:016X} R15: {:016X}\n"
" RIP: %.16I64X EFLAGS: %.8lX\n", " RIP: {:016X} EFLAGS: {:08X}\n",
ep->ContextRecord->Rax, ep->ContextRecord->Rax,
ep->ContextRecord->Rbx, ep->ContextRecord->Rbx,
ep->ContextRecord->Rcx, ep->ContextRecord->Rcx,
@ -254,10 +243,10 @@ static char *PrintModuleInfo(char *output, const char *last, HMODULE mod)
ep->ContextRecord->EFlags ep->ContextRecord->EFlags
); );
#elif defined(_M_IX86) #elif defined(_M_IX86)
buffer += seprintf(buffer, last, fmt::format_to(output_iterator,
" EAX: %.8X EBX: %.8X ECX: %.8X EDX: %.8X\n" " EAX: {:08X} EBX: {:08X} ECX: {:08X} EDX: {:08X}\n"
" ESI: %.8X EDI: %.8X EBP: %.8X ESP: %.8X\n" " ESI: {:08X} EDI: {:08X} EBP: {:08X} ESP: {:08X}\n"
" EIP: %.8X EFLAGS: %.8X\n", " EIP: {:08X} EFLAGS: {:08X}\n",
(int)ep->ContextRecord->Eax, (int)ep->ContextRecord->Eax,
(int)ep->ContextRecord->Ebx, (int)ep->ContextRecord->Ebx,
(int)ep->ContextRecord->Ecx, (int)ep->ContextRecord->Ecx,
@ -270,15 +259,15 @@ static char *PrintModuleInfo(char *output, const char *last, HMODULE mod)
(int)ep->ContextRecord->EFlags (int)ep->ContextRecord->EFlags
); );
#elif defined(_M_ARM64) #elif defined(_M_ARM64)
buffer += seprintf(buffer, last, fmt::format_to(output_iterator,
" X0: %.16I64X X1: %.16I64X X2: %.16I64X X3: %.16I64X\n" " X0: {:016X} X1: {:016X} X2: {:016X} X3: {:016X}\n"
" X4: %.16I64X X5: %.16I64X X6: %.16I64X X7: %.16I64X\n" " X4: {:016X} X5: {:016X} X6: {:016X} X7: {:016X}\n"
" X8: %.16I64X X9: %.16I64X X10: %.16I64X X11: %.16I64X\n" " X8: {:016X} X9: {:016X} X10: {:016X} X11: {:016X}\n"
" X12: %.16I64X X13: %.16I64X X14: %.16I64X X15: %.16I64X\n" " X12: {:016X} X13: {:016X} X14: {:016X} X15: {:016X}\n"
" X16: %.16I64X X17: %.16I64X X18: %.16I64X X19: %.16I64X\n" " X16: {:016X} X17: {:016X} X18: {:016X} X19: {:016X}\n"
" X20: %.16I64X X21: %.16I64X X22: %.16I64X X23: %.16I64X\n" " X20: {:016X} X21: {:016X} X22: {:016X} X23: {:016X}\n"
" X24: %.16I64X X25: %.16I64X X26: %.16I64X X27: %.16I64X\n" " X24: {:016X} X25: {:016X} X26: {:016X} X27: {:016X}\n"
" X28: %.16I64X Fp: %.16I64X Lr: %.16I64X\n", " X28: {:016X} Fp: {:016X} Lr: {:016X}\n",
ep->ContextRecord->X0, ep->ContextRecord->X0,
ep->ContextRecord->X1, ep->ContextRecord->X1,
ep->ContextRecord->X2, ep->ContextRecord->X2,
@ -313,7 +302,7 @@ static char *PrintModuleInfo(char *output, const char *last, HMODULE mod)
); );
#endif #endif
buffer += seprintf(buffer, last, "\n Bytes at instruction pointer:\n"); fmt::format_to(output_iterator, "\n Bytes at instruction pointer:\n");
#ifdef _M_AMD64 #ifdef _M_AMD64
byte *b = (byte*)ep->ContextRecord->Rip; byte *b = (byte*)ep->ContextRecord->Rip;
#elif defined(_M_IX86) #elif defined(_M_IX86)
@ -323,18 +312,18 @@ static char *PrintModuleInfo(char *output, const char *last, HMODULE mod)
#endif #endif
for (int i = 0; i != 24; i++) { for (int i = 0; i != 24; i++) {
if (IsBadReadPtr(b, 1)) { if (IsBadReadPtr(b, 1)) {
buffer += seprintf(buffer, last, " ??"); // OCR: WAS: , 0); fmt::format_to(output_iterator, " ??"); // OCR: WAS: , 0);
} else { } else {
buffer += seprintf(buffer, last, " %.2X", *b); fmt::format_to(output_iterator, " {:02X}", *b);
} }
b++; b++;
} }
return buffer + seprintf(buffer, last, "\n\n"); fmt::format_to(output_iterator, "\n\n");
} }
/* virtual */ char *CrashLogWindows::LogStacktrace(char *buffer, const char *last) const /* virtual */ void CrashLogWindows::LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const
{ {
buffer += seprintf(buffer, last, "Stack trace:\n"); fmt::format_to(output_iterator, "Stack trace:\n");
#ifdef _M_AMD64 #ifdef _M_AMD64
uint32 *b = (uint32*)ep->ContextRecord->Rsp; uint32 *b = (uint32*)ep->ContextRecord->Rsp;
#elif defined(_M_IX86) #elif defined(_M_IX86)
@ -345,15 +334,15 @@ static char *PrintModuleInfo(char *output, const char *last, HMODULE mod)
for (int j = 0; j != 24; j++) { for (int j = 0; j != 24; j++) {
for (int i = 0; i != 8; i++) { for (int i = 0; i != 8; i++) {
if (IsBadReadPtr(b, sizeof(uint32))) { if (IsBadReadPtr(b, sizeof(uint32))) {
buffer += seprintf(buffer, last, " ????????"); // OCR: WAS - , 0); fmt::format_to(output_iterator, " ????????"); // OCR: WAS - , 0);
} else { } else {
buffer += seprintf(buffer, last, " %.8X", *b); fmt::format_to(output_iterator, " {:08X}", *b);
} }
b++; b++;
} }
buffer += seprintf(buffer, last, "\n"); fmt::format_to(output_iterator, "\n");
} }
return buffer + seprintf(buffer, last, "\n"); fmt::format_to(output_iterator, "\n");
} }
#if defined(_MSC_VER) #if defined(_MSC_VER)
@ -364,7 +353,7 @@ static const uint MAX_FRAMES = 64;
#include <dbghelp.h> #include <dbghelp.h>
#pragma warning(default:4091) #pragma warning(default:4091)
char *CrashLogWindows::AppendDecodedStacktrace(char *buffer, const char *last) const void CrashLogWindows::AppendDecodedStacktrace(std::back_insert_iterator<std::string> &output_iterator) const
{ {
DllLoader dbghelp(L"dbghelp.dll"); DllLoader dbghelp(L"dbghelp.dll");
struct ProcPtrs { struct ProcPtrs {
@ -389,7 +378,7 @@ char *CrashLogWindows::AppendDecodedStacktrace(char *buffer, const char *last) c
dbghelp.GetProcAddress("SymGetLineFromAddr64"), dbghelp.GetProcAddress("SymGetLineFromAddr64"),
}; };
buffer += seprintf(buffer, last, "\nDecoded stack trace:\n"); fmt::format_to(output_iterator, "\nDecoded stack trace:\n");
/* Try to load the functions from the DLL, if that fails because of a too old dbghelp.dll, just skip it. */ /* Try to load the functions from the DLL, if that fails because of a too old dbghelp.dll, just skip it. */
if (dbghelp.Success()) { if (dbghelp.Success()) {
@ -440,7 +429,7 @@ char *CrashLogWindows::AppendDecodedStacktrace(char *buffer, const char *last) c
hCur, GetCurrentThread(), &frame, &ctx, nullptr, proc.pSymFunctionTableAccess64, proc.pSymGetModuleBase64, nullptr)) break; hCur, GetCurrentThread(), &frame, &ctx, nullptr, proc.pSymFunctionTableAccess64, proc.pSymGetModuleBase64, nullptr)) break;
if (frame.AddrPC.Offset == frame.AddrReturn.Offset) { if (frame.AddrPC.Offset == frame.AddrReturn.Offset) {
buffer += seprintf(buffer, last, " <infinite loop>\n"); fmt::format_to(output_iterator, " <infinite loop>\n");
break; break;
} }
@ -454,27 +443,27 @@ char *CrashLogWindows::AppendDecodedStacktrace(char *buffer, const char *last) c
} }
/* Print module and instruction pointer. */ /* Print module and instruction pointer. */
buffer += seprintf(buffer, last, "[%02d] %-20s " PRINTF_PTR, num, mod_name, frame.AddrPC.Offset); fmt::format_to(output_iterator, "[{:02}] {:20s} {:X}", num, mod_name, frame.AddrPC.Offset);
/* Get symbol name and line info if possible. */ /* Get symbol name and line info if possible. */
DWORD64 offset; DWORD64 offset;
if (proc.pSymGetSymFromAddr64(hCur, frame.AddrPC.Offset, &offset, sym_info)) { if (proc.pSymGetSymFromAddr64(hCur, frame.AddrPC.Offset, &offset, sym_info)) {
buffer += seprintf(buffer, last, " %s + %I64u", sym_info->Name, offset); fmt::format_to(output_iterator, " {} + {}", sym_info->Name, offset);
DWORD line_offs; DWORD line_offs;
IMAGEHLP_LINE64 line; IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (proc.pSymGetLineFromAddr64(hCur, frame.AddrPC.Offset, &line_offs, &line)) { if (proc.pSymGetLineFromAddr64(hCur, frame.AddrPC.Offset, &line_offs, &line)) {
buffer += seprintf(buffer, last, " (%s:%d)", line.FileName, line.LineNumber); fmt::format_to(output_iterator, " ({}:{})", line.FileName, line.LineNumber);
} }
} }
buffer += seprintf(buffer, last, "\n"); fmt::format_to(output_iterator, "\n");
} }
proc.pSymCleanup(hCur); proc.pSymCleanup(hCur);
} }
return buffer + seprintf(buffer, last, "\n*** End of additional info ***\n"); fmt::format_to(output_iterator, "\n*** End of additional info ***\n");
} }
/* virtual */ int CrashLogWindows::WriteCrashDump(char *filename, const char *filename_last) const /* virtual */ int CrashLogWindows::WriteCrashDump(char *filename, const char *filename_last) const
@ -498,8 +487,8 @@ char *CrashLogWindows::AppendDecodedStacktrace(char *buffer, const char *last) c
MINIDUMP_USER_STREAM_INFORMATION musi; MINIDUMP_USER_STREAM_INFORMATION musi;
userstream.Type = LastReservedStream + 1; userstream.Type = LastReservedStream + 1;
userstream.Buffer = (void*)this->crashlog; userstream.Buffer = (void*)this->crashlog.data();
userstream.BufferSize = (ULONG)strlen(this->crashlog) + 1; userstream.BufferSize = (ULONG)this->crashlog.size() + 1;
musi.UserStreamCount = 1; musi.UserStreamCount = 1;
musi.UserStreamArray = &userstream; musi.UserStreamArray = &userstream;
@ -559,10 +548,11 @@ static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep)
CrashLogWindows *log = new CrashLogWindows(ep); CrashLogWindows *log = new CrashLogWindows(ep);
CrashLogWindows::current = log; CrashLogWindows::current = log;
char *buf = log->FillCrashLog(log->crashlog, lastof(log->crashlog)); auto output_iterator = std::back_inserter(log->crashlog);
log->FillCrashLog(output_iterator);
log->WriteCrashDump(log->crashdump_filename, lastof(log->crashdump_filename)); log->WriteCrashDump(log->crashdump_filename, lastof(log->crashdump_filename));
log->AppendDecodedStacktrace(buf, lastof(log->crashlog)); log->AppendDecodedStacktrace(output_iterator);
log->WriteCrashLog(log->crashlog, log->crashlog_filename, lastof(log->crashlog_filename)); log->WriteCrashLog(log->crashlog.c_str(), log->crashlog_filename, lastof(log->crashlog_filename));
log->WriteScreenshot(log->screenshot_filename, lastof(log->screenshot_filename)); log->WriteScreenshot(log->screenshot_filename, lastof(log->screenshot_filename));
log->SendSurvey(); log->SendSurvey();
@ -680,10 +670,10 @@ static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARA
/* We need to put the crash-log in a separate buffer because the default /* We need to put the crash-log in a separate buffer because the default
* buffer in MB_TO_WIDE is not large enough (512 chars) */ * buffer in MB_TO_WIDE is not large enough (512 chars) */
wchar_t filenamebuf[MAX_PATH * 2]; wchar_t filenamebuf[MAX_PATH * 2];
wchar_t crash_msgW[lengthof(CrashLogWindows::current->crashlog)]; wchar_t crash_msgW[65536];
/* Convert unix -> dos newlines because the edit box only supports that properly :( */ /* Convert unix -> dos newlines because the edit box only supports that properly :( */
const char *unix_nl = CrashLogWindows::current->crashlog; const char *unix_nl = CrashLogWindows::current->crashlog.data();
char dos_nl[lengthof(CrashLogWindows::current->crashlog)]; char dos_nl[65536];
char *p = dos_nl; char *p = dos_nl;
WChar c; WChar c;
while ((c = Utf8Consume(&unix_nl)) && p < lastof(dos_nl) - 4) { // 4 is max number of bytes per character while ((c = Utf8Consume(&unix_nl)) && p < lastof(dos_nl) - 4) { // 4 is max number of bytes per character