xref: /oneTBB/test/common/memory_usage.h (revision c21e688a)
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