495cc8bb94ea27a24a13dc112988be685ac01eec
[platform/upstream/coreclr.git] / src / pal / src / misc / sysinfo.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 /*++
6
7
8
9 Module Name:
10
11     sysinfo.c
12
13 Abstract:
14
15     Implements GetSystemInfo.
16
17 Revision History:
18
19
20
21 --*/
22
23 #include "pal/palinternal.h"
24
25 #include <sched.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #if HAVE_SYSCTL
30 #include <sys/sysctl.h>
31 #elif !HAVE_SYSCONF
32 #error Either sysctl or sysconf is required for GetSystemInfo.
33 #endif
34
35 #if HAVE_SYSINFO
36 #include <sys/sysinfo.h>
37 #endif
38
39 #include <sys/param.h>
40
41 #if HAVE_SYS_VMPARAM_H
42 #include <sys/vmparam.h>
43 #endif  // HAVE_SYS_VMPARAM_H
44
45 #if HAVE_XSWDEV
46 #include <vm/vm_param.h>
47 #endif // HAVE_XSWDEV
48
49 #if HAVE_MACH_VM_TYPES_H
50 #include <mach/vm_types.h>
51 #endif // HAVE_MACH_VM_TYPES_H
52
53 #if HAVE_MACH_VM_PARAM_H
54 #include <mach/vm_param.h>
55 #endif  // HAVE_MACH_VM_PARAM_H
56
57 #if HAVE_MACHINE_VMPARAM_H
58 #include <machine/vmparam.h>
59 #endif  // HAVE_MACHINE_VMPARAM_H
60
61 #if defined(_TARGET_MAC64)
62 #include <mach/vm_statistics.h>
63 #include <mach/mach_types.h>
64 #include <mach/mach_init.h>
65 #include <mach/mach_host.h>
66 #endif // defined(_TARGET_MAC64)
67
68 // On some platforms sys/user.h ends up defining _DEBUG; if so
69 // remove the definition before including the header and put
70 // back our definition afterwards
71 #if USER_H_DEFINES_DEBUG
72 #define OLD_DEBUG _DEBUG
73 #undef _DEBUG
74 #endif
75 #include <sys/user.h>
76 #if USER_H_DEFINES_DEBUG
77 #undef _DEBUG
78 #define _DEBUG OLD_DEBUG
79 #undef OLD_DEBUG
80 #endif
81
82 #include "pal/dbgmsg.h"
83
84 #include <algorithm>
85
86 SET_DEFAULT_DEBUG_CHANNEL(MISC);
87
88 #ifndef __APPLE__
89 #if HAVE_SYSCONF && HAVE__SC_AVPHYS_PAGES
90 #define SYSCONF_PAGES _SC_AVPHYS_PAGES
91 #elif HAVE_SYSCONF && HAVE__SC_PHYS_PAGES
92 #define SYSCONF_PAGES _SC_PHYS_PAGES
93 #else
94 #error Dont know how to get page-size on this architecture!
95 #endif
96 #endif // __APPLE__
97
98
99 DWORD
100 PALAPI
101 PAL_GetLogicalCpuCountFromOS()
102 {
103     int nrcpus = 0;
104
105 #if HAVE_SYSCONF
106
107 #if defined(_ARM_) || defined(_ARM64_)
108 #define SYSCONF_GET_NUMPROCS       _SC_NPROCESSORS_CONF
109 #define SYSCONF_GET_NUMPROCS_NAME "_SC_NPROCESSORS_CONF"
110 #else
111 #define SYSCONF_GET_NUMPROCS       _SC_NPROCESSORS_ONLN
112 #define SYSCONF_GET_NUMPROCS_NAME "_SC_NPROCESSORS_ONLN"
113 #endif
114     nrcpus = sysconf(SYSCONF_GET_NUMPROCS);
115     if (nrcpus < 1)
116     {
117         ASSERT("sysconf failed for %s (%d)\n", SYSCONF_GET_NUMPROCS_NAME, errno);
118     }
119 #elif HAVE_SYSCTL
120     int rc;
121     size_t sz;
122     int mib[2];
123
124     sz = sizeof(nrcpus);
125     mib[0] = CTL_HW;
126     mib[1] = HW_NCPU;
127     rc = sysctl(mib, 2, &nrcpus, &sz, NULL, 0);
128     if (rc != 0)
129     {
130         ASSERT("sysctl failed for HW_NCPU (%d)\n", errno);
131     }
132 #endif // HAVE_SYSCONF
133
134     return nrcpus;
135 }
136
137 /*++
138 Function:
139   GetSystemInfo
140
141 GetSystemInfo
142
143 The GetSystemInfo function returns information about the current system.
144
145 Parameters
146
147 lpSystemInfo
148        [out] Pointer to a SYSTEM_INFO structure that receives the information.
149
150 Return Values
151
152 This function does not return a value.
153
154 Note:
155   fields returned by this function are:
156     dwNumberOfProcessors
157     dwPageSize
158 Others are set to zero.
159
160 --*/
161 VOID
162 PALAPI
163 GetSystemInfo(
164           OUT LPSYSTEM_INFO lpSystemInfo)
165 {
166     int nrcpus = 0;
167     long pagesize;
168
169     PERF_ENTRY(GetSystemInfo);
170     ENTRY("GetSystemInfo (lpSystemInfo=%p)\n", lpSystemInfo);
171
172     pagesize = getpagesize();
173
174     lpSystemInfo->wProcessorArchitecture_PAL_Undefined = 0;
175     lpSystemInfo->wReserved_PAL_Undefined = 0;
176     lpSystemInfo->dwPageSize = pagesize;
177     lpSystemInfo->dwActiveProcessorMask_PAL_Undefined = 0;
178
179     nrcpus = PAL_GetLogicalCpuCountFromOS();
180     TRACE("dwNumberOfProcessors=%d\n", nrcpus);
181     lpSystemInfo->dwNumberOfProcessors = nrcpus;
182
183 #ifdef VM_MAXUSER_ADDRESS
184     lpSystemInfo->lpMaximumApplicationAddress = (PVOID) VM_MAXUSER_ADDRESS;
185 #elif defined(__linux__)
186     lpSystemInfo->lpMaximumApplicationAddress = (PVOID) (1ull << 47);
187 #elif defined(USERLIMIT)
188     lpSystemInfo->lpMaximumApplicationAddress = (PVOID) USERLIMIT;
189 #elif defined(_WIN64)
190 #if defined(USRSTACK64)
191     lpSystemInfo->lpMaximumApplicationAddress = (PVOID) USRSTACK64;
192 #else // !USRSTACK64
193 #error How come USRSTACK64 is not defined for 64bit?
194 #endif // USRSTACK64
195 #elif defined(USRSTACK)
196     lpSystemInfo->lpMaximumApplicationAddress = (PVOID) USRSTACK;
197 #else
198 #error The maximum application address is not known on this platform.
199 #endif
200
201     lpSystemInfo->lpMinimumApplicationAddress = (PVOID) pagesize;
202
203     lpSystemInfo->dwProcessorType_PAL_Undefined = 0;
204
205     lpSystemInfo->dwAllocationGranularity = pagesize;
206
207     lpSystemInfo->wProcessorLevel_PAL_Undefined = 0;
208     lpSystemInfo->wProcessorRevision_PAL_Undefined = 0;
209
210     LOGEXIT("GetSystemInfo returns VOID\n");
211     PERF_EXIT(GetSystemInfo);
212 }
213
214 /*++
215 Function:
216   GlobalMemoryStatusEx
217
218 GlobalMemoryStatusEx
219
220 Retrieves information about the system's current usage of both physical and virtual memory.
221
222 Return Values
223
224 This function returns a BOOL to indicate its success status.
225
226 --*/
227 BOOL
228 PALAPI
229 GlobalMemoryStatusEx(
230             IN OUT LPMEMORYSTATUSEX lpBuffer)
231 {
232
233     PERF_ENTRY(GlobalMemoryStatusEx);
234     ENTRY("GlobalMemoryStatusEx (lpBuffer=%p)\n", lpBuffer);
235
236     lpBuffer->dwMemoryLoad = 0;
237     lpBuffer->ullTotalPhys = 0;
238     lpBuffer->ullAvailPhys = 0;
239     lpBuffer->ullTotalPageFile = 0;
240     lpBuffer->ullAvailPageFile = 0;
241     lpBuffer->ullTotalVirtual = 0;
242     lpBuffer->ullAvailVirtual = 0;
243     lpBuffer->ullAvailExtendedVirtual = 0;
244
245     BOOL fRetVal = FALSE;
246     int mib[3];
247     int rc;
248
249     // Get the physical memory size
250 #if HAVE_SYSCONF && HAVE__SC_PHYS_PAGES
251     int64_t physical_memory;
252
253     // Get the Physical memory size
254     physical_memory = sysconf( _SC_PHYS_PAGES ) * sysconf( _SC_PAGE_SIZE );
255     lpBuffer->ullTotalPhys = (DWORDLONG)physical_memory;
256     fRetVal = TRUE;
257 #elif HAVE_SYSCTL
258     int64_t physical_memory;
259     size_t length;
260
261     // Get the Physical memory size
262     mib[0] = CTL_HW;
263     mib[1] = HW_MEMSIZE;
264     length = sizeof(INT64);
265     rc = sysctl(mib, 2, &physical_memory, &length, NULL, 0);
266     if (rc != 0)
267     {
268         ASSERT("sysctl failed for HW_MEMSIZE (%d)\n", errno);
269     }
270     else
271     {
272         lpBuffer->ullTotalPhys = (DWORDLONG)physical_memory;
273         fRetVal = TRUE;
274     }
275
276 #endif // HAVE_SYSCTL
277
278     // Get swap file size, consider the ability to get the values optional
279     // (don't return FALSE from the GlobalMemoryStatusEx)
280 #if HAVE_XSW_USAGE
281     // This is available on OSX
282     struct xsw_usage xsu;
283     mib[0] = CTL_VM;
284     mib[1] = VM_SWAPUSAGE;
285     size_t length = sizeof(xsu);
286     rc = sysctl(mib, 2, &xsu, &length, NULL, 0);
287     if (rc == 0)
288     {
289         lpBuffer->ullTotalPageFile = xsu.xsu_total;
290         lpBuffer->ullAvailPageFile = xsu.xsu_avail;
291     }
292 #elif HAVE_XSWDEV
293     // E.g. FreeBSD
294     struct xswdev xsw;
295
296     size_t length = 2;
297     rc = sysctlnametomib("vm.swap_info", mib, &length);
298     if (rc == 0)
299     {
300         int pagesize = getpagesize();
301         // Aggregate the information for all swap files on the system
302         for (mib[2] = 0; ; mib[2]++)
303         {
304             length = sizeof(xsw);
305             rc = sysctl(mib, 3, &xsw, &length, NULL, 0);
306             if ((rc < 0) || (xsw.xsw_version != XSWDEV_VERSION))
307             {
308                 // All the swap files were processed or coreclr was built against
309                 // a version of headers not compatible with the current XSWDEV_VERSION.
310                 break;
311             }
312
313             DWORDLONG avail = xsw.xsw_nblks - xsw.xsw_used;
314             lpBuffer->ullTotalPageFile += (DWORDLONG)xsw.xsw_nblks * pagesize;
315             lpBuffer->ullAvailPageFile += (DWORDLONG)avail * pagesize;
316         }
317     }
318 #elif HAVE_SYSINFO
319     // Linux
320     struct sysinfo info;
321     rc = sysinfo(&info);
322     if (rc == 0)
323     {
324         lpBuffer->ullTotalPageFile = info.totalswap;
325         lpBuffer->ullAvailPageFile = info.freeswap;
326 #if HAVE_SYSINFO_WITH_MEM_UNIT
327         // A newer version of the sysinfo structure represents all the sizes
328         // in mem_unit instead of bytes
329         lpBuffer->ullTotalPageFile *= info.mem_unit;
330         lpBuffer->ullAvailPageFile *= info.mem_unit;
331 #endif // HAVE_SYSINFO_WITH_MEM_UNIT
332     }
333 #endif // HAVE_SYSINFO
334
335     // Get the physical memory in use - from it, we can get the physical memory available.
336     // We do this only when we have the total physical memory available.
337     if (lpBuffer->ullTotalPhys > 0)
338     {
339 #ifndef __APPLE__
340         lpBuffer->ullAvailPhys = sysconf(SYSCONF_PAGES) * sysconf(_SC_PAGE_SIZE);
341         INT64 used_memory = lpBuffer->ullTotalPhys - lpBuffer->ullAvailPhys;
342         lpBuffer->dwMemoryLoad = (DWORD)((used_memory * 100) / lpBuffer->ullTotalPhys);
343 #else
344         vm_size_t page_size;
345         mach_port_t mach_port;
346         mach_msg_type_number_t count;
347         vm_statistics_data_t vm_stats;
348         mach_port = mach_host_self();
349         count = sizeof(vm_stats) / sizeof(natural_t);
350         if (KERN_SUCCESS == host_page_size(mach_port, &page_size))
351         {
352             if (KERN_SUCCESS == host_statistics(mach_port, HOST_VM_INFO, (host_info_t)&vm_stats, &count))
353             {
354                 lpBuffer->ullAvailPhys = (int64_t)vm_stats.free_count * (int64_t)page_size;
355                 INT64 used_memory = ((INT64)vm_stats.active_count + (INT64)vm_stats.inactive_count + (INT64)vm_stats.wire_count) *  (INT64)page_size;
356                 lpBuffer->dwMemoryLoad = (DWORD)((used_memory * 100) / lpBuffer->ullTotalPhys);
357             }
358         }
359         mach_port_deallocate(mach_task_self(), mach_port);
360 #endif // __APPLE__
361     }
362
363     // There is no API to get the total virtual address space size on 
364     // Unix, so we use a constant value representing 128TB, which is 
365     // the approximate size of total user virtual address space on
366     // the currently supported Unix systems.
367     static const UINT64 _128TB = (1ull << 47); 
368     lpBuffer->ullTotalVirtual = _128TB;
369     lpBuffer->ullAvailVirtual = lpBuffer->ullAvailPhys;
370
371     LOGEXIT("GlobalMemoryStatusEx returns %d\n", fRetVal);
372     PERF_EXIT(GlobalMemoryStatusEx);
373
374     return fRetVal;
375 }
376
377 PALIMPORT
378 DWORD
379 PALAPI
380 GetCurrentProcessorNumber()
381 {
382 #if HAVE_SCHED_GETCPU
383     return sched_getcpu();
384 #else //HAVE_SCHED_GETCPU
385     return -1;
386 #endif //HAVE_SCHED_GETCPU
387 }
388
389 BOOL
390 PALAPI
391 PAL_HasGetCurrentProcessorNumber()
392 {
393     return HAVE_SCHED_GETCPU;
394 }
395
396 bool
397 ReadMemoryValueFromFile(const char* filename, size_t* val)
398 {
399     bool result = false;
400     char *line = nullptr;
401     size_t lineLen = 0;
402     char* endptr = nullptr;
403     size_t num = 0, l, multiplier;
404
405     if (val == nullptr)
406         return false;
407
408     FILE* file = fopen(filename, "r");
409     if (file == nullptr)
410         goto done;
411
412     if (getline(&line, &lineLen, file) == -1)
413         goto done;
414
415     errno = 0;
416     num = strtoull(line, &endptr, 0);
417     if (errno != 0)
418         goto done;
419
420     multiplier = 1;
421     switch(*endptr)
422     {
423         case 'g':
424         case 'G': multiplier = 1024;
425         case 'm':
426         case 'M': multiplier = multiplier*1024;
427         case 'k':
428         case 'K': multiplier = multiplier*1024;
429     }
430
431     *val = num * multiplier;
432     result = true;
433     if (*val/multiplier != num)
434         result = false;
435 done:
436     if (file)
437         fclose(file);
438     free(line);
439     return result;
440 }
441
442 size_t
443 PALAPI
444 PAL_GetLogicalProcessorCacheSizeFromOS()
445 {
446     size_t cacheSize = 0;
447
448 #ifdef _SC_LEVEL1_DCACHE_SIZE
449     cacheSize = std::max(cacheSize, (size_t)sysconf(_SC_LEVEL1_DCACHE_SIZE));
450 #endif
451 #ifdef _SC_LEVEL2_CACHE_SIZE
452     cacheSize = std::max(cacheSize, (size_t)sysconf(_SC_LEVEL2_CACHE_SIZE));
453 #endif
454 #ifdef _SC_LEVEL3_CACHE_SIZE
455     cacheSize = std::max(cacheSize, (size_t)sysconf(_SC_LEVEL3_CACHE_SIZE));
456 #endif
457 #ifdef _SC_LEVEL4_CACHE_SIZE
458     cacheSize = std::max(cacheSize, (size_t)sysconf(_SC_LEVEL4_CACHE_SIZE));
459 #endif
460
461 #if defined(_ARM64_)
462     if(cacheSize == 0)
463     {
464         size_t size;
465
466         if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index0/size", &size))
467             cacheSize = std::max(cacheSize, size);
468         if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index1/size", &size))
469             cacheSize = std::max(cacheSize, size);
470         if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index2/size", &size))
471             cacheSize = std::max(cacheSize, size);
472         if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index3/size", &size))
473             cacheSize = std::max(cacheSize, size);
474         if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index4/size", &size))
475             cacheSize = std::max(cacheSize, size);
476     }
477
478     if(cacheSize == 0)
479     {
480         // It is currently expected to be missing cache size info
481         //
482         // _SC_LEVEL*_*CACHE_SIZE is not yet present.  Work is in progress to enable this for arm64
483         //
484         // /sys/devices/system/cpu/cpu*/cache/index*/ is also not yet present in most systems.
485         // Arm64 patch is in Linux kernel tip.
486         //
487         // midr_el1 is available in "/sys/devices/system/cpu/cpu0/regs/identification/midr_el1",
488         // but without an exhaustive list of ARM64 processors any decode of midr_el1
489         // Would likely be incomplete
490
491         // Published information on ARM64 architectures is limited.
492         // If we use recent high core count chips as a guide for state of the art, we find
493         // total L3 cache to be 1-2MB/core.  As always, there are exceptions.
494
495         // Estimate cache size based on CPU count
496         // Assume lower core count are lighter weight parts which are likely to have smaller caches
497         // Assume L3$/CPU grows linearly from 256K to 1.5M/CPU as logicalCPUs grows from 2 to 12 CPUs
498         DWORD logicalCPUs = PAL_GetLogicalCpuCountFromOS();
499
500         cacheSize = logicalCPUs*std::min(1536, std::max(256, (int)logicalCPUs*128))*1024;
501     }
502 #endif
503
504     return cacheSize;
505 }