Changeset - r27432:005dbba2ae93
[Not reviewed]
master
0 5 0
Rubidium - 13 months ago 2023-05-20 17:45:38
rubidium@openttd.org
Codechange: replace seprintf with fmt::format for filling the crash log data
5 files changed with 234 insertions and 315 deletions:
0 comments (0 inline, 0 general)
src/crashlog.cpp
Show inline comments
 
@@ -67,76 +67,72 @@
 
#endif /* WITH_SDL || WITH_SDL2 */
 
#ifdef WITH_ZLIB
 
# include <zlib.h>
 
#endif
 
#ifdef WITH_CURL
 
# include <curl/curl.h>
 
#endif
 

	
 
#include "safeguards.h"
 

	
 
/* 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)
 
			"MSVC %d", _MSC_VER
 
			"MSVC {}", _MSC_VER
 
#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)
 
			"ICC %d", __ICC
 
			"ICC {}", __ICC
 
#elif defined(__GNUC__)
 
			"GCC %d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__
 
			"GCC {}.{}.{}", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__
 
#elif defined(__WATCOMC__)
 
			"WatcomC %d", __WATCOMC__
 
			"WatcomC {}", __WATCOMC__
 
#else
 
			"<unknown>"
 
#endif
 
			);
 
#if defined(__VERSION__)
 
			return buffer + seprintf(buffer, last,  " \"" __VERSION__ "\"\n\n");
 
	fmt::format_to(output_iterator, " \"" __VERSION__ "\"\n\n");
 
#else
 
			return buffer + seprintf(buffer, last,  "\n\n");
 
	fmt::format_to(output_iterator, "\n\n");
 
#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. */
 
	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. */
 
	return buffer;
 
}
 

	
 
/**
 
 * Writes OpenTTD's version to the buffer.
 
 * @param buffer The begin where to write at.
 
 * @param last   The last position in the buffer to write to.
 
 * @return the position of the \c '\0' character after the buffer.
 
 * @param output_iterator Iterator to write the output to.
 
 */
 
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"
 
			" Version:    %s (%d)\n"
 
			" NewGRF ver: %08x\n"
 
			" Bits:       %d\n"
 
			" Endian:     %s\n"
 
			" Dedicated:  %s\n"
 
			" Build date: %s\n\n",
 
			" Version:    {} ({})\n"
 
			" NewGRF ver: {:08x}\n"
 
			" Bits:       {}\n"
 
			" Endian:     {}\n"
 
			" Dedicated:  {}\n"
 
			" Build date: {}\n\n",
 
			_openttd_revision,
 
			_openttd_revision_modified,
 
			_openttd_newgrf_version,
 
#ifdef POINTER_IS_64BIT
 
			64,
 
#else
 
			32,
 
#endif
 
#if (TTD_ENDIAN == TTD_LITTLE_ENDIAN)
 
			"little",
 
#else
 
			"big",
 
@@ -144,248 +140,232 @@ char *CrashLog::LogOpenTTDVersion(char *
 
#ifdef DEDICATED
 
			"yes",
 
#else
 
			"no",
 
#endif
 
			_openttd_build_date
 
	);
 
}
 

	
 
/**
 
 * Writes the (important) configuration settings to the buffer.
 
 * E.g. graphics set, sound set, blitter and AIs.
 
 * @param buffer The begin where to write at.
 
 * @param last   The last position in the buffer to write to.
 
 * @return the position of the \c '\0' character after the buffer.
 
 * @param output_iterator Iterator to write the output to.
 
 */
 
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"
 
			" Blitter:      %s\n"
 
			" Graphics set: %s (%u)\n"
 
			" Language:     %s\n"
 
			" Music driver: %s\n"
 
			" Music set:    %s (%u)\n"
 
			" Network:      %s\n"
 
			" Sound driver: %s\n"
 
			" Sound set:    %s (%u)\n"
 
			" Video driver: %s\n\n",
 
			" Blitter:      {}\n"
 
			" Graphics set: {} ({})\n"
 
			" Language:     {}\n"
 
			" Music driver: {}\n"
 
			" Music set:    {} ({})\n"
 
			" Network:      {}\n"
 
			" Sound driver: {}\n"
 
			" Sound set:    {} ({})\n"
 
			" Video driver: {}\n\n",
 
			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,
 
			_current_language == nullptr ? "none" : _current_language->file,
 
			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,
 
			_networking ? (_network_server ? "server" : "client") : "no",
 
			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,
 
			VideoDriver::GetInstance() == nullptr ? "none" : VideoDriver::GetInstance()->GetInfoString()
 
	);
 

	
 
	buffer += seprintf(buffer, last,
 
	fmt::format_to(output_iterator,
 
			"Fonts:\n"
 
			" Small:  %s\n"
 
			" Medium: %s\n"
 
			" Large:  %s\n"
 
			" Mono:   %s\n\n",
 
			" Small:  {}\n"
 
			" Medium: {}\n"
 
			" Large:  {}\n"
 
			" Mono:   {}\n\n",
 
			FontCache::Get(FS_SMALL)->GetFontName(),
 
			FontCache::Get(FS_NORMAL)->GetFontName(),
 
			FontCache::Get(FS_LARGE)->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()) {
 
		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 {
 
			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) {
 
		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");
 

	
 
	return buffer;
 
	fmt::format_to(output_iterator, "\n");
 
}
 

	
 
/**
 
 * Writes information (versions) of the used libraries.
 
 * @param buffer The begin where to write at.
 
 * @param last   The last position in the buffer to write to.
 
 * @return the position of the \c '\0' character after the buffer.
 
 * @param output_iterator Iterator to write the output to.
 
 */
 
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
 
	buffer += seprintf(buffer, last, " Allegro:    %s\n", allegro_id);
 
	fmt::format_to(output_iterator, " Allegro:    {}\n", allegro_id);
 
#endif /* WITH_ALLEGRO */
 

	
 
#ifdef WITH_FONTCONFIG
 
	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 */
 

	
 
#ifdef WITH_FREETYPE
 
	FT_Library library;
 
	int major, minor, patch;
 
	FT_Init_FreeType(&library);
 
	FT_Library_Version(library, &major, &minor, &patch);
 
	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 */
 

	
 
#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 */
 

	
 
#if defined(WITH_ICU_I18N)
 
	/* 4 times 0-255, separated by dots (.) and a trailing '\0' */
 
	char buf[4 * 3 + 3 + 1];
 
	UVersionInfo ver;
 
	u_getVersion(ver);
 
	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 */
 

	
 
#ifdef WITH_LIBLZMA
 
	buffer += seprintf(buffer, last, " LZMA:       %s\n", lzma_version_string());
 
	fmt::format_to(output_iterator, " LZMA:       {}\n", lzma_version_string());
 
#endif
 

	
 
#ifdef WITH_LZO
 
	buffer += seprintf(buffer, last, " LZO:        %s\n", lzo_version_string());
 
	fmt::format_to(output_iterator, " LZO:        {}\n", lzo_version_string());
 
#endif
 

	
 
#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 */
 

	
 
#ifdef WITH_SDL
 
	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)
 
	SDL_version 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
 

	
 
#ifdef WITH_ZLIB
 
	buffer += seprintf(buffer, last, " Zlib:       %s\n", zlibVersion());
 
	fmt::format_to(output_iterator, " Zlib:       {}\n", zlibVersion());
 
#endif
 

	
 
#ifdef WITH_CURL
 
	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) {
 
		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 {
 
		buffer += seprintf(buffer, last, " Curl SSL:   none\n");
 
		fmt::format_to(output_iterator, " Curl SSL:   none\n");
 
	}
 
#endif
 

	
 
	buffer += seprintf(buffer, last, "\n");
 
	return buffer;
 
	fmt::format_to(output_iterator, "\n");
 
}
 

	
 
