Merge branch 'v0.4'
[platform/upstream/nodejs.git] / src / platform_cygwin.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/sysinfo.h>
29 #include <unistd.h> // getpagesize, sysconf
30 #include <stdio.h> // sscanf, snprintf
31 #include <string.h>
32
33 #include <windows.h>
34
35
36 namespace node {
37
38 using namespace v8;
39
40 static char buf[MAXPATHLEN + 1];
41 static char *process_title = NULL;
42 double Platform::prog_start_time = Platform::GetUptime();
43
44 // Does the about the same as perror(), but for windows api functions
45 static void _winapi_perror(const char* prefix = NULL) {
46   DWORD errorno = GetLastError();
47   char *errmsg;
48
49   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
50                 NULL, errorno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errmsg, 0, NULL);
51
52   // FormatMessage messages include a newline character
53
54   if (prefix) {
55     fprintf(stderr, "%s: %s", prefix, errmsg);
56   } else {
57     fputs(errmsg, stderr);
58   }
59 }
60
61
62 char** Platform::SetupArgs(int argc, char *argv[]) {
63   return argv;
64 }
65
66
67 void Platform::SetProcessTitle(char *title) {
68   // We need to convert _title_ to UTF-16 first, because that's what windows uses internally.
69   // It would be more efficient to use the UTF-16 value that we can obtain from v8,
70   // but it's not accessible from here.
71
72   // Max title length; according to the specs it should be 64K but in practice it's a little over 30000,
73   // but who needs titles that long anyway?
74   const int MAX_TITLE_LENGTH = 30001;
75
76   int length;
77   WCHAR *title_w;
78
79   // Find out how big the buffer for the wide-char title must be
80   length = MultiByteToWideChar(CP_UTF8, 0, title, -1, NULL, 0);
81   if (!length) {
82     _winapi_perror("MultiByteToWideChar");
83     return;
84   }
85
86   // Convert to wide-char string
87   title_w = new WCHAR[length];
88   length = MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w, length);
89   if (!length) {
90     _winapi_perror("MultiByteToWideChar");
91     delete title_w;
92     return;
93   };
94
95   // If the title must be truncated insert a \0 terminator there
96   if (length > MAX_TITLE_LENGTH) {
97     title_w[MAX_TITLE_LENGTH - 1] = *L"\0";
98   }
99
100   if (!SetConsoleTitleW(title_w)) {
101     _winapi_perror("SetConsoleTitleW");
102   }
103
104   free(process_title);
105   process_title = strdup(title);
106
107   delete title_w;
108 }
109
110
111 static inline char* _getProcessTitle() {
112   WCHAR *title_w;
113   char *title;
114   int length, length_w;
115
116   length_w = GetConsoleTitleW((WCHAR*)L"\0", sizeof(WCHAR));
117
118   // If length is zero, there may be an error or the title may be empty
119   if (!length_w) {
120     if (GetLastError()) {
121       _winapi_perror("GetConsoleTitleW");
122       return NULL;
123     } else {
124       // The title is empty, so return empty string
125       process_title = strdup("\0");
126       return process_title;
127     }
128   }
129
130   // Room for \0 terminator
131   length_w++;
132
133   title_w = new WCHAR[length_w];
134
135   if (!GetConsoleTitleW(title_w, length_w * sizeof(WCHAR))) {
136     _winapi_perror("GetConsoleTitleW");
137     delete title_w;
138     return NULL;
139   }
140
141   // Find out what the size of the buffer is that we need
142   length = WideCharToMultiByte(CP_UTF8, 0, title_w, length_w, NULL, 0, NULL, NULL);
143   if (!length) {
144     _winapi_perror("WideCharToMultiByte");
145     delete title_w;
146     return NULL;
147   }
148
149   title = (char *) malloc(length);
150   if (!title) {
151     perror("malloc");
152     delete title_w;
153     return NULL;
154   }
155
156   // Do utf16 -> utf8 conversion here
157   if (!WideCharToMultiByte(CP_UTF8, 0, title_w, -1, title, length, NULL, NULL)) {
158     _winapi_perror("WideCharToMultiByte");
159     delete title_w;
160     free(title);
161     return NULL;
162   }
163
164   delete title_w;
165
166   return title;
167 }
168
169
170 const char* Platform::GetProcessTitle(int *len) {
171   // If the process_title was never read before nor explicitly set,
172   // we must query it with getConsoleTitleW
173   if (!process_title) {
174     process_title = _getProcessTitle();
175   }
176
177   if (process_title) {
178     *len = strlen(process_title);
179     return process_title;
180   } else {
181     *len = 0;
182     return NULL;
183   }
184 }
185
186
187 int Platform::GetMemory(size_t *rss, size_t *vsize) {
188   FILE *f = fopen("/proc/self/stat", "r");
189   if (!f) return -1;
190
191   int itmp;
192   char ctmp;
193   size_t page_size = getpagesize();
194
195   /* PID */
196   if (fscanf(f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
197   /* Exec file */
198   if (fscanf (f, "%s ", buf) == 0) goto error; /* coverity[secure_coding] */
199   /* State */
200   if (fscanf (f, "%c ", &ctmp) == 0) goto error; /* coverity[secure_coding] */
201   /* Parent process */
202   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
203   /* Process group */
204   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
205   /* Session id */
206   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
207   /* TTY */
208   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
209   /* TTY owner process group */
210   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
211   /* Flags */
212   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
213   /* Minor faults (no memory page) */
214   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
215   /* Minor faults, children */
216   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
217   /* Major faults (memory page faults) */
218   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
219   /* Major faults, children */
220   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
221   /* utime */
222   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
223   /* stime */
224   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
225   /* utime, children */
226   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
227   /* stime, children */
228   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
229   /* jiffies remaining in current time slice */
230   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
231   /* 'nice' value */
232   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
233   /* jiffies until next timeout */
234   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
235   /* jiffies until next SIGALRM */
236   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
237   /* start time (jiffies since system boot) */
238   if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
239
240   /* Virtual memory size */
241   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
242   *vsize = (size_t) itmp;
243
244   /* Resident set size */
245   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
246   *rss = (size_t) itmp * page_size;
247
248   /* rlim */
249   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
250   /* Start of text */
251   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
252   /* End of text */
253   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
254   /* Start of stack */
255   if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */
256
257   fclose (f);
258
259   return 0;
260
261 error:
262   fclose (f);
263   return -1;
264 }
265
266
267 int Platform::GetExecutablePath(char* buffer, size_t* size) {
268   *size = readlink("/proc/self/exe", buffer, *size - 1);
269   if (*size <= 0) return -1;
270   buffer[*size] = '\0';
271   return 0;
272 }
273
274 int Platform::GetCPUInfo(Local<Array> *cpus) {
275   HandleScope scope;
276   Local<Object> cpuinfo;
277   Local<Object> cputimes;
278   unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
279                multiplier = ((uint64_t)1000L / ticks), cpuspeed;
280   int numcpus = 0, i = 0;
281   unsigned long long ticks_user, ticks_sys, ticks_idle, ticks_nice, ticks_intr;
282   char line[512], speedPath[256], model[512];
283   FILE *fpStat = fopen("/proc/stat", "r");
284   FILE *fpModel = fopen("/proc/cpuinfo", "r");
285   FILE *fpSpeed;
286
287   if (fpModel) {
288     while (fgets(line, 511, fpModel) != NULL) {
289       if (strncmp(line, "model name", 10) == 0) {
290         numcpus++;
291         if (numcpus == 1) {
292           char *p = strchr(line, ':') + 2;
293           strcpy(model, p);
294           model[strlen(model)-1] = 0;
295         }
296       } else if (strncmp(line, "cpu MHz", 7) == 0) {
297         if (numcpus == 1) {
298           sscanf(line, "%*s %*s : %u", &cpuspeed);
299         }
300       }
301     }
302     fclose(fpModel);
303   }
304
305   *cpus = Array::New(numcpus);
306
307   if (fpStat) {
308     while (fgets(line, 511, fpStat) != NULL) {
309       if (strncmp(line, "cpu ", 4) == 0) {
310         continue;
311       } else if (strncmp(line, "cpu", 3) != 0) {
312         break;
313       }
314
315       sscanf(line, "%*s %llu %llu %llu %llu",
316              &ticks_user, &ticks_nice, &ticks_sys, &ticks_idle);
317       snprintf(speedPath, sizeof(speedPath),
318                "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i);
319
320       fpSpeed = fopen(speedPath, "r");
321       if (fpSpeed) {
322         if (fgets(line, 511, fpSpeed) != NULL) {
323           sscanf(line, "%u", &cpuspeed);
324           cpuspeed /= 1000;
325         }
326         fclose(fpSpeed);
327       }
328       cpuinfo = Object::New();
329       cputimes = Object::New();
330       cputimes->Set(String::New("user"), Number::New(ticks_user * multiplier));
331       cputimes->Set(String::New("nice"), Number::New(ticks_nice * multiplier));
332       cputimes->Set(String::New("sys"), Number::New(ticks_sys * multiplier));
333       cputimes->Set(String::New("idle"), Number::New(ticks_idle * multiplier));
334       cputimes->Set(String::New("irq"), Number::New(0));
335
336       cpuinfo->Set(String::New("model"), String::New(model));
337       cpuinfo->Set(String::New("speed"), Number::New(cpuspeed));
338
339       cpuinfo->Set(String::New("times"), cputimes);
340       (*cpus)->Set(i++, cpuinfo);
341     }
342     fclose(fpStat);
343   }
344
345   return 0;
346 }
347
348 double Platform::GetFreeMemory() {
349   double pagesize = static_cast<double>(sysconf(_SC_PAGESIZE));
350   double pages = static_cast<double>(sysconf(_SC_AVPHYS_PAGES));
351
352   return static_cast<double>(pages * pagesize);
353 }
354
355 double Platform::GetTotalMemory() {
356   double pagesize = static_cast<double>(sysconf(_SC_PAGESIZE));
357   double pages = static_cast<double>(sysconf(_SC_PHYS_PAGES));
358
359   return pages * pagesize;
360 }
361
362 double Platform::GetUptimeImpl() {
363   double amount;
364   char line[512];
365   FILE *fpUptime = fopen("/proc/uptime", "r");
366
367   if (fpUptime) {
368     if (fgets(line, 511, fpUptime) != NULL) {
369       sscanf(line, "%lf %*lf", &amount);
370     }
371     fclose(fpUptime);
372   }
373
374   return amount;
375 }
376
377 int Platform::GetLoadAvg(Local<Array> *loads) {
378   // Unsupported as of cygwin 1.7.7
379   return -1;
380 }
381
382
383 }  // namespace node