Offer to generate .tspack from crash handler

This commit is contained in:
Soreepeong 2022-09-14 13:36:29 +09:00
parent 921cf0087c
commit 8718de6121
11 changed files with 9371 additions and 9 deletions

View file

@ -80,6 +80,7 @@ void from_json(const nlohmann::json& json, DalamudStartInfo& config) {
config.Language = json.value("Language", config.Language);
config.GameVersion = json.value("GameVersion", config.GameVersion);
config.DelayInitializeMs = json.value("DelayInitializeMs", config.DelayInitializeMs);
config.TroubleshootingPackData = json.value("TroubleshootingPackData", std::string{});
config.BootLogPath = json.value("BootLogPath", config.BootLogPath);
config.BootShowConsole = json.value("BootShowConsole", config.BootShowConsole);
@ -100,6 +101,8 @@ void from_json(const nlohmann::json& json, DalamudStartInfo& config) {
for (const auto& val : *it)
config.BootUnhookDlls.insert(unicode::convert<std::string>(val.get<std::string>(), &unicode::lower));
}
config.CrashHandlerShow = json.value("CrashHandlerShow", config.CrashHandlerShow);
}
void DalamudStartInfo::from_envvars() {

View file

@ -34,6 +34,7 @@ struct DalamudStartInfo {
ClientLanguage Language = ClientLanguage::English;
std::string GameVersion;
int DelayInitializeMs = 0;
std::string TroubleshootingPackData;
std::string BootLogPath;
bool BootShowConsole = false;
@ -47,6 +48,8 @@ struct DalamudStartInfo {
std::set<std::string> BootEnabledGameFixes{};
std::set<std::string> BootUnhookDlls{};
bool CrashHandlerShow = false;
friend void from_json(const nlohmann::json&, DalamudStartInfo&);
void from_envvars();
};

View file

@ -15,4 +15,5 @@ struct exception_info
uint64_t nLifetime;
HANDLE hThreadHandle;
DWORD dwStackTraceLength;
DWORD dwTroubleshootingPackDataLength;
};

View file

@ -175,12 +175,16 @@ LONG exception_handler(EXCEPTION_POINTERS* ex)
}
exinfo.dwStackTraceLength = static_cast<DWORD>(stackTrace.size());
exinfo.dwTroubleshootingPackDataLength = static_cast<DWORD>(g_startInfo.TroubleshootingPackData.size());
if (DWORD written; !WriteFile(g_crashhandler_pipe_write, &exinfo, static_cast<DWORD>(sizeof exinfo), &written, nullptr) || sizeof exinfo != written)
return EXCEPTION_CONTINUE_SEARCH;
if (DWORD written; !WriteFile(g_crashhandler_pipe_write, &stackTrace[0], static_cast<DWORD>(std::span(stackTrace).size_bytes()), &written, nullptr) || std::span(stackTrace).size_bytes() != written)
return EXCEPTION_CONTINUE_SEARCH;
if (DWORD written; !WriteFile(g_crashhandler_pipe_write, &g_startInfo.TroubleshootingPackData[0], static_cast<DWORD>(std::span(g_startInfo.TroubleshootingPackData).size_bytes()), &written, nullptr) || std::span(g_startInfo.TroubleshootingPackData).size_bytes() != written)
return EXCEPTION_CONTINUE_SEARCH;
SuspendThread(GetCurrentThread());
return EXCEPTION_CONTINUE_SEARCH;
}
@ -224,11 +228,7 @@ bool veh::add_handler(bool doFullDump, const std::string& workingDirectory)
siex.StartupInfo.cb = sizeof siex;
siex.StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
#ifdef NDEBUG
siex.StartupInfo.wShowWindow = SW_HIDE;
#else
siex.StartupInfo.wShowWindow = SW_SHOW;
#endif
siex.StartupInfo.wShowWindow = g_startInfo.CrashHandlerShow ? SW_SHOW : SW_HIDE;
// set up list of handles to inherit to child process
std::vector<char> attributeListBuf;

