From eab0ec298b81138c4c602259ebf930f583113b95 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 29 Jun 2019 15:26:04 -0400 Subject: refactored preloadSsl() into preloadDll() removed old HGID check moved formatSystemMessage() to uibase added Environment class, lists loaded modules, logged at startup --- src/settings.cpp | 31 ------------------------------- 1 file changed, 31 deletions(-) (limited to 'src/settings.cpp') diff --git a/src/settings.cpp b/src/settings.cpp index b45912d4..59d3369a 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -82,37 +82,6 @@ private: }; -QString formatSystemMessage(DWORD id) -{ - wchar_t* message = nullptr; - - const auto ret = FormatMessageW( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - id, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(&message), - 0, NULL); - - QString s; - const QString idString = QString("0x%1").arg(id, 0, 16); - - if (ret == 0 || !message) { - s = idString; - } else { - s = QString("%1 (%2)") - .arg(QString::fromStdWString(message).trimmed()) - .arg(idString); - } - - LocalFree(message); - - return s; -} - - Settings *Settings::s_Instance = nullptr; -- cgit v1.3.1 From dfe8093c1ad16e1611c12e88d52d1ac38371d3f6 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 1 Jul 2019 08:42:58 -0400 Subject: added --crashdump to generate dumps of a running MO process added dump_running_process.bat to start another instance of MO with that flag --- CMakeLists.txt | 2 + dump_running_process.bat | 2 + src/main.cpp | 45 +++++- src/settings.cpp | 10 +- src/shared/util.cpp | 392 ++++++++++++++++++++++++++++++++++++++++++++--- src/shared/util.h | 17 ++ 6 files changed, 443 insertions(+), 25 deletions(-) create mode 100644 dump_running_process.bat (limited to 'src/settings.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index ced097e1..94c76373 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,3 +29,5 @@ if(NOT EXISTS ${vcxproj_user_file}) "\n" "\n") endif() + +INSTALL(FILES dump_running_process.bat DESTINATION bin) diff --git a/dump_running_process.bat b/dump_running_process.bat new file mode 100644 index 00000000..4697fe5e --- /dev/null +++ b/dump_running_process.bat @@ -0,0 +1,2 @@ +pushd "%~dp0" +start ModOrganizer.exe --crashdump diff --git a/src/main.cpp b/src/main.cpp index 518d31a0..f74a9cf4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -485,8 +485,6 @@ int runApplication(MOApplication &application, SingleInstance &instance, for (const auto& m : env.loadedModules()) { qDebug().nospace().noquote() << " . " << m.toString(); } - - return 0; } QString dataPath = application.property("dataPath").toString(); @@ -682,9 +680,52 @@ int runApplication(MOApplication &application, SingleInstance &instance, } } +int doCoreDump(env::CoreDumpTypes type) +{ + // open a console + AllocConsole(); + + // redirect stdin, stdout and stderr to it + FILE* in=nullptr; + FILE* out=nullptr; + FILE* err=nullptr; + freopen_s(&in, "CONIN$", "r", stdin); + freopen_s(&out, "CONOUT$", "w", stdout); + freopen_s(&err, "CONOUT$", "w", stderr); + + // dump + const auto b = env::coredumpOther(type); + if (!b) { + std::wcerr << L"\n>>>> a minidump file was not written\n\n"; + } + + std::wcerr << L"Press enter to continue..."; + std::wcin.get(); + + // close redirected handles + std::fclose(err); + std::fclose(out); + std::fclose(in); + + // close console + FreeConsole(); + + return (b ? 0 : 1); +} int main(int argc, char *argv[]) { + // handle --crashdump first + for (int i=1; i. #include #include #include -#include #include +#include + +#include #include #include @@ -36,6 +38,8 @@ along with Mod Organizer. If not, see . #pragma comment(lib, "Wbemuuid.lib") using MOBase::formatSystemMessage; +using MOBase::formatSystemMessageQ; +namespace fs = std::filesystem; namespace MOShared { @@ -276,6 +280,9 @@ struct HandleCloser } }; +using HandlePtr = std::unique_ptr; + + struct LibraryFreer { using pointer = HINSTANCE; @@ -362,7 +369,7 @@ public: { qCritical() << "query '" << QString::fromStdString(query) << "' failed, " - << formatSystemMessage(ret); + << formatSystemMessageQ(ret); return {}; } @@ -385,7 +392,7 @@ private: if (FAILED(ret)) { qCritical() << "CoCreateInstance for WbemLocator failed, " - << formatSystemMessage(ret); + << formatSystemMessageQ(ret); throw failed(); } @@ -406,7 +413,7 @@ private: qCritical() << "locator->ConnectServer() failed for namespace " << "'" << QString::fromStdString(ns) << "', " - << formatSystemMessage(res); + << formatSystemMessageQ(res); throw failed(); } @@ -423,7 +430,7 @@ private: if (FAILED(ret)) { qCritical() - << "CoSetProxyBlanket() failed, " << formatSystemMessage(ret); + << "CoSetProxyBlanket() failed, " << formatSystemMessageQ(ret); throw failed(); } @@ -454,7 +461,7 @@ const std::vector& Environment::securityFeatures() const void Environment::getLoadedModules() { - std::unique_ptr snapshot(CreateToolhelp32Snapshot( + HandlePtr snapshot(CreateToolhelp32Snapshot( TH32CS_SNAPMODULE32 | TH32CS_SNAPMODULE, GetCurrentProcessId())); if (snapshot.get() == INVALID_HANDLE_VALUE) @@ -463,7 +470,7 @@ void Environment::getLoadedModules() qCritical().nospace().noquote() << "CreateToolhelp32Snapshot() failed, " - << formatSystemMessage(e); + << formatSystemMessageQ(e); return; } @@ -477,7 +484,7 @@ void Environment::getLoadedModules() const auto e = GetLastError(); qCritical().nospace().noquote() - << "Module32First() failed, " << formatSystemMessage(e); + << "Module32First() failed, " << formatSystemMessageQ(e); return; } @@ -498,7 +505,7 @@ void Environment::getLoadedModules() } qCritical().nospace().noquote() - << "Module32Next() failed, " << formatSystemMessage(e); + << "Module32Next() failed, " << formatSystemMessageQ(e); break; } @@ -520,7 +527,7 @@ void Environment::getSecurityFeatures() auto ret = o->Get(L"displayName", 0, &prop, 0, 0); if (FAILED(ret)) { - qCritical() << "failed to get displayName, " << formatSystemMessage(ret); + qCritical() << "failed to get displayName, " << formatSystemMessageQ(ret); return; } @@ -529,7 +536,10 @@ void Environment::getSecurityFeatures() ret = o->Get(L"productState", 0, &prop, 0, 0); if (FAILED(ret)) { - qCritical() << "failed to get productState, " << formatSystemMessage(ret); + qCritical() + << "failed to get productState, " + << formatSystemMessageQ(ret); + return; } @@ -538,7 +548,10 @@ void Environment::getSecurityFeatures() ret = o->Get(L"instanceGuid", 0, &prop, 0, 0); if (FAILED(ret)) { - qCritical() << "failed to get instanceGuid, " << formatSystemMessage(ret); + qCritical() + << "failed to get instanceGuid, " + << formatSystemMessageQ(ret); + return; } @@ -673,7 +686,7 @@ Module::FileInfo Module::getFileInfo() const qCritical().nospace().noquote() << "GetFileVersionInfoSizeW() failed on '" << m_path << "', " - << formatSystemMessage(e); + << formatSystemMessageQ(e); return {}; } @@ -686,7 +699,7 @@ Module::FileInfo Module::getFileInfo() const qCritical().nospace().noquote() << "GetFileVersionInfoW() failed on '" << m_path << "', " - << formatSystemMessage(e); + << formatSystemMessageQ(e); return {}; } @@ -804,7 +817,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const // time on the file // opening the file - std::unique_ptr h(CreateFileW( + HandlePtr h(CreateFileW( m_path.toStdWString().c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)); @@ -813,7 +826,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const qCritical().nospace().noquote() << "can't open file '" << m_path << "' for timestamp, " - << formatSystemMessage(e); + << formatSystemMessageQ(e); return {}; } @@ -823,7 +836,7 @@ QDateTime Module::getTimestamp(const VS_FIXEDFILEINFO& fi) const const auto e = GetLastError(); qCritical().nospace().noquote() << "can't get file time for '" << m_path << "', " - << formatSystemMessage(e); + << formatSystemMessageQ(e); return {}; } @@ -1075,7 +1088,7 @@ WindowsInfo::Release WindowsInfo::getRelease() const std::optional WindowsInfo::getElevated() const { - std::unique_ptr token; + HandlePtr token; { HANDLE rawToken = 0; @@ -1085,7 +1098,7 @@ std::optional WindowsInfo::getElevated() const qCritical() << "while trying to check if process is elevated, " - << "OpenProcessToken() failed: " << formatSystemMessage(e); + << "OpenProcessToken() failed: " << formatSystemMessageQ(e); return {}; } @@ -1101,7 +1114,7 @@ std::optional WindowsInfo::getElevated() const qCritical() << "while trying to check if process is elevated, " - << "GetTokenInformation() failed: " << formatSystemMessage(e); + << "GetTokenInformation() failed: " << formatSystemMessageQ(e); return {}; } @@ -1180,6 +1193,345 @@ QString SecurityFeature::toString() const return s; } + +struct Process +{ + std::wstring filename; + DWORD pid; + + Process(std::wstring f, DWORD id) + : filename(std::move(f)), pid(id) + { + } +}; + +std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE) +{ + DWORD bufferSize = MAX_PATH; + + for (int tries=0; tries<10; ++tries) + { + auto buffer = std::make_unique(bufferSize + 1); + std::fill(buffer.get(), buffer.get() + bufferSize + 1, 0); + + DWORD writtenSize = 0; + + if (process == INVALID_HANDLE_VALUE) { + // query this process + writtenSize = GetModuleFileNameW(0, buffer.get(), bufferSize); + } else { + // query another process + writtenSize = GetModuleBaseNameW(process, 0, buffer.get(), bufferSize); + } + + if (writtenSize == 0) { + const auto e = GetLastError(); + std::wcerr << formatSystemMessage(e) << L"\n"; + break; + } else if (writtenSize >= bufferSize) { + // buffer is too small, try again + bufferSize *= 2; + } else { + // if GetModuleFileName() works, `writtenSize` does not include the null + // terminator + const std::wstring s(buffer.get(), writtenSize); + const fs::path path(s); + + return path.filename().native(); + } + } + + + std::wstring what; + if (process == INVALID_HANDLE_VALUE) { + what = L"the current process"; + } else { + what = L"pid " + std::to_wstring(reinterpret_cast(process)); + } + + std::wcerr << L"failed to get filename for " << what << L"\n"; + return {}; +} + +std::vector runningProcessesIds() +{ + // initial size of 300 processes, unlikely to be more than that + std::size_t size = 300; + + for (int tries=0; tries<10; ++tries) { + auto ids = std::make_unique(size); + std::fill(ids.get(), ids.get() + size, 0); + + DWORD bytesGiven = static_cast(size * sizeof(ids[0])); + DWORD bytesWritten = 0; + + if (!EnumProcesses(ids.get(), bytesGiven, &bytesWritten)) + { + const auto e = GetLastError(); + + std::wcerr + << L"failed to enumerate processes, " + << formatSystemMessage(e) << L"\n"; + + return {}; + } + + if (bytesWritten == bytesGiven) { + size *= 2; + continue; + } + + const auto count = bytesWritten / sizeof(ids[0]); + return std::vector(ids.get(), ids.get() + count); + } + + std::cerr << L"too many processes to enumerate"; + return {}; +} + +std::vector runningProcesses() +{ + const auto pids = runningProcessesIds(); + std::vector v; + + for (const auto& pid : pids) { + if (pid == 0) { + // the idle process seems to be picked up by EnumProcesses() + continue; + } + + HandlePtr h(OpenProcess( + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)); + + if (!h) { + const auto e = GetLastError(); + + if (e != ERROR_ACCESS_DENIED) { + // don't log access denied, will happen a lot when not elevated + std::wcerr + << L"failed to open process " << pid << L", " + << formatSystemMessage(e) << L"\n"; + } + + continue; + } + + auto filename = processFilename(h.get()); + if (!filename.empty()) { + v.emplace_back(std::move(filename), pid); + } + } + + return v; +} + +DWORD findOtherPid() +{ + const std::wstring defaultName = L"ModOrganizer.exe"; + + std::wclog << L"looking for the other process...\n"; + + const auto thisPid = GetCurrentProcessId(); + std::wclog << L"this process id is " << thisPid << L"\n"; + + auto filename = processFilename(); + if (filename.empty()) { + std::wcerr + << L"can't get current process filename, defaulting to " + << defaultName << L"\n"; + + filename = defaultName; + } else { + std::wclog << L"this process filename is " << filename << L"\n"; + } + + const auto processes = runningProcesses(); + std::wclog << L"there are " << processes.size() << L" processes running\n"; + + for (const auto& p : processes) { + if (p.filename == filename) { + if (p.pid != thisPid) { + return p.pid; + } + } + } + + std::wclog + << L"no process with this filename\n" + << L"MO may not be running, or it may be running as administrator\n" + << L"you can try running this again as administrator\n"; + + return 0; +} + + +std::wstring tempDir() +{ + const DWORD bufferSize = MAX_PATH + 1; + wchar_t buffer[bufferSize + 1] = {}; + + const auto written = GetTempPathW(bufferSize, buffer); + if (written == 0) { + const auto e = GetLastError(); + + std::wcerr + << L"failed to get temp path, " << formatSystemMessage(e) << L"\n"; + + return {}; + } + + // `written` does not include the null terminator + return std::wstring(buffer, buffer + written); +} + +HandlePtr tempFile(const std::wstring dir) +{ + const auto now = std::time(0); + const auto tm = std::gmtime(&now); + + std::wostringstream oss; + oss + << L"ModOrganizer-" + << std::setw(4) << (1900 + tm->tm_year) + << std::setw(2) << std::setfill(L'0') << (tm->tm_mon + 1) + << std::setw(2) << std::setfill(L'0') << tm->tm_mday << "T" + << std::setw(2) << std::setfill(L'0') << tm->tm_hour + << std::setw(2) << std::setfill(L'0') << tm->tm_min + << std::setw(2) << std::setfill(L'0') << tm->tm_sec; + + const std::wstring prefix = oss.str(); + const std::wstring ext = L".dmp"; + + std::wstring path = dir + L"\\" + prefix + ext; + for (int i=0; i<100; ++i) { + std::wclog << L"trying file '" << path << L"'\n"; + + HandlePtr h (CreateFileW( + path.c_str(), GENERIC_WRITE, 0, nullptr, + CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr)); + + if (h.get() != INVALID_HANDLE_VALUE) { + return h; + } + + const auto e = GetLastError(); + if (e != ERROR_FILE_EXISTS) { + // probably no write access + std::wcerr + << L"failed to create dump file, " << formatSystemMessage(e) << L"\n"; + + return {}; + } + + path = dir + L"\\" + prefix + L"-" + std::to_wstring(i + 1) + ext; + } + + std::wcerr << L"can't create dump file, ran out of filenames\n"; + return {}; +} + +HandlePtr dumpFile() +{ + // try the current directory + HandlePtr h = tempFile(L"."); + if (h.get() != INVALID_HANDLE_VALUE) { + return h; + } + + std::wclog << L"cannot write dump file in current directory\n"; + + // try the temp directory + const auto dir = tempDir(); + + if (dir.empty()) { + std::wclog << L"can't get the temp directory\n"; + } else { + h = tempFile(dir.c_str()); + if (h.get() != INVALID_HANDLE_VALUE) { + return h; + } + } + + std::wcerr << L"nowhere to write the dump file\n"; + return {}; +} + +bool createMiniDump(HANDLE process, CoreDumpTypes type) +{ + const DWORD pid = GetProcessId(process); + + const HandlePtr file = dumpFile(); + if (!file) { + return false; + } + + auto flags = _MINIDUMP_TYPE( + MiniDumpNormal | + MiniDumpWithHandleData | + MiniDumpWithUnloadedModules | + MiniDumpWithProcessThreadData); + + if (type == CoreDumpTypes::Data) { + std::wclog << L"writing minidump with data\n"; + flags = _MINIDUMP_TYPE(flags | MiniDumpWithDataSegs); + } else if (type == CoreDumpTypes::Full) { + std::wclog << L"writing full minidump\n"; + flags = _MINIDUMP_TYPE(flags | MiniDumpWithFullMemory); + } else { + std::wclog << L"writing mini minidump\n"; + } + + const auto ret = MiniDumpWriteDump( + process, pid, file.get(), flags, nullptr, nullptr, nullptr); + + if (!ret) { + const auto e = GetLastError(); + + std::wcerr + << L"failed to write mini dump, " << formatSystemMessage(e) << L"\n"; + + return false; + } + + std::wclog << L"minidump written correctly\n"; + return true; +} + + +bool coredump(CoreDumpTypes type) +{ + std::wclog << L"creating minidump for the current process\n"; + return createMiniDump(GetCurrentProcess(), type); +} + +bool coredumpOther(CoreDumpTypes type) +{ + std::wclog << L"creating minidump for an running process\n"; + + const auto pid = findOtherPid(); + if (pid == 0) { + std::wcerr << L"no other process found\n"; + return false; + } + + std::wclog << L"found other process with pid " << pid << L"\n"; + + HandlePtr handle(OpenProcess( + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)); + + if (!handle) { + const auto e = GetLastError(); + + std::wcerr + << L"failed to open process " << pid << L", " + << formatSystemMessage(e) << L"\n"; + + return false; + } + + return createMiniDump(handle.get(), type); +} + } // namespace env } // namespace MOShared diff --git a/src/shared/util.h b/src/shared/util.h index 6b842f8c..2296651c 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -284,6 +284,23 @@ private: void getSecurityFeatures(); }; + +enum class CoreDumpTypes +{ + Mini = 1, + Data, + Full +}; + +// creates a minidump file for the given process +// +bool coredump(CoreDumpTypes type); + +// finds another process with the same name as this one and creates a minidump +// file for it +// +bool coredumpOther(CoreDumpTypes type); + } // namespace env -- cgit v1.3.1