mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 06:43:32 +00:00
Bug 1702086 - Part1: Introduce EnumerateProcessModules. r=gerald
This patch introduces `EnumerateProcessModules` to enumerate all loaded modules in the local process so that Gecko profiler and baseprofiler can use it. Differential Revision: https://phabricator.services.mozilla.com/D115252
This commit is contained in:
parent
0da3a4882d
commit
d64610fed5
@ -12,6 +12,7 @@
|
||||
|
||||
#include "mozilla/glue/WindowsUnicode.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/WindowsEnumProcessModules.h"
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
|
||||
#include <cctype>
|
||||
@ -114,7 +115,7 @@ static bool GetPdbInfo(uintptr_t aStart, std::string& aSignature,
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string GetVersion(wchar_t* dllPath) {
|
||||
static std::string GetVersion(const wchar_t* dllPath) {
|
||||
DWORD infoSize = GetFileVersionInfoSizeW(dllPath, nullptr);
|
||||
if (infoSize == 0) {
|
||||
return {};
|
||||
@ -144,42 +145,19 @@ static std::string GetVersion(wchar_t* dllPath) {
|
||||
SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
|
||||
SharedLibraryInfo sharedLibraryInfo;
|
||||
|
||||
HANDLE hProcess = GetCurrentProcess();
|
||||
mozilla::UniquePtr<HMODULE[]> hMods;
|
||||
size_t modulesNum = 0;
|
||||
if (hProcess != NULL) {
|
||||
DWORD modulesSize;
|
||||
if (!EnumProcessModules(hProcess, nullptr, 0, &modulesSize)) {
|
||||
return sharedLibraryInfo;
|
||||
}
|
||||
modulesNum = modulesSize / sizeof(HMODULE);
|
||||
hMods = mozilla::MakeUnique<HMODULE[]>(modulesNum);
|
||||
if (!EnumProcessModules(hProcess, hMods.get(), modulesNum * sizeof(HMODULE),
|
||||
&modulesSize)) {
|
||||
return sharedLibraryInfo;
|
||||
}
|
||||
// The list may have shrunk between calls
|
||||
if (modulesSize / sizeof(HMODULE) < modulesNum) {
|
||||
modulesNum = modulesSize / sizeof(HMODULE);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < modulesNum; i++) {
|
||||
wchar_t modulePath[MAX_PATH + 1];
|
||||
if (!GetModuleFileNameExW(hProcess, hMods[i], modulePath,
|
||||
std::size(modulePath))) {
|
||||
continue;
|
||||
}
|
||||
auto addSharedLibraryFromModuleInfo = [&sharedLibraryInfo](
|
||||
const wchar_t* aModulePath,
|
||||
HMODULE aModule) {
|
||||
mozilla::UniquePtr<char[]> utf8ModulePath(
|
||||
mozilla::glue::WideToUTF8(modulePath));
|
||||
mozilla::glue::WideToUTF8(aModulePath));
|
||||
if (!utf8ModulePath) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
MODULEINFO module = {0};
|
||||
if (!GetModuleInformation(hProcess, hMods[i], &module,
|
||||
if (!GetModuleInformation(mozilla::nt::kCurrentProcess, aModule, &module,
|
||||
sizeof(MODULEINFO))) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string modulePathStr(utf8ModulePath.get());
|
||||
@ -220,7 +198,7 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
|
||||
"000000000000000000000000000000000", moduleNameStr,
|
||||
modulePathStr, pdbNameStr, pdbNameStr, "", "");
|
||||
sharedLibraryInfo.AddSharedLibrary(shlib);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
#endif // !defined(_M_ARM64)
|
||||
|
||||
@ -238,7 +216,7 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
|
||||
// can read the memory mapped at the base address before we can safely
|
||||
// proceed to actually access those pages.
|
||||
HMODULE handleLock =
|
||||
LoadLibraryExW(modulePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
||||
LoadLibraryExW(aModulePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
||||
MEMORY_BASIC_INFORMATION vmemInfo = {0};
|
||||
std::string pdbSig;
|
||||
uint32_t pdbAge;
|
||||
@ -264,12 +242,13 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
|
||||
(uintptr_t)module.lpBaseOfDll + module.SizeOfImage,
|
||||
0, // DLLs are always mapped at offset 0 on Windows
|
||||
breakpadId, moduleNameStr, modulePathStr, pdbNameStr,
|
||||
pdbPathStr, GetVersion(modulePath), "");
|
||||
pdbPathStr, GetVersion(aModulePath), "");
|
||||
sharedLibraryInfo.AddSharedLibrary(shlib);
|
||||
|
||||
FreeLibrary(handleLock); // ok to free null handles
|
||||
}
|
||||
};
|
||||
|
||||
mozilla::EnumerateProcessModules(addSharedLibraryFromModuleInfo);
|
||||
return sharedLibraryInfo;
|
||||
}
|
||||
|
||||
|
61
mozglue/misc/WindowsEnumProcessModules.h
Normal file
61
mozglue/misc/WindowsEnumProcessModules.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_WindowsEnumProcessModules_h
|
||||
#define mozilla_WindowsEnumProcessModules_h
|
||||
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
|
||||
#include "mozilla/FunctionRef.h"
|
||||
#include "mozilla/NativeNt.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/WinHeaderOnlyUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Why don't we use CreateToolhelp32Snapshot instead of EnumProcessModules?
|
||||
// CreateToolhelp32Snapshot gets the ANSI versions of module path strings
|
||||
// via ntdll!RtlQueryProcessDebugInformation and stores them into a snapshot.
|
||||
// Module32FirstW/Module32NextW re-converts ANSI into Unicode, but it cannot
|
||||
// restore lost information. This means we still need GetModuleFileNameEx
|
||||
// even when we use CreateToolhelp32Snapshot, but EnumProcessModules is faster.
|
||||
inline bool EnumerateProcessModules(
|
||||
const FunctionRef<void(const wchar_t*, HMODULE)>& aCallback) {
|
||||
DWORD modulesSize;
|
||||
if (!::EnumProcessModules(nt::kCurrentProcess, nullptr, 0, &modulesSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD modulesNum = modulesSize / sizeof(HMODULE);
|
||||
UniquePtr<HMODULE[]> modules = MakeUnique<HMODULE[]>(modulesNum);
|
||||
if (!::EnumProcessModules(nt::kCurrentProcess, modules.get(),
|
||||
modulesNum * sizeof(HMODULE), &modulesSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The list may have shrunk between calls
|
||||
if (modulesSize / sizeof(HMODULE) < modulesNum) {
|
||||
modulesNum = modulesSize / sizeof(HMODULE);
|
||||
}
|
||||
|
||||
for (DWORD i = 0; i < modulesNum; ++i) {
|
||||
UniquePtr<wchar_t[]> modulePath = GetFullModulePath(modules[i]);
|
||||
if (!modulePath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Please note that modules[i] could be invalid if the module
|
||||
// was unloaded after GetFullModulePath succeeded.
|
||||
aCallback(modulePath.get(), modules[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_WindowsEnumProcessModules_h
|
@ -59,6 +59,7 @@ if CONFIG["OS_ARCH"] == "WINNT":
|
||||
"DynamicallyLinkedFunctionPtr.h",
|
||||
"ImportDir.h",
|
||||
"NativeNt.h",
|
||||
"WindowsEnumProcessModules.h",
|
||||
"WindowsMapRemoteView.h",
|
||||
"WindowsProcessMitigations.h",
|
||||
]
|
||||
|
@ -8,9 +8,11 @@
|
||||
#include "mozilla/NativeNt.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/WindowsEnumProcessModules.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
const wchar_t kNormal[] = L"Foo.dll";
|
||||
const wchar_t kHex12[] = L"Foo.ABCDEF012345.dll";
|
||||
@ -65,6 +67,127 @@ bool TestVirtualQuery(HANDLE aProcess, LPCVOID aAddress) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// This class copies the self executable file to the %temp%\<outer>\<inner>
|
||||
// folder. The length of its path is longer than MAX_PATH.
|
||||
class LongNameModule {
|
||||
wchar_t mOuterDirBuffer[MAX_PATH];
|
||||
wchar_t mInnerDirBuffer[MAX_PATH * 2];
|
||||
wchar_t mTargetFileBuffer[MAX_PATH * 2];
|
||||
|
||||
const wchar_t* mOuterDir;
|
||||
const wchar_t* mInnerDir;
|
||||
const wchar_t* mTargetFile;
|
||||
|
||||
public:
|
||||
explicit LongNameModule(const wchar_t* aNewLeafNameAfterCopy)
|
||||
: mOuterDir(nullptr), mInnerDir(nullptr), mTargetFile(nullptr) {
|
||||
const wchar_t kFolderName160Chars[] =
|
||||
L"0123456789ABCDEF0123456789ABCDEF"
|
||||
L"0123456789ABCDEF0123456789ABCDEF"
|
||||
L"0123456789ABCDEF0123456789ABCDEF"
|
||||
L"0123456789ABCDEF0123456789ABCDEF"
|
||||
L"0123456789ABCDEF0123456789ABCDEF";
|
||||
UniquePtr<wchar_t[]> thisExe = GetFullBinaryPath();
|
||||
if (!thisExe) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the buffer is too small, GetTempPathW returns the required
|
||||
// length including a null character, while on a successful case
|
||||
// it returns the number of copied characters which does not include
|
||||
// a null character. This means len == MAX_PATH should never happen
|
||||
// and len > MAX_PATH means GetTempPathW failed.
|
||||
wchar_t tempDir[MAX_PATH];
|
||||
DWORD len = ::GetTempPathW(MAX_PATH, tempDir);
|
||||
if (!len || len >= MAX_PATH) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (FAILED(::StringCbPrintfW(mOuterDirBuffer, sizeof(mOuterDirBuffer),
|
||||
L"\\\\?\\%s%s", tempDir,
|
||||
kFolderName160Chars)) ||
|
||||
!::CreateDirectoryW(mOuterDirBuffer, nullptr)) {
|
||||
return;
|
||||
}
|
||||
mOuterDir = mOuterDirBuffer;
|
||||
|
||||
if (FAILED(::StringCbPrintfW(mInnerDirBuffer, sizeof(mInnerDirBuffer),
|
||||
L"\\\\?\\%s%s\\%s", tempDir,
|
||||
kFolderName160Chars, kFolderName160Chars)) ||
|
||||
!::CreateDirectoryW(mInnerDirBuffer, nullptr)) {
|
||||
return;
|
||||
}
|
||||
mInnerDir = mInnerDirBuffer;
|
||||
|
||||
if (FAILED(::StringCbPrintfW(mTargetFileBuffer, sizeof(mTargetFileBuffer),
|
||||
L"\\\\?\\%s%s\\%s\\%s", tempDir,
|
||||
kFolderName160Chars, kFolderName160Chars,
|
||||
aNewLeafNameAfterCopy)) ||
|
||||
!::CopyFileW(thisExe.get(), mTargetFileBuffer,
|
||||
/*bFailIfExists*/ TRUE)) {
|
||||
return;
|
||||
}
|
||||
mTargetFile = mTargetFileBuffer;
|
||||
}
|
||||
|
||||
~LongNameModule() {
|
||||
if (mTargetFile) {
|
||||
::DeleteFileW(mTargetFile);
|
||||
}
|
||||
if (mInnerDir) {
|
||||
::RemoveDirectoryW(mInnerDir);
|
||||
}
|
||||
if (mOuterDir) {
|
||||
::RemoveDirectoryW(mOuterDir);
|
||||
}
|
||||
}
|
||||
|
||||
operator const wchar_t*() const { return mTargetFile; }
|
||||
};
|
||||
|
||||
bool TestModuleInfo() {
|
||||
UNICODE_STRING newLeafName;
|
||||
::RtlInitUnicodeString(&newLeafName,
|
||||
L"\u672D\u5E4C\u5473\u564C.\u30E9\u30FC\u30E1\u30F3");
|
||||
|
||||
LongNameModule longNameModule(newLeafName.Buffer);
|
||||
if (!longNameModule) {
|
||||
printf(
|
||||
"TEST-FAILED | NativeNt | "
|
||||
"Failed to copy the executable to a long directory path\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
{
|
||||
nsModuleHandle module(::LoadLibraryW(longNameModule));
|
||||
|
||||
bool detectedTarget = false;
|
||||
auto moduleCallback = [&](const wchar_t* aModulePath, HMODULE aModule) {
|
||||
UNICODE_STRING modulePath, moduleName;
|
||||
::RtlInitUnicodeString(&modulePath, aModulePath);
|
||||
GetLeafName(&moduleName, &modulePath);
|
||||
if (::RtlEqualUnicodeString(&moduleName, &newLeafName,
|
||||
/*aCaseInsensitive*/ TRUE)) {
|
||||
detectedTarget = true;
|
||||
}
|
||||
};
|
||||
|
||||
if (!mozilla::EnumerateProcessModules(moduleCallback)) {
|
||||
printf("TEST-FAILED | NativeNt | EnumerateProcessModules failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!detectedTarget) {
|
||||
printf(
|
||||
"TEST-FAILED | NativeNt | "
|
||||
"EnumerateProcessModules missed the target file\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LauncherResult<HMODULE> GetModuleHandleFromLeafName(const wchar_t* aName) {
|
||||
UNICODE_STRING name;
|
||||
::RtlInitUnicodeString(&name, aName);
|
||||
@ -290,6 +413,10 @@ int wmain(int argc, wchar_t* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!TestModuleInfo()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("TEST-PASS | NativeNt | All tests ran successfully\n");
|
||||
return 0;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "nsWindowsHelpers.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/WindowsEnumProcessModules.h"
|
||||
#include "mozilla/WindowsProcessMitigations.h"
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#include "nsNativeCharsetUtils.h"
|
||||
@ -79,7 +80,7 @@ static bool GetPdbInfo(uintptr_t aStart, nsID& aSignature, uint32_t& aAge,
|
||||
return true;
|
||||
}
|
||||
|
||||
static nsCString GetVersion(WCHAR* dllPath) {
|
||||
static nsCString GetVersion(const WCHAR* dllPath) {
|
||||
DWORD infoSize = GetFileVersionInfoSizeW(dllPath, nullptr);
|
||||
if (infoSize == 0) {
|
||||
return ""_ns;
|
||||
@ -110,40 +111,16 @@ static nsCString GetVersion(WCHAR* dllPath) {
|
||||
SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
|
||||
SharedLibraryInfo sharedLibraryInfo;
|
||||
|
||||
HANDLE hProcess = GetCurrentProcess();
|
||||
mozilla::UniquePtr<HMODULE[]> hMods;
|
||||
size_t modulesNum = 0;
|
||||
if (hProcess != NULL) {
|
||||
DWORD modulesSize;
|
||||
if (!EnumProcessModules(hProcess, nullptr, 0, &modulesSize)) {
|
||||
return sharedLibraryInfo;
|
||||
}
|
||||
modulesNum = modulesSize / sizeof(HMODULE);
|
||||
hMods = mozilla::MakeUnique<HMODULE[]>(modulesNum);
|
||||
if (!EnumProcessModules(hProcess, hMods.get(), modulesNum * sizeof(HMODULE),
|
||||
&modulesSize)) {
|
||||
return sharedLibraryInfo;
|
||||
}
|
||||
// The list may have shrunk between calls
|
||||
if (modulesSize / sizeof(HMODULE) < modulesNum) {
|
||||
modulesNum = modulesSize / sizeof(HMODULE);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < modulesNum; i++) {
|
||||
WCHAR modulePath[MAX_PATH + 1];
|
||||
if (!GetModuleFileNameEx(hProcess, hMods[i], modulePath,
|
||||
sizeof(modulePath) / sizeof(WCHAR))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto addSharedLibraryFromModuleInfo = [&sharedLibraryInfo](
|
||||
const wchar_t* aModulePath,
|
||||
HMODULE aModule) {
|
||||
MODULEINFO module = {0};
|
||||
if (!GetModuleInformation(hProcess, hMods[i], &module,
|
||||
if (!GetModuleInformation(mozilla::nt::kCurrentProcess, aModule, &module,
|
||||
sizeof(MODULEINFO))) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString modulePathStr(modulePath);
|
||||
nsAutoString modulePathStr(aModulePath);
|
||||
nsAutoString moduleNameStr = modulePathStr;
|
||||
int32_t pos = moduleNameStr.RFindCharInSet(u"\\/");
|
||||
if (pos != kNotFound) {
|
||||
@ -177,7 +154,7 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
|
||||
"000000000000000000000000000000000"_ns, moduleNameStr,
|
||||
modulePathStr, pdbNameStr, pdbNameStr, ""_ns, "");
|
||||
sharedLibraryInfo.AddSharedLibrary(shlib);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
#endif // !defined(_M_ARM64)
|
||||
|
||||
@ -199,7 +176,7 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
|
||||
// can read the memory mapped at the base address before we can safely
|
||||
// proceed to actually access those pages.
|
||||
HMODULE handleLock =
|
||||
LoadLibraryEx(modulePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
||||
LoadLibraryEx(aModulePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
||||
MEMORY_BASIC_INFORMATION vmemInfo = {0};
|
||||
nsID pdbSig;
|
||||
uint32_t pdbAge;
|
||||
@ -233,12 +210,13 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
|
||||
(uintptr_t)module.lpBaseOfDll + module.SizeOfImage,
|
||||
0, // DLLs are always mapped at offset 0 on Windows
|
||||
breakpadId, moduleNameStr, modulePathStr, pdbNameStr,
|
||||
pdbPathStr, GetVersion(modulePath), "");
|
||||
pdbPathStr, GetVersion(aModulePath), "");
|
||||
sharedLibraryInfo.AddSharedLibrary(shlib);
|
||||
|
||||
FreeLibrary(handleLock); // ok to free null handles
|
||||
}
|
||||
};
|
||||
|
||||
mozilla::EnumerateProcessModules(addSharedLibraryFromModuleInfo);
|
||||
return sharedLibraryInfo;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user