/**
 
 * Writes the gamelog data to the buffer.
 
 * @param buffer The begin where to write at.
 
 * @param last   The last position in the buffer to write to.
 
 * @return the position of the \c '\0' character after the buffer.
 
 * @param output_iterator Iterator to write the output to.
 
 */
 
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) {
 
		buffer += seprintf(buffer, last, "%s\n", s.c_str());
 
	_gamelog.Print([&output_iterator](const std::string &s) {
 
		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.
 
 * @param buffer The begin where to write at.
 
 * @param last   The last position in the buffer to write to.
 
 * @return the position of the \c '\0' character after the buffer.
 
 * @param output_iterator Iterator to write the output to.
 
 */
 
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;
 
	for (NewsItem *news = _latest_news; i < 32 && news != nullptr; news = news->prev, i++) {
 
		TimerGameCalendar::YearMonthDay 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,
 
		                   news->reftype1, news->ref1, news->reftype2, news->ref2);
 
	}
 
	buffer += seprintf(buffer, last, "\n");
 
	return buffer;
 
	fmt::format_to(output_iterator, "\n");
 
}
 

	
 
/**
 
 * Create a timestamped filename.
 
 * @param filename      The begin where to write at.
 
 * @param filename_last The last position in the buffer to write to.
 
 * @param ext           The extension for the filename.
 
 * @param with_dir      Whether to prepend the filename with the personal directory.
 
 * @return the number of added characters.
 
 */
 
int CrashLog::CreateFileName(char *filename, const char *filename_last, const char *ext, bool with_dir) const
 
{
 
	static std::string crashname;
 

	
 
	if (crashname.empty()) {
 
		crashname = fmt::format("crash{:%Y%m%d%H%M%S}", fmt::gmtime(time(nullptr)));
 
	}
 
	return seprintf(filename, filename_last, "%s%s%s", with_dir ? _personal_dir.c_str() : "", crashname.c_str(), ext);
 
}
 

	
 
/**
 
 * Fill the crash log buffer with all data of a crash log.
 
 * @param buffer The begin where to write at.
 
 * @param last   The last position in the buffer to write to.
 
 * @return the position of the \c '\0' character after the buffer.
 
 * @param output_iterator Iterator to write the output to.
 
 */
 
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");
 
	std::string temp = fmt::format("Crash at: {:%Y-%m-%d %H:%M:%S} (UTC)\n", fmt::gmtime(time(nullptr)));
 
	buffer = strecpy(buffer, temp.c_str(), last);
 
	fmt::format_to(output_iterator, "*** OpenTTD Crash Report ***\n\n");
 
	fmt::format_to(output_iterator, "Crash at: {:%Y-%m-%d %H:%M:%S} (UTC)\n", fmt::gmtime(time(nullptr)));
 

	
 
	TimerGameCalendar::YearMonthDay 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());
 
	buffer = this->LogOpenTTDVersion(buffer, last);
 
	buffer = this->LogRegisters(buffer, last);
 
	buffer = this->LogStacktrace(buffer, last);
 
	buffer = this->LogOSVersion(buffer, last);
 
	buffer = this->LogCompiler(buffer, last);
 
	buffer = this->LogConfiguration(buffer, last);
 
	buffer = this->LogLibraries(buffer, last);
 
	buffer = this->LogModules(buffer, last);
 
	buffer = this->LogGamelog(buffer, last);
 
	buffer = this->LogRecentNews(buffer, last);
 
	this->LogError(output_iterator, CrashLog::message);
 
	this->LogOpenTTDVersion(output_iterator);
 
	this->LogRegisters(output_iterator);
 
	this->LogStacktrace(output_iterator);
 
	this->LogOSVersion(output_iterator);
 
	this->LogCompiler(output_iterator);
 
	this->LogConfiguration(output_iterator);
 
	this->LogLibraries(output_iterator);
 
	this->LogModules(output_iterator);
 
	this->LogGamelog(output_iterator);
 
	this->LogRecentNews(output_iterator);
 

	
 
	buffer += seprintf(buffer, last, "*** End of OpenTTD Crash Report ***\n");
 
	return buffer;
 
	fmt::format_to(output_iterator, "*** End of OpenTTD Crash Report ***\n");
 
}
 

	
 
/**
 
 * Write the crash log to a file.
 
 * @note On success the filename will be filled with the full path of the
 
 *       crash log file. Make sure filename is at least \c MAX_PATH big.
 
 * @param buffer The begin of the buffer to write to the disk.
 
 * @param filename      Output for the filename of the written file.
 
 * @param filename_last The last position in the filename buffer.
 
 * @return true when the crash log was successfully written.
 
 */
 
