| Index: base/memory/i_heart_windows_memory.cc
|
| diff --git a/base/memory/i_heart_windows_memory.cc b/base/memory/i_heart_windows_memory.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..70bc8623f35d9c2fb6dd04f3f7e9c700f2129afb
|
| --- /dev/null
|
| +++ b/base/memory/i_heart_windows_memory.cc
|
| @@ -0,0 +1,526 @@
|
| +#include <cstdio>
|
| +
|
| +#include <vector>
|
| +
|
| +#include "base/command_line.h"
|
| +#include "base/process/launch.h"
|
| +#include "base/process/process.h"
|
| +#include "base/process/process_metrics.h"
|
| +#include "base/threading/platform_thread.h"
|
| +
|
| +namespace {
|
| +using ProcessList = std::vector<base::Process*>;
|
| +
|
| +void SleepForever() {
|
| + for (;;) {
|
| + base::PlatformThread::Sleep(base::TimeDelta::FromMinutes(1));
|
| + }
|
| +}
|
| +
|
| +void LoadDll() {}
|
| +
|
| +void CreateSharedMemoryButDontShare() {}
|
| +
|
| +void ShareMeSomeMemory(int num_regions, ProcessList process_list) {
|
| + for (base::Process* process : process_list) {
|
| + CHECK(process);
|
| + }
|
| +}
|
| +
|
| +void ShareMeSomeCOWsAndTouchThem(int num_regions, ProcessList process_list) {
|
| + for (base::Process* process : process_list) {
|
| + CHECK(process);
|
| + }
|
| +}
|
| +
|
| +void ForceSwap() {
|
| + char* leak = new char[12345];
|
| + leak = nullptr;
|
| +}
|
| +
|
| +struct SavedStats {
|
| + size_t private_bytes;
|
| + size_t shared_bytes;
|
| + uint64_t pss_bytes;
|
| + base::WorkingSetKBytes working_set;
|
| + base::CommittedKBytes committed;
|
| +};
|
| +
|
| +void SaveStats(SavedStats* saved_stats, base::Process* process) {
|
| + std::unique_ptr<base::ProcessMetrics> metrics =
|
| + base::ProcessMetrics::CreateProcessMetrics(process->Handle());
|
| +
|
| + CHECK(metrics->GetMemoryBytes(&(saved_stats->private_bytes), &(saved_stats->shared_bytes)));
|
| + CHECK(metrics->GetProportionalSetSizeBytes(&(saved_stats->pss_bytes)));
|
| + CHECK(metrics->GetWorkingSetKBytes(&saved_stats->working_set));
|
| + metrics->GetCommittedKBytes(&saved_stats->committed);
|
| +}
|
| +
|
| +void DumpDiff(SavedStats* first, SavedStats* second) {
|
| + LOG(ERROR) << " - private kbytes: " << ((int64_t)second->private_bytes - (int64_t)first->private_bytes) / 1024;
|
| + LOG(ERROR) << " - shared kbytes: " << ((int64_t)second->shared_bytes - (int64_t)first->shared_bytes) / 1024;
|
| + LOG(ERROR) << " - pss kbytes: " << ((int64_t)second->pss_bytes - (int64_t)first->pss_bytes) / 1024;
|
| + LOG(ERROR) << " - workingset private kb: " << ((int64_t)second->working_set.priv - (int64_t)first->working_set.priv);
|
| + LOG(ERROR) << " - workingset mapped kb: " << ((int64_t)second->working_set.shareable - (int64_t)first->working_set.shareable);
|
| + LOG(ERROR) << " - workingset image kb: " << ((int64_t)second->working_set.shared - (int64_t)first->working_set.shared);
|
| + LOG(ERROR) << " - committed private kb: " << ((int64_t)second->committed.priv - (int64_t)first->committed.priv);
|
| + LOG(ERROR) << " - committed mapped kb: " << ((int64_t)second->committed.mapped - (int64_t)first->committed.mapped);
|
| + LOG(ERROR) << " - committed image kb: " << ((int64_t)second->committed.image - (int64_t)first->committed.image);
|
| + LOG(ERROR) << std::endl << std::endl;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +void malloc1() {
|
| + LOG(ERROR) << "malloc 16MB, and touch every page";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| +
|
| + char*a = (char*)malloc(16 * 1024 * 1024);
|
| + for (int i = 0; i < 16 * 1024 * 1024; ++i) {
|
| + a[i] = 'a';
|
| + }
|
| + SaveStats(&second, &process);
|
| +
|
| + DumpDiff(&first, &second);
|
| +}
|
| +
|
| +void malloc2() {
|
| + LOG(ERROR) << "malloc 16MB, but only touch every 16th page";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| +
|
| + char*a = (char*)malloc(16 * 1024 * 1024);
|
| + for(int i = 0; i < 16 * 1024 * 1024 / (16 * 4096); ++i) {
|
| + a[i * 16 * 4096] = 'a';
|
| + }
|
| + SaveStats(&second, &process);
|
| +
|
| + DumpDiff(&first, &second);
|
| +}
|
| +
|
| +void malloc3() {
|
| + LOG(ERROR) << "malloc 16MB, touch every page, then free";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| +
|
| + char*a = (char*)malloc(16 * 1024 * 1024);
|
| + for (int i = 0; i < 16 * 1024 * 1024; ++i) {
|
| + a[i] = 'a';
|
| + }
|
| + free(a);
|
| + SaveStats(&second, &process);
|
| +
|
| + DumpDiff(&first, &second);
|
| +}
|
| +
|
| +void malloc4() {
|
| + LOG(ERROR) << "malloc 16MB in 1024 chunks, touch every page, then free every chunk that isn't a multiple of 16";
|
| + LOG(ERROR) << "I expect resident memory to hit ~4MB, but instead it stays at 16MB! What's going on?";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + std::vector<char*> chunks;
|
| + chunks.resize(16 * 1024);
|
| +
|
| + SaveStats(&first, &process);
|
| + for (int i = 0; i < 16 * 1024; ++i) {
|
| + chunks[i] = (char*)malloc(1024);
|
| + chunks[i][0] = 'a';
|
| + }
|
| +
|
| + for (int i = 0; i < 16 * 1024; ++i) {
|
| + if (i % 16 != 0)
|
| + free(chunks[i]);
|
| + }
|
| + SaveStats(&second, &process);
|
| +
|
| + DumpDiff(&first, &second);
|
| +}
|
| +
|
| +void anonymous_file_mapping1() {
|
| + LOG(ERROR) << "create 16MB anonymous file mapping, and touch every page";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| + HANDLE h = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
|
| + 16 * 1024 * 1024, NULL);
|
| + char*a = (char*) MapViewOfFile(h, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 16 * 1024 *1024);
|
| + for (int i = 0; i < 16 * 1024 * 1024; ++i)
|
| + a[i] = 'a';
|
| + SaveStats(&second, &process);
|
| + DumpDiff(&first, &second);
|
| +}
|
| +
|
| +void anonymous_file_mapping2() {
|
| + LOG(ERROR) << "create 16MB anonymous file mapping";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| + CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
|
| + 16 * 1024 * 1024, NULL);
|
| + SaveStats(&second, &process);
|
| + DumpDiff(&first, &second);
|
| +}
|
| +void anonymous_file_mapping3() {
|
| + LOG(ERROR) << "create and map 16MB anonymous file mapping";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| + HANDLE h = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
|
| + 16 * 1024 * 1024, NULL);
|
| + MapViewOfFile(h, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 16 * 1024 *1024);
|
| + SaveStats(&second, &process);
|
| + DumpDiff(&first, &second);
|
| +}
|
| +
|
| +void virtual_alloc1() {
|
| + LOG(ERROR) << "virtual_alloc 16MB";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| +
|
| + VirtualAlloc(NULL, 16 * 1024 * 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
| + SaveStats(&second, &process);
|
| +
|
| + DumpDiff(&first, &second);
|
| +}
|
| +
|
| +void virtual_alloc2() {
|
| + LOG(ERROR) << "virtual_alloc 16MB, then write to 8MB of pages";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| +
|
| + char* a = (char*)VirtualAlloc(NULL, 16 * 1024 * 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
| + for (int i = 0; i < 8 * 1024 * 1024; ++i) {
|
| + a[i] = 'a';
|
| + }
|
| + SaveStats(&second, &process);
|
| +
|
| + DumpDiff(&first, &second);
|
| +}
|
| +
|
| +void virtual_alloc3() {
|
| + LOG(ERROR) << "virtual_alloc 16MB, then write to 8MB of pages, then decommits 4MB of the written pages";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| +
|
| + char* a = (char*)VirtualAlloc(NULL, 16 * 1024 * 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
| + for (int i = 0; i < 8 * 1024 * 1024; ++i) {
|
| + a[i] = 'a';
|
| + }
|
| + VirtualFree(a, 4 * 1024 * 1024, MEM_DECOMMIT);
|
| + SaveStats(&second, &process);
|
| +
|
| + DumpDiff(&first, &second);
|
| +}
|
| +
|
| +void virtual_alloc4() {
|
| + LOG(ERROR) << "virtual_alloc 16MB, then write to 8MB of pages, then reset 4MB of the written pages";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| +
|
| + char* a = (char*)VirtualAlloc(NULL, 16 * 1024 * 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
| + for (int i = 0; i < 8 * 1024 * 1024; ++i) {
|
| + a[i] = 'a';
|
| + }
|
| + VirtualAlloc(a, 4 * 1024 * 1024, MEM_RESET, PAGE_NOACCESS);
|
| + SaveStats(&second, &process);
|
| +
|
| + DumpDiff(&first, &second);
|
| +}
|
| +
|
| +void virtual_alloc5() {
|
| + LOG(ERROR) << "virtual_alloc 16MB, then write to 8MB of pages, then discard 4MB of the written pages";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| +
|
| + char* a = (char*)VirtualAlloc(NULL, 16 * 1024 * 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
| + for (int i = 0; i < 8 * 1024 * 1024; ++i) {
|
| + a[i] = 'a';
|
| + }
|
| + using DiscardVirtualMemoryFunction =
|
| + DWORD(WINAPI*)(PVOID virtualAddress, SIZE_T size);
|
| + DiscardVirtualMemoryFunction discard_virtual_memory =
|
| + reinterpret_cast<DiscardVirtualMemoryFunction>(GetProcAddress(
|
| + GetModuleHandle(L"Kernel32.dll"), "DiscardVirtualMemory"));
|
| + discard_virtual_memory(a, 4 * 1024 * 1024);
|
| + SaveStats(&second, &process);
|
| +
|
| + DumpDiff(&first, &second);
|
| +}
|
| +void heap_alloc1() {
|
| + LOG(ERROR) << "HeapAlloc 16MB";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| +
|
| + HeapAlloc(GetProcessHeap(), 0, 16 * 1024 * 1024);
|
| + SaveStats(&second, &process);
|
| +
|
| + DumpDiff(&first, &second);
|
| +}
|
| +void heap_alloc2() {
|
| + LOG(ERROR) << "HeapAlloc 16MB, and touch 8MB";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| +
|
| + char* a = (char*)HeapAlloc(GetProcessHeap(), 0, 16 * 1024 * 1024);
|
| + for (int i = 0; i < 8 * 1024 * 1024; ++i) {
|
| + a[i] = 'a';
|
| + }
|
| + SaveStats(&second, &process);
|
| +
|
| + DumpDiff(&first, &second);
|
| +}
|
| +void heap_alloc3() {
|
| + LOG(ERROR) << "HeapAlloc 16MB, and touch 8MB, then decommit 4MB";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| +
|
| + char* a = (char*)HeapAlloc(GetProcessHeap(), 0, 16 * 1024 * 1024);
|
| + for (int i = 0; i < 8 * 1024 * 1024; ++i) {
|
| + a[i] = 'a';
|
| + }
|
| + VirtualFree(a, 4 * 1024 * 1024, MEM_DECOMMIT);
|
| + SaveStats(&second, &process);
|
| +
|
| + DumpDiff(&first, &second);
|
| +}
|
| +
|
| +void leak_1gb() {
|
| + LOG(ERROR) << "leak 1gb";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| + char* a = (char*)malloc(1024 * 1024 * 1024);
|
| + for (int i = 0; i < 1024 * 1024 * 1024 / 4096; ++i)
|
| + a[i * 4096] = 'a';
|
| + SaveStats(&second, &process);
|
| + DumpDiff(&first, &second);
|
| +}
|
| +
|
| +void swap1() {
|
| + LOG(ERROR) << "This will crash your machine. Are you sure you want to continue (y)?";
|
| + char dummy;
|
| + scanf("%c", &dummy);
|
| + if (dummy != 'y')
|
| + return;
|
| +
|
| + LOG(ERROR) << "malloc 256MB, touch every page, then spawn children that consume 64GB of memory";
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| +
|
| + char*a = (char*)malloc(256 * 1024 * 1024);
|
| + for (int i = 0; i < 256 * 1024 * 1024 / 4096; ++i) {
|
| + a[i * 4096] = 'a';
|
| + }
|
| + for (int i = 0; i < 64; ++i) {
|
| + base::LaunchOptions options;
|
| + base::CommandLine child_command(*base::CommandLine::ForCurrentProcess());
|
| + child_command.AppendSwitch("leak_1gb");
|
| + base::LaunchProcess(child_command, options);
|
| + }
|
| +
|
| + // Wait 100 seconds for memory to blow up.
|
| + base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(100));
|
| + SaveStats(&second, &process);
|
| + DumpDiff(&first, &second);
|
| +}
|
| +
|
| +void swap2() {
|
| + LOG(ERROR) << "Spawns 256 process, each of which commits 1GB with VirtualAlloc but only makes 4MB of pages resident.";
|
| + for (int i = 0; i < 256; ++i) {
|
| + base::LaunchOptions options;
|
| + base::CommandLine child_command(*base::CommandLine::ForCurrentProcess());
|
| + child_command.AppendSwitch("swap2_child");
|
| + base::LaunchProcess(child_command, options);
|
| + }
|
| +
|
| + // Wait 3 seconds for memory to blow up.
|
| + base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(3));
|
| +
|
| + LOG(ERROR) << "try to allocate 1GB using malloc";
|
| + char*b = (char*)malloc(1024 * 1024 * 1024);
|
| + CHECK(b);
|
| + for (int i = 0; i < 1024 * 1024 * 1024 / 4096; ++i) {
|
| + b[i * 4096] = 'a';
|
| + }
|
| + LOG(ERROR) << "allocated 1GB using malloc";
|
| +
|
| + LOG(ERROR) << "try to allocate 1GB using virtual alloc";
|
| + for (int i = 0; i < 1024; i++) {
|
| + char* a = (char*)VirtualAlloc(NULL, 1024 * 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
| + CHECK(a);
|
| + a[0] = 'a';
|
| + }
|
| + LOG(ERROR) << "allocated 1GB using virtual alloc";
|
| +}
|
| +
|
| +void swap2_child() {
|
| + base::Process process(base::Process::Current());
|
| + SavedStats first, second;
|
| + SaveStats(&first, &process);
|
| + for (int i = 0; i < 1024; i++) {
|
| + char* a = (char*)VirtualAlloc(NULL, 1024 * 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
| +
|
| + // Occasional errors.
|
| + if (!a)
|
| + continue;
|
| +
|
| + a[0] = 'a';
|
| + }
|
| + SaveStats(&second, &process);
|
| + DumpDiff(&first, &second);
|
| +}
|
| +
|
| +int main(int argc, char* argv[]) {
|
| + base::CommandLine::Init(argc, argv);
|
| + const base::CommandLine& current_command =
|
| + *base::CommandLine::ForCurrentProcess();
|
| +
|
| + if (current_command.HasSwitch("sleepy")) {
|
| + new char[base::Process::Current().Pid()];
|
| + SleepForever();
|
| + }
|
| +
|
| + if (current_command.HasSwitch("malloc1")) {
|
| + malloc1();
|
| + SleepForever();
|
| + }
|
| +
|
| + if (current_command.HasSwitch("malloc2")) {
|
| + malloc2();
|
| + SleepForever();
|
| + }
|
| +
|
| + if (current_command.HasSwitch("malloc3")) {
|
| + malloc3();
|
| + SleepForever();
|
| + }
|
| +
|
| + if (current_command.HasSwitch("malloc4")) {
|
| + malloc4();
|
| + SleepForever();
|
| + }
|
| +
|
| + if (current_command.HasSwitch("anonymous_file_mapping1")) {
|
| + anonymous_file_mapping1();
|
| + SleepForever();
|
| + }
|
| +
|
| + if (current_command.HasSwitch("anonymous_file_mapping2")) {
|
| + anonymous_file_mapping2();
|
| + SleepForever();
|
| + }
|
| + if (current_command.HasSwitch("anonymous_file_mapping3")) {
|
| + anonymous_file_mapping3();
|
| + SleepForever();
|
| + }
|
| + if (current_command.HasSwitch("virtual_alloc1")) {
|
| + virtual_alloc1();
|
| + SleepForever();
|
| + }
|
| + if (current_command.HasSwitch("virtual_alloc2")) {
|
| + virtual_alloc2();
|
| + SleepForever();
|
| + }
|
| + if (current_command.HasSwitch("virtual_alloc3")) {
|
| + virtual_alloc3();
|
| + SleepForever();
|
| + }
|
| + if (current_command.HasSwitch("virtual_alloc4")) {
|
| + virtual_alloc4();
|
| + SleepForever();
|
| + }
|
| + if (current_command.HasSwitch("virtual_alloc5")) {
|
| + virtual_alloc4();
|
| + SleepForever();
|
| + }
|
| + if (current_command.HasSwitch("heap_alloc1")) {
|
| + heap_alloc1();
|
| + SleepForever();
|
| + }
|
| + if (current_command.HasSwitch("heap_alloc2")) {
|
| + heap_alloc2();
|
| + SleepForever();
|
| + }
|
| + if (current_command.HasSwitch("heap_alloc3")) {
|
| + heap_alloc3();
|
| + SleepForever();
|
| + }
|
| + if (current_command.HasSwitch("leak_1gb")) {
|
| + leak_1gb();
|
| + SleepForever();
|
| + }
|
| + if (current_command.HasSwitch("swap1")) {
|
| + swap1();
|
| + SleepForever();
|
| + }
|
| + if (current_command.HasSwitch("swap2_child")) {
|
| + swap2_child();
|
| + SleepForever();
|
| + }
|
| + if (current_command.HasSwitch("swap2")) {
|
| + swap2();
|
| + SleepForever();
|
| + }
|
| + // Launch 2 child processes to mess with.
|
| + // base::LaunchOptions options;
|
| + base::Process a(base::Process::Current());
|
| + // base::CommandLine child_command(current_command);
|
| + // child_command.AppendSwitch("sleepy");
|
| + // base::Process b(base::LaunchProcess(child_command, options));
|
| + // base::Process c(base::LaunchProcess(child_command, options));
|
| +
|
| + // // Test sharing a DLL.
|
| + // LoadDll();
|
| +
|
| + // // Test shared memory that's not shared.
|
| + // CreateSharedMemoryButDontShare();
|
| +
|
| + // // Test sharing with just one other process.
|
| + // ShareMeSomeMemory(2, {&b});
|
| + // ShareMeSomeMemory(4, {&c});
|
| +
|
| + // // Test sharing with bunches of processes.
|
| + // ShareMeSomeMemory(7, {&b, &c});
|
| +
|
| + // // Test sharing Copy on write pgae.
|
| + // ShareMeSomeCOWsAndTouchThem(12, {&b, &c});
|
| +
|
| + // // Force us to swap out memory.
|
| + // ForceSwap();
|
| + // DumpStats({&a});
|
| +
|
| + // for (int i = 0; i < 1000; ++i) {
|
| + // char*a = (char*)malloc(4096 * 16);
|
| + // *a = 'a';
|
| + // // for (int j = 0; j < 16; ++j)
|
| + // // a[j * 4096] = 'a';
|
| + // // base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
|
| + // }
|
| +
|
| + // // Print out stats.
|
| + // DumpStats({&a});
|
| + //DumpStats({&a, &b, &c});
|
| +
|
| + // Wait until input so it can be inspected.
|
| + char dummy;
|
| + scanf("%c", &dummy);
|
| +
|
| + // Clean up 2 child processes.
|
| + // b.Terminate(0, true);
|
| + // c.Terminate(0, true);
|
| +
|
| + return 0;
|
| +}
|
|
|