From d75c338dd6e9afe09356870cde456fd0c0e87e76 Mon Sep 17 00:00:00 2001 From: Brian White Date: Wed, 22 Dec 2010 13:55:47 -0500 Subject: [PATCH] Add more functionality to the os module --- doc/api/os.markdown | 97 ++++++++++++++- lib/os.js | 9 +- src/node.cc | 12 +- src/node_os.cc | 101 +++++++++++++++- src/platform.h | 9 +- src/platform_cygwin.cc | 128 +++++++++++++++++++- src/platform_darwin.cc | 131 +++++++++++++++++++- src/platform_darwin_proctitle.cc | 2 +- src/platform_freebsd.cc | 139 ++++++++++++++++++++- src/platform_linux.cc | 128 ++++++++++++++++++-- src/platform_openbsd.cc | 201 +++++++++++++++++++++++++++++++ test/simple/test-os-hostname.js | 5 - test/simple/test-os.js | 12 ++ wscript | 2 +- 14 files changed, 932 insertions(+), 44 deletions(-) create mode 100644 src/platform_openbsd.cc delete mode 100644 test/simple/test-os-hostname.js create mode 100644 test/simple/test-os.js diff --git a/doc/api/os.markdown b/doc/api/os.markdown index 4429bddbfb..3c4e1b44fd 100644 --- a/doc/api/os.markdown +++ b/doc/api/os.markdown @@ -2,6 +2,101 @@ Use `require('os')` to access this module. -### os.getHostname() +### os.hostname() Returns the hostname of the operating system. + +### os.type() + +Returns the operating system name. + +### os.release() + +Returns the operating system release. + +### os.uptime() + +Returns the system uptime in seconds. + +### os.loadavg() + +Returns an array containing the 1, 5, and 15 minute load averages. + +### os.totalmem() + +Returns the total amount of system memory in bytes. + +### os.freemem() + +Returns the amount of free system memory in bytes. + +### os.cpus() + +Returns an array of objects containing information about each CPU/core installed: model, speed (in MHz), and times (an object containing the number of CPU ticks spent in: user, nice, sys, idle, and irq). + +Example inspection of os.cpus: + + [ { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 252020, + nice: 0, + sys: 30340, + idle: 1070356870, + irq: 0 } }, + { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 306960, + nice: 0, + sys: 26980, + idle: 1071569080, + irq: 0 } }, + { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 248450, + nice: 0, + sys: 21750, + idle: 1070919370, + irq: 0 } }, + { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 256880, + nice: 0, + sys: 19430, + idle: 1070905480, + irq: 20 } }, + { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 511580, + nice: 20, + sys: 40900, + idle: 1070842510, + irq: 0 } }, + { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 291660, + nice: 0, + sys: 34360, + idle: 1070888000, + irq: 10 } }, + { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 308260, + nice: 0, + sys: 55410, + idle: 1071129970, + irq: 880 } }, + { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + speed: 2926, + times: + { user: 266450, + nice: 1480, + sys: 34920, + idle: 1072572010, + irq: 30 } } ] \ No newline at end of file diff --git a/lib/os.js b/lib/os.js index 14a582c2e2..5b1bb22d18 100644 --- a/lib/os.js +++ b/lib/os.js @@ -1,3 +1,10 @@ var binding = process.binding('os'); -exports.getHostname = binding.getHostname; +exports.hostname = binding.getHostname; +exports.loadavg = binding.getLoadAvg; +exports.uptime = binding.getUptime; +exports.freemem = binding.getFreeMem; +exports.totalmem = binding.getTotalMem; +exports.cpus = binding.getCPUs; +exports.type = binding.getOSType; +exports.release = binding.getOSRelease; \ No newline at end of file diff --git a/src/node.cc b/src/node.cc index 8039d79914..9d8405571f 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1186,7 +1186,7 @@ static void CheckStatus(EV_P_ ev_timer *watcher, int revents) { // check memory size_t rss, vsize; - if (!ev_is_active(&gc_idle) && OS::GetMemory(&rss, &vsize) == 0) { + if (!ev_is_active(&gc_idle) && Platform::GetMemory(&rss, &vsize) == 0) { if (rss > 1024*1024*128) { // larger than 128 megs, just start the idle watcher ev_idle_start(EV_A_ &gc_idle); @@ -1211,7 +1211,7 @@ v8::Handle MemoryUsage(const v8::Arguments& args) { size_t rss, vsize; - int r = OS::GetMemory(&rss, &vsize); + int r = Platform::GetMemory(&rss, &vsize); if (r != 0) { return ThrowException(Exception::Error(String::New(strerror(errno)))); @@ -1519,7 +1519,7 @@ static Handle ProcessTitleGetter(Local property, const AccessorInfo& info) { HandleScope scope; int len; - const char *s = OS::GetProcessTitle(&len); + const char *s = Platform::GetProcessTitle(&len); return scope.Close(s ? String::New(s, len) : String::Empty()); } @@ -1529,7 +1529,7 @@ static void ProcessTitleSetter(Local property, const AccessorInfo& info) { HandleScope scope; String::Utf8Value title(value->ToString()); - OS::SetProcessTitle(*title); + Platform::SetProcessTitle(*title); } @@ -1685,7 +1685,7 @@ static void Load(int argc, char *argv[]) { size_t size = 2*PATH_MAX; char execPath[size]; - if (OS::GetExecutablePath(execPath, &size) != 0) { + if (Platform::GetExecutablePath(execPath, &size) != 0) { // as a last ditch effort, fallback on argv[0] ? process->Set(String::NewSymbol("execPath"), String::New(argv[0])); } else { @@ -1874,7 +1874,7 @@ static int RegisterSignalHandler(int signal, void (*handler)(int)) { int Start(int argc, char *argv[]) { // Hack aroung with the argv pointer. Used for process.title = "blah". - argv = node::OS::SetupArgs(argc, argv); + argv = node::Platform::SetupArgs(argc, argv); // Parse a few arguments which are specific to Node. node::ParseArgs(&argc, argv); diff --git a/src/node_os.cc b/src/node_os.cc index 68b08959ac..ad7b71a80e 100644 --- a/src/node_os.cc +++ b/src/node_os.cc @@ -3,8 +3,12 @@ #include #include +#include "platform.h" + #include -#include // gethostname +#include // gethostname, sysconf +#include // sysctl +#include // sysctl namespace node { @@ -13,19 +17,108 @@ using namespace v8; static Handle GetHostname(const Arguments& args) { HandleScope scope; char s[255]; - int r = gethostname(s, 255); - if (r < 0) { - return ThrowException(ErrnoException(errno, "gethostname")); + if (gethostname(s, 255) < 0) { + return Undefined(); } return scope.Close(String::New(s)); } +static Handle GetOSType(const Arguments& args) { + HandleScope scope; + char type[256]; + static int which[] = {CTL_KERN, KERN_OSTYPE}; + size_t size = sizeof(type); + + if (sysctl(which, 2, &type, &size, NULL, 0) < 0) { + return Undefined(); + } + + return scope.Close(String::New(type)); +} + +static Handle GetOSRelease(const Arguments& args) { + HandleScope scope; + char release[256]; + static int which[] = {CTL_KERN, KERN_OSRELEASE}; + size_t size = sizeof(release); + + if (sysctl(which, 2, &release, &size, NULL, 0) < 0) { + return Undefined(); + } + + return scope.Close(String::New(release)); +} + +static Handle GetCPUInfo(const Arguments& args) { + HandleScope scope; + Local cpus; + int r = Platform::GetCPUInfo(&cpus); + + if (r < 0) { + return Undefined(); + } + + return scope.Close(cpus); +} + +static Handle GetFreeMemory(const Arguments& args) { + HandleScope scope; + double amount = Platform::GetFreeMemory(); + + if (amount < 0) { + return Undefined(); + } + + return scope.Close(Number::New(amount)); +} + +static Handle GetTotalMemory(const Arguments& args) { + HandleScope scope; + double amount = Platform::GetTotalMemory(); + + if (amount < 0) { + return Undefined(); + } + + return scope.Close(Number::New(amount)); +} + +static Handle GetUptime(const Arguments& args) { + HandleScope scope; + double uptime = Platform::GetUptime(); + + if (uptime < 0) { + return Undefined(); + } + + return scope.Close(Number::New(uptime)); +} + +static Handle GetLoadAvg(const Arguments& args) { + HandleScope scope; + Local loads = Array::New(3); + int r = Platform::GetLoadAvg(&loads); + + if (r < 0) { + return Undefined(); + } + + return scope.Close(loads); +} + void OS::Initialize(v8::Handle target) { HandleScope scope; NODE_SET_METHOD(target, "getHostname", GetHostname); + NODE_SET_METHOD(target, "getLoadAvg", GetLoadAvg); + NODE_SET_METHOD(target, "getUptime", GetUptime); + NODE_SET_METHOD(target, "getTotalMem", GetTotalMemory); + NODE_SET_METHOD(target, "getFreeMem", GetFreeMemory); + NODE_SET_METHOD(target, "getCPUs", GetCPUInfo); + NODE_SET_METHOD(target, "getOSType", GetOSType); + NODE_SET_METHOD(target, "getOSRelease", GetOSRelease); } diff --git a/src/platform.h b/src/platform.h index f025230725..bf7cd52fa3 100644 --- a/src/platform.h +++ b/src/platform.h @@ -1,9 +1,11 @@ #ifndef NODE_PLATFORM_H_ #define NODE_PLATFORM_H_ +#include + namespace node { -class OS { +class Platform { public: static char** SetupArgs(int argc, char *argv[]); static void SetProcessTitle(char *title); @@ -11,6 +13,11 @@ class OS { static int GetMemory(size_t *rss, size_t *vsize); static int GetExecutablePath(char* buffer, size_t* size); + static int GetCPUInfo(v8::Local *cpus); + static double GetFreeMemory(); + static double GetTotalMemory(); + static double GetUptime(); + static int GetLoadAvg(v8::Local *loads); }; diff --git a/src/platform_cygwin.cc b/src/platform_cygwin.cc index 4fcade8bce..de0a3ba5ed 100644 --- a/src/platform_cygwin.cc +++ b/src/platform_cygwin.cc @@ -1,13 +1,22 @@ #include "node.h" #include "platform.h" +#include + #include // for MAXPATHLEN -#include // getpagesize +#include +#include +#include // getpagesize, sysconf +#include // sscanf, snprintf +#include + #include namespace node { +using namespace v8; + static char buf[MAXPATHLEN + 1]; static char *process_title = NULL; @@ -30,12 +39,12 @@ static void _winapi_perror(const char* prefix = NULL) { } -char** OS::SetupArgs(int argc, char *argv[]) { +char** Platform::SetupArgs(int argc, char *argv[]) { return argv; } -void OS::SetProcessTitle(char *title) { +void Platform::SetProcessTitle(char *title) { // We need to convert _title_ to UTF-16 first, because that's what windows uses internally. // It would be more efficient to use the UTF-16 value that we can obtain from v8, // but it's not accessible from here. @@ -139,7 +148,7 @@ static inline char* _getProcessTitle() { } -const char* OS::GetProcessTitle(int *len) { +const char* Platform::GetProcessTitle(int *len) { // If the process_title was never read before nor explicitly set, // we must query it with getConsoleTitleW if (!process_title) { @@ -156,7 +165,7 @@ const char* OS::GetProcessTitle(int *len) { } -int OS::GetMemory(size_t *rss, size_t *vsize) { +int Platform::GetMemory(size_t *rss, size_t *vsize) { FILE *f = fopen("/proc/self/stat", "r"); if (!f) return -1; @@ -236,11 +245,118 @@ error: } -int OS::GetExecutablePath(char* buffer, size_t* size) { +int Platform::GetExecutablePath(char* buffer, size_t* size) { *size = readlink("/proc/self/exe", buffer, *size - 1); if (*size <= 0) return -1; buffer[*size] = '\0'; return 0; } +int Platform::GetCPUInfo(Local *cpus) { + Local cpuinfo; + Local cputimes; + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), + multiplier = ((uint64_t)1000L / ticks), cpuspeed; + int numcpus = 0, i = 0; + unsigned long long ticks_user, ticks_sys, ticks_idle, ticks_nice, ticks_intr; + char line[512], speedPath[256], model[512]; + FILE *fpStat = fopen("/proc/stat", "r"); + FILE *fpModel = fopen("/proc/cpuinfo", "r"); + FILE *fpSpeed; + + if (fpModel) { + while (fgets(line, 511, fpModel) != NULL) { + if (strncmp(line, "model name", 10) == 0) { + numcpus++; + if (numcpus == 1) { + char *p = strchr(line, ':') + 2; + strcpy(model, p); + model[strlen(model)-1] = 0; + } + } else if (strncmp(line, "cpu MHz", 7) == 0) { + if (numcpus == 1) { + sscanf(line, "%*s %*s : %u", &cpuspeed); + } + } + } + fclose(fpModel); + } + + *cpus = Array::New(numcpus); + + if (fpStat) { + while (fgets(line, 511, fpStat) != NULL) { + if (strncmp(line, "cpu ", 4) == 0) + continue; + else if (strncmp(line, "intr ", 5) == 0) + break; + sscanf(line, "%*s %llu %llu %llu %llu %*llu %llu", + &ticks_user, &ticks_nice, &ticks_sys, &ticks_idle, &ticks_intr); + snprintf(speedPath, sizeof(speedPath), + "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i); + fpSpeed = fopen(speedPath, "r"); + if (fpSpeed) { + if (fgets(line, 511, fpSpeed) != NULL) { + sscanf(line, "%u", &cpuspeed); + cpuspeed /= 1000; + } + fclose(fpSpeed); + } + cpuinfo = Object::New(); + cputimes = Object::New(); + cputimes->Set(String::New("user"), Number::New(ticks_user * multiplier)); + cputimes->Set(String::New("nice"), Number::New(ticks_nice * multiplier)); + cputimes->Set(String::New("sys"), Number::New(ticks_sys * multiplier)); + cputimes->Set(String::New("idle"), Number::New(ticks_idle * multiplier)); + cputimes->Set(String::New("irq"), Number::New(ticks_intr * multiplier)); + + cpuinfo->Set(String::New("model"), String::New(model)); + cpuinfo->Set(String::New("speed"), Number::New(cpuspeed)); + + cpuinfo->Set(String::New("times"), cputimes); + (*cpus)->Set(i++, cpuinfo); + } + fclose(fpStat); + } + + return 0; +} + +double Platform::GetFreeMemory() { + double pagesize = static_cast(sysconf(_SC_PAGESIZE)); + double pages = static_cast(sysconf(_SC_AVPHYS_PAGES)); + + return static_cast(pages * pagesize); +} + +double Platform::GetTotalMemory() { + double pagesize = static_cast(sysconf(_SC_PAGESIZE)); + double pages = static_cast(sysconf(_SC_PHYS_PAGES)); + + return pages * pagesize; +} + +double Platform::GetUptime() { + struct sysinfo info; + + if (sysinfo(&info) < 0) { + return -1; + } + + return static_cast(info.uptime); +} + +int Platform::GetLoadAvg(Local *loads) { + struct sysinfo info; + + if (sysinfo(&info) < 0) { + return -1; + } + (*loads)->Set(0, Number::New(static_cast(info.loads[0]) / 65536.0)); + (*loads)->Set(1, Number::New(static_cast(info.loads[1]) / 65536.0)); + (*loads)->Set(2, Number::New(static_cast(info.loads[2]) / 65536.0)); + + return 0; +} + } // namespace node diff --git a/src/platform_darwin.cc b/src/platform_darwin.cc index f02dc494a3..828d511056 100644 --- a/src/platform_darwin.cc +++ b/src/platform_darwin.cc @@ -1,27 +1,38 @@ #include "node.h" #include "platform.h" +#include + #include -#include +#include +#include #include /* _NSGetExecutablePath */ #include /* PATH_MAX */ +#include // sysconf +#include +#include +#include + namespace node { + +using namespace v8; + static char *process_title; -char** OS::SetupArgs(int argc, char *argv[]) { +char** Platform::SetupArgs(int argc, char *argv[]) { process_title = argc ? strdup(argv[0]) : NULL; return argv; } -// OS::SetProcessTitle implemented in platform_darwin_proctitle.cc +// Platform::SetProcessTitle implemented in platform_darwin_proctitle.cc } // namespace node #include "platform_darwin_proctitle.cc" namespace node { -const char* OS::GetProcessTitle(int *len) { +const char* Platform::GetProcessTitle(int *len) { if (process_title) { *len = strlen(process_title); return process_title; @@ -32,7 +43,7 @@ const char* OS::GetProcessTitle(int *len) { // Researched by Tim Becker and Michael Knight // http://blog.kuriositaet.de/?p=257 -int OS::GetMemory(size_t *rss, size_t *vsize) { +int Platform::GetMemory(size_t *rss, size_t *vsize) { struct task_basic_info t_info; mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; @@ -50,7 +61,7 @@ int OS::GetMemory(size_t *rss, size_t *vsize) { } -int OS::GetExecutablePath(char* buffer, size_t* size) { +int Platform::GetExecutablePath(char* buffer, size_t* size) { uint32_t usize = *size; int result = _NSGetExecutablePath(buffer, &usize); if (result) return result; @@ -68,4 +79,112 @@ int OS::GetExecutablePath(char* buffer, size_t* size) { return 0; } +int Platform::GetCPUInfo(Local *cpus) { + Local cpuinfo; + Local cputimes; + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), + multiplier = ((uint64_t)1000L / ticks); + char model[512]; + uint64_t cpuspeed; + size_t size; + + size = sizeof(model); + if (sysctlbyname("hw.model", &model, &size, NULL, 0) < 0) { + return -1; + } + size = sizeof(cpuspeed); + if (sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0) < 0) { + return -1; + } + + natural_t numcpus; + mach_msg_type_number_t count; + processor_cpu_load_info_data_t *info; + if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus, + reinterpret_cast(&info), + &count) != KERN_SUCCESS) { + return -1; + } + *cpus = Array::New(numcpus); + for (int i = 0; i < numcpus; i++) { + cpuinfo = Object::New(); + cputimes = Object::New(); + cputimes->Set(String::New("user"), + Number::New((uint64_t)(info[i].cpu_ticks[0]) * multiplier)); + cputimes->Set(String::New("nice"), + Number::New((uint64_t)(info[i].cpu_ticks[3]) * multiplier)); + cputimes->Set(String::New("sys"), + Number::New((uint64_t)(info[i].cpu_ticks[1]) * multiplier)); + cputimes->Set(String::New("idle"), + Number::New((uint64_t)(info[i].cpu_ticks[2]) * multiplier)); + cputimes->Set(String::New("irq"), Number::New(0)); + + cpuinfo->Set(String::New("model"), String::New(model)); + cpuinfo->Set(String::New("speed"), Number::New(cpuspeed/1000000)); + + cpuinfo->Set(String::New("times"), cputimes); + (*cpus)->Set(i, cpuinfo); + } + vm_deallocate(mach_task_self(), (vm_address_t)info, count); + + return 0; +} + +double Platform::GetFreeMemory() { + double pagesize = static_cast(sysconf(_SC_PAGESIZE)); + vm_statistics_data_t info; + mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t); + + if (host_statistics(mach_host_self(), HOST_VM_INFO, + (host_info_t)&info, &count) != KERN_SUCCESS) { + return -1; + } + + return (static_cast(info.free_count)) * pagesize; +} + +double Platform::GetTotalMemory() { + uint64_t info; + static int which[] = {CTL_HW, HW_MEMSIZE}; + size_t size = sizeof(info); + + if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + return -1; + } + + return static_cast(info); +} + +double Platform::GetUptime() { + time_t now; + struct timeval info; + size_t size = sizeof(info); + static int which[] = {CTL_KERN, KERN_BOOTTIME}; + + if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + return -1; + } + now = time(NULL); + + return static_cast(now - info.tv_sec); +} + +int Platform::GetLoadAvg(Local *loads) { + struct loadavg info; + size_t size = sizeof(info); + static int which[] = {CTL_VM, VM_LOADAVG}; + + if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + return -1; + } + (*loads)->Set(0, Number::New(static_cast(info.ldavg[0]) + / static_cast(info.fscale))); + (*loads)->Set(1, Number::New(static_cast(info.ldavg[1]) + / static_cast(info.fscale))); + (*loads)->Set(2, Number::New(static_cast(info.ldavg[2]) + / static_cast(info.fscale))); + + return 0; +} + } // namespace node diff --git a/src/platform_darwin_proctitle.cc b/src/platform_darwin_proctitle.cc index 6c58ae1c94..e6a1ddd371 100644 --- a/src/platform_darwin_proctitle.cc +++ b/src/platform_darwin_proctitle.cc @@ -38,7 +38,7 @@ namespace node { -void OS::SetProcessTitle(char *title) { +void Platform::SetProcessTitle(char *title) { static int symbol_lookup_status = 0; // 1=ok, 2=unavailable if (symbol_lookup_status == 2) { // feature is unavailable diff --git a/src/platform_freebsd.cc b/src/platform_freebsd.cc index 0c8b2599f9..6eb5ca2903 100644 --- a/src/platform_freebsd.cc +++ b/src/platform_freebsd.cc @@ -1,33 +1,41 @@ #include "node.h" #include "platform.h" +#include + #include #include #include #include #include +#include +#include #include #include #include #include +#include namespace node { + +using namespace v8; + static char *process_title; -char** OS::SetupArgs(int argc, char *argv[]) { +char** Platform::SetupArgs(int argc, char *argv[]) { process_title = argc ? strdup(argv[0]) : NULL; return argv; } -void OS::SetProcessTitle(char *title) { +void Platform::SetProcessTitle(char *title) { if (process_title) free(process_title); process_title = strdup(title); setproctitle(title); } -const char* OS::GetProcessTitle(int *len) { +const char* Platform::GetProcessTitle(int *len) { if (process_title) { *len = strlen(process_title); return process_title; @@ -36,7 +44,7 @@ const char* OS::GetProcessTitle(int *len) { return NULL; } -int OS::GetMemory(size_t *rss, size_t *vsize) { +int Platform::GetMemory(size_t *rss, size_t *vsize) { kvm_t *kd = NULL; struct kinfo_proc *kinfo = NULL; pid_t pid; @@ -64,7 +72,7 @@ error: } -int OS::GetExecutablePath(char* buffer, size_t* size) { +int Platform::GetExecutablePath(char* buffer, size_t* size) { int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; @@ -78,4 +86,125 @@ int OS::GetExecutablePath(char* buffer, size_t* size) { return 0; } +int Platform::GetCPUInfo(Local *cpus) { + Local cpuinfo; + Local cputimes; + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), + multiplier = ((uint64_t)1000L / ticks), cpuspeed, maxcpus, + cur = 0; + char model[512]; + int numcpus; + size_t size; + + size = sizeof(model); + if (sysctlbyname("hw.model", &model, &size, NULL, 0) < 0) { + return -1; + } + size = sizeof(numcpus); + if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0) < 0) { + return -1; + } + + *cpus = Array::New(numcpus); + + size = sizeof(cpuspeed); + if (sysctlbyname("hw.clockrate", &cpuspeed, &size, NULL, 0) < 0) { + return -1; + } + // kern.cp_times on FreeBSD i386 gives an array up to maxcpus instead of ncpu + size = sizeof(maxcpus); + if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) { + return -1; + } + size = maxcpus * CPUSTATES * sizeof(long); + long cp_times[size]; + if (sysctlbyname("kern.cp_times", &cp_times, &size, NULL, 0) < 0) { + return -1; + } + for (int i = 0; i < numcpus; i++) { + cpuinfo = Object::New(); + cputimes = Object::New(); + cputimes->Set(String::New("user"), + Number::New((uint64_t)(cp_times[CP_USER+cur]) * multiplier)); + cputimes->Set(String::New("nice"), + Number::New((uint64_t)(cp_times[CP_NICE+cur]) * multiplier)); + cputimes->Set(String::New("sys"), + Number::New((uint64_t)(cp_times[CP_SYS+cur]) * multiplier)); + cputimes->Set(String::New("idle"), + Number::New((uint64_t)(cp_times[CP_IDLE+cur]) * multiplier)); + cputimes->Set(String::New("irq"), + Number::New((uint64_t)(cp_times[CP_INTR+cur]) * multiplier)); + + cpuinfo->Set(String::New("model"), String::New(model)); + cpuinfo->Set(String::New("speed"), Number::New(cpuspeed)); + + cpuinfo->Set(String::New("times"), cputimes); + (*cpus)->Set(i, cpuinfo); + cur+=CPUSTATES; + } + + return 0; +} + +double Platform::GetFreeMemory() { + double pagesize = static_cast(sysconf(_SC_PAGESIZE)); + unsigned long info; + size_t size = sizeof(info); + + if (sysctlbyname("vm.stats.vm.v_free_count", &info, &size, NULL, 0) < 0) { + return -1; + } + + return (static_cast(info)) * pagesize; +} + +double Platform::GetTotalMemory() { +#if defined(HW_PHYSMEM64) + uint64_t info; + static int which[] = {CTL_HW, HW_PHYSMEM64}; +#else + unsigned int info; + static int which[] = {CTL_HW, HW_PHYSMEM}; +#endif + size_t size = sizeof(info); + + if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + return -1; + } + + return static_cast(info); +} + +double Platform::GetUptime() { + time_t now; + struct timeval info; + size_t size = sizeof(info); + static int which[] = {CTL_KERN, KERN_BOOTTIME}; + + if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + return -1; + } + now = time(NULL); + + return static_cast(now - info.tv_sec); +} + +int Platform::GetLoadAvg(Local *loads) { + struct loadavg info; + size_t size = sizeof(info); + static int which[] = {CTL_VM, VM_LOADAVG}; + + if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + return -1; + } + (*loads)->Set(0, Number::New(static_cast(info.ldavg[0]) + / static_cast(info.fscale))); + (*loads)->Set(1, Number::New(static_cast(info.ldavg[1]) + / static_cast(info.fscale))); + (*loads)->Set(2, Number::New(static_cast(info.ldavg[2]) + / static_cast(info.fscale))); + + return 0; +} + } // namespace node diff --git a/src/platform_linux.cc b/src/platform_linux.cc index 9c4ccc4c99..00732a0c03 100644 --- a/src/platform_linux.cc +++ b/src/platform_linux.cc @@ -1,8 +1,13 @@ #include "node.h" #include "platform.h" +#include + #include // for MAXPATHLEN -#include // getpagesize +#include +#include +#include // getpagesize, sysconf +#include // sscanf, snprintf /* SetProcessTitle */ #include @@ -10,27 +15,28 @@ #include // free #include // strdup - namespace node { +using namespace v8; + static char buf[MAXPATHLEN + 1]; static char *process_title; -char** OS::SetupArgs(int argc, char *argv[]) { +char** Platform::SetupArgs(int argc, char *argv[]) { process_title = strdup(argv[0]); return argv; } -void OS::SetProcessTitle(char *title) { +void Platform::SetProcessTitle(char *title) { if (process_title) free(process_title); process_title = strdup(title); prctl(PR_SET_NAME, process_title); } -const char* OS::GetProcessTitle(int *len) { +const char* Platform::GetProcessTitle(int *len) { if (process_title) { *len = strlen(process_title); return process_title; @@ -40,7 +46,7 @@ const char* OS::GetProcessTitle(int *len) { } -int OS::GetMemory(size_t *rss, size_t *vsize) { +int Platform::GetMemory(size_t *rss, size_t *vsize) { FILE *f = fopen("/proc/self/stat", "r"); if (!f) return -1; @@ -135,11 +141,119 @@ error: } -int OS::GetExecutablePath(char* buffer, size_t* size) { +int Platform::GetExecutablePath(char* buffer, size_t* size) { *size = readlink("/proc/self/exe", buffer, *size - 1); if (*size <= 0) return -1; buffer[*size] = '\0'; return 0; } +int Platform::GetCPUInfo(Local *cpus) { + HandleScope scope; + Local cpuinfo; + Local cputimes; + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), + multiplier = ((uint64_t)1000L / ticks), cpuspeed; + int numcpus = 0, i = 0; + unsigned long long ticks_user, ticks_sys, ticks_idle, ticks_nice, ticks_intr; + char line[512], speedPath[256], model[512]; + FILE *fpStat = fopen("/proc/stat", "r"); + FILE *fpModel = fopen("/proc/cpuinfo", "r"); + FILE *fpSpeed; + + if (fpModel) { + while (fgets(line, 511, fpModel) != NULL) { + if (strncmp(line, "model name", 10) == 0) { + numcpus++; + if (numcpus == 1) { + char *p = strchr(line, ':') + 2; + strcpy(model, p); + model[strlen(model)-1] = 0; + } + } else if (strncmp(line, "cpu MHz", 7) == 0) { + if (numcpus == 1) { + sscanf(line, "%*s %*s : %u", &cpuspeed); + } + } + } + fclose(fpModel); + } + + *cpus = Array::New(numcpus); + + if (fpStat) { + while (fgets(line, 511, fpStat) != NULL) { + if (strncmp(line, "cpu ", 4) == 0) + continue; + else if (strncmp(line, "intr ", 5) == 0) + break; + sscanf(line, "%*s %llu %llu %llu %llu %*llu %llu", + &ticks_user, &ticks_nice, &ticks_sys, &ticks_idle, &ticks_intr); + snprintf(speedPath, sizeof(speedPath), + "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i); + fpSpeed = fopen(speedPath, "r"); + if (fpSpeed) { + if (fgets(line, 511, fpSpeed) != NULL) { + sscanf(line, "%u", &cpuspeed); + cpuspeed /= 1000; + } + fclose(fpSpeed); + } + cpuinfo = Object::New(); + cputimes = Object::New(); + cputimes->Set(String::New("user"), Number::New(ticks_user * multiplier)); + cputimes->Set(String::New("nice"), Number::New(ticks_nice * multiplier)); + cputimes->Set(String::New("sys"), Number::New(ticks_sys * multiplier)); + cputimes->Set(String::New("idle"), Number::New(ticks_idle * multiplier)); + cputimes->Set(String::New("irq"), Number::New(ticks_intr * multiplier)); + + cpuinfo->Set(String::New("model"), String::New(model)); + cpuinfo->Set(String::New("speed"), Number::New(cpuspeed)); + + cpuinfo->Set(String::New("times"), cputimes); + (*cpus)->Set(i++, cpuinfo); + } + fclose(fpStat); + } + + return 0; +} + +double Platform::GetFreeMemory() { + double pagesize = static_cast(sysconf(_SC_PAGESIZE)); + double pages = static_cast(sysconf(_SC_AVPHYS_PAGES)); + + return static_cast(pages * pagesize); +} + +double Platform::GetTotalMemory() { + double pagesize = static_cast(sysconf(_SC_PAGESIZE)); + double pages = static_cast(sysconf(_SC_PHYS_PAGES)); + + return pages * pagesize; +} + +double Platform::GetUptime() { + struct sysinfo info; + + if (sysinfo(&info) < 0) { + return -1; + } + + return static_cast(info.uptime); +} + +int Platform::GetLoadAvg(Local *loads) { + struct sysinfo info; + + if (sysinfo(&info) < 0) { + return -1; + } + (*loads)->Set(0, Number::New(static_cast(info.loads[0]) / 65536.0)); + (*loads)->Set(1, Number::New(static_cast(info.loads[1]) / 65536.0)); + (*loads)->Set(2, Number::New(static_cast(info.loads[2]) / 65536.0)); + + return 0; +} + } // namespace node diff --git a/src/platform_openbsd.cc b/src/platform_openbsd.cc new file mode 100644 index 0000000000..b8a3209bcd --- /dev/null +++ b/src/platform_openbsd.cc @@ -0,0 +1,201 @@ +#include "node.h" +#include "platform.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +namespace node { + +using namespace v8; + +static char *process_title; + +char** Platform::SetupArgs(int argc, char *argv[]) { + process_title = argc ? strdup(argv[0]) : NULL; + return argv; +} + + +void Platform::SetProcessTitle(char *title) { + if (process_title) free(process_title); + process_title = strdup(title); + setproctitle(title); +} + +const char* Platform::GetProcessTitle(int *len) { + if (process_title) { + *len = strlen(process_title); + return process_title; + } + *len = 0; + return NULL; +} + +int Platform::GetMemory(size_t *rss, size_t *vsize) { + kvm_t *kd = NULL; + struct kinfo_proc2 *kinfo = NULL; + pid_t pid; + int nprocs, max_size = sizeof(struct kinfo_proc2); + size_t page_size = getpagesize(); + + pid = getpid(); + + kd = kvm_open(NULL, _PATH_MEM, NULL, O_RDONLY, "kvm_open"); + if (kd == NULL) goto error; + + kinfo = kvm_getproc2(kd, KERN_PROC_PID, pid, max_size, &nprocs); + if (kinfo == NULL) goto error; + + *rss = kinfo->p_vm_rssize * page_size; + *vsize = kinfo->p_uru_ixrss; + + kvm_close(kd); + + return 0; + +error: + if (kd) kvm_close(kd); + return -1; +} + + +int Platform::GetExecutablePath(char* buffer, size_t* size) { + *size = 0; + return -1; +} + +int Platform::GetCPUInfo(Local *cpus) { + Local cpuinfo; + Local cputimes; + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), + multiplier = ((uint64_t)1000L / ticks), cpuspeed; + uint64_t info[CPUSTATES]; + char model[512]; + int numcpus = 1; + static int which[] = {CTL_HW, HW_MODEL, NULL}; + size_t size; + + size = sizeof(model); + if (sysctl(which, 2, &model, &size, NULL, 0) < 0) { + return -1; + } + which[1] = HW_NCPU; + size = sizeof(numcpus); + if (sysctl(which, 2, &numcpus, &size, NULL, 0) < 0) { + return -1; + } + + *cpus = Array::New(numcpus); + + which[1] = HW_CPUSPEED; + size = sizeof(cpuspeed); + if (sysctl(which, 2, &cpuspeed, &size, NULL, 0) < 0) { + return -1; + } + size = sizeof(info); + which[0] = CTL_KERN; + which[1] = KERN_CPTIME2; + for (int i = 0; i < numcpus; i++) { + which[2] = i; + size = sizeof(info); + if (sysctl(which, 3, &info, &size, NULL, 0) < 0) { + return -1; + } + cpuinfo = Object::New(); + cputimes = Object::New(); + cputimes->Set(String::New("user"), + Number::New((uint64_t)(info[CP_USER]) * multiplier)); + cputimes->Set(String::New("nice"), + Number::New((uint64_t)(info[CP_NICE]) * multiplier)); + cputimes->Set(String::New("sys"), + Number::New((uint64_t)(info[CP_SYS]) * multiplier)); + cputimes->Set(String::New("idle"), + Number::New((uint64_t)(info[CP_IDLE]) * multiplier)); + cputimes->Set(String::New("irq"), + Number::New((uint64_t)(info[CP_INTR]) * multiplier)); + + cpuinfo->Set(String::New("model"), String::New(model)); + cpuinfo->Set(String::New("speed"), Number::New(cpuspeed)); + + cpuinfo->Set(String::New("times"), cputimes); + (*cpus)->Set(i, cpuinfo); + } + return 0; +} + +double Platform::GetFreeMemory() { + double pagesize = static_cast(sysconf(_SC_PAGESIZE)); + struct uvmexp info; + size_t size = sizeof(info); + static int which[] = {CTL_VM, VM_UVMEXP}; + + if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + return -1; + } + + return static_cast(info.free) * pagesize; +} + +double Platform::GetTotalMemory() { +#if defined(HW_PHYSMEM64) + uint64_t info; + static int which[] = {CTL_HW, HW_PHYSMEM64}; +#else + unsigned int info; + static int which[] = {CTL_HW, HW_PHYSMEM}; +#endif + size_t size = sizeof(info); + + if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + return -1; + } + + return static_cast(info); +} + +double Platform::GetUptime() { + time_t now; + struct timeval info; + size_t size = sizeof(info); + static int which[] = {CTL_KERN, KERN_BOOTTIME}; + + if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + return -1; + } + now = time(NULL); + + return static_cast(now - info.tv_sec); +} + +int Platform::GetLoadAvg(Local *loads) { + struct loadavg info; + size_t size = sizeof(info); + static int which[] = {CTL_VM, VM_LOADAVG}; + + if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + return -1; + } + (*loads)->Set(0, Number::New(static_cast(info.ldavg[0]) + / static_cast(info.fscale))); + (*loads)->Set(1, Number::New(static_cast(info.ldavg[1]) + / static_cast(info.fscale))); + (*loads)->Set(2, Number::New(static_cast(info.ldavg[2]) + / static_cast(info.fscale))); + + return 0; +} + +} // namespace node diff --git a/test/simple/test-os-hostname.js b/test/simple/test-os-hostname.js deleted file mode 100644 index b323622717..0000000000 --- a/test/simple/test-os-hostname.js +++ /dev/null @@ -1,5 +0,0 @@ -var common = require('../common'); -var assert = require('assert'); -var os = require('os'); - -assert.ok(os.getHostname().length > 0); diff --git a/test/simple/test-os.js b/test/simple/test-os.js new file mode 100644 index 0000000000..25cabab3f9 --- /dev/null +++ b/test/simple/test-os.js @@ -0,0 +1,12 @@ +var common = require('../common'); +var assert = require('assert'); +var os = require('os'); + +assert.ok(os.hostname().length > 0); +assert.ok(os.loadavg().length > 0); +assert.ok(os.uptime() > 0); +assert.ok(os.freemem() > 0); +assert.ok(os.totalmem() > 0); +assert.ok(os.cpus().length > 0); +assert.ok(os.type().length > 0); +assert.ok(os.release().length > 0); \ No newline at end of file diff --git a/wscript b/wscript index d0d94774f1..7f39031b8e 100644 --- a/wscript +++ b/wscript @@ -197,7 +197,7 @@ def configure(conf): conf.env.append_value("CCFLAGS", "-rdynamic") conf.env.append_value("LINKFLAGS_DL", "-rdynamic") - if sys.platform.startswith("freebsd"): + if sys.platform.startswith("freebsd") or sys.platform.startswith("openbsd"): conf.check(lib='kvm', uselib_store='KVM') #if Options.options.debug: