mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
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:
parent
475ac81cce
commit
56c8359b27
@ -253,12 +253,58 @@ bool ReadProcessData(HANDLE process,
|
|||||||
return true;
|
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() : name(), dll_base(0), size(0), timestamp() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessInfo::Module::~Module() {
|
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()
|
ProcessInfo::ProcessInfo()
|
||||||
: process_id_(),
|
: process_id_(),
|
||||||
inherited_from_process_id_(),
|
inherited_from_process_id_(),
|
||||||
@ -266,6 +312,7 @@ ProcessInfo::ProcessInfo()
|
|||||||
peb_address_(0),
|
peb_address_(0),
|
||||||
peb_size_(0),
|
peb_size_(0),
|
||||||
modules_(),
|
modules_(),
|
||||||
|
memory_info_(),
|
||||||
is_64_bit_(false),
|
is_64_bit_(false),
|
||||||
is_wow64_(false),
|
is_wow64_(false),
|
||||||
initialized_() {
|
initialized_() {
|
||||||
@ -320,6 +367,11 @@ bool ProcessInfo::Initialize(HANDLE process) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ReadMemoryInfo(process, this)) {
|
||||||
|
LOG(ERROR) << "ReadMemoryInfo failed";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -361,4 +413,9 @@ bool ProcessInfo::Modules(std::vector<Module>* modules) const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<ProcessInfo::MemoryInfo>& ProcessInfo::MemoryInformation()
|
||||||
|
const {
|
||||||
|
return memory_info_;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -49,6 +49,40 @@ class ProcessInfo {
|
|||||||
time_t timestamp;
|
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();
|
||||||
~ProcessInfo();
|
~ProcessInfo();
|
||||||
|
|
||||||
@ -92,6 +126,9 @@ class ProcessInfo {
|
|||||||
//! first element.
|
//! first element.
|
||||||
bool Modules(std::vector<Module>* modules) const;
|
bool Modules(std::vector<Module>* modules) const;
|
||||||
|
|
||||||
|
//! \brief Retrieves information about all pages mapped into the process.
|
||||||
|
const std::vector<MemoryInfo>& MemoryInformation() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <class Traits>
|
template <class Traits>
|
||||||
friend bool GetProcessBasicInformation(HANDLE process,
|
friend bool GetProcessBasicInformation(HANDLE process,
|
||||||
@ -104,12 +141,15 @@ class ProcessInfo {
|
|||||||
WinVMAddress peb_address_vmaddr,
|
WinVMAddress peb_address_vmaddr,
|
||||||
ProcessInfo* process_info);
|
ProcessInfo* process_info);
|
||||||
|
|
||||||
|
friend bool ReadMemoryInfo(HANDLE process, ProcessInfo* process_info);
|
||||||
|
|
||||||
pid_t process_id_;
|
pid_t process_id_;
|
||||||
pid_t inherited_from_process_id_;
|
pid_t inherited_from_process_id_;
|
||||||
std::wstring command_line_;
|
std::wstring command_line_;
|
||||||
WinVMAddress peb_address_;
|
WinVMAddress peb_address_;
|
||||||
WinVMSize peb_size_;
|
WinVMSize peb_size_;
|
||||||
std::vector<Module> modules_;
|
std::vector<Module> modules_;
|
||||||
|
std::vector<MemoryInfo> memory_info_;
|
||||||
bool is_64_bit_;
|
bool is_64_bit_;
|
||||||
bool is_wow64_;
|
bool is_wow64_;
|
||||||
InitializationStateDcheck initialized_;
|
InitializationStateDcheck initialized_;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "util/win/process_info.h"
|
#include "util/win/process_info.h"
|
||||||
|
|
||||||
#include <imagehlp.h>
|
#include <imagehlp.h>
|
||||||
|
#include <intrin.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
|
||||||
#include "base/files/file_path.h"
|
#include "base/files/file_path.h"
|
||||||
@ -55,6 +56,26 @@ bool IsProcessWow64(HANDLE process_handle) {
|
|||||||
return is_wow64;
|
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) {
|
TEST(ProcessInfo, Self) {
|
||||||
ProcessInfo process_info;
|
ProcessInfo process_info;
|
||||||
ASSERT_TRUE(process_info.Initialize(GetCurrentProcess()));
|
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
|
// 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.
|
// don't match the on-disk times. Just make sure we got some data here.
|
||||||
EXPECT_GT(modules[1].timestamp, 0);
|
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) {
|
void TestOtherProcess(const base::string16& directory_modification) {
|
||||||
ProcessInfo process_info;
|
ProcessInfo process_info;
|
||||||
|
|
||||||
UUID started_uuid(UUID::InitializeWithNewTag{});
|
|
||||||
UUID done_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(
|
ScopedKernelHANDLE done(
|
||||||
CreateEvent(nullptr, true, false, done_uuid.ToString16().c_str()));
|
CreateEvent(nullptr, true, false, done_uuid.ToString16().c_str()));
|
||||||
ASSERT_TRUE(done.get());
|
ASSERT_TRUE(done.get());
|
||||||
@ -126,20 +148,20 @@ void TestOtherProcess(const base::string16& directory_modification) {
|
|||||||
.value();
|
.value();
|
||||||
|
|
||||||
std::wstring args;
|
std::wstring args;
|
||||||
AppendCommandLineArgument(started_uuid.ToString16(), &args);
|
|
||||||
args += L" ";
|
|
||||||
AppendCommandLineArgument(done_uuid.ToString16(), &args);
|
AppendCommandLineArgument(done_uuid.ToString16(), &args);
|
||||||
|
|
||||||
ChildLauncher child(child_test_executable, args);
|
ChildLauncher child(child_test_executable, args);
|
||||||
child.Start();
|
child.Start();
|
||||||
|
|
||||||
// Wait until the test has completed initialization.
|
// The child sends us a code address we can look up in the memory map.
|
||||||
ASSERT_EQ(WaitForSingleObject(started.get(), INFINITE), WAIT_OBJECT_0);
|
WinVMAddress code_address;
|
||||||
|
CheckedReadFile(
|
||||||
|
child.stdout_read_handle(), &code_address, sizeof(code_address));
|
||||||
|
|
||||||
ASSERT_TRUE(process_info.Initialize(child.process_handle()));
|
ASSERT_TRUE(process_info.Initialize(child.process_handle()));
|
||||||
|
|
||||||
// Tell the test it's OK to shut down now that we've read our data.
|
// 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;
|
std::vector<ProcessInfo::Module> modules;
|
||||||
EXPECT_TRUE(process_info.Modules(&modules));
|
EXPECT_TRUE(process_info.Modules(&modules));
|
||||||
@ -159,6 +181,8 @@ void TestOtherProcess(const base::string16& directory_modification) {
|
|||||||
EXPECT_EQ(kLz32dllName,
|
EXPECT_EQ(kLz32dllName,
|
||||||
modules.back().name.substr(modules.back().name.size() -
|
modules.back().name.substr(modules.back().name.size() -
|
||||||
wcslen(kLz32dllName)));
|
wcslen(kLz32dllName)));
|
||||||
|
|
||||||
|
VerifyAddressInInCodePage(process_info, code_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ProcessInfo, OtherProcess) {
|
TEST(ProcessInfo, OtherProcess) {
|
||||||
|
@ -12,19 +12,20 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <intrin.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
// A simple binary to be loaded and inspected by ProcessInfo.
|
// A simple binary to be loaded and inspected by ProcessInfo.
|
||||||
int wmain(int argc, wchar_t** argv) {
|
int wmain(int argc, wchar_t** argv) {
|
||||||
if (argc != 3)
|
if (argc != 2)
|
||||||
abort();
|
abort();
|
||||||
|
|
||||||
// Get handles to the events we use to communicate with our parent.
|
// Get a handle to the event we use to communicate with our parent.
|
||||||
HANDLE started_event = CreateEvent(nullptr, true, false, argv[1]);
|
HANDLE done_event = CreateEvent(nullptr, true, false, argv[1]);
|
||||||
HANDLE done_event = CreateEvent(nullptr, true, false, argv[2]);
|
if (!done_event)
|
||||||
if (!started_event || !done_event)
|
|
||||||
abort();
|
abort();
|
||||||
|
|
||||||
// Load an unusual module (that we don't depend upon) so we can do an
|
// 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"))
|
if (!LoadLibrary(L"lz32.dll"))
|
||||||
abort();
|
abort();
|
||||||
|
|
||||||
if (!SetEvent(started_event))
|
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
if (out == INVALID_HANDLE_VALUE)
|
||||||
abort();
|
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)
|
if (WaitForSingleObject(done_event, INFINITE) != WAIT_OBJECT_0)
|
||||||
abort();
|
abort();
|
||||||
|
|
||||||
CloseHandle(done_event);
|
CloseHandle(done_event);
|
||||||
CloseHandle(started_event);
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user