bool CrashLog::WriteCrashLog(const char *buffer, char *filename, const char *filename_last) const
 
@@ -466,34 +446,36 @@ void CrashLog::SendSurvey() const
 
 * to make a crash dump and crash savegame. It uses DEBUG to write
 
 * information like paths to the console.
 
 * @return true when everything is made successfully.
 
 */
 
bool CrashLog::MakeCrashLog() const
 
{
 
	/* Don't keep looping logging crashes. */
 
	static bool crashlogged = false;
 
	if (crashlogged) return false;
 
	crashlogged = true;
 

	
 
	char filename[MAX_PATH];
 
	char buffer[65536];
 
	std::string buffer;
 
	buffer.reserve(65536);
 
	auto output_iterator = std::back_inserter(buffer);
 
	bool ret = true;
 

	
 
	fmt::print("Crash encountered, generating crash log...\n");
 
	this->FillCrashLog(buffer, lastof(buffer));
 
	this->FillCrashLog(output_iterator);
 
	fmt::print("{}\n", buffer);
 
	fmt::print("Crash log generated.\n\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) {
 
		fmt::print("Crash log written to {}. Please add this file to any bug reports.\n\n", filename);
 
	} else {
 
		fmt::print("Writing crash log failed. Please attach the output above to any bug reports.\n\n");
 
		ret = false;
 
	}
 

	
 
	/* Don't mention writing crash dumps because not all platforms support it. */
 
	int dret = this->WriteCrashDump(filename, lastof(filename));
 
	if (dret < 0) {
 
		fmt::print("Writing crash dump failed.\n\n");
 
		ret = false;
src/crashlog.h
Show inline comments
 
@@ -11,88 +11,76 @@
 
#define CRASHLOG_H
 

	
 
/**
 
 * Helper class for creating crash logs.
 
 */
 
class CrashLog {
 
private:
 
	/** Error message coming from #FatalError(format, ...). */
 
	static std::string message;
 
protected:
 
	/**
 
	 * Writes OS' version to the buffer.
 
	 * @param buffer The begin where to write at.
 
	 * @param last   The last position in the buffer to write to.
 
	 * @return the position of the \c '\0' character after the buffer.
 
	 * @param output_iterator Iterator to write the output to.
 
	 */
 
	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.
 
	 * @param buffer The begin where to write at.
 
	 * @param last   The last position in the buffer to write to.
 
	 * @return the position of the \c '\0' character after the buffer.
 
	 * @param output_iterator Iterator to write the output to.
 
	 */
 
	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.
 
	 * @param buffer  The begin where to write at.
 
	 * @param last    The last position in the buffer to write to.
 
	 * @param output_iterator Iterator to write the output to.
 
	 * @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
 
	 * available.
 
	 * @param buffer The begin where to write at.
 
	 * @param last   The last position in the buffer to write to.
 
	 * @return the position of the \c '\0' character after the buffer.
 
	 * @param output_iterator Iterator to write the output to.
 
	 */
 
	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
 
	 * information about it available.
 
	 * @param buffer The begin where to write at.
 
	 * @param last   The last position in the buffer to write to.
 
	 * @return the position of the \c '\0' character after the buffer.
 
	 * @param output_iterator Iterator to write the output to.
 
	 */
 
	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
 
	 * is information about it available.
 
	 * @param buffer The begin where to write at.
 
	 * @param last   The last position in the buffer to write to.
 
	 * @return the position of the \c '\0' character after the buffer.
 
	 * @param output_iterator Iterator to write the output to.
 
	 */
 
	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;
 
	char *LogConfiguration(char *buffer, const char *last) const;
 
	char *LogLibraries(char *buffer, const char *last) const;
 
	char *LogGamelog(char *buffer, const char *last) const;
 
	char *LogRecentNews(char *buffer, const char *list) const;
 
	void LogOpenTTDVersion(std::back_insert_iterator<std::string> &output_iterator) const;
 
	void LogConfiguration(std::back_insert_iterator<std::string> &output_iterator) const;
 
	void LogLibraries(std::back_insert_iterator<std::string> &output_iterator) const;
 
	void LogGamelog(std::back_insert_iterator<std::string> &output_iterator) 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;
 

	
 
public:
 
	/** Stub destructor to silence some compilers. */
 
	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;
 

	
 
	/**
 
	 * Write the (crash) dump to a file.
 
	 * @note On success the filename will be filled with the full path of the
 
	 *       crash dump file. Make sure filename is at least \c MAX_PATH big.
 
	 * @param filename      Output for the filename of the written file.
 
	 * @param filename_last The last position in the filename buffer.
 
	 * @return if less than 0, error. If 0 no dump is made, otherwise the dump
 
	 *         was successful (not all OSes support dumping files).
 
	 */
 
	virtual int WriteCrashDump(char *filename, const char *filename_last) const;
src/os/macosx/crashlog_osx.cpp
Show inline comments
 
@@ -22,190 +22,149 @@
 
#include <cxxabi.h>
 

	
 
#include "../../safeguards.h"
 

	
 

	
 
/* Macro testing a stack address for valid alignment. */
 
#if defined(__i386__)
 
#define IS_ALIGNED(addr) (((uintptr_t)(addr) & 0xf) == 8)
 
#else
 
#define IS_ALIGNED(addr) (((uintptr_t)(addr) & 0xf) == 0)
 
#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
 

	
 
/**
 
 * OSX implementation for the crash logger.
 
 */
 
class CrashLogOSX : public CrashLog {
 
	/** Signal that has been thrown. */
 
	int signum;
 

	
 
	char filename_log[MAX_PATH];        ///< Path of crash.log
 
	char filename_save[MAX_PATH];       ///< Path of crash.sav
 
	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;
 
		GetMacOSVersion(&ver_maj, &ver_min, &ver_bug);
 

	
 
		const NXArchInfo *arch = NXGetLocalArchInfo();
 

	
 
		return buffer + seprintf(buffer, last,
 
		fmt::format_to(output_iterator,
 
				"Operating system:\n"
 
				" Name:     Mac OS X\n"
 
				" Release:  %d.%d.%d\n"
 
				" Machine:  %s\n"
 
				" Min Ver:  %d\n"
 
				" Max Ver:  %d\n",
 
				" Release:  {}.{}.{}\n"
 
				" Machine:  {}\n"
 
				" Min Ver:  {}\n"
 
				" Max Ver:  {}\n",
 
				ver_maj, ver_min, ver_bug,
 
				arch != nullptr ? arch->description : "unknown",
 
				MAC_OS_X_VERSION_MIN_REQUIRED,
 
				MAC_OS_X_VERSION_MAX_ALLOWED
 
		);
 
	}
 

	
 
	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"
 
				" Signal:  %s (%d)\n"
 
				" Message: %s\n\n",
 
				" Signal:  {} ({})\n"
 
				" Message: {}\n\n",
 
				strsignal(this->signum),
 
				this->signum,
 
				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,
 
		 * we're rolling our own here. Mostly based on
 
		 * http://stackoverflow.com/questions/289820/getting-the-current-stack-trace-on-mac-os-x
 
		 * and some details looked up in the Darwin sources. */
 
		buffer += seprintf(buffer, last, "\nStacktrace:\n");
 
		fmt::format_to(output_iterator, "\nStacktrace:\n");
 

	
 
		void **frame;
 
#if defined(__ppc__) || defined(__ppc64__)
 
		/* Apple says __builtin_frame_address can be broken on PPC. */
 
		__asm__ volatile("mr %0, r1" : "=r" (frame));
 
#else
 
		frame = (void **)__builtin_frame_address(0);
 
#endif
 

	
 
		for (int i = 0; frame != nullptr && i < MAX_STACK_FRAMES; i++) {
 
			/* Get IP for current stack frame. */
 
#if defined(__ppc__) || defined(__ppc64__)
 
			void *ip = frame[2];
 
#else
 
			void *ip = frame[1];
 
#endif
 
			if (ip == nullptr) break;
 

	
 
			/* Print running index. */
 
			buffer += seprintf(buffer, last, " [%02d]", i);
 
			fmt::format_to(output_iterator, " [{:02}]", i);
 

	
 
			Dl_info dli;
 
			bool dl_valid = dladdr(ip, &dli) != 0;
 

	
 
			const char *fname = "???";
 
			if (dl_valid && dli.dli_fname) {
 
				/* Valid image name? Extract filename from the complete path. */
 
				const char *s = strrchr(dli.dli_fname, '/');
 
				if (s != nullptr) {
 
					fname = s + 1;
 
				} else {
 
					fname = dli.dli_fname;
 
				}
 
			}
 
			/* 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. */
 
			if (dl_valid && dli.dli_sname != nullptr && dli.dli_saddr != nullptr) {
 
				/* Try to demangle a possible C++ symbol. */
 
				int status = -1;
 
				char *func_name = abi::__cxa_demangle(dli.dli_sname, nullptr, 0, &status);
 

	
 
				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);
 
			}
 
			buffer += seprintf(buffer, last, "\n");
 
			fmt::format_to(output_iterator, "\n");
 

	
 
			/* Get address of next stack frame. */
 
			void **next = (void **)frame[0];
 
			/* Frame address not increasing or not aligned? Broken stack, exit! */
 
			if (next <= frame || !IS_ALIGNED(next)) break;
 
			frame = next;
 
		}
 

	
 
		return buffer + seprintf(buffer, last, "\n");
 
		fmt::format_to(output_iterator, "\n");
 
	}
 

	
 
public:
 
	/**
 
	 * A crash log is always generated by signal.
 
	 * @param signum the signal that was caused by the crash.
 
	 */
 
	CrashLogOSX(int signum) : signum(signum)
 
	{
 
		filename_log[0] = '\0';
 
		filename_save[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. */
 
	void DisplayCrashDialog() const
 
	{
 
		static const char crash_title[] =
 
			"A serious fault condition occurred in the game. The game will shut down.";
 

	
 
		char message[1024];
 
		seprintf(message, lastof(message),
 
				 "Please send the generated crash information and the last (auto)save to the developers. "
 
				 "This will greatly help debugging. The correct place to do this is https://github.com/OpenTTD/OpenTTD/issues.\n\n"
 
				 "Generated file(s):\n%s\n%s\n%s",
 
				 this->filename_log, this->filename_save, this->filename_screenshot);
src/os/unix/crashlog_unix.cpp
Show inline comments
 
@@ -29,114 +29,114 @@
 
#include <unistd.h>
 
#endif
 

	
 
#include "../../safeguards.h"
 

	
 
/**
 
 * Unix implementation for the crash logger.
 
 */
 
class CrashLogUnix : public CrashLog {
 
	/** Signal that has been thrown. */
 
	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;
 
		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"
 
				" Name:     %s\n"
 
				" Release:  %s\n"
 
				" Version:  %s\n"
 
				" Machine:  %s\n",
 
				" Name:     {}\n"
 
				" Release:  {}\n"
 
				" Version:  {}\n"
 
				" Machine:  {}\n",
 
				name.sysname,
 
				name.release,
 
				name.version,
 
				name.machine
 
		);
 
	}
 

	
 
	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,
 
				"Crash reason:\n"
 
				" Signal:  %s (%d)\n"
 
				" Message: %s\n\n",
 
		fmt::format_to(output_iterator,
 
			   "Crash reason:\n"
 
				" Signal:  {} ({})\n"
 
				" Message: {}\n\n",
 
				strsignal(this->signum),
 
				this->signum,
 
				message
 
		);
 
	}
 

	
 
#if defined(SUNOS)
 
	/** Data needed while walking up the stack */
 
	struct StackWalkerParams {
 
		char **bufptr;    ///< Buffer
 
		const char *last; ///< End of buffer
 
		int counter;      ///< We are at counter-th stack level
 
		std::back_insert_iterator<std::string> *output_iterator; ///< Buffer
 
		int counter; ///< We are at counter-th stack level
 
	};
 

	
 
	/**
 
	 * Callback used while walking up the stack.
 
	 * @param pc program counter
 
	 * @param sig 'active' signal (unused)
 
	 * @param params parameters
 
	 * @return always 0, continue walking up the stack
 
	 */
 
	static int SunOSStackWalker(uintptr_t pc, int sig, void *params)
 
	{
 
		StackWalkerParams *wp = (StackWalkerParams *)params;
 

	
 
		/* Resolve program counter to file and nearest symbol (if possible) */
 
		Dl_info dli;
 
		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);
 
		} 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++;
 

	
 
		return 0;
 
	}
 
#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__)
 
		void *trace[64];
 
		int trace_size = backtrace(trace, lengthof(trace));
 

	
 
		char **messages = backtrace_symbols(trace, trace_size);
 
		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);
 
