1 | // Copyright 2011 Google Inc. All Rights Reserved. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | #include "util.h" |
16 | |
17 | #ifdef __CYGWIN__ |
18 | #include <windows.h> |
19 | #include <io.h> |
20 | #elif defined( _WIN32) |
21 | #include <windows.h> |
22 | #include <io.h> |
23 | #include <share.h> |
24 | #endif |
25 | |
26 | #include <assert.h> |
27 | #include <errno.h> |
28 | #include <fcntl.h> |
29 | #include <stdarg.h> |
30 | #include <stdio.h> |
31 | #include <stdlib.h> |
32 | #include <string.h> |
33 | #include <sys/stat.h> |
34 | #include <sys/types.h> |
35 | |
36 | #ifndef _WIN32 |
37 | #include <unistd.h> |
38 | #include <sys/time.h> |
39 | #endif |
40 | |
41 | #include <vector> |
42 | |
43 | #if defined(__APPLE__) || defined(__FreeBSD__) |
44 | #include <sys/sysctl.h> |
45 | #elif defined(__SVR4) && defined(__sun) |
46 | #include <unistd.h> |
47 | #include <sys/loadavg.h> |
48 | #elif defined(_AIX) && !defined(__PASE__) |
49 | #include <libperfstat.h> |
50 | #elif defined(linux) || defined(__GLIBC__) |
51 | #include <sys/sysinfo.h> |
52 | #include <fstream> |
53 | #include <map> |
54 | #include "string_piece_util.h" |
55 | #endif |
56 | |
57 | #if defined(__FreeBSD__) |
58 | #include <sys/cpuset.h> |
59 | #endif |
60 | |
61 | #include "edit_distance.h" |
62 | |
63 | using namespace std; |
64 | |
65 | void Fatal(const char* msg, ...) { |
66 | va_list ap; |
67 | fprintf(stderr, "ninja: fatal: " ); |
68 | va_start(ap, msg); |
69 | vfprintf(stderr, msg, ap); |
70 | va_end(ap); |
71 | fprintf(stderr, "\n" ); |
72 | #ifdef _WIN32 |
73 | // On Windows, some tools may inject extra threads. |
74 | // exit() may block on locks held by those threads, so forcibly exit. |
75 | fflush(stderr); |
76 | fflush(stdout); |
77 | ExitProcess(1); |
78 | #else |
79 | exit(1); |
80 | #endif |
81 | } |
82 | |
83 | void Warning(const char* msg, va_list ap) { |
84 | fprintf(stderr, "ninja: warning: " ); |
85 | vfprintf(stderr, msg, ap); |
86 | fprintf(stderr, "\n" ); |
87 | } |
88 | |
89 | void Warning(const char* msg, ...) { |
90 | va_list ap; |
91 | va_start(ap, msg); |
92 | Warning(msg, ap); |
93 | va_end(ap); |
94 | } |
95 | |
96 | void Error(const char* msg, va_list ap) { |
97 | fprintf(stderr, "ninja: error: " ); |
98 | vfprintf(stderr, msg, ap); |
99 | fprintf(stderr, "\n" ); |
100 | } |
101 | |
102 | void Error(const char* msg, ...) { |
103 | va_list ap; |
104 | va_start(ap, msg); |
105 | Error(msg, ap); |
106 | va_end(ap); |
107 | } |
108 | |
109 | void Info(const char* msg, va_list ap) { |
110 | fprintf(stdout, "ninja: " ); |
111 | vfprintf(stdout, msg, ap); |
112 | fprintf(stdout, "\n" ); |
113 | } |
114 | |
115 | void Info(const char* msg, ...) { |
116 | va_list ap; |
117 | va_start(ap, msg); |
118 | Info(msg, ap); |
119 | va_end(ap); |
120 | } |
121 | |
122 | void CanonicalizePath(string* path, uint64_t* slash_bits) { |
123 | size_t len = path->size(); |
124 | char* str = 0; |
125 | if (len > 0) |
126 | str = &(*path)[0]; |
127 | CanonicalizePath(str, &len, slash_bits); |
128 | path->resize(len); |
129 | } |
130 | |
131 | static bool IsPathSeparator(char c) { |
132 | #ifdef _WIN32 |
133 | return c == '/' || c == '\\'; |
134 | #else |
135 | return c == '/'; |
136 | #endif |
137 | } |
138 | |
139 | void CanonicalizePath(char* path, size_t* len, uint64_t* slash_bits) { |
140 | // WARNING: this function is performance-critical; please benchmark |
141 | // any changes you make to it. |
142 | if (*len == 0) { |
143 | return; |
144 | } |
145 | |
146 | const int kMaxPathComponents = 60; |
147 | char* components[kMaxPathComponents]; |
148 | int component_count = 0; |
149 | |
150 | char* start = path; |
151 | char* dst = start; |
152 | const char* src = start; |
153 | const char* end = start + *len; |
154 | |
155 | if (IsPathSeparator(*src)) { |
156 | #ifdef _WIN32 |
157 | |
158 | // network path starts with // |
159 | if (*len > 1 && IsPathSeparator(*(src + 1))) { |
160 | src += 2; |
161 | dst += 2; |
162 | } else { |
163 | ++src; |
164 | ++dst; |
165 | } |
166 | #else |
167 | ++src; |
168 | ++dst; |
169 | #endif |
170 | } |
171 | |
172 | while (src < end) { |
173 | if (*src == '.') { |
174 | if (src + 1 == end || IsPathSeparator(src[1])) { |
175 | // '.' component; eliminate. |
176 | src += 2; |
177 | continue; |
178 | } else if (src[1] == '.' && (src + 2 == end || IsPathSeparator(src[2]))) { |
179 | // '..' component. Back up if possible. |
180 | if (component_count > 0) { |
181 | dst = components[component_count - 1]; |
182 | src += 3; |
183 | --component_count; |
184 | } else { |
185 | *dst++ = *src++; |
186 | *dst++ = *src++; |
187 | *dst++ = *src++; |
188 | } |
189 | continue; |
190 | } |
191 | } |
192 | |
193 | if (IsPathSeparator(*src)) { |
194 | src++; |
195 | continue; |
196 | } |
197 | |
198 | if (component_count == kMaxPathComponents) |
199 | Fatal("path has too many components : %s" , path); |
200 | components[component_count] = dst; |
201 | ++component_count; |
202 | |
203 | while (src != end && !IsPathSeparator(*src)) |
204 | *dst++ = *src++; |
205 | *dst++ = *src++; // Copy '/' or final \0 character as well. |
206 | } |
207 | |
208 | if (dst == start) { |
209 | *dst++ = '.'; |
210 | *dst++ = '\0'; |
211 | } |
212 | |
213 | *len = dst - start - 1; |
214 | #ifdef _WIN32 |
215 | uint64_t bits = 0; |
216 | uint64_t bits_mask = 1; |
217 | |
218 | for (char* c = start; c < start + *len; ++c) { |
219 | switch (*c) { |
220 | case '\\': |
221 | bits |= bits_mask; |
222 | *c = '/'; |
223 | NINJA_FALLTHROUGH; |
224 | case '/': |
225 | bits_mask <<= 1; |
226 | } |
227 | } |
228 | |
229 | *slash_bits = bits; |
230 | #else |
231 | *slash_bits = 0; |
232 | #endif |
233 | } |
234 | |
235 | static inline bool IsKnownShellSafeCharacter(char ch) { |
236 | if ('A' <= ch && ch <= 'Z') return true; |
237 | if ('a' <= ch && ch <= 'z') return true; |
238 | if ('0' <= ch && ch <= '9') return true; |
239 | |
240 | switch (ch) { |
241 | case '_': |
242 | case '+': |
243 | case '-': |
244 | case '.': |
245 | case '/': |
246 | return true; |
247 | default: |
248 | return false; |
249 | } |
250 | } |
251 | |
252 | static inline bool IsKnownWin32SafeCharacter(char ch) { |
253 | switch (ch) { |
254 | case ' ': |
255 | case '"': |
256 | return false; |
257 | default: |
258 | return true; |
259 | } |
260 | } |
261 | |
262 | static inline bool StringNeedsShellEscaping(const string& input) { |
263 | for (size_t i = 0; i < input.size(); ++i) { |
264 | if (!IsKnownShellSafeCharacter(input[i])) return true; |
265 | } |
266 | return false; |
267 | } |
268 | |
269 | static inline bool StringNeedsWin32Escaping(const string& input) { |
270 | for (size_t i = 0; i < input.size(); ++i) { |
271 | if (!IsKnownWin32SafeCharacter(input[i])) return true; |
272 | } |
273 | return false; |
274 | } |
275 | |
276 | void GetShellEscapedString(const string& input, string* result) { |
277 | assert(result); |
278 | |
279 | if (!StringNeedsShellEscaping(input)) { |
280 | result->append(input); |
281 | return; |
282 | } |
283 | |
284 | const char kQuote = '\''; |
285 | const char kEscapeSequence[] = "'\\'" ; |
286 | |
287 | result->push_back(kQuote); |
288 | |
289 | string::const_iterator span_begin = input.begin(); |
290 | for (string::const_iterator it = input.begin(), end = input.end(); it != end; |
291 | ++it) { |
292 | if (*it == kQuote) { |
293 | result->append(span_begin, it); |
294 | result->append(kEscapeSequence); |
295 | span_begin = it; |
296 | } |
297 | } |
298 | result->append(span_begin, input.end()); |
299 | result->push_back(kQuote); |
300 | } |
301 | |
302 | |
303 | void GetWin32EscapedString(const string& input, string* result) { |
304 | assert(result); |
305 | if (!StringNeedsWin32Escaping(input)) { |
306 | result->append(input); |
307 | return; |
308 | } |
309 | |
310 | const char kQuote = '"'; |
311 | const char kBackslash = '\\'; |
312 | |
313 | result->push_back(kQuote); |
314 | size_t consecutive_backslash_count = 0; |
315 | string::const_iterator span_begin = input.begin(); |
316 | for (string::const_iterator it = input.begin(), end = input.end(); it != end; |
317 | ++it) { |
318 | switch (*it) { |
319 | case kBackslash: |
320 | ++consecutive_backslash_count; |
321 | break; |
322 | case kQuote: |
323 | result->append(span_begin, it); |
324 | result->append(consecutive_backslash_count + 1, kBackslash); |
325 | span_begin = it; |
326 | consecutive_backslash_count = 0; |
327 | break; |
328 | default: |
329 | consecutive_backslash_count = 0; |
330 | break; |
331 | } |
332 | } |
333 | result->append(span_begin, input.end()); |
334 | result->append(consecutive_backslash_count, kBackslash); |
335 | result->push_back(kQuote); |
336 | } |
337 | |
338 | int ReadFile(const string& path, string* contents, string* err) { |
339 | #ifdef _WIN32 |
340 | // This makes a ninja run on a set of 1500 manifest files about 4% faster |
341 | // than using the generic fopen code below. |
342 | err->clear(); |
343 | HANDLE f = ::CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, |
344 | OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); |
345 | if (f == INVALID_HANDLE_VALUE) { |
346 | err->assign(GetLastErrorString()); |
347 | return -ENOENT; |
348 | } |
349 | |
350 | for (;;) { |
351 | DWORD len; |
352 | char buf[64 << 10]; |
353 | if (!::ReadFile(f, buf, sizeof(buf), &len, NULL)) { |
354 | err->assign(GetLastErrorString()); |
355 | contents->clear(); |
356 | ::CloseHandle(f); |
357 | return -EIO; |
358 | } |
359 | if (len == 0) |
360 | break; |
361 | contents->append(buf, len); |
362 | } |
363 | ::CloseHandle(f); |
364 | return 0; |
365 | #else |
366 | FILE* f = fopen(path.c_str(), "rb" ); |
367 | if (!f) { |
368 | err->assign(strerror(errno)); |
369 | return -errno; |
370 | } |
371 | |
372 | #ifdef __USE_LARGEFILE64 |
373 | struct stat64 st; |
374 | if (fstat64(fileno(f), &st) < 0) { |
375 | #else |
376 | struct stat st; |
377 | if (fstat(fileno(f), &st) < 0) { |
378 | #endif |
379 | err->assign(strerror(errno)); |
380 | fclose(f); |
381 | return -errno; |
382 | } |
383 | |
384 | // +1 is for the resize in ManifestParser::Load |
385 | contents->reserve(st.st_size + 1); |
386 | |
387 | char buf[64 << 10]; |
388 | size_t len; |
389 | while (!feof(f) && (len = fread(buf, 1, sizeof(buf), f)) > 0) { |
390 | contents->append(buf, len); |
391 | } |
392 | if (ferror(f)) { |
393 | err->assign(strerror(errno)); // XXX errno? |
394 | contents->clear(); |
395 | fclose(f); |
396 | return -errno; |
397 | } |
398 | fclose(f); |
399 | return 0; |
400 | #endif |
401 | } |
402 | |
403 | void SetCloseOnExec(int fd) { |
404 | #ifndef _WIN32 |
405 | int flags = fcntl(fd, F_GETFD); |
406 | if (flags < 0) { |
407 | perror("fcntl(F_GETFD)" ); |
408 | } else { |
409 | if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) |
410 | perror("fcntl(F_SETFD)" ); |
411 | } |
412 | #else |
413 | HANDLE hd = (HANDLE) _get_osfhandle(fd); |
414 | if (! SetHandleInformation(hd, HANDLE_FLAG_INHERIT, 0)) { |
415 | fprintf(stderr, "SetHandleInformation(): %s" , GetLastErrorString().c_str()); |
416 | } |
417 | #endif // ! _WIN32 |
418 | } |
419 | |
420 | |
421 | const char* SpellcheckStringV(const string& text, |
422 | const vector<const char*>& words) { |
423 | const bool kAllowReplacements = true; |
424 | const int kMaxValidEditDistance = 3; |
425 | |
426 | int min_distance = kMaxValidEditDistance + 1; |
427 | const char* result = NULL; |
428 | for (vector<const char*>::const_iterator i = words.begin(); |
429 | i != words.end(); ++i) { |
430 | int distance = EditDistance(*i, text, kAllowReplacements, |
431 | kMaxValidEditDistance); |
432 | if (distance < min_distance) { |
433 | min_distance = distance; |
434 | result = *i; |
435 | } |
436 | } |
437 | return result; |
438 | } |
439 | |
440 | const char* SpellcheckString(const char* text, ...) { |
441 | // Note: This takes a const char* instead of a string& because using |
442 | // va_start() with a reference parameter is undefined behavior. |
443 | va_list ap; |
444 | va_start(ap, text); |
445 | vector<const char*> words; |
446 | const char* word; |
447 | while ((word = va_arg(ap, const char*))) |
448 | words.push_back(word); |
449 | va_end(ap); |
450 | return SpellcheckStringV(text, words); |
451 | } |
452 | |
453 | #ifdef _WIN32 |
454 | string GetLastErrorString() { |
455 | DWORD err = GetLastError(); |
456 | |
457 | char* msg_buf; |
458 | FormatMessageA( |
459 | FORMAT_MESSAGE_ALLOCATE_BUFFER | |
460 | FORMAT_MESSAGE_FROM_SYSTEM | |
461 | FORMAT_MESSAGE_IGNORE_INSERTS, |
462 | NULL, |
463 | err, |
464 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
465 | (char*)&msg_buf, |
466 | 0, |
467 | NULL); |
468 | string msg = msg_buf; |
469 | LocalFree(msg_buf); |
470 | return msg; |
471 | } |
472 | |
473 | void Win32Fatal(const char* function, const char* hint) { |
474 | if (hint) { |
475 | Fatal("%s: %s (%s)" , function, GetLastErrorString().c_str(), hint); |
476 | } else { |
477 | Fatal("%s: %s" , function, GetLastErrorString().c_str()); |
478 | } |
479 | } |
480 | #endif |
481 | |
482 | bool islatinalpha(int c) { |
483 | // isalpha() is locale-dependent. |
484 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); |
485 | } |
486 | |
487 | string StripAnsiEscapeCodes(const string& in) { |
488 | string stripped; |
489 | stripped.reserve(in.size()); |
490 | |
491 | for (size_t i = 0; i < in.size(); ++i) { |
492 | if (in[i] != '\33') { |
493 | // Not an escape code. |
494 | stripped.push_back(in[i]); |
495 | continue; |
496 | } |
497 | |
498 | // Only strip CSIs for now. |
499 | if (i + 1 >= in.size()) break; |
500 | if (in[i + 1] != '[') continue; // Not a CSI. |
501 | i += 2; |
502 | |
503 | // Skip everything up to and including the next [a-zA-Z]. |
504 | while (i < in.size() && !islatinalpha(in[i])) |
505 | ++i; |
506 | } |
507 | return stripped; |
508 | } |
509 | |
510 | #if defined(linux) || defined(__GLIBC__) |
511 | std::pair<int64_t, bool> readCount(const std::string& path) { |
512 | std::ifstream file(path.c_str()); |
513 | if (!file.is_open()) |
514 | return std::make_pair(0, false); |
515 | int64_t n = 0; |
516 | file >> n; |
517 | if (file.good()) |
518 | return std::make_pair(n, true); |
519 | return std::make_pair(0, false); |
520 | } |
521 | |
522 | struct MountPoint { |
523 | int mountId; |
524 | int parentId; |
525 | StringPiece deviceId; |
526 | StringPiece root; |
527 | StringPiece mountPoint; |
528 | vector<StringPiece> options; |
529 | vector<StringPiece> optionalFields; |
530 | StringPiece fsType; |
531 | StringPiece mountSource; |
532 | vector<StringPiece> superOptions; |
533 | bool parse(const string& line) { |
534 | vector<StringPiece> pieces = SplitStringPiece(line, ' '); |
535 | if (pieces.size() < 10) |
536 | return false; |
537 | size_t optionalStart = 0; |
538 | for (size_t i = 6; i < pieces.size(); i++) { |
539 | if (pieces[i] == "-" ) { |
540 | optionalStart = i + 1; |
541 | break; |
542 | } |
543 | } |
544 | if (optionalStart == 0) |
545 | return false; |
546 | if (optionalStart + 3 != pieces.size()) |
547 | return false; |
548 | mountId = atoi(pieces[0].AsString().c_str()); |
549 | parentId = atoi(pieces[1].AsString().c_str()); |
550 | deviceId = pieces[2]; |
551 | root = pieces[3]; |
552 | mountPoint = pieces[4]; |
553 | options = SplitStringPiece(pieces[5], ','); |
554 | optionalFields = |
555 | vector<StringPiece>(&pieces[6], &pieces[optionalStart - 1]); |
556 | fsType = pieces[optionalStart]; |
557 | mountSource = pieces[optionalStart + 1]; |
558 | superOptions = SplitStringPiece(pieces[optionalStart + 2], ','); |
559 | return true; |
560 | } |
561 | string translate(string& path) const { |
562 | // path must be sub dir of root |
563 | if (path.compare(0, root.len_, root.str_, root.len_) != 0) { |
564 | return string(); |
565 | } |
566 | path.erase(0, root.len_); |
567 | if (path == ".." || (path.length() > 2 && path.compare(0, 3, "../" ) == 0)) { |
568 | return string(); |
569 | } |
570 | return mountPoint.AsString() + "/" + path; |
571 | } |
572 | }; |
573 | |
574 | struct CGroupSubSys { |
575 | int id; |
576 | string name; |
577 | vector<string> subsystems; |
578 | bool parse(string& line) { |
579 | size_t first = line.find(':'); |
580 | if (first == string::npos) |
581 | return false; |
582 | line[first] = '\0'; |
583 | size_t second = line.find(':', first + 1); |
584 | if (second == string::npos) |
585 | return false; |
586 | line[second] = '\0'; |
587 | id = atoi(line.c_str()); |
588 | name = line.substr(second + 1); |
589 | vector<StringPiece> pieces = |
590 | SplitStringPiece(StringPiece(line.c_str() + first + 1), ','); |
591 | for (size_t i = 0; i < pieces.size(); i++) { |
592 | subsystems.push_back(pieces[i].AsString()); |
593 | } |
594 | return true; |
595 | } |
596 | }; |
597 | |
598 | map<string, string> ParseMountInfo(map<string, CGroupSubSys>& subsystems) { |
599 | map<string, string> cgroups; |
600 | ifstream mountinfo("/proc/self/mountinfo" ); |
601 | if (!mountinfo.is_open()) |
602 | return cgroups; |
603 | while (!mountinfo.eof()) { |
604 | string line; |
605 | getline(mountinfo, line); |
606 | MountPoint mp; |
607 | if (!mp.parse(line)) |
608 | continue; |
609 | if (mp.fsType != "cgroup" ) |
610 | continue; |
611 | for (size_t i = 0; i < mp.superOptions.size(); i++) { |
612 | string opt = mp.superOptions[i].AsString(); |
613 | map<string, CGroupSubSys>::iterator subsys = subsystems.find(opt); |
614 | if (subsys == subsystems.end()) |
615 | continue; |
616 | string newPath = mp.translate(subsys->second.name); |
617 | if (!newPath.empty()) |
618 | cgroups.insert(make_pair(opt, newPath)); |
619 | } |
620 | } |
621 | return cgroups; |
622 | } |
623 | |
624 | map<string, CGroupSubSys> ParseSelfCGroup() { |
625 | map<string, CGroupSubSys> cgroups; |
626 | ifstream cgroup("/proc/self/cgroup" ); |
627 | if (!cgroup.is_open()) |
628 | return cgroups; |
629 | string line; |
630 | while (!cgroup.eof()) { |
631 | getline(cgroup, line); |
632 | CGroupSubSys subsys; |
633 | if (!subsys.parse(line)) |
634 | continue; |
635 | for (size_t i = 0; i < subsys.subsystems.size(); i++) { |
636 | cgroups.insert(make_pair(subsys.subsystems[i], subsys)); |
637 | } |
638 | } |
639 | return cgroups; |
640 | } |
641 | |
642 | int ParseCPUFromCGroup() { |
643 | map<string, CGroupSubSys> subsystems = ParseSelfCGroup(); |
644 | map<string, string> cgroups = ParseMountInfo(subsystems); |
645 | map<string, string>::iterator cpu = cgroups.find("cpu" ); |
646 | if (cpu == cgroups.end()) |
647 | return -1; |
648 | std::pair<int64_t, bool> quota = readCount(cpu->second + "/cpu.cfs_quota_us" ); |
649 | if (!quota.second || quota.first == -1) |
650 | return -1; |
651 | std::pair<int64_t, bool> period = |
652 | readCount(cpu->second + "/cpu.cfs_period_us" ); |
653 | if (!period.second) |
654 | return -1; |
655 | if (period.first == 0) |
656 | return -1; |
657 | return quota.first / period.first; |
658 | } |
659 | #endif |
660 | |
661 | int GetProcessorCount() { |
662 | #ifdef _WIN32 |
663 | DWORD cpuCount = 0; |
664 | #ifndef _WIN64 |
665 | // Need to use GetLogicalProcessorInformationEx to get real core count on |
666 | // machines with >64 cores. See https://stackoverflow.com/a/31209344/21475 |
667 | DWORD len = 0; |
668 | if (!GetLogicalProcessorInformationEx(RelationProcessorCore, nullptr, &len) |
669 | && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { |
670 | std::vector<char> buf(len); |
671 | int cores = 0; |
672 | if (GetLogicalProcessorInformationEx(RelationProcessorCore, |
673 | reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX>( |
674 | buf.data()), &len)) { |
675 | for (DWORD i = 0; i < len; ) { |
676 | auto info = reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX>( |
677 | buf.data() + i); |
678 | if (info->Relationship == RelationProcessorCore && |
679 | info->Processor.GroupCount == 1) { |
680 | for (KAFFINITY core_mask = info->Processor.GroupMask[0].Mask; |
681 | core_mask; core_mask >>= 1) { |
682 | cores += (core_mask & 1); |
683 | } |
684 | } |
685 | i += info->Size; |
686 | } |
687 | if (cores != 0) { |
688 | cpuCount = cores; |
689 | } |
690 | } |
691 | } |
692 | #endif |
693 | if (cpuCount == 0) { |
694 | cpuCount = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); |
695 | } |
696 | JOBOBJECT_CPU_RATE_CONTROL_INFORMATION info; |
697 | // reference: |
698 | // https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_cpu_rate_control_information |
699 | if (QueryInformationJobObject(NULL, JobObjectCpuRateControlInformation, &info, |
700 | sizeof(info), NULL)) { |
701 | if (info.ControlFlags & (JOB_OBJECT_CPU_RATE_CONTROL_ENABLE | |
702 | JOB_OBJECT_CPU_RATE_CONTROL_HARD_CAP)) { |
703 | return cpuCount * info.CpuRate / 10000; |
704 | } |
705 | } |
706 | return cpuCount; |
707 | #else |
708 | int cgroupCount = -1; |
709 | int schedCount = -1; |
710 | #if defined(linux) || defined(__GLIBC__) |
711 | cgroupCount = ParseCPUFromCGroup(); |
712 | #endif |
713 | // The number of exposed processors might not represent the actual number of |
714 | // processors threads can run on. This happens when a CPU set limitation is |
715 | // active, see https://github.com/ninja-build/ninja/issues/1278 |
716 | #if defined(__FreeBSD__) |
717 | cpuset_t mask; |
718 | CPU_ZERO(&mask); |
719 | if (cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(mask), |
720 | &mask) == 0) { |
721 | return CPU_COUNT(&mask); |
722 | } |
723 | #elif defined(CPU_COUNT) |
724 | cpu_set_t set; |
725 | if (sched_getaffinity(getpid(), sizeof(set), &set) == 0) { |
726 | schedCount = CPU_COUNT(&set); |
727 | } |
728 | #endif |
729 | if (cgroupCount >= 0 && schedCount >= 0) return std::min(cgroupCount, schedCount); |
730 | if (cgroupCount < 0 && schedCount < 0) return sysconf(_SC_NPROCESSORS_ONLN); |
731 | return std::max(cgroupCount, schedCount); |
732 | #endif |
733 | } |
734 | |
735 | #if defined(_WIN32) || defined(__CYGWIN__) |
736 | static double CalculateProcessorLoad(uint64_t idle_ticks, uint64_t total_ticks) |
737 | { |
738 | static uint64_t previous_idle_ticks = 0; |
739 | static uint64_t previous_total_ticks = 0; |
740 | static double previous_load = -0.0; |
741 | |
742 | uint64_t idle_ticks_since_last_time = idle_ticks - previous_idle_ticks; |
743 | uint64_t total_ticks_since_last_time = total_ticks - previous_total_ticks; |
744 | |
745 | bool first_call = (previous_total_ticks == 0); |
746 | bool ticks_not_updated_since_last_call = (total_ticks_since_last_time == 0); |
747 | |
748 | double load; |
749 | if (first_call || ticks_not_updated_since_last_call) { |
750 | load = previous_load; |
751 | } else { |
752 | // Calculate load. |
753 | double idle_to_total_ratio = |
754 | ((double)idle_ticks_since_last_time) / total_ticks_since_last_time; |
755 | double load_since_last_call = 1.0 - idle_to_total_ratio; |
756 | |
757 | // Filter/smooth result when possible. |
758 | if(previous_load > 0) { |
759 | load = 0.9 * previous_load + 0.1 * load_since_last_call; |
760 | } else { |
761 | load = load_since_last_call; |
762 | } |
763 | } |
764 | |
765 | previous_load = load; |
766 | previous_total_ticks = total_ticks; |
767 | previous_idle_ticks = idle_ticks; |
768 | |
769 | return load; |
770 | } |
771 | |
772 | static uint64_t FileTimeToTickCount(const FILETIME & ft) |
773 | { |
774 | uint64_t high = (((uint64_t)(ft.dwHighDateTime)) << 32); |
775 | uint64_t low = ft.dwLowDateTime; |
776 | return (high | low); |
777 | } |
778 | |
779 | double GetLoadAverage() { |
780 | FILETIME idle_time, kernel_time, user_time; |
781 | BOOL get_system_time_succeeded = |
782 | GetSystemTimes(&idle_time, &kernel_time, &user_time); |
783 | |
784 | double posix_compatible_load; |
785 | if (get_system_time_succeeded) { |
786 | uint64_t idle_ticks = FileTimeToTickCount(idle_time); |
787 | |
788 | // kernel_time from GetSystemTimes already includes idle_time. |
789 | uint64_t total_ticks = |
790 | FileTimeToTickCount(kernel_time) + FileTimeToTickCount(user_time); |
791 | |
792 | double processor_load = CalculateProcessorLoad(idle_ticks, total_ticks); |
793 | posix_compatible_load = processor_load * GetProcessorCount(); |
794 | |
795 | } else { |
796 | posix_compatible_load = -0.0; |
797 | } |
798 | |
799 | return posix_compatible_load; |
800 | } |
801 | #elif defined(__PASE__) |
802 | double GetLoadAverage() { |
803 | return -0.0f; |
804 | } |
805 | #elif defined(_AIX) |
806 | double GetLoadAverage() { |
807 | perfstat_cpu_total_t cpu_stats; |
808 | if (perfstat_cpu_total(NULL, &cpu_stats, sizeof(cpu_stats), 1) < 0) { |
809 | return -0.0f; |
810 | } |
811 | |
812 | // Calculation taken from comment in libperfstats.h |
813 | return double(cpu_stats.loadavg[0]) / double(1 << SBITS); |
814 | } |
815 | #elif defined(__UCLIBC__) || (defined(__BIONIC__) && __ANDROID_API__ < 29) |
816 | double GetLoadAverage() { |
817 | struct sysinfo si; |
818 | if (sysinfo(&si) != 0) |
819 | return -0.0f; |
820 | return 1.0 / (1 << SI_LOAD_SHIFT) * si.loads[0]; |
821 | } |
822 | #elif defined(__HAIKU__) |
823 | double GetLoadAverage() { |
824 | return -0.0f; |
825 | } |
826 | #else |
827 | double GetLoadAverage() { |
828 | double loadavg[3] = { 0.0f, 0.0f, 0.0f }; |
829 | if (getloadavg(loadavg, 3) < 0) { |
830 | // Maybe we should return an error here or the availability of |
831 | // getloadavg(3) should be checked when ninja is configured. |
832 | return -0.0f; |
833 | } |
834 | return loadavg[0]; |
835 | } |
836 | #endif // _WIN32 |
837 | |
838 | string ElideMiddle(const string& str, size_t width) { |
839 | switch (width) { |
840 | case 0: return "" ; |
841 | case 1: return "." ; |
842 | case 2: return ".." ; |
843 | case 3: return "..." ; |
844 | } |
845 | const int kMargin = 3; // Space for "...". |
846 | string result = str; |
847 | if (result.size() > width) { |
848 | size_t elide_size = (width - kMargin) / 2; |
849 | result = result.substr(0, elide_size) |
850 | + "..." |
851 | + result.substr(result.size() - elide_size, elide_size); |
852 | } |
853 | return result; |
854 | } |
855 | |
856 | bool Truncate(const string& path, size_t size, string* err) { |
857 | #ifdef _WIN32 |
858 | int fh = _sopen(path.c_str(), _O_RDWR | _O_CREAT, _SH_DENYNO, |
859 | _S_IREAD | _S_IWRITE); |
860 | int success = _chsize(fh, size); |
861 | _close(fh); |
862 | #else |
863 | int success = truncate(path.c_str(), size); |
864 | #endif |
865 | // Both truncate() and _chsize() return 0 on success and set errno and return |
866 | // -1 on failure. |
867 | if (success < 0) { |
868 | *err = strerror(errno); |
869 | return false; |
870 | } |
871 | return true; |
872 | } |
873 | |