Skip to content

Commit

Permalink
Move memoryUsage() into C on Linux
Browse files Browse the repository at this point in the history
  • Loading branch information
ry committed Nov 3, 2009
1 parent 3a70129 commit b3b3cfe
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 75 deletions.
61 changes: 0 additions & 61 deletions lib/sys.js
Expand Up @@ -67,64 +67,3 @@ exports.exec = function (command) {

return promise;
};

exports.memoryUsage = function () {
if (process.platform == "linux2") {
var data = require("file").read("/proc/self/stat").wait();
// from linux-2.6.29/Documentation/filesystems/proc.tx
// 0 pid process id
// 1 tcomm filename of the executable
// 2 state state (R is running, S is sleeping, D is sleeping in an
// uninterruptible wait, Z is zombie, T is traced or stopped)
// 3 ppid process id of the parent process
// 4 pgrp pgrp of the process
// 5 sid session id
// 6 tty_nr tty the process uses
// 7 tty_pgrp pgrp of the tty
// 8 flags task flags
// 9 min_flt number of minor faults
// 10 cmin_flt number of minor faults with child's
// 11 maj_flt number of major faults
// 12 cmaj_flt number of major faults with child's
// 13 utime user mode jiffies
// 14 stime kernel mode jiffies
// 15 cutime user mode jiffies with child's
// 16 cstime kernel mode jiffies with child's
// 17 priority priority level
// 18 nice nice level
// 19 num_threads number of threads
// 20 it_real_value (obsolete, always 0)
// 21 start_time time the process started after system boot
// 22 vsize virtual memory size
// 23 rss resident set memory size
// 24 rsslim current limit in bytes on the rss
// 25 start_code address above which program text can run
// 26 end_code address below which program text can run
// 27 start_stack address of the start of the stack
// 28 esp current value of ESP
// 29 eip current value of EIP
// 30 pending bitmap of pending signals (obsolete)
// 31 blocked bitmap of blocked signals (obsolete)
// 32 sigign bitmap of ignored signals (obsolete)
// 33 sigcatch bitmap of catched signals (obsolete)
// 34 wchan address where process went to sleep
// 35 0 (place holder)
// 36 0 (place holder)
// 37 exit_signal signal to send to parent thread on exit
// 38 task_cpu which CPU the task is scheduled on
// 39 rt_priority realtime priority
// 40 policy scheduling policy (man sched_setscheduler)
// 41 blkio_ticks time spent waiting for block IO
var fields = data.split(" ");

// 22 vsize virtual memory size
// 23 rss resident set memory size

return { vsize: parseInt(fields[22]), rss: 4096*parseInt(fields[23]) };

} else if (process.platform == "darwin") {
return process.macGetMemory();
} else {
throw new Error("Unsupported on your platform! Complain to Ryan!");
}
};
124 changes: 111 additions & 13 deletions src/node.cc
Expand Up @@ -254,17 +254,15 @@ v8::Handle<v8::Value> Exit(const v8::Arguments& args) {
}

#ifdef __APPLE__
#define HAVE_GETMEM 1
/* Researched by Tim Becker and Michael Knight
* http://blog.kuriositaet.de/?p=257
* http://blog.kuriositaet.de/?p=257
*/

#include <mach/task.h>
#include <mach/mach_init.h>