#elif defined(SUNOS)
 
		ucontext_t uc;
 
		if (getcontext(&uc) != 0) {
 
			buffer += seprintf(buffer, last, " getcontext() failed\n\n");
 
			fmt::format_to(output_iterator, " getcontext() failed\n\n");
 
			return buffer;
 
		}
 

	
 
		StackWalkerParams wp = { &buffer, last, 0 };
 
		StackWalkerParams wp = { &output_iterator, 0 };
 
		walkcontext(&uc, &CrashLogUnix::SunOSStackWalker, &wp);
 
#else
 
		buffer += seprintf(buffer, last, " Not supported.\n");
 
		fmt::format_to(output_iterator, " Not supported.\n");
 
#endif
 
		return buffer + seprintf(buffer, last, "\n");
 
		fmt::format_to(output_iterator, "\n");
 
	}
 
public:
 
	/**
 
	 * A crash log is always generated by signal.
 
	 * @param signum the signal that was caused by the crash.
 
	 */
 
	CrashLogUnix(int signum) :
 
		signum(signum)
 
	{
 
	}
 
};
 

	
src/os/windows/crashlog_win.cpp
Show inline comments
 
@@ -16,111 +16,100 @@
 
#include "../../strings_func.h"
 
#include "../../gamelog.h"
 
