win: Gather memory information
Not yet written as MINIDUMP_MEMORY_INFO_LIST to minidump. R=mark@chromium.org BUG=crashpad:20, crashpad:46 Review URL: https://codereview.chromium.org/1369833002 .
This commit is contained in:
Родитель
475ac81cce
Коммит
56c8359b27
|
@ -253,12 +253,58 @@ bool ReadProcessData(HANDLE process,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ReadMemoryInfo(HANDLE process, ProcessInfo* process_info) {
|
||||
DCHECK(process_info->memory_info_.empty());
|
||||
|
||||
SYSTEM_INFO system_info;
|
||||
GetSystemInfo(&system_info);
|
||||
const WinVMAddress min_address =
|
||||
reinterpret_cast<WinVMAddress>(system_info.lpMinimumApplicationAddress);
|
||||
const WinVMAddress max_address =
|
||||
reinterpret_cast<WinVMAddress>(system_info.lpMaximumApplicationAddress);
|
||||
MEMORY_BASIC_INFORMATION memory_basic_information;
|
||||
for (WinVMAddress address = min_address; address <= max_address;
|
||||
address += memory_basic_information.RegionSize) {
|
||||
size_t result = VirtualQueryEx(process,
|
||||
reinterpret_cast<void*>(address),
|
||||
&memory_basic_information,
|
||||
sizeof(memory_basic_information));
|
||||
if (result == 0) {
|
||||
PLOG(ERROR) << "VirtualQueryEx";
|
||||
return false;
|
||||
}
|
||||
|
||||
process_info->memory_info_.push_back(
|
||||
ProcessInfo::MemoryInfo(memory_basic_information));
|
||||
|
||||
if (memory_basic_information.RegionSize == 0) {
|
||||
LOG(ERROR) << "RegionSize == 0";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() {
|
||||
}
|
||||
|
||||
ProcessInfo::Module::~Module() {
|
||||
}
|
||||
|
||||
ProcessInfo::MemoryInfo::MemoryInfo(const MEMORY_BASIC_INFORMATION& mbi)
|
||||
: base_address(reinterpret_cast<WinVMAddress>(mbi.BaseAddress)),
|
||||
region_size(mbi.RegionSize),
|
||||
allocation_base(reinterpret_cast<WinVMAddress>(mbi.AllocationBase)),
|
||||
state(mbi.State),
|
||||
allocation_protect(mbi.AllocationProtect),
|
||||
protect(mbi.Protect),
|
||||
type(mbi.Type) {
|
||||
}
|
||||
|
||||
ProcessInfo::MemoryInfo::~MemoryInfo() {
|
||||
}
|
||||
|
||||
ProcessInfo::ProcessInfo()
|
||||
: process_id_(),
|
||||
inherited_from_process_id_(),
|
||||
|
@ -266,6 +312,7 @@ ProcessInfo::ProcessInfo()
|
|||
peb_address_(0),
|
||||
peb_size_(0),
|
||||
modules_(),
|
||||
memory_info_(),
|
||||
is_64_bit_(false),
|
||||
is_wow64_(false),
|
||||
initialized_() {
|
||||
|
@ -320,6 +367,11 @@ bool ProcessInfo::Initialize(HANDLE process) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!ReadMemoryInfo(process, this)) {
|
||||
LOG(ERROR) << "ReadMemoryInfo failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||
return true;
|
||||
}
|
||||
|
@ -361,4 +413,9 @@ bool ProcessInfo::Modules(std::vector<Module>* modules) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
const std::vector<ProcessInfo::MemoryInfo>& ProcessInfo::MemoryInformation()
|
||||
const {
|
||||
return memory_info_;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
|
|
@ -49,6 +49,40 @@ class ProcessInfo {
|
|||
time_t timestamp;
|
||||
};
|
||||
|
||||
// \brief Contains information about a range of pages in the virtual address
|
||||
// space of a process.
|
||||
struct MemoryInfo {
|
||||
explicit MemoryInfo(const MEMORY_BASIC_INFORMATION& mbi);
|
||||
~MemoryInfo();
|
||||
|
||||
//! \brief The base address of the region of pages.
|
||||
WinVMAddress base_address;
|
||||
|
||||
//! \brief The size of the region beginning at base_address in bytes.
|
||||
WinVMSize region_size;
|
||||
|
||||
//! \brief The base address of a range of pages that was allocated by
|
||||
//! `VirtualAlloc()`. The page pointed to base_address is within this
|
||||
//! range of pages.
|
||||
WinVMAddress allocation_base;
|
||||
|
||||
//! \brief The state of the pages, one of `MEM_COMMIT`, `MEM_FREE`, or
|
||||
//! `MEM_RESERVE`.
|
||||
uint32_t state;
|
||||
|
||||
//! \brief The memory protection option when this page was originally
|
||||
//! allocated. This will be `PAGE_EXECUTE`, `PAGE_EXECUTE_READ`, etc.
|
||||
uint32_t allocation_protect;
|
||||
|
||||
//! \brief The current memoryprotection state. This will be `PAGE_EXECUTE`,
|
||||
//! `PAGE_EXECUTE_READ`, etc.
|
||||
uint32_t protect;
|
||||
|
||||
//! \brief The type of the pages. This will be one of `MEM_IMAGE`,
|
||||
//! `MEM_MAPPED`, or `MEM_PRIVATE`.
|
||||
uint32_t type;
|
||||
};
|
||||
|
||||
ProcessInfo();
|
||||
~ProcessInfo();
|
||||
|
||||
|
@ -92,6 +126,9 @@ class ProcessInfo {
|
|||
//! first element.
|
||||
bool Modules(std::vector<Module>* modules) const;
|
||||
|
||||
//! \brief Retrieves information about all pages mapped into the process.
|
||||
const std::vector<MemoryInfo>& MemoryInformation() const;
|
||||
|
||||
private:
|
||||
template <class Traits>
|
||||
friend bool GetProcessBasicInformation(HANDLE process,
|
||||
|
@ -104,12 +141,15 @@ class ProcessInfo {
|
|||
WinVMAddress peb_address_vmaddr,
|
||||
ProcessInfo* process_info);
|
||||
|
||||
friend bool ReadMemoryInfo(HANDLE process, ProcessInfo* process_info);
|
||||
|
||||
pid_t process_id_;
|
||||
pid_t inherited_from_process_id_;
|
||||
std::wstring command_line_;
|
||||
WinVMAddress peb_address_;
|
||||
WinVMSize peb_size_;
|
||||
std::vector<Module> modules_;
|
||||
std::vector<MemoryInfo> memory_info_;
|
||||
bool is_64_bit_;
|
||||
bool is_wow64_;
|
||||
InitializationStateDcheck initialized_;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "util/win/process_info.h"
|
||||
|
||||
#include <imagehlp.h>
|
||||
#include <intrin.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
|
@ -55,6 +56,26 @@ bool IsProcessWow64(HANDLE process_handle) {
|
|||
return is_wow64;
|
||||
}
|
||||
|
||||
void VerifyAddressInInCodePage(const ProcessInfo& process_info,
|
||||
WinVMAddress code_address) {
|
||||
// Make sure the child code address is an code page address with the right
|
||||
// information.
|
||||
const std::vector<ProcessInfo::MemoryInfo>& memory_info =
|
||||
process_info.MemoryInformation();
|
||||
bool found_region = false;
|
||||
for (const auto& mi : memory_info) {
|
||||
if (mi.base_address <= code_address &&
|
||||
mi.base_address + mi.region_size > code_address) {
|
||||
EXPECT_EQ(MEM_COMMIT, mi.state);
|
||||
EXPECT_EQ(PAGE_EXECUTE_READ, mi.protect);
|
||||
EXPECT_EQ(MEM_IMAGE, mi.type);
|
||||
EXPECT_FALSE(found_region);
|
||||
found_region = true;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(found_region);
|
||||
}
|
||||
|
||||
TEST(ProcessInfo, Self) {
|
||||
ProcessInfo process_info;
|
||||
ASSERT_TRUE(process_info.Initialize(GetCurrentProcess()));
|
||||
|
@ -101,17 +122,18 @@ TEST(ProcessInfo, Self) {
|
|||
// System modules are forced to particular stamps and the file header values
|
||||
// don't match the on-disk times. Just make sure we got some data here.
|
||||
EXPECT_GT(modules[1].timestamp, 0);
|
||||
|
||||
// Find something we know is a code address and confirm expected memory
|
||||
// information settings.
|
||||
VerifyAddressInInCodePage(process_info,
|
||||
reinterpret_cast<WinVMAddress>(_ReturnAddress()));
|
||||
}
|
||||
|
||||
void TestOtherProcess(const base::string16& directory_modification) {
|
||||
ProcessInfo process_info;
|
||||
|
||||
UUID started_uuid(UUID::InitializeWithNewTag{});
|
||||
UUID done_uuid(UUID::InitializeWithNewTag{});
|
||||
|
||||
ScopedKernelHANDLE started(
|
||||
CreateEvent(nullptr, true, false, started_uuid.ToString16().c_str()));
|
||||
ASSERT_TRUE(started.get());
|
||||
ScopedKernelHANDLE done(
|
||||
CreateEvent(nullptr, true, false, done_uuid.ToString16().c_str()));
|
||||
ASSERT_TRUE(done.get());
|
||||
|
@ -126,20 +148,20 @@ void TestOtherProcess(const base::string16& directory_modification) {
|
|||
.value();
|
||||
|
||||
std::wstring args;
|
||||
AppendCommandLineArgument(started_uuid.ToString16(), &args);
|
||||
args += L" ";
|
||||
AppendCommandLineArgument(done_uuid.ToString16(), &args);
|
||||
|
||||
ChildLauncher child(child_test_executable, args);
|
||||
child.Start();
|
||||
|
||||
// Wait until the test has completed initialization.
|
||||
ASSERT_EQ(WaitForSingleObject(started.get(), INFINITE), WAIT_OBJECT_0);
|
||||
// The child sends us a code address we can look up in the memory map.
|
||||
WinVMAddress code_address;
|
||||
CheckedReadFile(
|
||||
child.stdout_read_handle(), &code_address, sizeof(code_address));
|
||||
|
||||
ASSERT_TRUE(process_info.Initialize(child.process_handle()));
|
||||
|
||||
// Tell the test it's OK to shut down now that we've read our data.
|
||||
SetEvent(done.get());
|
||||
EXPECT_TRUE(SetEvent(done.get()));
|
||||
|
||||
std::vector<ProcessInfo::Module> modules;
|
||||
EXPECT_TRUE(process_info.Modules(&modules));
|
||||
|
@ -159,6 +181,8 @@ void TestOtherProcess(const base::string16& directory_modification) {
|
|||
EXPECT_EQ(kLz32dllName,
|
||||
modules.back().name.substr(modules.back().name.size() -
|
||||
wcslen(kLz32dllName)));
|
||||
|
||||
VerifyAddressInInCodePage(process_info, code_address);
|
||||
}
|
||||
|
||||
TEST(ProcessInfo, OtherProcess) {
|
||||
|
|
|
@ -12,19 +12,20 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <intrin.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <wchar.h>
|
||||
#include <windows.h>
|
||||
|
||||
// A simple binary to be loaded and inspected by ProcessInfo.
|
||||
int wmain(int argc, wchar_t** argv) {
|
||||
if (argc != 3)
|
||||
if (argc != 2)
|
||||
abort();
|
||||
|
||||
// Get handles to the events we use to communicate with our parent.
|
||||
HANDLE started_event = CreateEvent(nullptr, true, false, argv[1]);
|
||||
HANDLE done_event = CreateEvent(nullptr, true, false, argv[2]);
|
||||
if (!started_event || !done_event)
|
||||
// Get a handle to the event we use to communicate with our parent.
|
||||
HANDLE done_event = CreateEvent(nullptr, true, false, argv[1]);
|
||||
if (!done_event)
|
||||
abort();
|
||||
|
||||
// Load an unusual module (that we don't depend upon) so we can do an
|
||||
|
@ -32,14 +33,22 @@ int wmain(int argc, wchar_t** argv) {
|
|||
if (!LoadLibrary(L"lz32.dll"))
|
||||
abort();
|
||||
|
||||
if (!SetEvent(started_event))
|
||||
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (out == INVALID_HANDLE_VALUE)
|
||||
abort();
|
||||
// We just want any valid address that's known to be code.
|
||||
uint64_t code_address = reinterpret_cast<uint64_t>(_ReturnAddress());
|
||||
DWORD bytes_written;
|
||||
if (!WriteFile(
|
||||
out, &code_address, sizeof(code_address), &bytes_written, nullptr) ||
|
||||
bytes_written != sizeof(code_address)) {
|
||||
abort();
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(done_event, INFINITE) != WAIT_OBJECT_0)
|
||||
abort();
|
||||
|
||||
CloseHandle(done_event);
|
||||
CloseHandle(started_event);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче