tuklib_physmem.c revision 207753
1///////////////////////////////////////////////////////////////////////////////
2//
3/// \file       tuklib_physmem.c
4/// \brief      Get the amount of physical memory
5//
6//  Author:     Lasse Collin
7//
8//  This file has been put into the public domain.
9//  You can do whatever you want with this file.
10//
11///////////////////////////////////////////////////////////////////////////////
12
13#include "tuklib_physmem.h"
14
15// We want to use Windows-specific code on Cygwin, which also has memory
16// information available via sysconf(), but on Cygwin 1.5 and older it
17// gives wrong results (from our point of view).
18#if defined(_WIN32) || defined(__CYGWIN__)
19#	ifndef _WIN32_WINNT
20#		define _WIN32_WINNT 0x0500
21#	endif
22#	include <windows.h>
23
24#elif defined(__OS2__)
25#	define INCL_DOSMISC
26#	include <os2.h>
27
28#elif defined(__DJGPP__)
29#	include <dpmi.h>
30
31#elif defined(__VMS)
32#	include <lib$routines.h>
33#	include <syidef.h>
34#	include <ssdef.h>
35
36#elif defined(TUKLIB_PHYSMEM_SYSCONF)
37#	include <unistd.h>
38
39#elif defined(TUKLIB_PHYSMEM_SYSCTL)
40#	ifdef HAVE_SYS_PARAM_H
41#		include <sys/param.h>
42#	endif
43#	include <sys/sysctl.h>
44
45// IRIX
46#elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
47#	include <invent.h>
48
49// This sysinfo() is Linux-specific.
50#elif defined(TUKLIB_PHYSMEM_SYSINFO)
51#	include <sys/sysinfo.h>
52#endif
53
54
55extern uint64_t
56tuklib_physmem(void)
57{
58	uint64_t ret = 0;
59
60#if defined(_WIN32) || defined(__CYGWIN__)
61	if ((GetVersion() & 0xFF) >= 5) {
62		// Windows 2000 and later have GlobalMemoryStatusEx() which
63		// supports reporting values greater than 4 GiB. To keep the
64		// code working also on older Windows versions, use
65		// GlobalMemoryStatusEx() conditionally.
66		HMODULE kernel32 = GetModuleHandle("kernel32.dll");
67		if (kernel32 != NULL) {
68			BOOL (WINAPI *gmse)(LPMEMORYSTATUSEX) = GetProcAddress(
69					kernel32, "GlobalMemoryStatusEx");
70			if (gmse != NULL) {
71				MEMORYSTATUSEX meminfo;
72				meminfo.dwLength = sizeof(meminfo);
73				if (gmse(&meminfo))
74					ret = meminfo.ullTotalPhys;
75			}
76		}
77	}
78
79	if (ret == 0) {
80		// GlobalMemoryStatus() is supported by Windows 95 and later,
81		// so it is fine to link against it unconditionally. Note that
82		// GlobalMemoryStatus() has no return value.
83		MEMORYSTATUS meminfo;
84		meminfo.dwLength = sizeof(meminfo);
85		GlobalMemoryStatus(&meminfo);
86		ret = meminfo.dwTotalPhys;
87	}
88
89#elif defined(__OS2__)
90	unsigned long mem;
91	if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM,
92			&mem, sizeof(mem)) == 0)
93		ret = mem;
94
95#elif defined(__DJGPP__)
96	__dpmi_free_mem_info meminfo;
97	if (__dpmi_get_free_memory_information(&meminfo) == 0
98			&& meminfo.total_number_of_physical_pages
99				!= (unsigned long)-1)
100		ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096;
101
102#elif defined(__VMS)
103	int vms_mem;
104	int val = SYI$_MEMSIZE;
105	if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL)
106		ret = (uint64_t)vms_mem * 8192;
107
108#elif defined(TUKLIB_PHYSMEM_SYSCONF)
109	const long pagesize = sysconf(_SC_PAGESIZE);
110	const long pages = sysconf(_SC_PHYS_PAGES);
111	if (pagesize != -1 || pages != -1)
112		// According to docs, pagesize * pages can overflow.
113		// Simple case is 32-bit box with 4 GiB or more RAM,
114		// which may report exactly 4 GiB of RAM, and "long"
115		// being 32-bit will overflow. Casting to uint64_t
116		// hopefully avoids overflows in the near future.
117		ret = (uint64_t)pagesize * (uint64_t)pages;
118
119#elif defined(TUKLIB_PHYSMEM_SYSCTL)
120	int name[2] = {
121		CTL_HW,
122#ifdef HW_PHYSMEM64
123		HW_PHYSMEM64
124#else
125		HW_PHYSMEM
126#endif
127	};
128	union {
129		uint32_t u32;
130		uint64_t u64;
131	} mem;
132	size_t mem_ptr_size = sizeof(mem.u64);
133	if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) {
134		// IIRC, 64-bit "return value" is possible on some 64-bit
135		// BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64),
136		// so support both.
137		if (mem_ptr_size == sizeof(mem.u64))
138			ret = mem.u64;
139		else if (mem_ptr_size == sizeof(mem.u32))
140			ret = mem.u32;
141	}
142
143#elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
144	inv_state_t *st = NULL;
145	if (setinvent_r(&st) != -1) {
146		inventory_t *i;
147		while ((i = getinvent_r(st)) != NULL) {
148			if (i->inv_class == INV_MEMORY
149					&& i->inv_type == INV_MAIN_MB) {
150				ret = (uint64_t)i->inv_state << 20;
151				break;
152			}
153		}
154
155		endinvent_r(st);
156	}
157
158#elif defined(TUKLIB_PHYSMEM_SYSINFO)
159	struct sysinfo si;
160	if (sysinfo(&si) == 0)
161		ret = (uint64_t)si.totalram * si.mem_unit;
162#endif
163
164	return ret;
165}
166