v8::Handle<v8::Value> MacGetMemory(const v8::Arguments& args) {
HandleScope scope;

int getmem(size_t *rss, size_t *vsize) {
task_t task = MACH_PORT_NULL;
struct task_basic_info t_info;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
Expand All @@ -274,21 +272,123 @@ v8::Handle<v8::Value> MacGetMemory(const v8::Arguments& args) {
(task_info_t)&t_info,
&t_info_count);

if (KERN_SUCCESS != r) {
if (r != KERN_SUCCESS) return -1;

*rss = t_info.resident_size;
*vsize = t_info.virtual_size;

return 0;
}
#endif // __APPLE__

#ifdef __linux__
# define HAVE_GETMEM 1
# include <sys/param.h> /* for MAXPATHLEN */
# include <sys/user.h> /* for PAGE_SIZE */

int getmem(size_t *rss, size_t *vsize) {
FILE *f = fopen("/proc/self/stat", "r");
if (!f) return -1;

int itmp;
char ctmp;
char buffer[MAXPATHLEN];

/* PID */
if (fscanf(f, "%d ", &itmp) == 0) goto error;
/* Exec file */
if (fscanf (f, "%s ", &buffer[0]) == 0) goto error;
/* State */
if (fscanf (f, "%c ", &ctmp) == 0) goto error;
/* Parent process */
if (fscanf (f, "%d ", &itmp) == 0) goto error;
/* Process group */
if (fscanf (f, "%d ", &itmp) == 0) goto error;
/* Session id */
if (fscanf (f, "%d ", &itmp) == 0) goto error;
/* TTY */
if (fscanf (f, "%d ", &itmp) == 0) goto error;
/* TTY owner process group */
if (fscanf (f, "%d ", &itmp) == 0) goto error;
/* Flags */
if (fscanf (f, "%u ", &itmp) == 0) goto error;
/* Minor faults (no memory page) */
if (fscanf (f, "%u ", &itmp) == 0) goto error;
/* Minor faults, children */
if (fscanf (f, "%u ", &itmp) == 0) goto error;
/* Major faults (memory page faults) */
if (fscanf (f, "%u ", &itmp) == 0) goto error;
/* Major faults, children */
if (fscanf (f, "%u ", &itmp) == 0) goto error;
/* utime */
if (fscanf (f, "%d ", &itmp) == 0) goto error;
/* stime */
if (fscanf (f, "%d ", &itmp) == 0) goto error;
/* utime, children */
if (fscanf (f, "%d ", &itmp) == 0) goto error;
/* stime, children */
if (fscanf (f, "%d ", &itmp) == 0) goto error;
/* jiffies remaining in current time slice */
if (fscanf (f, "%d ", &itmp) == 0) goto error;
/* 'nice' value */
if (fscanf (f, "%d ", &itmp) == 0) goto error;
/* jiffies until next timeout */
if (fscanf (f, "%u ", &itmp) == 0) goto error;
/* jiffies until next SIGALRM */
if (fscanf (f, "%u ", &itmp) == 0) goto error;
/* start time (jiffies since system boot) */
if (fscanf (f, "%d ", &itmp) == 0) goto error;

/* Virtual memory size */
if (fscanf (f, "%u ", &itmp) == 0) goto error;
*vsize = (size_t) itmp;

/* Resident set size */
if (fscanf (f, "%u ", &itmp) == 0) goto error;
*rss = (size_t) itmp * PAGE_SIZE;

/* rlim */
if (fscanf (f, "%u ", &itmp) == 0) goto error;
/* Start of text */
if (fscanf (f, "%u ", &itmp) == 0) goto error;
/* End of text */
if (fscanf (f, "%u ", &itmp) == 0) goto error;
/* Start of stack */
if (fscanf (f, "%u ", &itmp) == 0) goto error;

fclose (f);

return 0;

error:
fclose (f);
return -1;
}
#endif // __linux__

v8::Handle<v8::Value> MemoryUsage(const v8::Arguments& args) {
HandleScope scope;

#ifndef HAVE_GETMEM
return ThrowException(Exception::Error(String::New("Not support on your platform. (Talk to Ryan.)")));
#else
size_t rss, vsize;

int r = getmem(&rss, &vsize);

if (r != 0) {
return ThrowException(Exception::Error(String::New(strerror(errno))));
}

size_t rss = t_info.resident_size;
size_t vs = t_info.virtual_size;

Local<Object> info = Object::New();

info->Set(String::NewSymbol("rss"), Integer::NewFromUnsigned(rss));
info->Set(String::NewSymbol("vsize"), Integer::NewFromUnsigned(vs));
info->Set(String::NewSymbol("vsize"), Integer::NewFromUnsigned(vsize));

return scope.Close(info);
#endif
}
#endif // __APPLE__


v8::Handle<v8::Value> Kill(const v8::Arguments& args) {
HandleScope scope;
Expand Down Expand Up @@ -510,9 +610,7 @@ static Local<Object> Load(int argc, char *argv[]) {
NODE_SET_METHOD(process, "cwd", Cwd);
NODE_SET_METHOD(process, "dlopen", DLOpen);
NODE_SET_METHOD(process, "kill", Kill);
#ifdef __APPLE__
NODE_SET_METHOD(process, "macGetMemory", MacGetMemory);
#endif
NODE_SET_METHOD(process, "memoryUsage", MemoryUsage);

// Assign the EventEmitter. It was created in main().
process->Set(String::NewSymbol("EventEmitter"),
Expand Down
2 changes: 1 addition & 1 deletion test/mjsunit/test-memory-usage.js
@@ -1,6 +1,6 @@
process.mixin(require("./common"));

var r = memoryUsage();
var r = process.memoryUsage();
puts(inspect(r));
assertTrue(r["rss"] > 0);
assertTrue(r["vsize"] > 0);

0 comments on commit b3b3cfe

Please sign in to comment.