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:
Scott Graham 2015-09-25 21:11:04 -07:00
Родитель 475ac81cce
Коммит 56c8359b27
4 изменённых файлов: 146 добавлений и 16 удалений

Просмотреть файл

@ -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;
}