#include "../../saveload/saveload.h"
 
#include "../../video/video_driver.hpp"
 

	
 
#include <windows.h>
 
#include <mmsystem.h>
 
#include <signal.h>
 
#include <psapi.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.
 
 */
 
class CrashLogWindows : public CrashLog {
 
	/** Information about the encountered exception */
 
	EXCEPTION_POINTERS *ep;
 

	
 
	char *LogOSVersion(char *buffer, const char *last) const override;
 
	char *LogError(char *buffer, const char *last, const char *message) const override;
 
	char *LogStacktrace(char *buffer, const char *last) const override;
 
	char *LogRegisters(char *buffer, const char *last) const override;
 
	char *LogModules(char *buffer, const char *last) const override;
 
	void LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const override;
 
	void LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const override;
 
	void LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const override;
 
	void LogRegisters(std::back_insert_iterator<std::string> &output_iterator) const override;
 
	void LogModules(std::back_insert_iterator<std::string> &output_iterator) const override;
 
public:
 
#if defined(_MSC_VER)
 
	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
 
	char *AppendDecodedStacktrace(char *buffer, const char *last) const { return buffer; }
 
	void AppendDecodedStacktrace(std::back_insert_iterator<std::string> &output_iterator) const {}
 
#endif /* _MSC_VER */
 

	
 
	/** Buffer for the generated crash log */
 
	char crashlog[65536];
 
	std::string crashlog;
 
	/** Buffer for the filename of the crash log */
 
	char crashlog_filename[MAX_PATH];
 
	/** Buffer for the filename of the crash dump */
 
	char crashdump_filename[MAX_PATH];
 
	/** Buffer for the filename of the crash screenshot */
 
	char screenshot_filename[MAX_PATH];
 

	
 
	/**
 
	 * A crash log is always generated when it's generated.
 
	 * @param ep the data related to the exception.
 
	 */
 
	CrashLogWindows(EXCEPTION_POINTERS *ep = nullptr) :
 
		ep(ep)
 
	{
 
		this->crashlog[0] = '\0';
 
		this->crashlog.reserve(65536);
 
		this->crashlog_filename[0] = '\0';
 
		this->crashdump_filename[0] = '\0';
 
		this->screenshot_filename[0] = '\0';
 
	}
 

	
 
	/**
 
	 * Points to the current crash log.
 
	 */
 
	static CrashLogWindows *current;
 
};
 

	
 
