diff options
Diffstat (limited to 'src/common/tuklib_physmem.c')
-rw-r--r-- | src/common/tuklib_physmem.c | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/src/common/tuklib_physmem.c b/src/common/tuklib_physmem.c new file mode 100644 index 0000000..69f6fd4 --- /dev/null +++ b/src/common/tuklib_physmem.c @@ -0,0 +1,234 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file tuklib_physmem.c +/// \brief Get the amount of physical memory +// +// Author: Lasse Collin +// +// This file has been put into the public domain. +// You can do whatever you want with this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tuklib_physmem.h" + +// We want to use Windows-specific code on Cygwin, which also has memory +// information available via sysconf(), but on Cygwin 1.5 and older it +// gives wrong results (from our point of view). +#if defined(_WIN32) || defined(__CYGWIN__) +# ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0500 +# endif +# include <windows.h> + +#elif defined(__OS2__) +# define INCL_DOSMISC +# include <os2.h> + +#elif defined(__DJGPP__) +# include <dpmi.h> + +#elif defined(__VMS) +# include <lib$routines.h> +# include <syidef.h> +# include <ssdef.h> + +#elif defined(AMIGA) || defined(__AROS__) +# define __USE_INLINE__ +# include <proto/exec.h> + +#elif defined(__QNX__) +# include <sys/syspage.h> +# include <string.h> + +#elif defined(TUKLIB_PHYSMEM_AIX) +# include <sys/systemcfg.h> + +#elif defined(TUKLIB_PHYSMEM_SYSCONF) +# include <unistd.h> + +#elif defined(TUKLIB_PHYSMEM_SYSCTL) +# ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +# endif +# include <sys/sysctl.h> + +// Tru64 +#elif defined(TUKLIB_PHYSMEM_GETSYSINFO) +# include <sys/sysinfo.h> +# include <machine/hal_sysinfo.h> + +// HP-UX +#elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC) +# include <sys/param.h> +# include <sys/pstat.h> + +// IRIX +#elif defined(TUKLIB_PHYSMEM_GETINVENT_R) +# include <invent.h> + +// This sysinfo() is Linux-specific. +#elif defined(TUKLIB_PHYSMEM_SYSINFO) +# include <sys/sysinfo.h> +#endif + + +// With GCC >= 8.1 with -Wextra and Clang >= 13 with -Wcast-function-type +// will warn about the Windows-specific code. +#if defined(__has_warning) +# if __has_warning("-Wcast-function-type") +# define CAN_DISABLE_WCAST_FUNCTION_TYPE 1 +# endif +#elif TUKLIB_GNUC_REQ(8,1) +# define CAN_DISABLE_WCAST_FUNCTION_TYPE 1 +#endif + + +extern uint64_t +tuklib_physmem(void) +{ + uint64_t ret = 0; + +#if defined(_WIN32) || defined(__CYGWIN__) + if ((GetVersion() & 0xFF) >= 5) { + // Windows 2000 and later have GlobalMemoryStatusEx() which + // supports reporting values greater than 4 GiB. To keep the + // code working also on older Windows versions, use + // GlobalMemoryStatusEx() conditionally. + HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll")); + if (kernel32 != NULL) { + typedef BOOL (WINAPI *gmse_type)(LPMEMORYSTATUSEX); +#ifdef CAN_DISABLE_WCAST_FUNCTION_TYPE +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + gmse_type gmse = (gmse_type)GetProcAddress( + kernel32, "GlobalMemoryStatusEx"); +#ifdef CAN_DISABLE_WCAST_FUNCTION_TYPE +# pragma GCC diagnostic pop +#endif + if (gmse != NULL) { + MEMORYSTATUSEX meminfo; + meminfo.dwLength = sizeof(meminfo); + if (gmse(&meminfo)) + ret = meminfo.ullTotalPhys; + } + } + } + + if (ret == 0) { + // GlobalMemoryStatus() is supported by Windows 95 and later, + // so it is fine to link against it unconditionally. Note that + // GlobalMemoryStatus() has no return value. + MEMORYSTATUS meminfo; + meminfo.dwLength = sizeof(meminfo); + GlobalMemoryStatus(&meminfo); + ret = meminfo.dwTotalPhys; + } + +#elif defined(__OS2__) + unsigned long mem; + if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, + &mem, sizeof(mem)) == 0) + ret = mem; + +#elif defined(__DJGPP__) + __dpmi_free_mem_info meminfo; + if (__dpmi_get_free_memory_information(&meminfo) == 0 + && meminfo.total_number_of_physical_pages + != (unsigned long)-1) + ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096; + +#elif defined(__VMS) + int vms_mem; + int val = SYI$_MEMSIZE; + if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL) + ret = (uint64_t)vms_mem * 8192; + +#elif defined(AMIGA) || defined(__AROS__) + ret = AvailMem(MEMF_TOTAL); + +#elif defined(__QNX__) + const struct asinfo_entry *entries = SYSPAGE_ENTRY(asinfo); + size_t count = SYSPAGE_ENTRY_SIZE(asinfo) / sizeof(struct asinfo_entry); + const char *strings = SYSPAGE_ENTRY(strings)->data; + + for (size_t i = 0; i < count; ++i) + if (strcmp(strings + entries[i].name, "ram") == 0) + ret += entries[i].end - entries[i].start + 1; + +#elif defined(TUKLIB_PHYSMEM_AIX) + ret = _system_configuration.physmem; + +#elif defined(TUKLIB_PHYSMEM_SYSCONF) + const long pagesize = sysconf(_SC_PAGESIZE); + const long pages = sysconf(_SC_PHYS_PAGES); + if (pagesize != -1 && pages != -1) + // According to docs, pagesize * pages can overflow. + // Simple case is 32-bit box with 4 GiB or more RAM, + // which may report exactly 4 GiB of RAM, and "long" + // being 32-bit will overflow. Casting to uint64_t + // hopefully avoids overflows in the near future. + ret = (uint64_t)pagesize * (uint64_t)pages; + +#elif defined(TUKLIB_PHYSMEM_SYSCTL) + int name[2] = { + CTL_HW, +#ifdef HW_PHYSMEM64 + HW_PHYSMEM64 +#else + HW_PHYSMEM +#endif + }; + union { + uint32_t u32; + uint64_t u64; + } mem; + size_t mem_ptr_size = sizeof(mem.u64); + if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) { + // IIRC, 64-bit "return value" is possible on some 64-bit + // BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64), + // so support both. + if (mem_ptr_size == sizeof(mem.u64)) + ret = mem.u64; + else if (mem_ptr_size == sizeof(mem.u32)) + ret = mem.u32; + } + +#elif defined(TUKLIB_PHYSMEM_GETSYSINFO) + // Docs are unclear if "start" is needed, but it doesn't hurt + // much to have it. + int memkb; + int start = 0; + if (getsysinfo(GSI_PHYSMEM, (caddr_t)&memkb, sizeof(memkb), &start) + != -1) + ret = (uint64_t)memkb * 1024; + +#elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC) + struct pst_static pst; + if (pstat_getstatic(&pst, sizeof(pst), 1, 0) != -1) + ret = (uint64_t)pst.physical_memory * (uint64_t)pst.page_size; + +#elif defined(TUKLIB_PHYSMEM_GETINVENT_R) + inv_state_t *st = NULL; + if (setinvent_r(&st) != -1) { + inventory_t *i; + while ((i = getinvent_r(st)) != NULL) { + if (i->inv_class == INV_MEMORY + && i->inv_type == INV_MAIN_MB) { + ret = (uint64_t)i->inv_state << 20; + break; + } + } + + endinvent_r(st); + } + +#elif defined(TUKLIB_PHYSMEM_SYSINFO) + struct sysinfo si; + if (sysinfo(&si) == 0) + ret = (uint64_t)si.totalram * si.mem_unit; +#endif + + return ret; +} |