diff options
Diffstat (limited to 'lib/os_mon/c_src/memsup.c')
-rw-r--r-- | lib/os_mon/c_src/memsup.c | 655 |
1 files changed, 655 insertions, 0 deletions
diff --git a/lib/os_mon/c_src/memsup.c b/lib/os_mon/c_src/memsup.c new file mode 100644 index 0000000000..241e7718db --- /dev/null +++ b/lib/os_mon/c_src/memsup.c @@ -0,0 +1,655 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +/* + * Purpose: Portprogram for supervision of memory usage. + * + * Synopsis: memsup + * + * PURPOSE OF THIS PROGRAM + * + * This program supervises the memory status of the entire system, and + * sends status reports upon request from the Erlang system + * + * SPAWNING FROM ERLANG + * + * This program is started from Erlang as follows, + * + * Port = open_port({spawn, 'memsup'}, [{packet,1}]) for UNIX and VxWorks + * + * Erlang sends one of the request condes defined in memsup.h and this program + * answers in one of two ways: + * * If the request is for simple memory data (which is used periodically + * for monitoring) the answer is simply sent in two packets. + * * If the request is for the system specific data, the answer is delivered + * in two packets per value, first a tag value, then the actual + * value. The values are delivered "as is", this interface is + * mainly for VxWorks. + * All numbers are sent as strings of hexadecimal digits. + * + * SUNOS FAKING + * + * When using SunOS 4, the memory report is faked. The total physical memory + * is always reported to be 256MB, and the used fraction to be 128MB. + * + * If capabilities, such as sysconf or procfs, is not defined on the system + * memsup will fake memory usage as well. + * + * Following ordering is defined for extended memory, + * Linux: procfs -> sysinfo -> sysconf -> fake + * Sunos: sysconf -> fake + * other: arch specific + * + * Todo: + * Memory retrieval should be defined by capabilities and not by archs. + * Ordering should be defined arch. + * + * STANDARD INPUT, OUTPUT AND ERROR + * + * This program communicates with Erlang through the standard + * input and output file descriptors (0 and 1). These descriptors + * (and the standard error descriptor 2) must NOT be closed + * explicitely by this program at termination (in UNIX it is + * taken care of by the operating system itself; in VxWorks + * it is taken care of by the spawn driver part of the Emulator). + * + * END OF FILE + * + * If a read from a file descriptor returns zero (0), it means + * that there is no process at the other end of the connection + * having the connection open for writing (end-of-file). + * + * COMPILING + * + * When the target is VxWorks the identifier VXWORKS must be defined for + * the preprocessor (usually by a -D option). + */ + +#if defined(sgi) || defined(__sgi) || defined(__sgi__) +#include <sys/types.h> +#include <sys/sysmp.h> +#endif + +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> + +#ifndef VXWORKS +#include <unistd.h> +#endif + +#if (defined(__unix__) || defined(unix)) && !defined(USG) +#include <sys/param.h> +#endif + +#include <stdarg.h> + +#include <string.h> +#include <time.h> +#include <errno.h> + +#ifdef VXWORKS +#include <vxWorks.h> +#include <ioLib.h> +#include <memLib.h> +#endif + +#ifdef BSD4_4 +#include <sys/types.h> +#include <sys/sysctl.h> +#if !defined (__OpenBSD__) && !defined (__NetBSD__) +#include <vm/vm_param.h> +#endif +#if defined (__FreeBSD__) || defined(__DragonFly__) +#include <sys/vmmeter.h> +#endif +#endif + +#if defined (__linux__) +#include <sys/sysinfo.h> +#endif + +/* commands */ +#include "memsup.h" + +#define CMD_SIZE 1 +#define MAX_CMD_BUF 10 +#define ERLIN_FD 0 +#define ERLOUT_FD 1 + + +/* procfs */ +#if defined(__linux__) +#include <fcntl.h> +#define MEMINFO "/proc/meminfo" +#endif + +/* prototypes */ + +static void print_error(const char *,...); +#ifdef VXWORKS +extern int erl_mem_info_get(MEM_PART_STATS *); +#endif + +#ifdef VXWORKS +#define MAIN memsup + +static MEM_PART_STATS latest; +static unsigned long latest_system_total; /* does not fit in the struct */ + +#else +#define MAIN main +#endif + + +/* + * example, we want procfs information, now give them something equivalent: + * + * MemTotal: 4029352 kB old HighTotal + LowTotal + * MemFree: 1674168 kB old HighFree + LowFree + * MemShared: 0 kB old now always zero; not calculated + * Buffers: 417164 kB old temporary storage for raw disk blocks + * Cached: 371312 kB old in-memory cache for files read from the disk (the page cache) + + * Active: 1408492 kB new + + * Inact_dirty: 7772 kB new + * Inact_clean: 2008 kB new + * Inact_target: 0 kB new + * Inact_laundry: 0 kB new, and might be missing too + + * HighTotal: 0 kB + * HighFree: 0 kB memory area for userspace programs or for the pagecache + * LowTotal: 4029352 kB + * LowFree: 1674168 kB Highmem + kernel stuff, slab allocates here + + * SwapTotal: 4194296 kB old total amount of swap space available + * SwapFree: 4194092 kB old Memory which has been evicted from RAM + * Inactive: 549224 kB 2.5.41+ + * Dirty: 872 kB 2.5.41+ Memory which is waiting to get written back to the disk + * Writeback: 0 kB 2.5.41+ Memory which is actively being written back to the disk + * AnonPages: 787616 kB ?? + * Mapped: 113612 kB 2.5.41+ files which have been mmaped, such as libraries + * Slab: 342864 kB 2.5.41+ in-kernel data structures cache + * CommitLimit: 6208972 kB ?? + * Committed_AS: 1141444 kB 2.5.41+ + * PageTables: 9368 kB 2.5.41+ + * VmallocTotal: 34359738367 kB ?? total size of vmalloc memory area + * VmallocUsed: 57376 kB ?? amount of vmalloc area which is used + * VmallocChunk: 34359677947 kB ?? largest contigious block of vmalloc area which is free + * ReverseMaps: 5738 2.5.41+ number of rmap pte chains + * SwapCached: 0 kB 2.5.??+ + * HugePages_Total: 0 2.5.??+ + * HugePages_Free: 0 2.5.??+ + * HugePages_Rsvd: 0 2.5.??+ + * Hugepagesize: 2048 kB 2.5.?? + * + * This information should be generalized for generic platform i.e. erlang. + */ + + + +#define F_MEM_TOTAL (1 << 0) +#define F_MEM_FREE (1 << 1) +#define F_MEM_BUFFERS (1 << 2) +#define F_MEM_CACHED (1 << 3) +#define F_MEM_SHARED (1 << 4) +#define F_SWAP_TOTAL (1 << 5) +#define F_SWAP_FREE (1 << 6) + +typedef struct { + unsigned int flag; + unsigned long pagesize; + unsigned long total; + unsigned long free; + unsigned long buffered; + unsigned long cached; + unsigned long shared; + unsigned long total_swap; + unsigned long free_swap; +} memory_ext; + +typedef struct mem_table_struct { + const char *name; /* memory type name */ + unsigned long *slot; /* slot in return struct */ +} mem_table_struct; + + +/* static variables */ + +static char *program_name; + +static void +send(unsigned long value, unsigned long pagesize) { + char buf[32]; + int left, bytes, res; + int hex_zeroes; + + for (hex_zeroes = 0; (pagesize % 16) == 0; pagesize /= 16) { + hex_zeroes++; + } + + sprintf(buf+1, "%lx", value*pagesize); + bytes = strlen(buf+1); + while (hex_zeroes-- > 0) { + bytes++; + buf[bytes] = '0'; + } + buf[0] = (char) bytes; + left = ++bytes; + + while (left > 0) { + res = write(ERLOUT_FD, buf+bytes-left, left); + if (res <= 0){ + perror("Error writing to pipe"); + exit(1); + } + left -= res; + } +} + +static void +send_tag(int value){ + unsigned char buf[2]; + int res,left; + + buf[0] = 1U; + buf[1] = (unsigned char) value; + left = 2; + while(left > 0) { + if((res = write(ERLOUT_FD, buf+left-2,left)) <= 0){ + perror("Error writing to pipe"); + exit(1); + } else { + left -= res; + } + } +} + + +#ifdef VXWORKS +static void load_statistics(void){ + if(memPartInfoGet(memSysPartId,&latest) != OK) + memset(&latest,0,sizeof(latest)); + latest_system_total = latest.numBytesFree + latest.numBytesAlloc; + erl_mem_info_get(&latest); /* if it fails, latest is untouched */ +} +#endif + +#ifdef BSD4_4 +static int +get_vmtotal(struct vmtotal *vt) { + static int vmtotal_mib[] = {CTL_VM, VM_METER}; + size_t size = sizeof *vt; + + return sysctl(vmtotal_mib, 2, vt, &size, NULL, 0) != -1; +} +#endif + +#if defined(__linux__) + + +static int +get_mem_procfs(memory_ext *me){ + int fd, nread; + char buffer[4097]; + char *bp; + unsigned long value; + + me->flag = 0; + + if ( (fd = open(MEMINFO, O_RDONLY)) < 0) return -1; + + if ( (nread = read(fd, buffer, 4096)) < 0) { + close(fd); + return -1; + } + close(fd); + + buffer[nread] = '\0'; + + /* Total and free is NEEDED! */ + + bp = strstr(buffer, "MemTotal:"); + if (sscanf(bp, "MemTotal: %lu kB\n", &(me->total))) me->flag |= F_MEM_TOTAL; + + bp = strstr(buffer, "MemFree:"); + if (sscanf(bp, "MemFree: %lu kB\n", &(me->free))) me->flag |= F_MEM_FREE; + + /* Extensions */ + + bp = strstr(buffer, "Buffers:"); + if (sscanf(bp, "Buffers: %lu kB\n", &(me->buffered))) me->flag |= F_MEM_BUFFERS; + + bp = strstr(buffer, "Cached:"); + if (sscanf(bp, "Cached: %lu kB\n", &(me->cached))) me->flag |= F_MEM_CACHED; + + + /* Swap */ + + bp = strstr(buffer, "SwapTotal:"); + if (sscanf(bp, "SwapTotal: %lu kB\n", &(me->total_swap))) me->flag |= F_SWAP_TOTAL; + + bp = strstr(buffer, "SwapFree:"); + if (sscanf(bp, "SwapFree: %lu kB\n", &(me->free_swap))) me->flag |= F_SWAP_FREE; + + me->pagesize = 1024; /* procfs defines its size in kB */ + + return 1; +} +#endif + + +/* arch specific functions */ + +#if defined(VXWORKS) +static int +get_extended_mem_vxwork(memory_ext *me) { + load_statistics(); + me->total = (latest.numBytesFree + latest.numBytesAlloc); + me->free = latest.numBytesFree; + me->pagesize = 1; + me->flag = F_MEM_TOTAL | F_MEM_FREE; + return 1; +} +#endif + + +#if defined(__linux__) /* ifdef SYSINFO */ +/* sysinfo does not include cached memory which is a problem. */ +static int +get_extended_mem_sysinfo(memory_ext *me) { + struct sysinfo info; + me->flag = 0; + if (sysinfo(&info) < 0) return -1; + me->pagesize = 1; + me->total = info.totalram; + me->free = info.freeram; + me->buffered = info.bufferram; + me->shared = info.sharedram; + me->total_swap = info.totalswap; + me->free_swap = info.freeswap; + + me->flag = F_MEM_TOTAL | F_MEM_FREE | F_MEM_SHARED | F_MEM_BUFFERS | F_SWAP_TOTAL | F_SWAP_FREE; + + return 1; +} +#endif + + +#if defined(_SC_AVPHYS_PAGES) +static int +get_extended_mem_sysconf(memory_ext *me) { + me->total = sysconf(_SC_PHYS_PAGES); + me->free = sysconf(_SC_AVPHYS_PAGES); + me->pagesize = sysconf(_SC_PAGESIZE); + + me->flag = F_MEM_TOTAL | F_MEM_FREE; + + return 1; +} +#endif + +#if defined(BSD4_4) +static int +get_extended_mem_bsd4(memory_ext *me) { + struct vmtotal vt; + long pgsz; + + if (!get_vmtotal(&vt)) return 0; + if ((pgsz = sysconf(_SC_PAGESIZE)) == -1) return 0; + + me->total = (vt.t_free + vt.t_rm); + me->free = vt.t_free; + me->pagesize = pgsz; + + me->flag = F_MEM_TOTAL | F_MEM_FREE; + + return 1; +} +#endif + +#if defined(sgi) || defined(__sgi) || defined(__sgi__) +static int +get_extended_mem_sgi(memory_ext *me) { + struct rminfo rmi; + if (sysmp(MP_SAGET, MPSA_RMINFO, &rmi, sizeof(rmi)) < 0) return -1; + + me->total = (unsigned long)(rmi.physmem); + me->free = (unsigned long)(rmi.freemem); + me->pagesize = (unsigned long)getpagesize(); + me->flag = F_MEM_TOTAL | F_MEM_FREE; + + return 1; +} +#endif + +static void +get_extended_mem(memory_ext *me) { +/* vxworks */ +#if defined(VXWORKS) + if (get_extended_mem_vxworks(me)) return; + +/* linux */ +#elif defined(__linux__) + if (get_mem_procfs(me)) return; + if (get_extended_mem_sysinfo(me)) return; + +/* bsd */ +#elif defined(BSD4_4) + if (get_extended_mem_bsd4(me)) return; + +/* sgi */ +#elif defined(sgi) || defined(__sgi) || defined(__sgi__) + if (get_extended_mem_sgi(me)) return; +#endif + +/* Does this exist on others than Solaris2? */ +#if defined(_SC_AVPHYS_PAGES) + if (get_extended_mem_sysconf(me)) return; + +/* We fake the rest */ +/* SunOS4 (for example) */ +#else + me->free = (1<<27); /* Fake! 128 MB used */ + me->total = (1<<28); /* Fake! 256 MB total */ + me->pagesize = 1; + me->flag = F_MEM_TOTAL | F_MEM_FREE; +#endif +} + + +static void +get_basic_mem(unsigned long *tot, unsigned long *used, unsigned long *pagesize){ +#if defined(VXWORKS) + load_statistics(); + *tot = (latest.numBytesFree + latest.numBytesAlloc); + *used = latest.numBytesAlloc; + *pagesize = 1; +#elif defined(_SC_AVPHYS_PAGES) /* Does this exist on others than Solaris2? */ + unsigned long avPhys, phys, pgSz; + + phys = sysconf(_SC_PHYS_PAGES); + avPhys = sysconf(_SC_AVPHYS_PAGES); + *used = (phys - avPhys); + *tot = phys; + *pagesize = sysconf(_SC_PAGESIZE); +#elif defined(__linux__) && !defined(_SC_AVPHYS_PAGES) + memory_ext me; + if (get_mem_procfs(&me) < 0) { + print_error("ProcFS read error."); + exit(1); + } + *tot = me.total; + *pagesize = me.pagesize; + *used = me.total - me.free; +#elif defined(BSD4_4) + struct vmtotal vt; + long pgsz; + + if (!get_vmtotal(&vt)) goto fail; + if ((pgsz = sysconf(_SC_PAGESIZE)) == -1) goto fail; + *tot = (vt.t_free + vt.t_rm); + *used = vt.t_rm; + *pagesize = pgsz; + return; +fail: + print_error("%s", strerror(errno)); + exit(1); +#elif defined(sgi) || defined(__sgi) || defined(__sgi__) + struct rminfo rmi; + if (sysmp(MP_SAGET, MPSA_RMINFO, &rmi, sizeof(rmi)) != -1) { + *tot = (unsigned long)(rmi.physmem); + *used = (unsigned long)(rmi.physmem - rmi.freemem); + *pagesize = (unsigned long)getpagesize(); + } else { + print_error("%s", strerror(errno)); + exit(1); + } +#else /* SunOS4 */ + *used = (1<<27); /* Fake! 128 MB used */ + *tot = (1<<28); /* Fake! 256 MB total */ + *pagesize = 1; +#endif +} + +static void +simple_show_mem(void){ + unsigned long tot, used, pagesize; + get_basic_mem(&tot, &used, &pagesize); + send(used, pagesize); + send(tot, pagesize); +} + +static void +extended_show_mem(void){ + memory_ext me; + unsigned long ps; + + get_extended_mem(&me); + ps = me.pagesize; + + if (me.flag & F_MEM_TOTAL) { send_tag(MEM_TOTAL); send(me.total, ps); } + if (me.flag & F_MEM_FREE) { send_tag(MEM_FREE); send(me.free, ps); } + + /* extensions */ + if (me.flag & F_MEM_BUFFERS){ send_tag(MEM_BUFFERS); send(me.buffered, ps); } + if (me.flag & F_MEM_CACHED) { send_tag(MEM_CACHED); send(me.cached, ps); } + if (me.flag & F_MEM_SHARED) { send_tag(MEM_SHARED); send(me.shared, ps); } + + /* swap */ + if (me.flag & F_SWAP_TOTAL) { send_tag(SWAP_TOTAL); send(me.total_swap, ps); } + if (me.flag & F_SWAP_FREE) { send_tag(SWAP_FREE); send(me.free_swap, ps); } + +#ifdef VXWORKS + send_tag(SM_SYSTEM_TOTAL); + send(latest_system_total, 1); + send_tag(SM_LARGEST_FREE); + send(latest.maxBlockSizeFree, 1); + send_tag(SM_NUMBER_OF_FREE); + send(latest.numBlocksFree, 1); +#else + /* total is system total*/ + if (me.flag & F_MEM_TOTAL) { send_tag(MEM_SYSTEM_TOTAL); send(me.total, ps); } +#endif + send_tag(SHOW_SYSTEM_MEM_END); +} + +static void +message_loop(int erlin_fd) +{ + char cmdLen, cmd; + int res; + + while (1){ + /* + * Wait for command from Erlang + */ + if ((res = read(erlin_fd, &cmdLen, 1)) < 0) { + print_error("Error reading from Erlang."); + return; + } + + if (res == 1) { /* Exactly one byte read ? */ + if (cmdLen == 1){ /* Should be! */ + switch (read(erlin_fd, &cmd, 1)){ + case 1: + switch (cmd){ + case SHOW_MEM: + simple_show_mem(); + break; + case SHOW_SYSTEM_MEM: + extended_show_mem(); + break; + default: /* ignore all other messages */ + break; + } + break; + + case 0: + print_error("Erlang has closed."); + return; + + default: + print_error("Error reading from Erlang."); + return; + } /* switch() */ + } else { /* cmdLen != 1 */ + print_error("Invalid command length (%d) received.", cmdLen); + return; + } + } else { /* Erlang end closed */ + print_error("Erlang has closed."); + return; + } + } +} + +/* + * main + */ +int +MAIN(int argc, char **argv) +{ + program_name = argv[0]; + message_loop(ERLIN_FD); + return 0; +} + + +/* + * print_error + * + */ +static void +print_error(const char *format,...) +{ + va_list args; + + va_start(args, format); + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, " \n"); +} + + + + + |