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
9Module Name:
10
11 sysinfo.c
12
13Abstract:
14
15 Implements GetSystemInfo.
16
17Revision 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
86SET_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
99DWORD
100PALAPI
101PAL_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/*++
138Function:
139 GetSystemInfo
140
141GetSystemInfo
142
143The GetSystemInfo function returns information about the current system.
144
145Parameters
146
147lpSystemInfo
148 [out] Pointer to a SYSTEM_INFO structure that receives the information.
149
150Return Values
151
152This function does not return a value.
153
154Note:
155 fields returned by this function are:
156 dwNumberOfProcessors
157 dwPageSize
158Others are set to zero.
159
160--*/
161VOID
162PALAPI
163GetSystemInfo(
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/*++
215Function:
216 GlobalMemoryStatusEx
217
218GlobalMemoryStatusEx
219
220Retrieves information about the system's current usage of both physical and virtual memory.
221
222Return Values
223
224This function returns a BOOL to indicate its success status.
225
226--*/
227BOOL
228PALAPI
229GlobalMemoryStatusEx(
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
377PALIMPORT
378DWORD
379PALAPI
380GetCurrentProcessorNumber()
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
389BOOL
390PALAPI
391PAL_HasGetCurrentProcessorNumber()
392{
393 return HAVE_SCHED_GETCPU;
394}
395
396bool
397ReadMemoryValueFromFile(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;
435done:
436 if (file)
437 fclose(file);
438 free(line);
439 return result;
440}
441
442size_t
443PALAPI
444PAL_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}
506