diff options
author | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2021-09-08 17:58:28 +0300 |
---|---|---|
committer | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2021-09-08 17:58:28 +0300 |
commit | afbac282eda46b9d6cc6b8ab9a959fcdbe1ef606 (patch) | |
tree | d076bad1000c286dc65ad727c9cda7bbebf46ab9 | |
parent | bcc600db9feb6aa50e0880422a071d0e39ae74aa (diff) |
current_process_memory_usage: Add system memory stats.
-rw-r--r-- | include/llfio/revision.hpp | 6 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/posix/utils.ipp | 340 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/import.hpp | 28 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/utils.ipp | 61 | ||||
-rw-r--r-- | include/llfio/v2.0/utils.hpp | 24 | ||||
-rw-r--r-- | test/tests/utils.cpp | 93 |
6 files changed, 339 insertions, 213 deletions
diff --git a/include/llfio/revision.hpp b/include/llfio/revision.hpp index 08851c80..d7f4cf22 100644 --- a/include/llfio/revision.hpp +++ b/include/llfio/revision.hpp @@ -1,4 +1,4 @@ // Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time -#define LLFIO_PREVIOUS_COMMIT_REF 8cec4ff8fcd601b774c0ed882fe869e73d23d136 -#define LLFIO_PREVIOUS_COMMIT_DATE "2021-08-30 14:53:35 +00:00" -#define LLFIO_PREVIOUS_COMMIT_UNIQUE 8cec4ff8 +#define LLFIO_PREVIOUS_COMMIT_REF bcc600db9feb6aa50e0880422a071d0e39ae74aa +#define LLFIO_PREVIOUS_COMMIT_DATE "2021-08-30 15:08:05 +00:00" +#define LLFIO_PREVIOUS_COMMIT_UNIQUE bcc600db diff --git a/include/llfio/v2.0/detail/impl/posix/utils.ipp b/include/llfio/v2.0/detail/impl/posix/utils.ipp index 15fe1a5a..ccd634e1 100644 --- a/include/llfio/v2.0/detail/impl/posix/utils.ipp +++ b/include/llfio/v2.0/detail/impl/posix/utils.ipp @@ -341,156 +341,187 @@ namespace utils private_paged_in = ??? MISSING */ - if(!(want & process_memory_usage::want::private_committed) || (want & process_memory_usage::want::private_committed_inaccurate)) + process_memory_usage ret; + if(!!(want & process_memory_usage::want::this_process)) { - process_memory_usage ret; - if((want & process_memory_usage::want::total_address_space_in_use) || (want & process_memory_usage::want::total_address_space_paged_in) || - (want & process_memory_usage::want::private_paged_in)) - { - std::vector<char> buffer(256); - OUTCOME_TRY(fill_buffer(buffer, "/proc/self/statm")); - if(buffer.size() > 1) - { - size_t file_and_shared_pages_paged_in = 0; - sscanf(buffer.data(), "%zu %zu %zu", &ret.total_address_space_in_use, &ret.total_address_space_paged_in, &file_and_shared_pages_paged_in); - ret.private_paged_in = ret.total_address_space_paged_in - file_and_shared_pages_paged_in; - ret.total_address_space_in_use *= page_size(); - ret.total_address_space_paged_in *= page_size(); - ret.private_paged_in *= page_size(); - //std::cout << string_view(buffer.data(), buffer.size()) << std::endl; - } - } - if(want & process_memory_usage::want::private_committed) + if(!(want & process_memory_usage::want::private_committed) || (want & process_memory_usage::want::private_committed_inaccurate)) { - std::vector<char> smaps_rollup(256), maps(65536); - auto r = fill_buffer(smaps_rollup, "/proc/self/smaps_rollup"); - if(!r) + if((want & process_memory_usage::want::total_address_space_in_use) || (want & process_memory_usage::want::total_address_space_paged_in) || + (want & process_memory_usage::want::private_paged_in)) { - if(r.error() == errc::no_such_file_or_directory) + std::vector<char> buffer(256); + OUTCOME_TRY(fill_buffer(buffer, "/proc/self/statm")); + if(buffer.size() > 1) { - // Linux kernel is too old - return errc::operation_not_supported; + size_t file_and_shared_pages_paged_in = 0; + sscanf(buffer.data(), "%zu %zu %zu", &ret.total_address_space_in_use, &ret.total_address_space_paged_in, &file_and_shared_pages_paged_in); + ret.private_paged_in = ret.total_address_space_paged_in - file_and_shared_pages_paged_in; + ret.total_address_space_in_use *= page_size(); + ret.total_address_space_paged_in *= page_size(); + ret.private_paged_in *= page_size(); + // std::cout << string_view(buffer.data(), buffer.size()) << std::endl; } - return std::move(r).error(); } - OUTCOME_TRY(fill_buffer(maps, "/proc/self/maps")); - uint64_t lazyfree = 0; + if(want & process_memory_usage::want::private_committed) { - string_view i(smaps_rollup.data(), smaps_rollup.size()); - OUTCOME_TRY(lazyfree, parse(i, "\nLazyFree:")); + std::vector<char> smaps_rollup(256), maps(65536); + auto r = fill_buffer(smaps_rollup, "/proc/self/smaps_rollup"); + if(!r) + { + if(r.error() == errc::no_such_file_or_directory) + { + // Linux kernel is too old + return errc::operation_not_supported; + } + return std::move(r).error(); + } + OUTCOME_TRY(fill_buffer(maps, "/proc/self/maps")); + uint64_t lazyfree = 0; + { + string_view i(smaps_rollup.data(), smaps_rollup.size()); + OUTCOME_TRY(lazyfree, parse(i, "\nLazyFree:")); + } + string_view i(maps.data(), maps.size()); + size_t anonymous = 0; + for(size_t idx = 0;;) + { + idx = i.find("\n", idx); + if(idx == i.npos) + { + break; + } + idx++; + size_t start = 0, end = 0, inode = 1; + char read = 0, write = 0, executable = 0, private_ = 0; + sscanf(i.data() + idx, "%zx-%zx %c%c%c%c %*u %*u:%*u %zd", &start, &end, &read, &write, &executable, &private_, &inode); + if(inode == 0 && read == 'r' && write == 'w' && executable == '-' && private_ == 'p') + { + anonymous += end - start; + // std::cout << (end - start) << " " << i.substr(idx, 40) << std::endl; + } + } + if(lazyfree != (uint64_t) -1) + { + anonymous -= (size_t) lazyfree; + } + ret.private_committed = anonymous; } - string_view i(maps.data(), maps.size()); - size_t anonymous = 0; - for(size_t idx = 0;;) + } + else + { + std::vector<char> buffer(1024 * 1024); + OUTCOME_TRY(fill_buffer(buffer, "/proc/self/smaps")); + const string_view totalview(buffer.data(), buffer.size()); + // std::cerr << totalview << std::endl; + std::vector<string_view> anon_entries, non_anon_entries; + anon_entries.reserve(32); + non_anon_entries.reserve(32); + auto find_item = [&](size_t idx) -> string_view { + auto x = totalview.rfind("\nSize:", idx); + if(x == string_view::npos) + { + return {}; + } + x = totalview.rfind("\n", x - 1); + if(x == string_view::npos) + { + x = 0; + } + else + { + x++; + } + return totalview.substr(x, idx - x); + }; + for(string_view item = find_item(totalview.size()); item != string_view(); item = find_item(item.data() - totalview.data())) { - idx = i.find("\n", idx); - if(idx == i.npos) + // std::cout << "***" << item << "***"; + // hexaddr-hexaddr flags offset dev:id inode [path] + size_t inode = 1; + sscanf(item.data(), "%*x-%*x %*c%*c%*c%*c %*x %*c%*c:%*c%*c %zu", &inode); + auto vmflagsidx = item.rfind("\nVmFlags:"); + if(vmflagsidx == string_view::npos) { - break; + return errc::illegal_byte_sequence; + } + // Is there " ac" after vmflagsidx? + if(string_view::npos != item.find(" ac", vmflagsidx) && inode == 0) + { + // std::cerr << "Adding anon entry at offset " << itemtopidx << std::endl; + anon_entries.push_back(item); } - idx++; - size_t start = 0, end = 0, inode = 1; - char read = 0, write = 0, executable = 0, private_ = 0; - sscanf(i.data() + idx, "%zx-%zx %c%c%c%c %*u %*u:%*u %zd", &start, &end, &read, &write, &executable, &private_, &inode); - if(inode == 0 && read == 'r' && write == 'w' && executable == '-' && private_ == 'p') + else { - anonymous += end - start; - // std::cout << (end - start) << " " << i.substr(idx, 40) << std::endl; + // std::cerr << "Adding non-anon entry at offset " << itemtopidx << std::endl; + non_anon_entries.push_back(item); } } - if(lazyfree != (uint64_t) -1) + // std::cerr << "Anon entries:"; + for(auto &i : anon_entries) { - anonymous -= (size_t) lazyfree; + OUTCOME_TRY(auto &&size, parse(i, "\nSize:")); + OUTCOME_TRY(auto &&rss, parse(i, "\nRss:")); + OUTCOME_TRY(auto &&anonymous, parse(i, "\nAnonymous:")); + OUTCOME_TRY(auto &&lazyfree, parse(i, "\nLazyFree:")); + if(size != (uint64_t) -1 && rss != (uint64_t) -1 && anonymous != (uint64_t) -1) + { + ret.total_address_space_in_use += size; + ret.total_address_space_paged_in += rss; + ret.private_committed += anonymous; + if(lazyfree != (uint64_t) -1) + { + ret.total_address_space_paged_in -= lazyfree; + ret.private_committed -= lazyfree; + } + ret.private_paged_in += rss; + } + // std::cerr << i << "\nSize = " << size << " Rss = " << rss << std::endl; } - ret.private_committed = anonymous; - } - return ret; - } - std::vector<char> buffer(1024 * 1024); - OUTCOME_TRY(fill_buffer(buffer, "/proc/self/smaps")); - const string_view totalview(buffer.data(), buffer.size()); - // std::cerr << totalview << std::endl; - std::vector<string_view> anon_entries, non_anon_entries; - anon_entries.reserve(32); - non_anon_entries.reserve(32); - auto find_item = [&](size_t idx) -> string_view { - auto x = totalview.rfind("\nSize:", idx); - if(x == string_view::npos) - { - return {}; - } - x = totalview.rfind("\n", x - 1); - if(x == string_view::npos) - { - x = 0; - } - else - { - x++; - } - return totalview.substr(x, idx - x); - }; - for(string_view item = find_item(totalview.size()); item != string_view(); item = find_item(item.data() - totalview.data())) - { - // std::cout << "***" << item << "***"; - // hexaddr-hexaddr flags offset dev:id inode [path] - size_t inode = 1; - sscanf(item.data(), "%*x-%*x %*c%*c%*c%*c %*x %*c%*c:%*c%*c %zu", &inode); - auto vmflagsidx = item.rfind("\nVmFlags:"); - if(vmflagsidx == string_view::npos) - { - return errc::illegal_byte_sequence; - } - // Is there " ac" after vmflagsidx? - if(string_view::npos != item.find(" ac", vmflagsidx) && inode == 0) - { - // std::cerr << "Adding anon entry at offset " << itemtopidx << std::endl; - anon_entries.push_back(item); - } - else - { - // std::cerr << "Adding non-anon entry at offset " << itemtopidx << std::endl; - non_anon_entries.push_back(item); - } - } - process_memory_usage ret; - // std::cerr << "Anon entries:"; - for(auto &i : anon_entries) - { - OUTCOME_TRY(auto &&size, parse(i, "\nSize:")); - OUTCOME_TRY(auto &&rss, parse(i, "\nRss:")); - OUTCOME_TRY(auto &&anonymous, parse(i, "\nAnonymous:")); - OUTCOME_TRY(auto &&lazyfree, parse(i, "\nLazyFree:")); - if(size != (uint64_t) -1 && rss != (uint64_t) -1 && anonymous != (uint64_t) -1) - { - ret.total_address_space_in_use += size; - ret.total_address_space_paged_in += rss; - ret.private_committed += anonymous; - if(lazyfree != (uint64_t) -1) + // std::cerr << "\n\nNon-anon entries:"; + for(auto &i : non_anon_entries) { - ret.total_address_space_paged_in -= lazyfree; - ret.private_committed -= lazyfree; + OUTCOME_TRY(auto &&size, parse(i, "\nSize:")); + OUTCOME_TRY(auto &&rss, parse(i, "\nRss:")); + OUTCOME_TRY(auto &&lazyfree, parse(i, "\nLazyFree:")); + if(size != (uint64_t) -1 && rss != (uint64_t) -1) + { + ret.total_address_space_in_use += size; + ret.total_address_space_paged_in += rss; + if(lazyfree != (uint64_t) -1) + { + ret.total_address_space_in_use -= lazyfree; + } + } + // std::cerr << i << "\nSize = " << size << " Rss = " << rss << std::endl; } - ret.private_paged_in += rss; } - // std::cerr << i << "\nSize = " << size << " Rss = " << rss << std::endl; } - // std::cerr << "\n\nNon-anon entries:"; - for(auto &i : non_anon_entries) + if(!!(want & process_memory_usage::want::this_system)) { - OUTCOME_TRY(auto &&size, parse(i, "\nSize:")); - OUTCOME_TRY(auto &&rss, parse(i, "\nRss:")); - OUTCOME_TRY(auto &&lazyfree, parse(i, "\nLazyFree:")); - if(size != (uint64_t) -1 && rss != (uint64_t) -1) + std::vector<char> buffer(1024); + OUTCOME_TRY(fill_buffer(buffer, "/proc/meminfo")); + if(buffer.size() > 1) { - ret.total_address_space_in_use += size; - ret.total_address_space_paged_in += rss; - if(lazyfree != (uint64_t) -1) + string_view i(buffer.data(), buffer.size()); + OUTCOME_TRY(ret.system_physical_memory_total, parse(i, "MemTotal:")); + OUTCOME_TRY(ret.system_physical_memory_available, parse(i, "\nMemAvailable:")); + if((uint64_t) -1 == ret.system_physical_memory_available) { - ret.total_address_space_in_use -= lazyfree; + // MemAvailable is >= Linux 3.14, so let's approximate what it would be + OUTCOME_TRY(auto &&memfree, parse(i, "\nMemFree:")); + OUTCOME_TRY(auto &&cached, parse(i, "\nCached:")); + OUTCOME_TRY(auto &&swapcached, parse(i, "\nSwapCached:")); + ret.system_physical_memory_available = memfree + cached + swapcached; } + OUTCOME_TRY(ret.system_commit_charge_maximum, parse(i, "\nCommitLimit:")); + OUTCOME_TRY(ret.system_commit_charge_available, parse(i, "\nCommitted_AS:")); + OUTCOME_TRY(auto &&lazyfree, parse(i, "\nLazyFree:")); + if(lazyfree == (uint64_t) -1) + { + lazyfree = 0; + } + ret.system_commit_charge_available = ret.system_commit_charge_maximum - ret.system_commit_charge_available + lazyfree; } - // std::cerr << i << "\nSize = " << size << " Rss = " << rss << std::endl; } return ret; } @@ -502,29 +533,48 @@ namespace utils (void) want; kern_return_t error; mach_msg_type_number_t outCount; - task_vm_info_data_t vmInfo; - // task_kernelmemory_info_data_t kmInfo; + process_memory_usage ret; + if(!!(wanted & process_memory_usage::want::this_process)) + { + task_vm_info_data_t vmInfo; + // task_kernelmemory_info_data_t kmInfo; - outCount = TASK_VM_INFO_COUNT; - error = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &outCount); - if(error != KERN_SUCCESS) + outCount = TASK_VM_INFO_COUNT; + error = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &outCount); + if(error != KERN_SUCCESS) + { + return errc::invalid_argument; + } + // outCount = TASK_KERNELMEMORY_INFO_COUNT; + // error = task_info(mach_task_self(), TASK_KERNELMEMORY_INFO, (task_info_t)&kmInfo, &outCount); + // if (error != KERN_SUCCESS) { + // return errc::invalid_argument; + //} + // std::cout << vmInfo.virtual_size << "\n" << vmInfo.region_count << "\n" << vmInfo.resident_size << "\n" << vmInfo.device << "\n" << vmInfo.internal << + // "\n" << vmInfo.external << "\n" << vmInfo.reusable << "\n" << vmInfo.purgeable_volatile_pmap<< "\n" << vmInfo.purgeable_volatile_resident << "\n" << + // vmInfo.purgeable_volatile_virtual << "\n" << vmInfo.compressed << "\n" << vmInfo.phys_footprint << std::endl; std::cout << "\n" << kmInfo.total_palloc + // << + // "\n" << kmInfo.total_pfree << "\n" << kmInfo.total_salloc << "\n" << kmInfo.total_sfree << std::endl; + ret.total_address_space_in_use = vmInfo.virtual_size; + ret.total_address_space_paged_in = vmInfo.resident_size; + ret.private_committed = vmInfo.internal + vmInfo.compressed; + ret.private_paged_in = vmInfo.phys_footprint; + } + if(!!(wanted & process_memory_usage::want::this_system)) { - return errc::invalid_argument; + vm_statistics_data_t vmStat; + outCount = HOST_VM_INFO_COUNT; + error = host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t) &vmStat, &outCount); + if(error != KERN_SUCCESS) + { + return errc::invalid_argument; + } + ret.system_physical_memory_total = ((uint64_t) vmStat.free_count + vmStat.active_count + vmStat.inactive_count + vmStat.wire_count) * page_size(); + ret.system_physical_memory_available = ((uint64_t) vmStat.free_count + vmStat.inactive_count) * page_size(); + // Not sure how to retrieve these on Mac OS + // ret.system_commit_charge_maximum = (uint64_t) pi.CommitLimit * page_size(); + // ret.system_commit_charge_available = (uint64_t) pi.CommitTotal * page_size(); } - // outCount = TASK_KERNELMEMORY_INFO_COUNT; - // error = task_info(mach_task_self(), TASK_KERNELMEMORY_INFO, (task_info_t)&kmInfo, &outCount); - // if (error != KERN_SUCCESS) { - // return errc::invalid_argument; - //} - // std::cout << vmInfo.virtual_size << "\n" << vmInfo.region_count << "\n" << vmInfo.resident_size << "\n" << vmInfo.device << "\n" << vmInfo.internal << - // "\n" << vmInfo.external << "\n" << vmInfo.reusable << "\n" << vmInfo.purgeable_volatile_pmap<< "\n" << vmInfo.purgeable_volatile_resident << "\n" << - // vmInfo.purgeable_volatile_virtual << "\n" << vmInfo.compressed << "\n" << vmInfo.phys_footprint << std::endl; std::cout << "\n" << kmInfo.total_palloc << - // "\n" << kmInfo.total_pfree << "\n" << kmInfo.total_salloc << "\n" << kmInfo.total_sfree << std::endl; - process_memory_usage ret; - ret.total_address_space_in_use = vmInfo.virtual_size; - ret.total_address_space_paged_in = vmInfo.resident_size; - ret.private_committed = vmInfo.internal + vmInfo.compressed; - ret.private_paged_in = vmInfo.phys_footprint; return ret; #else #error Unknown platform diff --git a/include/llfio/v2.0/detail/impl/windows/import.hpp b/include/llfio/v2.0/detail/impl/windows/import.hpp index 71001ce6..dd4b0ac2 100644 --- a/include/llfio/v2.0/detail/impl/windows/import.hpp +++ b/include/llfio/v2.0/detail/impl/windows/import.hpp @@ -539,6 +539,26 @@ namespace windows_nt_kernel using DiscardVirtualMemory_t = BOOL(NTAPI *)(_In_ PVOID VirtualAddress, _In_ SIZE_T Size); + typedef struct _PERFORMANCE_INFORMATION + { + DWORD cb; + SIZE_T CommitTotal; + SIZE_T CommitLimit; + SIZE_T CommitPeak; + SIZE_T PhysicalTotal; + SIZE_T PhysicalAvailable; + SIZE_T SystemCache; + SIZE_T KernelTotal; + SIZE_T KernelPaged; + SIZE_T KernelNonpaged; + SIZE_T PageSize; + DWORD HandleCount; + DWORD ProcessCount; + DWORD ThreadCount; + } PERFORMANCE_INFORMATION, *PPERFORMANCE_INFORMATION, PERFORMACE_INFORMATION, *PPERFORMACE_INFORMATION; + + using GetPerformanceInfo_t = BOOL(NTAPI*)(PPERFORMANCE_INFORMATION pPerformanceInformation, DWORD cb); + using RtlCaptureStackBackTrace_t = USHORT(NTAPI *)(_In_ ULONG FramesToSkip, _In_ ULONG FramesToCapture, _Out_ PVOID *BackTrace, _Out_opt_ PULONG BackTraceHash); @@ -965,6 +985,7 @@ namespace windows_nt_kernel static AdjustTokenPrivileges_t AdjustTokenPrivileges; static PrefetchVirtualMemory_t PrefetchVirtualMemory_; static DiscardVirtualMemory_t DiscardVirtualMemory_; + static GetPerformanceInfo_t GetPerformanceInfo; static SymInitialize_t SymInitialize; static SymGetLineFromAddr64_t SymGetLineFromAddr64; static RtlCaptureStackBackTrace_t RtlCaptureStackBackTrace; @@ -1300,6 +1321,13 @@ namespace windows_nt_kernel { DiscardVirtualMemory_ = reinterpret_cast<DiscardVirtualMemory_t>(GetProcAddress(kernel32, "DiscardVirtualMemory")); } + if(GetPerformanceInfo == nullptr) + { + if((GetPerformanceInfo = reinterpret_cast<GetPerformanceInfo_t>(GetProcAddress(kernel32, "K32GetPerformanceInfo"))) == nullptr) + { + abort(); + } + } #ifdef LLFIO_OP_STACKBACKTRACEDEPTH if(dbghelp) { diff --git a/include/llfio/v2.0/detail/impl/windows/utils.ipp b/include/llfio/v2.0/detail/impl/windows/utils.ipp index e12db462..4557e567 100644 --- a/include/llfio/v2.0/detail/impl/windows/utils.ipp +++ b/include/llfio/v2.0/detail/impl/windows/utils.ipp @@ -207,38 +207,55 @@ namespace utils return success(); } - result<process_memory_usage> current_process_memory_usage(process_memory_usage::want /*unused*/) noexcept + result<process_memory_usage> current_process_memory_usage(process_memory_usage::want wanted) noexcept { // Amazingly Win32 doesn't expose private working set, so to avoid having // to iterate all the pages in the process and calculate, use a hidden // NT kernel call windows_nt_kernel::init(); using namespace windows_nt_kernel; - ULONG written = 0; - _VM_COUNTERS_EX2 vmc; - memset(&vmc, 0, sizeof(vmc)); - NTSTATUS ntstat = NtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &vmc, sizeof(vmc), &written); - if(ntstat < 0) - { - return ntkernel_error(ntstat); - } process_memory_usage ret; - /* Notes: + if(!!(wanted & process_memory_usage::want::this_process)) + { + ULONG written = 0; + _VM_COUNTERS_EX2 vmc; + memset(&vmc, 0, sizeof(vmc)); + NTSTATUS ntstat = NtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &vmc, sizeof(vmc), &written); + if(ntstat < 0) + { + return ntkernel_error(ntstat); + } + /* Notes: - Apparently PrivateUsage is the commit charge on Windows. It always equals PagefileUsage. - It is the total amount of private anonymous pages committed. - SharedCommitUsage is amount of non-binary Shared memory committed. - Therefore total non-binary commit = PrivateUsage + SharedCommitUsage + Apparently PrivateUsage is the commit charge on Windows. It always equals PagefileUsage. + It is the total amount of private anonymous pages committed. + SharedCommitUsage is amount of non-binary Shared memory committed. + Therefore total non-binary commit = PrivateUsage + SharedCommitUsage - WorkingSetSize is the total amount of program binaries, non-binary shared memory, and anonymous pages faulted in. - PrivateWorkingSetSize is the amount of private anonymous pages faulted into the process. - Therefore remainder is all shared working set faulted into the process. - */ - ret.total_address_space_in_use = vmc.VirtualSize; - ret.total_address_space_paged_in = vmc.WorkingSetSize; + WorkingSetSize is the total amount of program binaries, non-binary shared memory, and anonymous pages faulted in. + PrivateWorkingSetSize is the amount of private anonymous pages faulted into the process. + Therefore remainder is all shared working set faulted into the process. + */ + ret.total_address_space_in_use = vmc.VirtualSize; + ret.total_address_space_paged_in = vmc.WorkingSetSize; - ret.private_committed = vmc.PrivateUsage; - ret.private_paged_in = vmc.PrivateWorkingSetSize; + ret.private_committed = vmc.PrivateUsage; + ret.private_paged_in = vmc.PrivateWorkingSetSize; + } + if(!!(wanted & process_memory_usage::want::this_system)) + { + PERFORMANCE_INFORMATION pi; + memset(&pi, 0, sizeof(pi)); + pi.cb = sizeof(pi); + if(!GetPerformanceInfo(&pi, sizeof(pi))) + { + return win32_error(); + } + ret.system_physical_memory_total = (uint64_t) pi.PhysicalTotal * pi.PageSize; + ret.system_physical_memory_available = (uint64_t) pi.PhysicalAvailable * pi.PageSize; + ret.system_commit_charge_maximum = (uint64_t) pi.CommitLimit * pi.PageSize; + ret.system_commit_charge_available = (uint64_t)(pi.CommitLimit - pi.CommitTotal) * pi.PageSize; + } return ret; } diff --git a/include/llfio/v2.0/utils.hpp b/include/llfio/v2.0/utils.hpp index 147dfc7b..4b8501da 100644 --- a/include/llfio/v2.0/utils.hpp +++ b/include/llfio/v2.0/utils.hpp @@ -205,11 +205,31 @@ namespace utils total_address_space_paged_in = 1U << 1U, private_committed = 1U << 2U, private_paged_in = 1U << 3U, + private_committed_inaccurate = 1U << 8U, - all = (unsigned) -1 // + system_physical_memory_total = 1U << 16U, + system_physical_memory_available = 1U << 17U, + system_commit_charge_maximum = 1U << 18U, + system_commit_charge_available = 1U << 19U, + + this_process = 0x0000ffff, // + this_system = 0xffff0000, // + all = 0xffffffff // } QUICKCPPLIB_BITFIELD_END(want) + //! The total physical memory in this system. + uint64_t system_physical_memory_total{0}; + //! The physical memory in this system not containing dirty pages i.e. is currently used for file system caching, or unused. + uint64_t system_physical_memory_available{0}; + + //! The maximum amount of memory which can be committed by all processes. This is typically physical RAM plus swap files. Note that swap files can be added + //! and removed over time. + uint64_t system_commit_charge_maximum{0}; + //! The amount of commit charge remaining before the maximum. Subtract this from `system_commit_charge_maximum` to determine the amount of commit charge + //! consumed by all processes in the system. + uint64_t system_commit_charge_available{0}; + //! The total virtual address space in use. size_t total_address_space_in_use{0}; //! The total memory currently paged into the process. Always `<= total_address_space_in_use`. Also known as "working set", or "resident set size including @@ -248,7 +268,7 @@ namespace utils We therefore supply as `private_committed` the same value as `private_paged_in`. */ LLFIO_HEADERS_ONLY_FUNC_SPEC result<process_memory_usage> - current_process_memory_usage(process_memory_usage::want want = process_memory_usage::want::all) noexcept; + current_process_memory_usage(process_memory_usage::want want = process_memory_usage::want::this_process) noexcept; /*! \brief CPU usage statistics for a process. */ diff --git a/test/tests/utils.cpp b/test/tests/utils.cpp index 82967e87..13ab969f 100644 --- a/test/tests/utils.cpp +++ b/test/tests/utils.cpp @@ -51,10 +51,9 @@ static inline void TestCurrentProcessCPUUsage() #ifndef __APPLE__ // On Mac CI at least, idle is approx 2x user which ought to not occur on a two CPU VM BOOST_CHECK(diff.system_ns_in_idle_mode <= 1100000000ULL * thread_count); #endif - std::cout << "With " << thread_count << " threads busy the process spent " << diff.process_ns_in_user_mode - << " ns in user mode and " << diff.process_ns_in_kernel_mode << " ns in kernel mode. The system spent " << diff.system_ns_in_user_mode - << " ns in user mode, " << diff.system_ns_in_kernel_mode << " ns in kernel mode, and " << diff.system_ns_in_idle_mode << " in idle mode." - << std::endl; + std::cout << "With " << thread_count << " threads busy the process spent " << diff.process_ns_in_user_mode << " ns in user mode and " + << diff.process_ns_in_kernel_mode << " ns in kernel mode. The system spent " << diff.system_ns_in_user_mode << " ns in user mode, " + << diff.system_ns_in_kernel_mode << " ns in kernel mode, and " << diff.system_ns_in_idle_mode << " in idle mode." << std::endl; done = true; for(auto &t : threads) { @@ -72,11 +71,12 @@ static inline void TestCurrentProcessMemoryUsage() namespace llfio = LLFIO_V2_NAMESPACE; static const llfio::utils::process_memory_usage *last_pmu; auto print = [](llfio::utils::process_memory_usage &pmu) -> std::ostream &(*) (std::ostream &) { - pmu = llfio::utils::current_process_memory_usage().value(); + pmu = llfio::utils::current_process_memory_usage(llfio::utils::process_memory_usage::want::all).value(); last_pmu = &pmu; return [](std::ostream &s) -> std::ostream & { return s << " " << (last_pmu->total_address_space_in_use / 1024.0 / 1024.0) << "," << (last_pmu->total_address_space_paged_in / 1024.0 / 1024.0) - << "," << (last_pmu->private_committed / 1024.0 / 1024.0) << "," << (last_pmu->private_paged_in / 1024.0 / 1024.0) << std::endl; + << "," << (last_pmu->private_committed / 1024.0 / 1024.0) << "," << (last_pmu->private_paged_in / 1024.0 / 1024.0) << "," + << ((last_pmu->system_commit_charge_maximum - last_pmu->system_commit_charge_available) / 1024.0 / 1024.0) << std::endl; }; }; auto fakefault = [](llfio::map_handle &maph) { @@ -108,7 +108,7 @@ static inline void TestCurrentProcessMemoryUsage() std::cout << "For page allocation:\n"; { llfio::utils::process_memory_usage before_anything, after_reserve, after_commit, after_fault, after_decommit, after_zero, after_do_not_store; - std::cout << " Total address space, Total address space paged in, Private bytes committed, Private bytes paged in\n\n"; + std::cout << " Total address space, Total address space paged in, Private bytes committed, Private bytes paged in, System commit charge\n\n"; std::cout << " Before anything:\n" << print(before_anything) << std::endl; { // Should raise total_address_space_in_use by 1Gb @@ -148,35 +148,41 @@ static inline void TestCurrentProcessMemoryUsage() std::cout << " After committing and faulting and do not storing 1Gb:\n" << print(after_do_not_store) << std::endl; } auto within = [](const llfio::utils::process_memory_usage &a, const llfio::utils::process_memory_usage &b, int total_address_space_in_use, - int total_address_space_paged_in, int private_committed, int private_paged_in) { + int total_address_space_paged_in, int private_committed, int private_paged_in, int system_committed) { auto check = [](size_t a, size_t b, int bound) { - auto diff = abs((b / 1024.0 / 1024.0) - (a / 1024.0 / 1024.0)); - return diff > bound - 10 && diff < bound + 10; + if(bound == INT_MAX) + { + return true; + } + auto diff = (b / 1024.0 / 1024.0) - (a / 1024.0 / 1024.0); + return diff > bound - 50 && diff < bound + 50; }; return check(a.total_address_space_in_use, b.total_address_space_in_use, total_address_space_in_use) // && check(a.total_address_space_paged_in, b.total_address_space_paged_in, total_address_space_paged_in) // && check(a.private_committed, b.private_committed, private_committed) // - && check(a.private_paged_in, b.private_paged_in, private_paged_in); + && check(a.private_paged_in, b.private_paged_in, private_paged_in) // + && check(b.system_commit_charge_available, a.system_commit_charge_available, system_committed); }; #ifdef __APPLE__ // Mac OS has no way of differentiating between committed and paged in pages :( - BOOST_CHECK(within(before_anything, after_reserve, 1024, 0, 0, 0)); - BOOST_CHECK(within(before_anything, after_commit, 1024, 0, 0, 0)); - BOOST_CHECK(within(before_anything, after_fault, 1024, 1024, 1024, 1024)); - BOOST_CHECK(within(before_anything, after_decommit, 1024, 0, 0, 0)); - BOOST_CHECK(within(before_anything, after_zero, 1024, 1024, 0, 0)); - BOOST_CHECK(within(before_anything, after_do_not_store, 1024, 1024, 1024, 1024)); // do_not_store() doesn't decrease RSS + BOOST_CHECK(within(before_anything, after_reserve, 1024, 0, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_commit, 1024, 0, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_fault, 1024, 1024, 1024, 1024, 0)); + BOOST_CHECK(within(before_anything, after_decommit, 1024, 0, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_zero, 1024, 1024, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_do_not_store, 1024, 1024, 1024, 1024, 0)); // do_not_store() doesn't decrease RSS #else - BOOST_CHECK(within(before_anything, after_reserve, 1024, 0, 0, 0)); - BOOST_CHECK(within(before_anything, after_commit, 1024, 0, 1024, 0)); - BOOST_CHECK(within(before_anything, after_fault, 1024, 1024, 1024, 1024)); - BOOST_CHECK(within(before_anything, after_decommit, 1024, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_reserve, 1024, 0, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_commit, 1024, 0, 1024, 0, 1024)); + BOOST_CHECK(within(before_anything, after_fault, 1024, 1024, 1024, 1024, 1024)); + BOOST_CHECK(within(before_anything, after_decommit, 1024, 0, 0, 0, 0)); #ifdef _WIN32 - BOOST_CHECK(within(before_anything, after_zero, 1024, 0, 1024, 0)); - BOOST_CHECK(within(before_anything, after_do_not_store, 1024, 0, 1024, 0)); // do_not_store() decreases RSS but not commit on Windows + BOOST_CHECK(within(before_anything, after_zero, 1024, 0, 1024, 0, 1024)); + BOOST_CHECK(within(before_anything, after_do_not_store, 1024, 0, 1024, 0, 1024)); // do_not_store() decreases RSS but not commit on Windows #else (void) after_zero; // may not evict faulted set on POSIX - BOOST_CHECK(within(before_anything, after_do_not_store, 1024, 1024, 0, 1024)); // do_not_store() decreases commit but does not RSS on POSIX + BOOST_CHECK(within(before_anything, after_do_not_store, 1024, 1024, 0, 1024, + INT_MAX)); // do_not_store() decreases commit but does not RSS on POSIX BUT not the system commit if Linux < 4.12 #endif #endif } @@ -189,7 +195,7 @@ static inline void TestCurrentProcessMemoryUsage() { auto sectionh = llfio::section_handle::section(1024 * 1024 * 1024).value(); llfio::utils::process_memory_usage before_anything, after_reserve, after_commit, after_fault, after_decommit, after_zero, after_do_not_store; - std::cout << " Total address space, Total address space paged in, Private bytes committed, Private bytes paged in\n\n"; + std::cout << " Total address space, Total address space paged in, Private bytes committed, Private bytes paged in, System commit charge\n\n"; std::cout << " Before anything:\n" << print(before_anything) << std::endl; { @@ -236,32 +242,37 @@ static inline void TestCurrentProcessMemoryUsage() } #endif auto within = [](const llfio::utils::process_memory_usage &a, const llfio::utils::process_memory_usage &b, int total_address_space_in_use, - int total_address_space_paged_in, int private_committed, int private_paged_in) { + int total_address_space_paged_in, int private_committed, int private_paged_in, int system_committed) { auto check = [](size_t a, size_t b, int bound) { - auto diff = abs((b / 1024.0 / 1024.0) - (a / 1024.0 / 1024.0)); - return diff > bound - 10 && diff < bound + 10; + if(bound == INT_MAX) + { + return true; + } + auto diff = (b / 1024.0 / 1024.0) - (a / 1024.0 / 1024.0); + return diff > bound - 50 && diff < bound + 50; }; return check(a.total_address_space_in_use, b.total_address_space_in_use, total_address_space_in_use) // && check(a.total_address_space_paged_in, b.total_address_space_paged_in, total_address_space_paged_in) // && check(a.private_committed, b.private_committed, private_committed) // - && check(a.private_paged_in, b.private_paged_in, private_paged_in); + && check(a.private_paged_in, b.private_paged_in, private_paged_in) // + && check(b.system_commit_charge_available, a.system_commit_charge_available, system_committed); }; #ifdef __APPLE__ // Mac OS has no way of differentiating between committed and paged in pages :( - BOOST_CHECK(within(before_anything, after_reserve, 1024, 0, 0, 0)); - BOOST_CHECK(within(before_anything, after_commit, 1024, 0, 0, 0)); - BOOST_CHECK(within(before_anything, after_fault, 1024, 1024, 0, 0)); - BOOST_CHECK(within(before_anything, after_decommit, 1024, 0, 0, 0)); - BOOST_CHECK(within(before_anything, after_zero, 1024, 1024, 0, 0)); // doesn't implement zero() - BOOST_CHECK(within(before_anything, after_do_not_store, 1024, 1024, 0, 0)); // do_not_store() doesn't decrease RSS + BOOST_CHECK(within(before_anything, after_reserve, 1024, 0, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_commit, 1024, 0, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_fault, 1024, 1024, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_decommit, 1024, 0, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_zero, 1024, 1024, 0, 0, 0)); // doesn't implement zero() + BOOST_CHECK(within(before_anything, after_do_not_store, 1024, 1024, 0, 0, 0)); // do_not_store() doesn't decrease RSS #else - BOOST_CHECK(within(before_anything, after_reserve, 1024, 0, 0, 0)); - BOOST_CHECK(within(before_anything, after_commit, 1024, 0, 0, 0)); - BOOST_CHECK(within(before_anything, after_fault, 1024, 1024, 0, 0)); + BOOST_CHECK(within(before_anything, after_reserve, 1024, 0, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_commit, 1024, 0, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_fault, 1024, 1024, 0, 0, 0)); #ifndef _WIN32 - BOOST_CHECK(within(before_anything, after_decommit, 1024, 0, 0, 0)); - BOOST_CHECK(within(before_anything, after_zero, 1024, 0, 0, 0)); - BOOST_CHECK(within(before_anything, after_do_not_store, 1024, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_decommit, 1024, 0, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_zero, 1024, 0, 0, 0, 0)); + BOOST_CHECK(within(before_anything, after_do_not_store, 1024, 0, 0, 0, INT_MAX)); // system commit varies if Linux > 4.12 #else (void) after_decommit; (void) after_zero; |