1 /* 2 Copyright (c) 2005-2022 Intel Corporation 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Declarations for simple estimate of the memory being used by a program. 18 // Not yet implemented for macOS*. 19 // This header is an optional part of the test harness. 20 // It assumes that "harness_assert.h" has already been included. 21 22 #ifndef __TBB_test_common_memory_usage_H_ 23 #define __TBB_test_common_memory_usage_H_ 24 25 #include "common/test.h" 26 #include "utils.h" 27 #include "utils_assert.h" 28 29 #if __unix__ || __sun 30 #include <sys/resource.h> 31 #include <unistd.h> 32 #include <sys/utsname.h> /* for uname */ 33 #include <errno.h> /* for use in LinuxKernelVersion() */ 34 35 // Parse file utility for THP info 36 #include "src/tbbmalloc/shared_utils.h" 37 38 #elif __APPLE__ && !__ARM_ARCH 39 #include <unistd.h> 40 #include <mach/mach.h> 41 #include <AvailabilityMacros.h> 42 #if MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_6 || __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0 43 #include <mach/shared_region.h> 44 #else 45 #include <mach/shared_memory_server.h> 46 #endif 47 #if SHARED_TEXT_REGION_SIZE || SHARED_DATA_REGION_SIZE 48 const size_t shared_size = SHARED_TEXT_REGION_SIZE+SHARED_DATA_REGION_SIZE; 49 #else 50 const size_t shared_size = 0; 51 #endif 52 53 #elif _WIN32 && !__TBB_WIN8UI_SUPPORT 54 #include <windows.h> 55 #include <psapi.h> 56 #if _MSC_VER 57 #pragma comment(lib, "psapi") 58 #endif 59 60 #endif /* OS selection */ 61 62 namespace utils { 63 64 enum MemoryStatType { 65 currentUsage, 66 peakUsage 67 }; 68 69 #if __unix__ LinuxKernelVersion()70 inline unsigned LinuxKernelVersion() 71 { 72 unsigned digit1, digit2, digit3; 73 struct utsname utsnameBuf; 74 75 if (-1 == uname(&utsnameBuf)) { 76 CHECK_MESSAGE(false, "Can't call uname: errno = " << errno); 77 } 78 if (3 != sscanf(utsnameBuf.release, "%u.%u.%u", &digit1, &digit2, &digit3)) { 79 CHECK_MESSAGE(false, "Unable to parse OS release: " << utsnameBuf.release); 80 } 81 return 1000000 * digit1 + 1000 * digit2 + digit3; 82 } 83 #endif 84 85 //! Return estimate of number of bytes of memory that this program is currently using. 86 /* Returns 0 if not implemented on platform. */ 87 std::size_t GetMemoryUsage(MemoryStatType stat = currentUsage) { 88 utils::suppress_unused_warning(stat); 89 #if __TBB_WIN8UI_SUPPORT || defined(WINAPI_FAMILY) 90 return 0; 91 #elif _WIN32 92 PROCESS_MEMORY_COUNTERS mem; 93 bool status = GetProcessMemoryInfo(GetCurrentProcess(), &mem, sizeof(mem)) != 0; 94 ASSERT(status, nullptr); 95 return stat == currentUsage ? mem.PagefileUsage : mem.PeakPagefileUsage; 96 #elif __unix__ 97 long unsigned size = 0; 98 FILE* fst = fopen("/proc/self/status", "r"); 99 ASSERT(fst != nullptr, nullptr); 100 const int BUF_SZ = 200; 101 char buf_stat[BUF_SZ]; 102 const char* pattern = stat == peakUsage ? "VmPeak: %lu" : "VmSize: %lu"; 103 while (nullptr != fgets(buf_stat, BUF_SZ, fst)) { 104 if (1 == sscanf(buf_stat, pattern, &size)) { 105 ASSERT(size, "Invalid value of memory consumption."); 106 break; 107 } 108 } 109 // VmPeak is available in kernels staring 2.6.15 110 if (stat != peakUsage || LinuxKernelVersion() >= 2006015) 111 ASSERT(size, "Invalid /proc/self/status format, pattern not found."); 112 fclose(fst); 113 return size * 1024; 114 #elif __APPLE__ && !__ARM_ARCH 115 // TODO: find how detect peak virtual memory size under macOS 116 if (stat == peakUsage) 117 return 0; 118 kern_return_t status; 119 task_basic_info info; 120 mach_msg_type_number_t msg_type = TASK_BASIC_INFO_COUNT; 121 status = task_info(mach_task_self(), TASK_BASIC_INFO, reinterpret_cast<task_info_t>(&info), &msg_type); 122 ASSERT(status == KERN_SUCCESS, nullptr); 123 return info.virtual_size - shared_size; 124 #else 125 return 0; 126 #endif 127 } 128 129 //! Use approximately a specified amount of stack space. 130 /** Recursion is used here instead of alloca because some implementations of alloca do not use the stack. */ 131 void UseStackSpace(size_t amount, char* top = nullptr) { 132 char x[1000]; 133 memset(x, -1, sizeof(x)); 134 if (!top) 135 top = x; 136 CHECK_MESSAGE(x <= top, "test assumes that stacks grow downwards"); 137 if (size_t(top - x) < amount) 138 UseStackSpace(amount, top); 139 } 140 141 #if __unix__ 142 isTHPEnabledOnMachine()143 inline bool isTHPEnabledOnMachine() { 144 long long thpPresent = 'n'; 145 long long hugePageSize = -1; 146 147 parseFileItem thpItem[] = { { "[alwa%cs] madvise never\n", thpPresent } }; 148 parseFileItem hpSizeItem[] = { { "Hugepagesize: %lld kB", hugePageSize } }; 149 150 parseFile</*BUFF_SIZE=*/100>("/sys/kernel/mm/transparent_hugepage/enabled", thpItem); 151 parseFile</*BUFF_SIZE=*/100>("/proc/meminfo", hpSizeItem); 152 153 if (hugePageSize > -1 && thpPresent == 'y') { 154 return true; 155 } else { 156 return false; 157 } 158 } getSystemTHPAllocatedSize()159 inline long long getSystemTHPAllocatedSize() { 160 long long anonHugePagesSize = 0; 161 parseFileItem meminfoItems[] = { 162 { "AnonHugePages: %lld kB", anonHugePagesSize } }; 163 parseFile</*BUFF_SIZE=*/100>("/proc/meminfo", meminfoItems); 164 return anonHugePagesSize; 165 } getSystemTHPCount()166 inline long long getSystemTHPCount() { 167 long long anonHugePages = 0; 168 parseFileItem vmstatItems[] = { 169 { "nr_anon_transparent_hugepages %lld", anonHugePages } }; 170 parseFile</*BUFF_SIZE=*/100>("/proc/vmstat", vmstatItems); 171 return anonHugePages; 172 } 173 174 #endif // __unix__ 175 176 } // namespace utils 177 #endif // __TBB_test_common_memory_usage_H_ 178