View file

@ -94,6 +94,7 @@ namespace Dalamud.Injector
args.Remove("--veh-full");
args.Remove("--no-plugin");
args.Remove("--no-3rd-plugin");
args.Remove("--crash-handler-console");
var mainCommand = args[1].ToLowerInvariant();
if (mainCommand.Length > 0 && mainCommand.Length <= 6 && "inject"[..mainCommand.Length] == mainCommand)
@ -252,6 +253,7 @@ namespace Dalamud.Injector
var assetDirectory = startInfo.AssetDirectory;
var delayInitializeMs = startInfo.DelayInitializeMs;
var languageStr = startInfo.Language.ToString().ToLowerInvariant();
var troubleshootingData = "{\"empty\": true, \"description\": \"No troubleshooting data supplied.\"}";
for (var i = 2; i < args.Count; i++)
{
@ -269,6 +271,8 @@ namespace Dalamud.Injector
delayInitializeMs = int.Parse(args[i][key.Length..]);
else if (args[i].StartsWith(key = "--dalamud-client-language="))
languageStr = args[i][key.Length..].ToLowerInvariant();
else if (args[i].StartsWith(key = "--dalamud-tspack-b64="))
troubleshootingData = Encoding.UTF8.GetString(Convert.FromBase64String(args[i][key.Length..]));
else
continue;
@ -313,6 +317,7 @@ namespace Dalamud.Injector
startInfo.Language = clientLanguage;
startInfo.DelayInitializeMs = delayInitializeMs;
startInfo.GameVersion = null;
startInfo.TroubleshootingPackData = troubleshootingData;
// Set boot defaults
startInfo.BootShowConsole = args.Contains("--console");
@ -329,6 +334,7 @@ namespace Dalamud.Injector
startInfo.NoLoadPlugins = args.Contains("--no-plugin");
startInfo.NoLoadThirdPartyPlugins = args.Contains("--no-third-plugin");
// startInfo.BootUnhookDlls = new List<string>() { "kernel32.dll", "ntdll.dll", "user32.dll" };
startInfo.CrashHandlerShow = args.Contains("--crash-handler-console");
return startInfo;
}
@ -364,7 +370,7 @@ namespace Dalamud.Injector
Console.WriteLine(" [--dalamud-client-language=0-3|j(apanese)|e(nglish)|d|g(erman)|f(rench)]");
Console.WriteLine("Verbose logging:\t[-v]");
Console.WriteLine("Show Console:\t[--console]");
Console.WriteLine("Show Console:\t[--console] [--crash-handler-console]");
Console.WriteLine("Enable ETW:\t[--etw]");
Console.WriteLine("Enable VEH:\t[--veh], [--veh-full]");
Console.WriteLine("Show messagebox:\t[--msgbox1], [--msgbox2], [--msgbox3]");

View file

@ -34,6 +34,7 @@ namespace Dalamud
this.Language = other.Language;
this.GameVersion = other.GameVersion;
this.DelayInitializeMs = other.DelayInitializeMs;
this.TroubleshootingPackData = other.TroubleshootingPackData;
this.NoLoadPlugins = other.NoLoadPlugins;
this.NoLoadThirdPartyPlugins = other.NoLoadThirdPartyPlugins;
this.BootLogPath = other.BootLogPath;
@ -47,6 +48,7 @@ namespace Dalamud
this.BootDotnetOpenProcessHookMode = other.BootDotnetOpenProcessHookMode;
this.BootEnabledGameFixes = other.BootEnabledGameFixes;
this.BootUnhookDlls = other.BootUnhookDlls;
this.CrashHandlerShow = other.CrashHandlerShow;
}
/// <summary>
@ -85,6 +87,11 @@ namespace Dalamud
[JsonConverter(typeof(GameVersionConverter))]
public GameVersion? GameVersion { get; set; }
/// <summary>
/// Gets or sets troubleshooting information to attach when generating a tspack file.
/// </summary>
public string TroubleshootingPackData { get; set; }
/// <summary>
/// Gets or sets a value that specifies how much to wait before a new Dalamud session.
/// </summary>
@ -154,5 +161,10 @@ namespace Dalamud
/// Gets or sets a list of DLLs that should be unhooked.
/// </summary>
public List<string>? BootUnhookDlls { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to show crash handler console window.
/// </summary>
public bool CrashHandlerShow { get; set; }
}
}

