diff --git a/src/frontend/Makefile.am b/src/frontend/Makefile.am index 94b9c859f..46000d289 100644 --- a/src/frontend/Makefile.am +++ b/src/frontend/Makefile.am @@ -121,6 +121,9 @@ libfte_la_SOURCES = \ fourier.h \ gens.c \ gens.h \ + get_avail_mem_size.c \ + get_resident_set_size.c \ + get_phys_mem_size.c \ hpgl.c \ hpgl.h \ inp.c \ diff --git a/src/frontend/get_avail_mem_size.c b/src/frontend/get_avail_mem_size.c new file mode 100644 index 000000000..30acf4d6a --- /dev/null +++ b/src/frontend/get_avail_mem_size.c @@ -0,0 +1,140 @@ +/* + * Author: Holger Vogt + * License: 3-clause BSD License + * + */ + +#include "ngspice/ngspice.h" + +#if defined(_WIN32) +#undef BOOLEAN +#include + +#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) +#include +#include +#include +#if defined(BSD) +#include +#endif +#if defined(__APPLE__) && defined(__MACH__) +#import +#import +#endif +#else +#error "Unable to define getMemorySize( ) for an unknown OS." +#endif + + +/** + * Returns the size of available memory (RAM) in bytes. + */ +unsigned long long getAvailableMemorySize( ) +{ +#if defined(HAVE__PROC_MEMINFO) + /* Cygwin , Linux--------------------------------- */ + /* Search for string "MemFree" */ + FILE *fp; + char buffer[2048]; + size_t bytes_read; + char *match; + unsigned long long mem_got; + + if ((fp = fopen("/proc/meminfo", "r")) == NULL) { + perror("fopen(\"/proc/meminfo\")"); + return 0L; + } + + bytes_read = fread(buffer, 1, sizeof(buffer), fp); + fclose(fp); + if (bytes_read == 0 || bytes_read == sizeof(buffer)) + return 0L; + buffer[bytes_read] = '\0'; + match = strstr(buffer, "MemFree"); + if (match == NULL) /* not found */ + return 0L; + sscanf(match, "MemFree: %llu", &mem_got); + return mem_got * 1024; + +#elif defined(_WIN32) + /* Windows. ------------------------------------------------- */ + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx( &status ); + return (size_t)status.ullAvailPhys; + +#elif defined(__APPLE__) && defined(__MACH__) + + mach_port_t host_port; + mach_msg_type_number_t host_size; + vm_size_t pagesize; + + host_port = mach_host_self(); + host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t); + host_page_size(host_port, &pagesize); + + vm_statistics_data_t vm_stat; + + if (host_statistics(host_port, HOST_VM_INFO, (host_info_t) &vm_stat, + &host_size) != KERN_SUCCESS) { + NSLog(@"Failed to fetch vm statistics"); + } + + /* Stats in bytes */ +/* natural_t mem_used = (vm_stat.active_count + vm_stat.inactive_count + + vm_stat.wire_count) * pagesize; */ + return vm_stat.free_count * pagesize; +// natural_t mem_total = mem_used + mem_free; + +#elif defined(__unix__) || defined(__unix) || defined(unix) + /* Linux/UNIX variants. ------------------------------------------- */ + /* Prefer sysctl() over sysconf() except sysctl() HW_REALMEM and HW_PHYSMEM */ + +#if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64)) + int mib[2]; + mib[0] = CTL_HW; +#if defined(HW_MEMSIZE) + mib[1] = HW_MEMSIZE; /* OSX. --------------------- */ +#elif defined(HW_PHYSMEM64) + mib[1] = HW_PHYSMEM64; /* NetBSD, OpenBSD. --------- */ +#endif + int64_t size = 0; /* 64-bit */ + size_t len = sizeof( size ); + if ( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 ) + return (size_t)size; + return 0L; /* Failed? */ + +#elif defined(_SC_AIX_REALMEM) + /* AIX. ----------------------------------------------------- */ + return (size_t)sysconf( _SC_AIX_REALMEM ) * (size_t)1024L; + +#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE) + /* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */ + return (size_t)sysconf( _SC_PHYS_PAGES ) * + (size_t)sysconf( _SC_PAGESIZE ); + +#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGE_SIZE) + /* Legacy. -------------------------------------------------- */ + return (size_t)sysconf( _SC_PHYS_PAGES ) * + (size_t)sysconf( _SC_PAGE_SIZE ); + +#elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM)) + /* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */ + int mib[2]; + mib[0] = CTL_HW; +#if defined(HW_REALMEM) + mib[1] = HW_REALMEM; /* FreeBSD. ----------------- */ +#elif defined(HW_PYSMEM) + mib[1] = HW_PHYSMEM; /* Others. ------------------ */ +#endif + unsigned int size = 0; /* 32-bit */ + size_t len = sizeof( size ); + if ( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 ) + return (size_t)size; + return 0L; /* Failed? */ +#endif /* sysctl and sysconf variants */ + +#else + return 0L; /* Unknown OS. */ +#endif +} diff --git a/src/frontend/get_phys_mem_size.c b/src/frontend/get_phys_mem_size.c new file mode 100644 index 000000000..ef6c47b91 --- /dev/null +++ b/src/frontend/get_phys_mem_size.c @@ -0,0 +1,119 @@ +/* + * Author: David Robert Nadeau + * Site: http://NadeauSoftware.com/ + * License: Creative Commons Attribution 3.0 Unported License + * http://creativecommons.org/licenses/by/3.0/deed.en_US + * Modified: Holger Vogt, 2019 + */ + +#include "ngspice/ngspice.h" + +#if defined(_WIN32) +#undef BOOLEAN +#include + +#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) +#include +#include +#include +#if defined(BSD) +#include +#endif + +#else +#error "Unable to define getMemorySize( ) for an unknown OS." +#endif + + + +/** + * Returns the size of physical memory (RAM) in bytes. + */ +unsigned long long getMemorySize() +{ +#if defined(HAVE__PROC_MEMINFO) + /* Cygwin , Linux--------------------------------- */ + FILE *fp; + char buffer[2048]; + size_t bytes_read; + char *match; + unsigned long long mem_got; + + if ((fp = fopen("/proc/meminfo", "r")) == NULL) { + perror("fopen(\"/proc/meminfo\")"); + return 0; + } + + bytes_read = fread(buffer, 1, sizeof(buffer), fp); + fclose(fp); + if (bytes_read == 0 || bytes_read == sizeof(buffer)) + return 0; + buffer[bytes_read] = '\0'; + /* Search for string "MemTotal" */ + match = strstr(buffer, "MemTotal"); + if (match == NULL) /* not found */ + return 0; + sscanf(match, "MemTotal: %llu", &mem_got); + return mem_got * 1024L; + +#elif defined(_WIN32) + /* Windows. ------------------------------------------------- */ + /* Use new 64-bit MEMORYSTATUSEX, not old 32-bit MEMORYSTATUS */ + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx( &status ); + return (unsigned long long) status.ullTotalPhys; + +#elif defined(__unix__) || defined(__unix) || defined(unix) || \ + (defined(__APPLE__) && defined(__MACH__)) + /* UNIX variants. ------------------------------------------- */ + /* Prefer sysctl() over sysconf() except sysctl() HW_REALMEM and HW_PHYSMEM */ + +#if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64)) + int mib[2]; + mib[0] = CTL_HW; +#if defined(HW_MEMSIZE) + mib[1] = HW_MEMSIZE; /* OSX. --------------------- */ +#elif defined(HW_PHYSMEM64) + mib[1] = HW_PHYSMEM64; /* NetBSD, OpenBSD. --------- */ +#endif + int64_t size = 0; /* 64-bit */ + size_t len = sizeof( size ); + if ( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 ) + return (unsigned long long) size; + return 0L; /* Failed? */ + +#elif defined(_SC_AIX_REALMEM) + /* AIX. ----------------------------------------------------- */ + return (unsigned long long) sysconf(_SC_AIX_REALMEM) * (size_t) 1024L; + +#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE) + /* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */ + return (unsigned long long) sysconf(_SC_PHYS_PAGES) * + (unsigned long long) sysconf(_SC_PAGESIZE); + +#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGE_SIZE) + /* Legacy. -------------------------------------------------- */ + return (unsigned long long) sysconf(_SC_PHYS_PAGES) * + (unsigned long long) sysconf(_SC_PAGE_SIZE); + +#elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM)) + /* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */ + int mib[2]; + mib[0] = CTL_HW; +#if defined(HW_REALMEM) + mib[1] = HW_REALMEM; /* FreeBSD. ----------------- */ +#elif defined(HW_PYSMEM) + mib[1] = HW_PHYSMEM; /* Others. ------------------ */ +#endif + unsigned long long size = 0; /* 32-bit */ + size_t len = sizeof( size ); + if ( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 ) + return (unsigned long long) size; + return 0L; /* Failed? */ +#endif /* sysctl and sysconf variants */ + +#else + return 0L; /* Unknown OS. */ +#endif +} diff --git a/src/frontend/get_resident_set_size.c b/src/frontend/get_resident_set_size.c new file mode 100644 index 000000000..2ebfa7927 --- /dev/null +++ b/src/frontend/get_resident_set_size.c @@ -0,0 +1,125 @@ +/* + * Author: David Robert Nadeau + * Site: http://NadeauSoftware.com/ + * License: Creative Commons Attribution 3.0 Unported License + * http://creativecommons.org/licenses/by/3.0/deed.en_US + * Modified: Holger Vogt, 2019 + */ + +#include "ngspice/ngspice.h" + +#if defined(_WIN32) +#undef BOOLEAN +#include +#include + +#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) +#include +#include + +#if defined(__APPLE__) && defined(__MACH__) +#include + +#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__))) +#include +#include + +#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) +#include + +#endif + +#else +#error "Cannot define getPeakRSS( ) or getCurrentRSS( ) for an unknown OS." +#endif + + +/** + * Returns the peak (maximum so far) resident set size (physical + * memory use) measured in bytes, or zero if the value cannot be + * determined on this OS. + */ +unsigned long long getPeakRSS() +{ +#if defined(HAVE_GETRUSAGE) + /* BSD, Linux, and OSX -------------------------------------- + * not (yet) available with CYGWIN */ + struct rusage rusage; + getrusage(RUSAGE_SELF, &rusage); +#if defined(__APPLE__) && defined(__MACH__) + return (unsigned long long) rusage.ru_maxrss; +#else + return (unsigned long long) (rusage.ru_maxrss * 1024L); +#endif + +#elif defined(_WIN32) + /* Windows -------------------------------------------------- */ + PROCESS_MEMORY_COUNTERS info; + GetProcessMemoryInfo( GetCurrentProcess( ), &info, sizeof(info) ); + return (unsigned long long) info.PeakWorkingSetSize; + +#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__))) + /* AIX and Solaris ------------------------------------------ */ + struct psinfo psinfo; + int fd = -1; + if ( (fd = open( "/proc/self/psinfo", O_RDONLY )) == -1 ) + return 0L; /* Can't open? */ + if ( read( fd, &psinfo, sizeof(psinfo) ) != sizeof(psinfo) ) + { + close( fd ); + return 0L; /* Can't read? */ + } + close( fd ); + return (unsigned long long) (psinfo.pr_rssize * 1024L); + +#else + /* Unknown OS ----------------------------------------------- */ + return 0L; /* Unsupported. */ +#endif +} + + + + + +/** + * Returns the current resident set size (physical memory use) measured + * in bytes, or zero if the value cannot be determined on this OS. + */ +unsigned long long getCurrentRSS( ) +{ +#if defined(_WIN32) + /* Windows -------------------------------------------------- */ + PROCESS_MEMORY_COUNTERS info; + GetProcessMemoryInfo( GetCurrentProcess( ), &info, sizeof(info) ); + return (unsigned long long) info.WorkingSetSize; + +#elif defined(__APPLE__) && defined(__MACH__) + /* OSX ------------------------------------------------------ */ + struct mach_task_basic_info info; + mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; + if ( task_info( mach_task_self( ), MACH_TASK_BASIC_INFO, + (task_info_t)&info, &infoCount ) != KERN_SUCCESS ) + return 0L; /* Can't access? */ + return (unsigned long long) info.resident_size; + +//#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) +#elif defined(HAVE__PROC_MEMINFO) + /* Linux ---------------------------------------------------- */ + unsigned long long rss = 0L; + FILE* fp = NULL; + if ( (fp = fopen( "/proc/self/statm", "r" )) == NULL ) + return (unsigned long long) 0L; /* Can't open? */ + if ( fscanf( fp, "%*s%llu", &rss ) != 1 ) + { + fclose( fp ); + return 0L; /* Can't read? */ + } + fclose( fp ); + return rss * (unsigned long long) sysconf(_SC_PAGESIZE); + +#else + /* AIX, BSD, Solaris, and Unknown OS ------------------------ */ + return (unsigned long long) 0L; /* Unsupported. */ +#endif +} diff --git a/src/frontend/resource.c b/src/frontend/resource.c index 59ae13166..4b684cbab 100644 --- a/src/frontend/resource.c +++ b/src/frontend/resource.c @@ -6,10 +6,9 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group /* * Resource-related routines. * - * New operation systems information options added: - * Windows 2000 and newer: Use GlobalMemoryStatusEx and GetProcessMemoryInfo - * LINUX (and maybe some others): Use the /proc virtual file information system - * Others: Use original code with sbrk(0) and some "ugly hacks" + * Time information is acquired here. + * Memory information is obtained in functions get_... for + * a large variety of current operating systems. */ #include "ngspice/ngspice.h" @@ -58,10 +57,6 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group #include #endif /* HAVE_WIN32 */ -/* Uncheck the following definition if you want to get the old usage information - #undef HAVE__PROC_MEMINFO -*/ - static void printres(char *name); static void fprintmem(FILE *stream, unsigned long long memory); @@ -72,25 +67,13 @@ static int get_sysmem(struct sys_mem *memall); struct sys_mem mem_t, mem_t_act; struct proc_mem mem_ng, mem_ng_act; -#else -static RETSIGTYPE fault(void); -static void *baseaddr(void); #endif -char *startdata; -char *enddata; - void init_rlimits(void) { -# if defined(HAVE_WIN32) || defined(HAVE__PROC_MEMINFO) - get_procm(&mem_ng); - get_sysmem(&mem_t); -# else - startdata = (char *) baseaddr(); - enddata = sbrk(0); -# endif + ft_ckspace(); } @@ -138,12 +121,12 @@ com_rusage(wordlist *wl) /* Find out if the user is approaching his maximum data size. - If usage is withing 90% of total available then a warning message is sent + If usage is withing 95% of total available then a warning message is sent to the error stream (cp_err) */ void ft_ckspace(void) { - unsigned long long usage, limit; + unsigned long long freemem, totalmem, usage, avail; #ifdef SHARED_MODULE /* False warning on some OSs, especially on Linux when loaded during runtime. @@ -151,40 +134,20 @@ ft_ckspace(void) return; #endif -#if defined(HAVE_WIN32) || defined(HAVE__PROC_MEMINFO) - get_procm(&mem_ng_act); - usage = mem_ng_act.size; - limit = mem_t.free; -#else - static size_t old_usage = 0; - char *hi; + freemem = getAvailableMemorySize(); + totalmem = getMemorySize(); + usage = getCurrentRSS(); + avail = usage + freemem; -#ifdef HAVE_GETRLIMIT - struct rlimit rld; - getrlimit(RLIMIT_DATA, &rld); - if (rld.rlim_cur == RLIM_INFINITY) - return; - limit = rld.rlim_cur - (enddata - startdata); /* rlim_max not used */ -#else /* HAVE_GETRLIMIT */ - /* SYSVRLIMIT */ - limit = ulimit(3, 0L) - (enddata - startdata); -#endif /* HAVE_GETRLIMIT */ - - hi = sbrk(0); - usage = (size_t) (hi - enddata); - - if (usage <= old_usage) + if (totalmem == 0 || freemem == 0 || usage == 0) return; - old_usage = usage; -#endif /* not HAS_WINGUI */ - - if ((double)usage > (double)limit * 0.9) { + if ((double)freemem < (double)totalmem * 0.05) { fprintf(cp_err, "Warning - approaching max data size: "); fprintf(cp_err, "current size = "); fprintmem(cp_err, usage); fprintf(cp_err, ", limit = "); - fprintmem(cp_err, limit); + fprintmem(cp_err, avail); fprintf(cp_err, "\n"); } } @@ -288,65 +251,25 @@ printres(char *name) } if (!name || eq(name, "space")) { -# ifdef __APPLE__ - # ifdef HAVE_GETRUSAGE - int ret; - size_t usage = 0, limit = 0; - struct rusage ruse; - memset(&ruse, 0, sizeof(ruse)); - ret = getrusage(RUSAGE_SELF, &ruse); - if (ret == -1) - perror("getrusage(): "); - usage = ruse.ru_maxrss; - - size_t physmem; - size_t len = sizeof(physmem); - static int mib[2] = { CTL_HW, HW_MEMSIZE }; - - if (sysctl (mib, 2, &physmem, &len, NULL, 0) == 0) - limit = physmem; - # endif -# else -# ifdef HAVE_GETRLIMIT - size_t usage = 0, limit = 0; - struct rlimit rld; - char *hi; - - getrlimit(RLIMIT_DATA, &rld); - limit = rld.rlim_cur - (size_t)(enddata - startdata); - hi = (char*) sbrk(0); - usage = (size_t) (hi - enddata); -# else /* HAVE_GETRLIMIT */ -# ifdef HAVE_ULIMIT - size_t usage = 0, limit = 0; - char *hi; - - limit = ulimit(3, 0L) - (size_t)(enddata - startdata); - hi = sbrk(0); - usage = (size_t) (hi - enddata); -# endif /* HAVE_ULIMIT */ -# endif /* HAVE_GETRLIMIT */ -# endif /* !__APPLE__ */ - -#if defined(HAVE_WIN32) || defined(HAVE__PROC_MEMINFO) - - get_procm(&mem_ng_act); - get_sysmem(&mem_t_act); - - /* get_sysmem returns bytes */ + unsigned long long mem = getMemorySize(); fprintf(cp_out, "Total DRAM available = "); - fprintmem(cp_out, mem_t_act.size); + fprintmem(cp_out, mem); fprintf(cp_out, ".\n"); - + mem = getAvailableMemorySize(); fprintf(cp_out, "DRAM currently available = "); - fprintmem(cp_out, mem_t_act.free); + fprintmem(cp_out, mem); + fprintf(cp_out, ".\n"); + mem = getPeakRSS(); + fprintf(cp_out, "Maximum ngspice program size = "); + fprintmem(cp_out, mem); + fprintf(cp_out, ".\n"); + mem = getCurrentRSS(); + fprintf(cp_out, "Current ngspice program size = "); + fprintmem(cp_out, mem); fprintf(cp_out, ".\n"); - /* get_procm returns Kilobytes */ - fprintf(cp_out, "Total ngspice program size = "); - fprintmem(cp_out, mem_ng_act.size); - fprintf(cp_out, ".\n"); #if defined(HAVE__PROC_MEMINFO) + get_procm(&mem_ng_act); fprintf(cp_out, "Resident set size = "); fprintmem(cp_out, mem_ng_act.resident); fprintf(cp_out, ".\n"); @@ -371,15 +294,6 @@ printres(char *name) fprintmem(cp_out, all_memory.dt); fprintf(cp_out, ".\n"); */ #endif /* HAVE__PROC_MEMINFO */ -#else /* HAS_WINGUI or HAVE__PROC_MEMINFO */ - fprintf(cp_out, "Current dynamic memory usage = "); - fprintmem(cp_out, usage); - fprintf(cp_out, ",\n"); - - fprintf(cp_out, "Dynamic memory limit = "); - fprintmem(cp_out, limit); - fprintf(cp_out, ".\n"); -#endif yy = TRUE; } diff --git a/src/frontend/resource.h b/src/frontend/resource.h index 6d5f8c38d..21cad8912 100644 --- a/src/frontend/resource.h +++ b/src/frontend/resource.h @@ -6,6 +6,11 @@ #ifndef ngspice_RESOURCE_H #define ngspice_RESOURCE_H +extern size_t getMemorySize(void); +extern size_t getPeakRSS(void); +extern size_t getCurrentRSS(void); +extern size_t getAvailableMemorySize(void); + void init_rlimits(void); void init_time(void); void com_rusage(wordlist *wl); diff --git a/visualc/vngspice.vcxproj b/visualc/vngspice.vcxproj index bd62737a5..c2769c32c 100644 --- a/visualc/vngspice.vcxproj +++ b/visualc/vngspice.vcxproj @@ -1416,6 +1416,9 @@ + + +