/* 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;
 
	os.dwOSVersionInfoSize = sizeof(os);
 
	GetVersionExA(&os);
 

	
 
	return buffer + seprintf(buffer, last,
 
	fmt::format_to(output_iterator,
 
			"Operating system:\n"
 
			" Name:     Windows\n"
 
			" Release:  %d.%d.%d (%s)\n",
 
			(int)os.dwMajorVersion,
 
			(int)os.dwMinorVersion,
 
			(int)os.dwBuildNumber,
 
			" Release:  {}.{}.{} ({})\n",
 
			os.dwMajorVersion,
 
			os.dwMinorVersion,
 
			os.dwBuildNumber,
 
			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"
 
			" Exception: %.8X\n"
 
#ifdef _M_AMD64
 
			" Location:  %.16IX\n"
 
#else
 
			" Location:  %.8X\n"
 
#endif
 
			" Message:   %s\n\n",
 
			(int)ep->ExceptionRecord->ExceptionCode,
 
			" Exception: {:08X}\n"
 
			" Location:  {:X}\n"
 
			" Message:   {}\n\n",
 
			ep->ExceptionRecord->ExceptionCode,
 
			(size_t)ep->ExceptionRecord->ExceptionAddress,
 
			message
 
	);
 
}
 

	
 
struct DebugFileInfo {
 
	uint32 size;
 
	uint32 crc32;
 
	SYSTEMTIME file_time;
 
};
 

	
 
static uint32 _crc_table[256];
 
@@ -170,124 +159,124 @@ static void GetFileInfo(DebugFileInfo *d
 
		}
 
		dfi->size = filesize;
 
		dfi->crc32 = crc ^ (uint32)-1;
 

	
 
		if (GetFileTime(file, nullptr, nullptr, &write_time)) {
 
			FileTimeToSystemTime(&write_time, &dfi->file_time);
 
		}
 
		CloseHandle(file);
 
	}
 
}
 

	
 

	
 
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];
 
	DebugFileInfo dfi;
 

	
 
	GetModuleFileName(mod, buffer, MAX_PATH);
 
	GetFileInfo(&dfi, buffer);
 
	output += seprintf(output, last, " %-20s handle: %p size: %d crc: %.8X date: %d-%.2d-%.2d %.2d:%.2d:%.2d\n",
 
		FS2OTTD(buffer).c_str(),
 
		mod,
 
	fmt::format_to(output_iterator, " {:20s} handle: {:X} size: {} crc: {:8X} date: {}-{:02}-{:02} {:02}:{:02}:{:02}\n",
 
		FS2OTTD(buffer),
 
		(size_t)mod,
 
		dfi.size,
 
		dfi.crc32,
 
		dfi.file_time.wYear,
 
		dfi.file_time.wMonth,
 
		dfi.file_time.wDay,
 
		dfi.file_time.wHour,
 
		dfi.file_time.wMinute,
 
		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();
 

	
 
	output += seprintf(output, last, "Module information:\n");
 
	fmt::format_to(output_iterator, "Module information:\n");
 

	
 
	HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
 
	if (proc != nullptr) {
 
		HMODULE modules[100];
 
		DWORD needed;
 
		BOOL res = EnumProcessModules(proc, modules, sizeof(modules), &needed);
 
		CloseHandle(proc);
 
		if (res) {
 
			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]);
 
			return output + seprintf(output, last, "\n");
 
			for (size_t i = 0; i != count; i++) PrintModuleInfo(output_iterator, modules[i]);
 
			fmt::format_to(output_iterator, "\n");
 
			return;
 
		}
 
	}
 
	output = PrintModuleInfo(output, last, nullptr);
 
	return output + seprintf(output, last, "\n");
 
	PrintModuleInfo(output_iterator, nullptr);
 
	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
 
	buffer += seprintf(buffer, last,
 
		" RAX: %.16I64X RBX: %.16I64X RCX: %.16I64X RDX: %.16I64X\n"
 
		" RSI: %.16I64X RDI: %.16I64X RBP: %.16I64X RSP: %.16I64X\n"
 
		" R8:  %.16I64X R9:  %.16I64X R10: %.16I64X R11: %.16I64X\n"
 
		" R12: %.16I64X R13: %.16I64X R14: %.16I64X R15: %.16I64X\n"
 
		" RIP: %.16I64X EFLAGS: %.8lX\n",
 
	fmt::format_to(output_iterator,
 
		" RAX: {:016X} RBX: {:016X} RCX: {:016X} RDX: {:016X}\n"
 
		" RSI: {:016X} RDI: {:016X} RBP: {:016X} RSP: {:016X}\n"
 
		" R8:  {:016X} R9:  {:016X} R10: {:016X} R11: {:016X}\n"
 
		" R12: {:016X} R13: {:016X} R14: {:016X} R15: {:016X}\n"
 
		" RIP: {:016X} EFLAGS: {:08X}\n",
 
		ep->ContextRecord->Rax,
 
		ep->ContextRecord->Rbx,
 
		ep->ContextRecord->Rcx,
 
		ep->ContextRecord->Rdx,
 
		ep->ContextRecord->Rsi,
 
		ep->ContextRecord->Rdi,
 
		ep->ContextRecord->Rbp,
 
		ep->ContextRecord->Rsp,
 
		ep->ContextRecord->R8,
 
		ep->ContextRecord->R9,
 
		ep->ContextRecord->R10,
 
		ep->ContextRecord->R11,
 
		ep->ContextRecord->R12,
 
		ep->ContextRecord->R13,
 
		ep->ContextRecord->R14,
 
		ep->ContextRecord->R15,
 
		ep->ContextRecord->Rip,
 
		ep->ContextRecord->EFlags
 
	);
 
#elif defined(_M_IX86)
 
	buffer += seprintf(buffer, last,
 
		" EAX: %.8X EBX: %.8X ECX: %.8X EDX: %.8X\n"
 
		" ESI: %.8X EDI: %.8X EBP: %.8X ESP: %.8X\n"
 
		" EIP: %.8X EFLAGS: %.8X\n",
 
	fmt::format_to(output_iterator,
 
		" EAX: {:08X} EBX: {:08X} ECX: {:08X} EDX: {:08X}\n"
 
		" ESI: {:08X} EDI: {:08X} EBP: {:08X} ESP: {:08X}\n"
 
		" EIP: {:08X} EFLAGS: {:08X}\n",
 
		(int)ep->ContextRecord->Eax,
 
		(int)ep->ContextRecord->Ebx,
 
		(int)ep->ContextRecord->Ecx,
 
		(int)ep->ContextRecord->Edx,
 
		(int)ep->ContextRecord->Esi,
 
		(int)ep->ContextRecord->Edi,
 
		(int)ep->ContextRecord->Ebp,
 
		(int)ep->ContextRecord->Esp,
 
		(int)ep->ContextRecord->Eip,
 
		(int)ep->ContextRecord->EFlags
 
	);
 
#elif defined(_M_ARM64)
 
	buffer += seprintf(buffer, last,
 
		" X0:  %.16I64X X1:  %.16I64X X2:  %.16I64X X3:  %.16I64X\n"
 
		" X4:  %.16I64X X5:  %.16I64X X6:  %.16I64X X7:  %.16I64X\n"
 
		" X8:  %.16I64X X9:  %.16I64X X10: %.16I64X X11: %.16I64X\n"
 
		" X12: %.16I64X X13: %.16I64X X14: %.16I64X X15: %.16I64X\n"
 
		" X16: %.16I64X X17: %.16I64X X18: %.16I64X X19: %.16I64X\n"
 
		" X20: %.16I64X X21: %.16I64X X22: %.16I64X X23: %.16I64X\n"
 
		" X24: %.16I64X X25: %.16I64X X26: %.16I64X X27: %.16I64X\n"
 
		" X28: %.16I64X Fp:  %.16I64X Lr:  %.16I64X\n",
 
	fmt::format_to(output_iterator,
 
		" X0:  {:016X} X1:  {:016X} X2:  {:016X} X3:  {:016X}\n"
 
		" X4:  {:016X} X5:  {:016X} X6:  {:016X} X7:  {:016X}\n"
 
		" X8:  {:016X} X9:  {:016X} X10: {:016X} X11: {:016X}\n"
 
		" X12: {:016X} X13: {:016X} X14: {:016X} X15: {:016X}\n"
 
		" X16: {:016X} X17: {:016X} X18: {:016X} X19: {:016X}\n"
 
		" X20: {:016X} X21: {:016X} X22: {:016X} X23: {:016X}\n"
 
		" X24: {:016X} X25: {:016X} X26: {:016X} X27: {:016X}\n"
 
		" X28: {:016X} Fp:  {:016X} Lr:  {:016X}\n",
 
		ep->ContextRecord->X0,
 
		ep->ContextRecord->X1,
 
		ep->ContextRecord->X2,
 
		ep->ContextRecord->X3,
 
		ep->ContextRecord->X4,
 
		ep->ContextRecord->X5,
 
		ep->ContextRecord->X6,
 
		ep->ContextRecord->X7,
 
		ep->ContextRecord->X8,
 
		ep->ContextRecord->X9,
 
		ep->ContextRecord->X10,
 
		ep->ContextRecord->X11,
 
@@ -304,101 +293,101 @@ static char *PrintModuleInfo(char *outpu
 
		ep->ContextRecord->X22,
 
		ep->ContextRecord->X23,
 
		ep->ContextRecord->X24,
 
		ep->ContextRecord->X25,
 
		ep->ContextRecord->X26,
 
		ep->ContextRecord->X27,
 
		ep->ContextRecord->X28,
 
		ep->ContextRecord->Fp,
 
		ep->ContextRecord->Lr
 
	);
 
#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
 
	byte *b = (byte*)ep->ContextRecord->Rip;
 
#elif defined(_M_IX86)
 
	byte *b = (byte*)ep->ContextRecord->Eip;
 
#elif defined(_M_ARM64)
 
	byte *b = (byte*)ep->ContextRecord->Pc;
 
#endif
 
	for (int i = 0; i != 24; i++) {
 
		if (IsBadReadPtr(b, 1)) {
 
			buffer += seprintf(buffer, last, " ??"); // OCR: WAS: , 0);
 
			fmt::format_to(output_iterator, " ??"); // OCR: WAS: , 0);
 
		} else {
 
			buffer += seprintf(buffer, last, " %.2X", *b);
 
			fmt::format_to(output_iterator, " {:02X}", *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
 
	uint32 *b = (uint32*)ep->ContextRecord->Rsp;
 
#elif defined(_M_IX86)
 
	uint32 *b = (uint32*)ep->ContextRecord->Esp;
 
#elif defined(_M_ARM64)
 
	uint32 *b = (uint32*)ep->ContextRecord->Sp;
 
#endif
 
	for (int j = 0; j != 24; j++) {
 
		for (int i = 0; i != 8; i++) {
 
			if (IsBadReadPtr(b, sizeof(uint32))) {
 
				buffer += seprintf(buffer, last, " ????????"); // OCR: WAS - , 0);
 
				fmt::format_to(output_iterator, " ????????"); // OCR: WAS - , 0);
 
			} else {
 
				buffer += seprintf(buffer, last, " %.8X", *b);
 
				fmt::format_to(output_iterator, " {:08X}", *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)
 
static const uint MAX_SYMBOL_LEN = 512;
 
static const uint MAX_FRAMES     = 64;
 

	
 
#pragma warning(disable:4091)
 
#include <dbghelp.h>
 
#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");
 
	struct ProcPtrs {
 
		BOOL (WINAPI * pSymInitialize)(HANDLE, PCSTR, BOOL);
 
		BOOL (WINAPI * pSymSetOptions)(DWORD);
 
		BOOL (WINAPI * pSymCleanup)(HANDLE);
 
		BOOL (WINAPI * pStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
 
		PVOID (WINAPI * pSymFunctionTableAccess64)(HANDLE, DWORD64);
 
		DWORD64 (WINAPI * pSymGetModuleBase64)(HANDLE, DWORD64);
 
		BOOL (WINAPI * pSymGetModuleInfo64)(HANDLE, DWORD64, PIMAGEHLP_MODULE64);
 
		BOOL (WINAPI * pSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64);
 
		BOOL (WINAPI * pSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
 
	} proc = {
 
		dbghelp.GetProcAddress("SymInitialize"),
 
		dbghelp.GetProcAddress("SymSetOptions"),
 
		dbghelp.GetProcAddress("SymCleanup"),
 
		dbghelp.GetProcAddress("StackWalk64"),
 
		dbghelp.GetProcAddress("SymFunctionTableAccess64"),
 
		dbghelp.GetProcAddress("SymGetModuleBase64"),
 
		dbghelp.GetProcAddress("SymGetModuleInfo64"),
 
		dbghelp.GetProcAddress("SymGetSymFromAddr64"),
 
		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. */
 
	if (dbghelp.Success()) {
 
		/* Initialize symbol handler. */
 
		HANDLE hCur = GetCurrentProcess();
 
		proc.pSymInitialize(hCur, nullptr, TRUE);
 
		/* Load symbols only when needed, fail silently on errors, demangle symbol names. */
 
		proc.pSymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_UNDNAME);
 

	
 
		/* Initialize starting stack frame from context record. */
 
		STACKFRAME64 frame;
 
		memset(&frame, 0, sizeof(frame));
 