View file

@ -1,3 +1,5 @@
#include <array>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iostream>
@ -14,23 +16,96 @@
#define NOMINMAX
#include <Windows.h>
#include <comdef.h>
#include <CommCtrl.h>
#include <DbgHelp.h>
#include <minidumpapiset.h>
#include <PathCch.h>
#include <Psapi.h>
#include <shellapi.h>
#include <ShlGuid.h>
#include <ShObjIdl.h>
#include <winhttp.h>
#pragma comment(lib, "comctl32.lib")
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
_COM_SMARTPTR_TYPEDEF(IFileOperation, __uuidof(IFileOperation));
_COM_SMARTPTR_TYPEDEF(IFileSaveDialog, __uuidof(IFileSaveDialog));
_COM_SMARTPTR_TYPEDEF(IShellItem, __uuidof(IShellItem));
_COM_SMARTPTR_TYPEDEF(IBindCtx, __uuidof(IBindCtx));
_COM_SMARTPTR_TYPEDEF(IStream, __uuidof(IStream));
static constexpr GUID Guid_IFileDialog_Tspack{ 0xfc057318, 0xad35, 0x4599, {0xa7, 0x68, 0xdd, 0xaf, 0x70, 0xbe, 0x98, 0x75} };
#include "resource.h"
#include "../Dalamud.Boot/crashhandler_shared.h"
#include "miniz.h"
HANDLE g_hProcess = nullptr;
bool g_bSymbolsAvailable = false;
std::string ws_to_u8(const std::wstring& ws) {
std::string s(WideCharToMultiByte(CP_UTF8, 0, ws.data(), static_cast<int>(ws.size()), nullptr, 0, nullptr, nullptr), '\0');
WideCharToMultiByte(CP_UTF8, 0, ws.data(), static_cast<int>(ws.size()), s.data(), static_cast<int>(s.size()), nullptr, nullptr);
return s;
}
std::wstring u8_to_ws(const std::string& s) {
std::wstring ws(MultiByteToWideChar(CP_UTF8, 0, s.data(), static_cast<int>(s.size()), nullptr, 0), '\0');
MultiByteToWideChar(CP_UTF8, 0, s.data(), static_cast<int>(s.size()), ws.data(), static_cast<int>(ws.size()));
return ws;
}
std::wstring get_window_string(HWND hWnd) {
std::wstring buf(GetWindowTextLengthW(hWnd), L'\0');
GetWindowTextW(hWnd, &buf[0], static_cast<int>(buf.size()));
return buf;
}
[[noreturn]]
void throw_hresult(HRESULT hr, const std::string& clue = {}) {
wchar_t* pwszMsg = nullptr;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
hr,
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
reinterpret_cast<LPWSTR>(&pwszMsg),
0,
nullptr);
if (!pwszMsg) {
if (clue.empty())
throw std::runtime_error(std::format("Error (HRESULT=0x{:08X})", static_cast<uint32_t>(hr)));
else
throw std::runtime_error(std::format("Error at {} (HRESULT=0x{:08X})", clue, static_cast<uint32_t>(hr)));
}
std::unique_ptr<wchar_t, decltype(LocalFree)*> pszMsgFree(pwszMsg, LocalFree);
if (clue.empty())
throw std::runtime_error(std::format("Error (HRESULT=0x{:08X}): {}", static_cast<uint32_t>(hr), ws_to_u8(pwszMsg)));
else
throw std::runtime_error(std::format("Error at {} (HRESULT=0x{:08X}): {}", clue, static_cast<uint32_t>(hr), ws_to_u8(pwszMsg)));
}
[[noreturn]]
void throw_last_error(const std::string& clue = {}) {
throw_hresult(HRESULT_FROM_WIN32(GetLastError()), clue);
}
HRESULT throw_if_failed(HRESULT hr, std::initializer_list<HRESULT> acceptables = {}, const std::string& clue = {}) {
if (SUCCEEDED(hr))
return hr;
for (const auto& h : acceptables) {
if (h == hr)
return hr;
}
throw_hresult(hr, clue);
}
std::wstring describe_module(const std::filesystem::path& path) {
DWORD verHandle = 0;
std::vector<uint8_t> block;
@ -366,6 +441,160 @@ std::wstring escape_shell_arg(const std::wstring& arg) {
return res;
}
void export_tspack(HWND hWndParent, const std::filesystem::path& logDir, const std::string& crashLog, const std::string& troubleshootingPackData) {
static const char* SourceLogFiles[] = {
"output.log",
"patcher.log",
"dalamud.log",
"dalamud.injector.log",
"dalamud.boot.log",
"aria.log",
};
static constexpr auto MaxSizePerLog = 1 * 1024 * 1024;
static constexpr std::array<COMDLG_FILTERSPEC, 2> OutputFileTypeFilterSpec{{
{ L"Dalamud Troubleshooting Pack File (*.tspack)", L"*.tspack" },
{ L"All files (*.*)", L"*" },
}};
IShellItemPtr pItem;
try {
SYSTEMTIME st;
GetLocalTime(&st);
IFileSaveDialogPtr pDialog;
throw_if_failed(pDialog.CreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER), {}, "pDialog.CreateInstance");
throw_if_failed(pDialog->SetClientGuid(Guid_IFileDialog_Tspack), {}, "pDialog->SetClientGuid");
throw_if_failed(pDialog->SetFileTypes(static_cast<UINT>(OutputFileTypeFilterSpec.size()), OutputFileTypeFilterSpec.data()), {}, "pDialog->SetFileTypes");
throw_if_failed(pDialog->SetFileTypeIndex(0), {}, "pDialog->SetFileTypeIndex");
throw_if_failed(pDialog->SetTitle(L"Export Dalamud Troubleshooting Pack"), {}, "pDialog->SetTitle");
throw_if_failed(pDialog->SetFileName(std::format(L"crash-{:04}{:02}{:02}{:02}{:02}{:02}.tspack", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond).c_str()), {}, "pDialog->SetFileName");
throw_if_failed(pDialog->SetDefaultExtension(L"tspack"), {}, "pDialog->SetDefaultExtension");
switch (throw_if_failed(pDialog->Show(hWndParent), { HRESULT_FROM_WIN32(ERROR_CANCELLED) }, "pDialog->Show")) {
case HRESULT_FROM_WIN32(ERROR_CANCELLED):
return;
}
throw_if_failed(pDialog->GetResult(&pItem), {}, "pDialog->GetResult");
IBindCtxPtr pBindCtx;
throw_if_failed(CreateBindCtx(0, &pBindCtx), {}, "CreateBindCtx");
auto options = BIND_OPTS{.cbStruct = sizeof(BIND_OPTS), .grfMode = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE};
throw_if_failed(pBindCtx->SetBindOptions(&options), {}, "pBindCtx->SetBindOptions");
IStreamPtr pStream;
throw_if_failed(pItem->BindToHandler(pBindCtx, BHID_Stream, IID_PPV_ARGS(&pStream)), {}, "pItem->BindToHandler");
throw_if_failed(pStream->SetSize({}), {}, "pStream->SetSize");
mz_zip_archive zipa{};
zipa.m_pIO_opaque = &*pStream;
zipa.m_pRead = [](void* pOpaque, mz_uint64 file_ofs, void* pBuf, size_t n) -> size_t {
const auto pStream = static_cast<IStream*>(pOpaque);
throw_if_failed(pStream->Seek({ .QuadPart = static_cast<int64_t>(file_ofs) }, STREAM_SEEK_SET, nullptr), {}, "pStream->Seek");
ULONG read;
throw_if_failed(pStream->Read(pBuf, static_cast<ULONG>(n), &read), {}, "pStream->Read");
return read;
};
zipa.m_pWrite = [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n) -> size_t {
const auto pStream = static_cast<IStream*>(pOpaque);
throw_if_failed(pStream->Seek({ .QuadPart = static_cast<int64_t>(file_ofs) }, STREAM_SEEK_SET, nullptr), {}, "pStream->Seek");
ULONG written;
throw_if_failed(pStream->Write(pBuf, static_cast<ULONG>(n), &written), {}, "pStream->Write");
return written;
};
const auto mz_throw_if_failed = [&zipa](mz_bool res, const std::string& clue) {
if (!res)
throw std::runtime_error(std::format("Failed to save file at {}: mz_error={} description={}", clue, static_cast<int>(mz_zip_get_last_error(&zipa)), mz_zip_get_error_string(mz_zip_get_last_error(&zipa))));
};
mz_throw_if_failed(mz_zip_writer_init_v2(&zipa, 0, 0), "mz_zip_writer_init_v2");
mz_throw_if_failed(mz_zip_writer_add_mem(&zipa, "trouble.json", troubleshootingPackData.data(), troubleshootingPackData.size(), MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE | MZ_BEST_COMPRESSION), "mz_zip_writer_add_mem: trouble.json");
mz_throw_if_failed(mz_zip_writer_add_mem(&zipa, "crash.log", crashLog.data(), crashLog.size(), MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE | MZ_BEST_COMPRESSION), "mz_zip_writer_add_mem: crash.log");
struct HandleAndBaseOffset {
HANDLE h;
int64_t off;
};
const auto fnHandleReader = [](void* pOpaque, mz_uint64 file_ofs, void* pBuf, size_t n) -> size_t {
const auto& info = *reinterpret_cast<const HandleAndBaseOffset*>(pOpaque);
if (!SetFilePointerEx(info.h, { .QuadPart = static_cast<int64_t>(info.off + file_ofs) }, nullptr, SEEK_SET))
throw_last_error("fnHandleReader: SetFilePointerEx");
if (DWORD read; !ReadFile(info.h, pBuf, static_cast<DWORD>(n), &read, nullptr))
throw_last_error("fnHandleReader: ReadFile");
else
return read;
};
for (const auto& pcszLogFileName : SourceLogFiles) {
const auto logFilePath = logDir / pcszLogFileName;
if (!exists(logFilePath))
continue;
const auto hLogFile = CreateFileW(logFilePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
if (hLogFile == INVALID_HANDLE_VALUE)
throw_last_error(std::format("indiv. log file: CreateFileW({})", ws_to_u8(logFilePath.wstring())));
std::unique_ptr<void, decltype(&CloseHandle)> hLogFileClose(hLogFile, &CloseHandle);
LARGE_INTEGER size, baseOffset{};
if (!SetFilePointerEx(hLogFile, {}, &size, SEEK_END))
throw_last_error(std::format("indiv. log file: SetFilePointerEx({})", ws_to_u8(logFilePath.wstring())));
if (size.QuadPart > MaxSizePerLog) {
if (!SetFilePointerEx(hLogFile, {.QuadPart = -MaxSizePerLog}, &baseOffset, SEEK_END))
throw_last_error(std::format("indiv. log file: SetFilePointerEx#2({})", ws_to_u8(logFilePath.wstring())));
}
auto handleInfo = HandleAndBaseOffset{.h = hLogFile, .off = baseOffset.QuadPart};
const auto modt = std::chrono::system_clock::to_time_t(std::chrono::clock_cast<std::chrono::system_clock>(last_write_time(logFilePath)));
mz_throw_if_failed(mz_zip_writer_add_read_buf_callback(
&zipa,
pcszLogFileName,
fnHandleReader, &handleInfo, // callback info
size.QuadPart - baseOffset.QuadPart,
&modt,
nullptr, 0, // comments
MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE | MZ_BEST_COMPRESSION, // flags and compression ratio
nullptr, 0, // user extra data (local)
nullptr, 0 // user extra data (central)
), std::format("mz_zip_writer_add_read_buf_callback({})", ws_to_u8(logFilePath.wstring())));
}
mz_throw_if_failed(mz_zip_writer_finalize_archive(&zipa), "mz_zip_writer_finalize_archive");
mz_throw_if_failed(mz_zip_writer_end(&zipa), "mz_zip_writer_end");
} catch (const std::exception& e) {
MessageBoxW(hWndParent, std::format(L"Failed to save file: {}", u8_to_ws(e.what())).c_str(), get_window_string(hWndParent).c_str(), MB_OK | MB_ICONERROR);
if (pItem) {
try {
IFileOperationPtr pFileOps;
throw_if_failed(pFileOps.CreateInstance(__uuidof(FileOperation), nullptr, CLSCTX_ALL));
throw_if_failed(pFileOps->SetOperationFlags(FOF_NO_UI));
throw_if_failed(pFileOps->DeleteItem(pItem, nullptr));
throw_if_failed(pFileOps->PerformOperations());
} catch (const std::exception& e2) {
std::wcerr << std::format(L"Failed to remove temporary file: {}", u8_to_ws(e2.what())) << std::endl;
}
pItem.Release();
}
}
if (pItem) {
PWSTR pwszFileName;
if (FAILED(pItem->GetDisplayName(SIGDN_FILESYSPATH, &pwszFileName))) {
if (FAILED(pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEEDITING, &pwszFileName))) {
MessageBoxW(hWndParent, L"The file has been saved to the specified path.", get_window_string(hWndParent).c_str(), MB_OK | MB_ICONINFORMATION);
} else {
std::unique_ptr<std::remove_pointer<PWSTR>::type, decltype(CoTaskMemFree)*> pszFileNamePtr(pwszFileName, CoTaskMemFree);
MessageBoxW(hWndParent, std::format(L"The file has been saved to: {}", pwszFileName).c_str(), get_window_string(hWndParent).c_str(), MB_OK | MB_ICONINFORMATION);
}
} else {
std::unique_ptr<std::remove_pointer<PWSTR>::type, decltype(CoTaskMemFree)*> pszFileNamePtr(pwszFileName, CoTaskMemFree);
ShellExecuteW(hWndParent, nullptr, L"explorer.exe", escape_shell_arg(std::format(L"/select,{}", pwszFileName)).c_str(), nullptr, SW_SHOW);
}
}
}
enum {
IdRadioRestartNormal = 101,
IdRadioRestartWithout3pPlugins,
@ -425,6 +654,7 @@ void restart_game_using_injector(int nRadioButton, const std::vector<std::wstrin
int main() {
enum crash_handler_special_exit_codes {
UnknownError = -99,
InvalidParameter = -101,
ProcessExitedUnknownExitCode = -102,
};
@ -527,8 +757,17 @@ int main() {
}
}
std::string troubleshootingPackData(exinfo.dwTroubleshootingPackDataLength, '\0');
if (exinfo.dwTroubleshootingPackDataLength) {
if (DWORD read; !ReadFile(hPipeRead, &troubleshootingPackData[0], exinfo.dwTroubleshootingPackDataLength, &read, nullptr)) {
std::cout << std::format("Failed to read troubleshooting pack data: error 0x{:x}", GetLastError()) << std::endl;
}
}
SYSTEMTIME st;
GetLocalTime(&st);
const auto dalamudLogPath = logDir.empty() ? std::filesystem::path() : logDir / L"Dalamud.log";
const auto dalamudBootLogPath = logDir.empty() ? std::filesystem::path() : logDir / L"Dalamud.boot.log";
const auto dumpPath = logDir.empty() ? std::filesystem::path() : logDir / std::format(L"dalamud_appcrash_{:04}{:02}{:02}_{:02}{:02}{:02}_{:03}_{}.dmp", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, dwProcessId);
const auto logPath = logDir.empty() ? std::filesystem::path() : logDir / std::format(L"dalamud_appcrash_{:04}{:02}{:02}_{:02}{:02}{:02}_{:03}_{}.log", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, dwProcessId);
std::wstring dumpError;
@ -571,9 +810,8 @@ int main() {
SymRefreshModuleList(GetCurrentProcess());
print_exception_info(exinfo.hThreadHandle, exinfo.ExceptionPointers, exinfo.ContextRecord, log);
auto window_log_str = log.str();
const auto window_log_str = log.str();
print_exception_info_extended(exinfo.ExceptionPointers, exinfo.ContextRecord, log);
std::wofstream(logPath) << log.str();
std::thread submitThread;
@ -637,6 +875,8 @@ int main() {
config.pszMainIcon = MAKEINTRESOURCE(IDI_ICON1);
config.pszMainInstruction = L"An error occurred";
config.pszContent = (L""
R"aa(Upload <a href="exporttspack">THIS FILE (click here)</a> if you want to ask in our <a href="discord">Discord server</a>.)aa" "\n"
"\n"
R"aa(This may be caused by a faulty plugin, a broken TexTools modification, any other third-party tool, or simply a bug in the game.)aa" "\n"
"\n"
R"aa(Try running integrity check in the XIVLauncher settings, and disabling plugins you don't need.)aa"
@ -644,6 +884,8 @@ int main() {
config.pButtons = buttons;
config.cButtons = ARRAYSIZE(buttons);
config.nDefaultButton = IdButtonRestart;
config.pszExpandedControlText = L"Hide stack trace";
config.pszCollapsedControlText = L"Stack trace for plugin developers";
config.pszExpandedInformation = window_log_str.c_str();
config.pszWindowTitle = L"Dalamud Error";
config.pRadioButtons = radios;
@ -681,6 +923,10 @@ int main() {
ShellExecuteW(hwnd, nullptr, L"explorer.exe", escape_shell_arg(std::format(L"/select,{}", logPath.wstring())).c_str(), nullptr, SW_SHOW);
} else if (link == L"logfile") {
ShellExecuteW(hwnd, nullptr, logPath.c_str(), nullptr, nullptr, SW_SHOW);
} else if (link == L"exporttspack") {
export_tspack(hwnd, logDir, ws_to_u8(log.str()), troubleshootingPackData);
} else if (link == L"discord") {
ShellExecuteW(hwnd, nullptr, L"https://goat.place", nullptr, nullptr, SW_SHOW);
} else if (link == L"resume") {
attemptResume = true;
DestroyWindow(hwnd);

View file

@ -79,9 +79,11 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="DalamudCrashHandler.cpp" />
<ClCompile Include="miniz.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\Dalamud.Boot\crashhandler_shared.h" />
<ClInclude Include="miniz.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>

View file

@ -18,6 +18,9 @@
<ClCompile Include="DalamudCrashHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniz.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\Dalamud.Boot\crashhandler_shared.h">
@ -26,6 +29,9 @@
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="miniz.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="DalamudCrashHandler.rc">

7733
DalamudCrashHandler/miniz.c Normal file

File diff suppressed because it is too large Load diff

1350
DalamudCrashHandler/miniz.h Normal file

File diff suppressed because it is too large Load diff