Merge branch 'v0.4'
[platform/upstream/nodejs.git] / src / platform_linux.cc
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 #include "node.h"
23 #include "platform.h"
24
25 #include <v8.h>
26
27 #include <sys/param.h> // for MAXPATHLEN
28 #include <sys/sysctl.h>
29 #include <sys/sysinfo.h>
30 #include <unistd.h> // getpagesize, sysconf
31 #include <stdio.h> // sscanf, snprintf
32
33 /* SetProcessTitle */
34 #include <sys/prctl.h>
35 #include <linux/prctl.h>
36 #include <stdlib.h> // free
37 #include <string.h> // strdup
38
39 #if HAVE_MONOTONIC_CLOCK
40 #include <time.h>
41 #endif
42
43 namespace node {
44
45 using namespace v8;
46
47 static char buf[MAXPATHLEN + 1];
48 static char *process_title;
49 double Platform::prog_start_time = Platform::GetUptime();
50
51
52 char** Platform::SetupArgs(int argc, char *argv[]) {
53   process_title = strdup(argv[0]);
54   return argv;
55 }
56
57
58 void Platform::SetProcessTitle(char *title) {
59   if (process_title) free(process_title);
60   process_title = strdup(title);
61   prctl(PR_SET_NAME, process_title);
62 }
63
64
65 const char* Platform::GetProcessTitle(int *len) {
66   if (process_title) {
67     *len = strlen(process_title);
68     return process_title;
69   }
70   *len = 0;
71   return NULL;
72 }
73
74
75 int Platform::GetMemory(size_t *rss, size_t *vsize) {
76   FILE *f = fopen("/proc/self/stat", "r");
77   if (!f) return -1;
78
79   int itmp;
80   char ctmp;
81   size_t page_size = getpagesize();
82   char *cbuf;
83   bool foundExeEnd;
84
85   /* PID */
86   if (fscanf(f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
87   /* Exec file */
88   cbuf = buf;
89   foundExeEnd = false;
90   if (fscanf (f, "%c", cbuf++) == 0) goto error; // (
91   while (1) {
92     if (fscanf(f, "%c", cbuf) == 0) goto error;
93     if (*cbuf == ')') {
94       foundExeEnd = true;
95     } else if (foundExeEnd && *cbuf == ' ') {
96       *cbuf = 0;
97       break;
98     }
99
100     cbuf++;
101   }
102   /* State */
103   if (fscanf (f, "%c ", &ctmp) == 0) goto error; /* coverity[secure_coding] */
104   /* Parent process */
105   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
106   /* Process group */
107   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
108   /* Session id */
109   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
110   /* TTY */
111   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
112   /* TTY owner process group */
113   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
114   /* Flags */
115   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
116   /* Minor faults (no memory page) */
117   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
118   /* Minor faults, children */
119   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
120   /* Major faults (memory page faults) */
121   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
122   /* Major faults, children */
123   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
124   /* utime */
125   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
126   /* stime */
127   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
128   /* utime, children */
129   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
130   /* stime, children */
131   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
132   /* jiffies remaining in current time slice */
133   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
134   /* 'nice' value */
135   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
136   /* jiffies until next timeout */
137   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
138   /* jiffies until next SIGALRM */
139   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
140   /* start time (jiffies since system boot) */
141   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
142
143   /* Virtual memory size */
144   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
145   *vsize = (size_t) itmp;
146
147   /* Resident set size */
148   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
149   *rss = (size_t) itmp * page_size;
150
151   /* rlim */
152   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
153   /* Start of text */
154   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
155   /* End of text */
156   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
157   /* Start of stack */
158   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
159
160   fclose (f);
161
162   return 0;
163
164 error:
165   fclose (f);
166   return -1;
167 }
168
169
170 int Platform::GetExecutablePath(char* buffer, size_t* size) {
171   *size = readlink("/proc/self/exe", buffer, *size - 1);
172   if (*size <= 0) return -1;
173   buffer[*size] = '\0';
174   return 0;
175 }
176
177 int Platform::GetCPUInfo(Local<Array> *cpus) {
178   HandleScope scope;
179   Local<Object> cpuinfo;
180   Local<Object> cputimes;
181   unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
182                multiplier = ((uint64_t)1000L / ticks), cpuspeed;
183   int numcpus = 0, i = 0;
184   unsigned long long ticks_user, ticks_sys, ticks_idle, ticks_nice, ticks_intr;
185   char line[512], speedPath[256], model[512];
186   FILE *fpStat = fopen("/proc/stat", "r");
187   FILE *fpModel = fopen("/proc/cpuinfo", "r");
188   FILE *fpSpeed;
189
190   if (fpModel) {
191     while (fgets(line, 511, fpModel) != NULL) {
192       if (strncmp(line, "model name", 10) == 0) {
193         numcpus++;
194         if (numcpus == 1) {
195           char *p = strchr(line, ':') + 2;
196           strcpy(model, p);
197           model[strlen(model)-1] = 0;
198         }
199       } else if (strncmp(line, "cpu MHz", 7) == 0) {
200         if (numcpus == 1) {
201           sscanf(line, "%*s %*s : %u", &cpuspeed);
202         }
203       }
204     }
205     fclose(fpModel);
206   }
207
208   *cpus = Array::New(numcpus);
209
210   if (fpStat) {
211     while (fgets(line, 511, fpStat) != NULL) {
212       if (strncmp(line, "cpu ", 4) == 0) {
213         continue;
214       } else if (strncmp(line, "cpu", 3) != 0) {
215         break;
216       }
217
218       sscanf(line, "%*s %llu %llu %llu %llu %*llu %llu",
219              &ticks_user, &ticks_nice, &ticks_sys, &ticks_idle, &ticks_intr);
220       snprintf(speedPath, sizeof(speedPath),
221                "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i);
222
223       fpSpeed = fopen(speedPath, "r");
224
225       if (fpSpeed) {
226         if (fgets(line, 511, fpSpeed) != NULL) {
227           sscanf(line, "%u", &cpuspeed);
228           cpuspeed /= 1000;
229         }
230         fclose(fpSpeed);
231       }
232
233       cpuinfo = Object::New();
234       cputimes = Object::New();
235       cputimes->Set(String::New("user"), Number::New(ticks_user * multiplier));
236       cputimes->Set(String::New("nice"), Number::New(ticks_nice * multiplier));
237       cputimes->Set(String::New("sys"), Number::New(ticks_sys * multiplier));
238       cputimes->Set(String::New("idle"), Number::New(ticks_idle * multiplier));
239       cputimes->Set(String::New("irq"), Number::New(ticks_intr * multiplier));
240
241       cpuinfo->Set(String::New("model"), String::New(model));
242       cpuinfo->Set(String::New("speed"), Number::New(cpuspeed));
243
244       cpuinfo->Set(String::New("times"), cputimes);
245       (*cpus)->Set(i++, cpuinfo);
246     }
247     fclose(fpStat);
248   }
249
250   return 0;
251 }
252
253 double Platform::GetFreeMemory() {
254   double pagesize = static_cast<double>(sysconf(_SC_PAGESIZE));
255   double pages = static_cast<double>(sysconf(_SC_AVPHYS_PAGES));
256
257   return static_cast<double>(pages * pagesize);
258 }
259
260 double Platform::GetTotalMemory() {
261   double pagesize = static_cast<double>(sysconf(_SC_PAGESIZE));
262   double pages = static_cast<double>(sysconf(_SC_PHYS_PAGES));
263
264   return pages * pagesize;
265 }
266
267 double Platform::GetUptimeImpl() {
268 #if HAVE_MONOTONIC_CLOCK
269   struct timespec now;
270   if (0 == clock_gettime(CLOCK_MONOTONIC, &now)) {
271     double uptime = now.tv_sec;
272     uptime += (double)now.tv_nsec / 1000000000.0;
273     return uptime;
274   }
275   return -1;
276 #else
277   struct sysinfo info;
278   if (sysinfo(&info) < 0) {
279     return -1;
280   }
281   return static_cast<double>(info.uptime);
282 #endif
283 }
284
285 int Platform::GetLoadAvg(Local<Array> *loads) {
286   struct sysinfo info;
287
288   if (sysinfo(&info) < 0) {
289     return -1;
290   }
291   (*loads)->Set(0, Number::New(static_cast<double>(info.loads[0]) / 65536.0));
292   (*loads)->Set(1, Number::New(static_cast<double>(info.loads[1]) / 65536.0));
293   (*loads)->Set(2, Number::New(static_cast<double>(info.loads[2]) / 65536.0));
294
295   return 0;
296 }
297
298 }  // namespace node