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.
15 Implements GetSystemInfo.
23 #include "pal/palinternal.h"
28 #include <sys/types.h>
30 #include <sys/sysctl.h>
32 #error Either sysctl or sysconf is required for GetSystemInfo.
36 #include <sys/sysinfo.h>
39 #include <sys/param.h>
41 #if HAVE_SYS_VMPARAM_H
42 #include <sys/vmparam.h>
43 #endif // HAVE_SYS_VMPARAM_H
46 #include <vm/vm_param.h>
49 #if HAVE_MACH_VM_TYPES_H
50 #include <mach/vm_types.h>
51 #endif // HAVE_MACH_VM_TYPES_H
53 #if HAVE_MACH_VM_PARAM_H
54 #include <mach/vm_param.h>
55 #endif // HAVE_MACH_VM_PARAM_H
57 #if HAVE_MACHINE_VMPARAM_H
58 #include <machine/vmparam.h>
59 #endif // HAVE_MACHINE_VMPARAM_H
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)
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
76 #if USER_H_DEFINES_DEBUG
78 #define _DEBUG OLD_DEBUG
82 #include "pal/dbgmsg.h"
86 SET_DEFAULT_DEBUG_CHANNEL(MISC);
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
94 #error Dont know how to get page-size on this architecture!
101 PAL_GetLogicalCpuCountFromOS()
107 #if defined(_ARM_) || defined(_ARM64_)
108 #define SYSCONF_GET_NUMPROCS _SC_NPROCESSORS_CONF
109 #define SYSCONF_GET_NUMPROCS_NAME "_SC_NPROCESSORS_CONF"
111 #define SYSCONF_GET_NUMPROCS _SC_NPROCESSORS_ONLN
112 #define SYSCONF_GET_NUMPROCS_NAME "_SC_NPROCESSORS_ONLN"
114 nrcpus = sysconf(SYSCONF_GET_NUMPROCS);
117 ASSERT("sysconf failed for %s (%d)\n", SYSCONF_GET_NUMPROCS_NAME, errno);
127 rc = sysctl(mib, 2, &nrcpus, &sz, NULL, 0);
130 ASSERT("sysctl failed for HW_NCPU (%d)\n", errno);
132 #endif // HAVE_SYSCONF
143 The GetSystemInfo function returns information about the current system.
148 [out] Pointer to a SYSTEM_INFO structure that receives the information.
152 This function does not return a value.
155 fields returned by this function are:
158 Others are set to zero.
164 OUT LPSYSTEM_INFO lpSystemInfo)
169 PERF_ENTRY(GetSystemInfo);
170 ENTRY("GetSystemInfo (lpSystemInfo=%p)\n", lpSystemInfo);
172 pagesize = getpagesize();
174 lpSystemInfo->wProcessorArchitecture_PAL_Undefined = 0;
175 lpSystemInfo->wReserved_PAL_Undefined = 0;
176 lpSystemInfo->dwPageSize = pagesize;
177 lpSystemInfo->dwActiveProcessorMask_PAL_Undefined = 0;
179 nrcpus = PAL_GetLogicalCpuCountFromOS();
180 TRACE("dwNumberOfProcessors=%d\n", nrcpus);
181 lpSystemInfo->dwNumberOfProcessors = nrcpus;
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;
193 #error How come USRSTACK64 is not defined for 64bit?
195 #elif defined(USRSTACK)
196 lpSystemInfo->lpMaximumApplicationAddress = (PVOID) USRSTACK;
198 #error The maximum application address is not known on this platform.
201 lpSystemInfo->lpMinimumApplicationAddress = (PVOID) pagesize;
203 lpSystemInfo->dwProcessorType_PAL_Undefined = 0;
205 lpSystemInfo->dwAllocationGranularity = pagesize;
207 lpSystemInfo->wProcessorLevel_PAL_Undefined = 0;
208 lpSystemInfo->wProcessorRevision_PAL_Undefined = 0;
210 LOGEXIT("GetSystemInfo returns VOID\n");
211 PERF_EXIT(GetSystemInfo);
220 Retrieves information about the system's current usage of both physical and virtual memory.
224 This function returns a BOOL to indicate its success status.
229 GlobalMemoryStatusEx(
230 IN OUT LPMEMORYSTATUSEX lpBuffer)
233 PERF_ENTRY(GlobalMemoryStatusEx);
234 ENTRY("GlobalMemoryStatusEx (lpBuffer=%p)\n", lpBuffer);
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;
245 BOOL fRetVal = FALSE;
249 // Get the physical memory size
250 #if HAVE_SYSCONF && HAVE__SC_PHYS_PAGES
251 int64_t physical_memory;
253 // Get the Physical memory size
254 physical_memory = sysconf( _SC_PHYS_PAGES ) * sysconf( _SC_PAGE_SIZE );
255 lpBuffer->ullTotalPhys = (DWORDLONG)physical_memory;
258 int64_t physical_memory;
261 // Get the Physical memory size
264 length = sizeof(INT64);
265 rc = sysctl(mib, 2, &physical_memory, &length, NULL, 0);
268 ASSERT("sysctl failed for HW_MEMSIZE (%d)\n", errno);
272 lpBuffer->ullTotalPhys = (DWORDLONG)physical_memory;
276 #endif // HAVE_SYSCTL
278 // Get swap file size, consider the ability to get the values optional
279 // (don't return FALSE from the GlobalMemoryStatusEx)
281 // This is available on OSX
282 struct xsw_usage xsu;
284 mib[1] = VM_SWAPUSAGE;
285 size_t length = sizeof(xsu);
286 rc = sysctl(mib, 2, &xsu, &length, NULL, 0);
289 lpBuffer->ullTotalPageFile = xsu.xsu_total;
290 lpBuffer->ullAvailPageFile = xsu.xsu_avail;
297 rc = sysctlnametomib("vm.swap_info", mib, &length);
300 int pagesize = getpagesize();
301 // Aggregate the information for all swap files on the system
302 for (mib[2] = 0; ; mib[2]++)
304 length = sizeof(xsw);
305 rc = sysctl(mib, 3, &xsw, &length, NULL, 0);
306 if ((rc < 0) || (xsw.xsw_version != XSWDEV_VERSION))
308 // All the swap files were processed or coreclr was built against
309 // a version of headers not compatible with the current XSWDEV_VERSION.
313 DWORDLONG avail = xsw.xsw_nblks - xsw.xsw_used;
314 lpBuffer->ullTotalPageFile += (DWORDLONG)xsw.xsw_nblks * pagesize;
315 lpBuffer->ullAvailPageFile += (DWORDLONG)avail * pagesize;
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
333 #endif // HAVE_SYSINFO
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)
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);
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))
352 if (KERN_SUCCESS == host_statistics(mach_port, HOST_VM_INFO, (host_info_t)&vm_stats, &count))
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);
359 mach_port_deallocate(mach_task_self(), mach_port);
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;
371 LOGEXIT("GlobalMemoryStatusEx returns %d\n", fRetVal);
372 PERF_EXIT(GlobalMemoryStatusEx);
380 GetCurrentProcessorNumber()
382 #if HAVE_SCHED_GETCPU
383 return sched_getcpu();
384 #else //HAVE_SCHED_GETCPU
386 #endif //HAVE_SCHED_GETCPU
391 PAL_HasGetCurrentProcessorNumber()
393 return HAVE_SCHED_GETCPU;
397 ReadMemoryValueFromFile(const char* filename, size_t* val)
400 char *line = nullptr;
402 char* endptr = nullptr;
403 size_t num = 0, l, multiplier;
408 FILE* file = fopen(filename, "r");
412 if (getline(&line, &lineLen, file) == -1)
416 num = strtoull(line, &endptr, 0);
424 case 'G': multiplier = 1024;
426 case 'M': multiplier = multiplier*1024;
428 case 'K': multiplier = multiplier*1024;
431 *val = num * multiplier;
433 if (*val/multiplier != num)
444 PAL_GetLogicalProcessorCacheSizeFromOS()
446 size_t cacheSize = 0;
448 #ifdef _SC_LEVEL1_DCACHE_SIZE
449 cacheSize = std::max(cacheSize, (size_t)sysconf(_SC_LEVEL1_DCACHE_SIZE));
451 #ifdef _SC_LEVEL2_CACHE_SIZE
452 cacheSize = std::max(cacheSize, (size_t)sysconf(_SC_LEVEL2_CACHE_SIZE));
454 #ifdef _SC_LEVEL3_CACHE_SIZE
455 cacheSize = std::max(cacheSize, (size_t)sysconf(_SC_LEVEL3_CACHE_SIZE));
457 #ifdef _SC_LEVEL4_CACHE_SIZE
458 cacheSize = std::max(cacheSize, (size_t)sysconf(_SC_LEVEL4_CACHE_SIZE));
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);
480 // It is currently expected to be missing cache size info
482 // _SC_LEVEL*_*CACHE_SIZE is not yet present. Work is in progress to enable this for arm64
484 // /sys/devices/system/cpu/cpu*/cache/index*/ is also not yet present in most systems.
485 // Arm64 patch is in Linux kernel tip.
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
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.
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();
500 cacheSize = logicalCPUs*std::min(1536, std::max(256, (int)logicalCPUs*128))*1024;