@@ -431,84 +420,84 @@ char *CrashLogWindows::AppendDecodedStac
 

	
 
		/* Walk stack at most MAX_FRAMES deep in case the stack is corrupt. */
 
		for (uint num = 0; num < MAX_FRAMES; num++) {
 
			if (!proc.pStackWalk64(
 
#ifdef _M_AMD64
 
				IMAGE_FILE_MACHINE_AMD64,
 
#else
 
				IMAGE_FILE_MACHINE_I386,
 
#endif
 
				hCur, GetCurrentThread(), &frame, &ctx, nullptr, proc.pSymFunctionTableAccess64, proc.pSymGetModuleBase64, nullptr)) break;
 

	
 
			if (frame.AddrPC.Offset == frame.AddrReturn.Offset) {
 
				buffer += seprintf(buffer, last, " <infinite loop>\n");
 
				fmt::format_to(output_iterator, " <infinite loop>\n");
 
				break;
 
			}
 

	
 
			/* Get module name. */
 
			const char *mod_name = "???";
 

	
 
			IMAGEHLP_MODULE64 module;
 
			module.SizeOfStruct = sizeof(module);
 
			if (proc.pSymGetModuleInfo64(hCur, frame.AddrPC.Offset, &module)) {
 
				mod_name = module.ModuleName;
 
			}
 

	
 
			/* 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. */
 
			DWORD64 offset;
 
			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;
 
				IMAGEHLP_LINE64 line;
 
				line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
 
				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);
 
	}
 

	
 
	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
 
{
 
	int ret = 0;
 
	DllLoader dbghelp(L"dbghelp.dll");
 
	if (dbghelp.Success()) {
 
		typedef BOOL (WINAPI *MiniDumpWriteDump_t)(HANDLE, DWORD, HANDLE,
 
				MINIDUMP_TYPE,
 
				CONST PMINIDUMP_EXCEPTION_INFORMATION,
 
				CONST PMINIDUMP_USER_STREAM_INFORMATION,
 
				CONST PMINIDUMP_CALLBACK_INFORMATION);
 
		MiniDumpWriteDump_t funcMiniDumpWriteDump = dbghelp.GetProcAddress("MiniDumpWriteDump");
 
		if (funcMiniDumpWriteDump != nullptr) {
 
			this->CreateFileName(filename, filename_last, ".dmp");
 
			HANDLE file  = CreateFile(OTTD2FS(filename).c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, 0);
 
			HANDLE proc  = GetCurrentProcess();
 
			DWORD procid = GetCurrentProcessId();
 
			MINIDUMP_EXCEPTION_INFORMATION mdei;
 
			MINIDUMP_USER_STREAM userstream;
 
			MINIDUMP_USER_STREAM_INFORMATION musi;
 

	
 
			userstream.Type        = LastReservedStream + 1;
 
			userstream.Buffer      = (void*)this->crashlog;
 
			userstream.BufferSize  = (ULONG)strlen(this->crashlog) + 1;
 
			userstream.Buffer      = (void*)this->crashlog.data();
 
			userstream.BufferSize  = (ULONG)this->crashlog.size() + 1;
 

	
 
			musi.UserStreamCount   = 1;
 
			musi.UserStreamArray   = &userstream;
 

	
 
			mdei.ThreadId = GetCurrentThreadId();
 
			mdei.ExceptionPointers  = ep;
 
			mdei.ClientPointers     = false;
 

	
 
			funcMiniDumpWriteDump(proc, procid, file, MiniDumpWithDataSegs, &mdei, &musi, nullptr);
 
			ret = 1;
 
		} else {
 
			ret = -1;
 
@@ -550,28 +539,29 @@ static LONG WINAPI ExceptionHandler(EXCE
 

	
 
	if (SaveloadCrashWithMissingNewGRFs()) {
 
		static const wchar_t _saveload_crash[] =
 
			L"A serious fault condition occurred in the game. The game will shut down.\n"
 
			L"As you loaded an savegame for which you do not have the required NewGRFs\n"
 
			L"no crash information will be generated.\n";
 
		MessageBox(nullptr, _saveload_crash, L"Fatal Application Failure", MB_ICONERROR);
 
		ExitProcess(3);
 
	}
 

	
 
	CrashLogWindows *log = new CrashLogWindows(ep);
 
	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->AppendDecodedStacktrace(buf, lastof(log->crashlog));
 
	log->WriteCrashLog(log->crashlog, log->crashlog_filename, lastof(log->crashlog_filename));
 
	log->AppendDecodedStacktrace(output_iterator);
 
	log->WriteCrashLog(log->crashlog.c_str(), log->crashlog_filename, lastof(log->crashlog_filename));
 
	log->WriteScreenshot(log->screenshot_filename, lastof(log->screenshot_filename));
 
	log->SendSurvey();
 

	
 
	/* Close any possible log files */
 
	CloseConsoleLogIfActive();
 

	
 
	if ((VideoDriver::GetInstance() == nullptr || VideoDriver::GetInstance()->HasGUI()) && _safe_esp != nullptr) {
 
#ifdef _M_AMD64
 
		ep->ContextRecord->Rip = (DWORD64)ShowCrashlogWindow;
 
		ep->ContextRecord->Rsp = (DWORD64)_safe_esp;
 
#elif defined(_M_IX86)
 
		ep->ContextRecord->Eip = (DWORD)ShowCrashlogWindow;
 
@@ -671,28 +661,28 @@ static void SetWndSize(HWND wnd, int mod
 
			(GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2,
 
			0, 0, SWP_NOSIZE);
 
	}
 
}
 

	
 
static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
 
{
 
	switch (msg) {
 
		case WM_INITDIALOG: {
 
			/* 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) */
 
			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 :( */
 
			const char *unix_nl = CrashLogWindows::current->crashlog;
 
			char dos_nl[lengthof(CrashLogWindows::current->crashlog)];
 
			const char *unix_nl = CrashLogWindows::current->crashlog.data();
 
			char dos_nl[65536];
 
			char *p = dos_nl;
 
			WChar c;
 
			while ((c = Utf8Consume(&unix_nl)) && p < lastof(dos_nl) - 4) { // 4 is max number of bytes per character
 
				if (c == '\n') p += Utf8Encode(p, '\r');
 
				p += Utf8Encode(p, c);
 
			}
 
			*p = '\0';
 

	
 
			/* Add path to crash.log and crash.dmp (if any) to the crash window text */
 
			size_t len = wcslen(_crash_desc) + 2;
 
			len += wcslen(convert_to_fs(CrashLogWindows::current->crashlog_filename, filenamebuf, lengthof(filenamebuf))) + 2;
 
			len += wcslen(convert_to_fs(CrashLogWindows::current->crashdump_filename, filenamebuf, lengthof(filenamebuf))) + 2;
0 comments (0 inline, 0 general)