process_util_linux.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2009 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/process_util.h" 6 7#include <ctype.h> 8#include <dirent.h> 9#include <dlfcn.h> 10#include <errno.h> 11#include <fcntl.h> 12#include <sys/time.h> 13#include <sys/types.h> 14#include <sys/wait.h> 15#include <time.h> 16#include <unistd.h> 17 18#include "base/file_util.h" 19#include "base/logging.h" 20#include "base/string_number_conversions.h" 21#include "base/string_split.h" 22#include "base/string_tokenizer.h" 23#include "base/string_util.h" 24#include "base/sys_info.h" 25#include "base/thread_restrictions.h" 26 27namespace { 28 29enum ParsingState { 30 KEY_NAME, 31 KEY_VALUE 32}; 33 34// Reads /proc/<pid>/stat and populates |proc_stats| with the values split by 35// spaces. Returns true if successful. 36bool GetProcStats(pid_t pid, std::vector<std::string>* proc_stats) { 37 // Synchronously reading files in /proc is safe. 38 base::ThreadRestrictions::ScopedAllowIO allow_io; 39 40 FilePath stat_file("/proc"); 41 stat_file = stat_file.Append(base::IntToString(pid)); 42 stat_file = stat_file.Append("stat"); 43 std::string mem_stats; 44 if (!file_util::ReadFileToString(stat_file, &mem_stats)) 45 return false; 46 base::SplitString(mem_stats, ' ', proc_stats); 47 return true; 48} 49 50// Reads /proc/<pid>/cmdline and populates |proc_cmd_line_args| with the command 51// line arguments. Returns true if successful. 52// Note: /proc/<pid>/cmdline contains command line arguments separated by single 53// null characters. We tokenize it into a vector of strings using '\0' as a 54// delimiter. 55bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) { 56 // Synchronously reading files in /proc is safe. 57 base::ThreadRestrictions::ScopedAllowIO allow_io; 58 59 FilePath cmd_line_file("/proc"); 60 cmd_line_file = cmd_line_file.Append(base::IntToString(pid)); 61 cmd_line_file = cmd_line_file.Append("cmdline"); 62 std::string cmd_line; 63 if (!file_util::ReadFileToString(cmd_line_file, &cmd_line)) 64 return false; 65 std::string delimiters; 66 delimiters.push_back('\0'); 67 Tokenize(cmd_line, delimiters, proc_cmd_line_args); 68 return true; 69} 70 71} // namespace 72 73namespace base { 74 75ProcessId GetParentProcessId(ProcessHandle process) { 76 // Synchronously reading files in /proc is safe. 77 base::ThreadRestrictions::ScopedAllowIO allow_io; 78 79 FilePath stat_file("/proc"); 80 stat_file = stat_file.Append(base::IntToString(process)); 81 stat_file = stat_file.Append("status"); 82 std::string status; 83 if (!file_util::ReadFileToString(stat_file, &status)) 84 return -1; 85 86 StringTokenizer tokenizer(status, ":\n"); 87 ParsingState state = KEY_NAME; 88 std::string last_key_name; 89 while (tokenizer.GetNext()) { 90 switch (state) { 91 case KEY_NAME: 92 last_key_name = tokenizer.token(); 93 state = KEY_VALUE; 94 break; 95 case KEY_VALUE: 96 DCHECK(!last_key_name.empty()); 97 if (last_key_name == "PPid") { 98 int ppid; 99 base::StringToInt(tokenizer.token(), &ppid); 100 return ppid; 101 } 102 state = KEY_NAME; 103 break; 104 } 105 } 106 NOTREACHED(); 107 return -1; 108} 109 110FilePath GetProcessExecutablePath(ProcessHandle process) { 111 FilePath stat_file("/proc"); 112 stat_file = stat_file.Append(base::IntToString(process)); 113 stat_file = stat_file.Append("exe"); 114 char exename[2048]; 115 ssize_t len = readlink(stat_file.value().c_str(), exename, sizeof(exename)); 116 if (len < 1) { 117 // No such process. Happens frequently in e.g. TerminateAllChromeProcesses 118 return FilePath(); 119 } 120 return FilePath(std::string(exename, len)); 121} 122 123ProcessIterator::ProcessIterator(const ProcessFilter* filter) 124 : filter_(filter) { 125 procfs_dir_ = opendir("/proc"); 126} 127 128ProcessIterator::~ProcessIterator() { 129 if (procfs_dir_) { 130 closedir(procfs_dir_); 131 procfs_dir_ = NULL; 132 } 133} 134 135bool ProcessIterator::CheckForNextProcess() { 136 // TODO(port): skip processes owned by different UID 137 138 dirent* slot = 0; 139 const char* openparen; 140 const char* closeparen; 141 std::vector<std::string> cmd_line_args; 142 143 // Arbitrarily guess that there will never be more than 200 non-process 144 // files in /proc. Hardy has 53. 145 int skipped = 0; 146 const int kSkipLimit = 200; 147 while (skipped < kSkipLimit) { 148 slot = readdir(procfs_dir_); 149 // all done looking through /proc? 150 if (!slot) 151 return false; 152 153 // If not a process, keep looking for one. 154 bool notprocess = false; 155 int i; 156 for (i = 0; i < NAME_MAX && slot->d_name[i]; ++i) { 157 if (!isdigit(slot->d_name[i])) { 158 notprocess = true; 159 break; 160 } 161 } 162 if (i == NAME_MAX || notprocess) { 163 skipped++; 164 continue; 165 } 166 167 // Read the process's command line. 168 std::string pid_string(slot->d_name); 169 int pid; 170 if (StringToInt(pid_string, &pid) && !GetProcCmdline(pid, &cmd_line_args)) 171 return false; 172 173 // Read the process's status. 174 char buf[NAME_MAX + 12]; 175 sprintf(buf, "/proc/%s/stat", slot->d_name); 176 FILE *fp = fopen(buf, "r"); 177 if (!fp) 178 return false; 179 const char* result = fgets(buf, sizeof(buf), fp); 180 fclose(fp); 181 if (!result) 182 return false; 183 184 // Parse the status. It is formatted like this: 185 // %d (%s) %c %d %d ... 186 // pid (name) runstate ppid gid 187 // To avoid being fooled by names containing a closing paren, scan 188 // backwards. 189 openparen = strchr(buf, '('); 190 closeparen = strrchr(buf, ')'); 191 if (!openparen || !closeparen) 192 return false; 193 char runstate = closeparen[2]; 194 195 // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped? 196 // Allowed values: D R S T Z 197 if (runstate != 'Z') 198 break; 199 200 // Nope, it's a zombie; somebody isn't cleaning up after their children. 201 // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.) 202 // There could be a lot of zombies, can't really decrement i here. 203 } 204 if (skipped >= kSkipLimit) { 205 NOTREACHED(); 206 return false; 207 } 208 209 // This seems fragile. 210 entry_.pid_ = atoi(slot->d_name); 211 entry_.ppid_ = atoi(closeparen + 3); 212 entry_.gid_ = atoi(strchr(closeparen + 4, ' ')); 213 214 entry_.cmd_line_args_.assign(cmd_line_args.begin(), cmd_line_args.end()); 215 216 // TODO(port): read pid's commandline's $0, like killall does. Using the 217 // short name between openparen and closeparen won't work for long names! 218 int len = closeparen - openparen - 1; 219 entry_.exe_file_.assign(openparen + 1, len); 220 return true; 221} 222 223bool NamedProcessIterator::IncludeEntry() { 224 // TODO(port): make this also work for non-ASCII filenames 225 if (WideToASCII(executable_name_) != entry().exe_file()) 226 return false; 227 return ProcessIterator::IncludeEntry(); 228} 229 230 231ProcessMetrics::ProcessMetrics(ProcessHandle process) 232 : process_(process), 233 last_time_(0), 234 last_system_time_(0), 235 last_cpu_(0) { 236 processor_count_ = base::SysInfo::NumberOfProcessors(); 237} 238 239// static 240ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { 241 return new ProcessMetrics(process); 242} 243 244// On linux, we return vsize. 245size_t ProcessMetrics::GetPagefileUsage() const { 246 std::vector<std::string> proc_stats; 247 if (!GetProcStats(process_, &proc_stats)) 248 LOG(WARNING) << "Failed to get process stats."; 249 const size_t kVmSize = 22; 250 if (proc_stats.size() > kVmSize) { 251 int vm_size; 252 base::StringToInt(proc_stats[kVmSize], &vm_size); 253 return static_cast<size_t>(vm_size); 254 } 255 return 0; 256} 257 258// On linux, we return the high water mark of vsize. 259size_t ProcessMetrics::GetPeakPagefileUsage() const { 260 std::vector<std::string> proc_stats; 261 if (!GetProcStats(process_, &proc_stats)) 262 LOG(WARNING) << "Failed to get process stats."; 263 const size_t kVmPeak = 21; 264 if (proc_stats.size() > kVmPeak) { 265 int vm_peak; 266 if (base::StringToInt(proc_stats[kVmPeak], &vm_peak)) 267 return vm_peak; 268 } 269 return 0; 270} 271 272// On linux, we return RSS. 273size_t ProcessMetrics::GetWorkingSetSize() const { 274 std::vector<std::string> proc_stats; 275 if (!GetProcStats(process_, &proc_stats)) 276 LOG(WARNING) << "Failed to get process stats."; 277 const size_t kVmRss = 23; 278 if (proc_stats.size() > kVmRss) { 279 int num_pages; 280 if (base::StringToInt(proc_stats[kVmRss], &num_pages)) 281 return static_cast<size_t>(num_pages) * getpagesize(); 282 } 283 return 0; 284} 285 286// On linux, we return the high water mark of RSS. 287size_t ProcessMetrics::GetPeakWorkingSetSize() const { 288 std::vector<std::string> proc_stats; 289 if (!GetProcStats(process_, &proc_stats)) 290 LOG(WARNING) << "Failed to get process stats."; 291 const size_t kVmHwm = 23; 292 if (proc_stats.size() > kVmHwm) { 293 int num_pages; 294 base::StringToInt(proc_stats[kVmHwm], &num_pages); 295 return static_cast<size_t>(num_pages) * getpagesize(); 296 } 297 return 0; 298} 299 300bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, 301 size_t* shared_bytes) { 302 WorkingSetKBytes ws_usage; 303 if (!GetWorkingSetKBytes(&ws_usage)) 304 return false; 305 306 if (private_bytes) 307 *private_bytes = ws_usage.priv << 10; 308 309 if (shared_bytes) 310 *shared_bytes = ws_usage.shared * 1024; 311 312 return true; 313} 314 315// Private and Shared working set sizes are obtained from /proc/<pid>/smaps. 316// When that's not available, use the values from /proc<pid>/statm as a 317// close approximation. 318// See http://www.pixelbeat.org/scripts/ps_mem.py 319bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { 320 // Synchronously reading files in /proc is safe. 321 base::ThreadRestrictions::ScopedAllowIO allow_io; 322 323 FilePath stat_file = 324 FilePath("/proc").Append(base::IntToString(process_)).Append("smaps"); 325 std::string smaps; 326 int private_kb = 0; 327 int pss_kb = 0; 328 bool have_pss = false; 329 if (file_util::ReadFileToString(stat_file, &smaps) && smaps.length() > 0) { 330 const std::string private_prefix = "Private_"; 331 const std::string pss_prefix = "Pss"; 332 StringTokenizer tokenizer(smaps, ":\n"); 333 StringPiece last_key_name; 334 ParsingState state = KEY_NAME; 335 while (tokenizer.GetNext()) { 336 switch (state) { 337 case KEY_NAME: 338 last_key_name = tokenizer.token_piece(); 339 state = KEY_VALUE; 340 break; 341 case KEY_VALUE: 342 if (last_key_name.empty()) { 343 NOTREACHED(); 344 return false; 345 } 346 if (last_key_name.starts_with(private_prefix)) { 347 int cur; 348 base::StringToInt(tokenizer.token(), &cur); 349 private_kb += cur; 350 } else if (last_key_name.starts_with(pss_prefix)) { 351 have_pss = true; 352 int cur; 353 base::StringToInt(tokenizer.token(), &cur); 354 pss_kb += cur; 355 } 356 state = KEY_NAME; 357 break; 358 } 359 } 360 } else { 361 // Try statm if smaps is empty because of the SUID sandbox. 362 // First we need to get the page size though. 363 int page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; 364 if (page_size_kb <= 0) 365 return false; 366 367 stat_file = 368 FilePath("/proc").Append(base::IntToString(process_)).Append("statm"); 369 std::string statm; 370 if (!file_util::ReadFileToString(stat_file, &statm) || statm.length() == 0) 371 return false; 372 373 std::vector<std::string> statm_vec; 374 base::SplitString(statm, ' ', &statm_vec); 375 if (statm_vec.size() != 7) 376 return false; // Not the format we expect. 377 378 int statm1, statm2; 379 base::StringToInt(statm_vec[1], &statm1); 380 base::StringToInt(statm_vec[2], &statm2); 381 private_kb = (statm1 - statm2) * page_size_kb; 382 } 383 ws_usage->priv = private_kb; 384 // Sharable is not calculated, as it does not provide interesting data. 385 ws_usage->shareable = 0; 386 387 ws_usage->shared = 0; 388 if (have_pss) 389 ws_usage->shared = pss_kb; 390 return true; 391} 392 393// To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING 394// in your kernel configuration. 395bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { 396 // Synchronously reading files in /proc is safe. 397 base::ThreadRestrictions::ScopedAllowIO allow_io; 398 399 std::string proc_io_contents; 400 FilePath io_file("/proc"); 401 io_file = io_file.Append(base::IntToString(process_)); 402 io_file = io_file.Append("io"); 403 if (!file_util::ReadFileToString(io_file, &proc_io_contents)) 404 return false; 405 406 (*io_counters).OtherOperationCount = 0; 407 (*io_counters).OtherTransferCount = 0; 408 409 StringTokenizer tokenizer(proc_io_contents, ": \n"); 410 ParsingState state = KEY_NAME; 411 std::string last_key_name; 412 while (tokenizer.GetNext()) { 413 switch (state) { 414 case KEY_NAME: 415 last_key_name = tokenizer.token(); 416 state = KEY_VALUE; 417 break; 418 case KEY_VALUE: 419 DCHECK(!last_key_name.empty()); 420 if (last_key_name == "syscr") { 421 base::StringToInt64(tokenizer.token(), 422 reinterpret_cast<int64*>(&(*io_counters).ReadOperationCount)); 423 } else if (last_key_name == "syscw") { 424 base::StringToInt64(tokenizer.token(), 425 reinterpret_cast<int64*>(&(*io_counters).WriteOperationCount)); 426 } else if (last_key_name == "rchar") { 427 base::StringToInt64(tokenizer.token(), 428 reinterpret_cast<int64*>(&(*io_counters).ReadTransferCount)); 429 } else if (last_key_name == "wchar") { 430 base::StringToInt64(tokenizer.token(), 431 reinterpret_cast<int64*>(&(*io_counters).WriteTransferCount)); 432 } 433 state = KEY_NAME; 434 break; 435 } 436 } 437 return true; 438} 439 440 441// Exposed for testing. 442int ParseProcStatCPU(const std::string& input) { 443 // /proc/<pid>/stat contains the process name in parens. In case the 444 // process name itself contains parens, skip past them. 445 std::string::size_type rparen = input.rfind(')'); 446 if (rparen == std::string::npos) 447 return -1; 448 449 // From here, we expect a bunch of space-separated fields, where the 450 // 0-indexed 11th and 12th are utime and stime. On two different machines 451 // I found 42 and 39 fields, so let's just expect the ones we need. 452 std::vector<std::string> fields; 453 base::SplitString(input.substr(rparen + 2), ' ', &fields); 454 if (fields.size() < 13) 455 return -1; // Output not in the format we expect. 456 457 int fields11, fields12; 458 base::StringToInt(fields[11], &fields11); 459 base::StringToInt(fields[12], &fields12); 460 return fields11 + fields12; 461} 462 463// Get the total CPU of a single process. Return value is number of jiffies 464// on success or -1 on error. 465static int GetProcessCPU(pid_t pid) { 466 // Synchronously reading files in /proc is safe. 467 base::ThreadRestrictions::ScopedAllowIO allow_io; 468 469 // Use /proc/<pid>/task to find all threads and parse their /stat file. 470 FilePath path = FilePath(StringPrintf("/proc/%d/task/", pid)); 471 472 DIR* dir = opendir(path.value().c_str()); 473 if (!dir) { 474 PLOG(ERROR) << "opendir(" << path.value() << ")"; 475 return -1; 476 } 477 478 int total_cpu = 0; 479 while (struct dirent* ent = readdir(dir)) { 480 if (ent->d_name[0] == '.') 481 continue; 482 483 FilePath stat_path = path.AppendASCII(ent->d_name).AppendASCII("stat"); 484 std::string stat; 485 if (file_util::ReadFileToString(stat_path, &stat)) { 486 int cpu = ParseProcStatCPU(stat); 487 if (cpu > 0) 488 total_cpu += cpu; 489 } 490 } 491 closedir(dir); 492 493 return total_cpu; 494} 495 496double ProcessMetrics::GetCPUUsage() { 497 // This queries the /proc-specific scaling factor which is 498 // conceptually the system hertz. To dump this value on another 499 // system, try 500 // od -t dL /proc/self/auxv 501 // and look for the number after 17 in the output; mine is 502 // 0000040 17 100 3 134512692 503 // which means the answer is 100. 504 // It may be the case that this value is always 100. 505 static const int kHertz = sysconf(_SC_CLK_TCK); 506 507 struct timeval now; 508 int retval = gettimeofday(&now, NULL); 509 if (retval) 510 return 0; 511 int64 time = TimeValToMicroseconds(now); 512 513 if (last_time_ == 0) { 514 // First call, just set the last values. 515 last_time_ = time; 516 last_cpu_ = GetProcessCPU(process_); 517 return 0; 518 } 519 520 int64 time_delta = time - last_time_; 521 DCHECK_NE(time_delta, 0); 522 if (time_delta == 0) 523 return 0; 524 525 int cpu = GetProcessCPU(process_); 526 527 // We have the number of jiffies in the time period. Convert to percentage. 528 // Note this means we will go *over* 100 in the case where multiple threads 529 // are together adding to more than one CPU's worth. 530 int percentage = 100 * (cpu - last_cpu_) / 531 (kHertz * TimeDelta::FromMicroseconds(time_delta).InSecondsF()); 532 533 last_time_ = time; 534 last_cpu_ = cpu; 535 536 return percentage; 537} 538 539namespace { 540 541// The format of /proc/meminfo is: 542// 543// MemTotal: 8235324 kB 544// MemFree: 1628304 kB 545// Buffers: 429596 kB 546// Cached: 4728232 kB 547// ... 548const size_t kMemTotalIndex = 1; 549const size_t kMemFreeIndex = 4; 550const size_t kMemBuffersIndex = 7; 551const size_t kMemCacheIndex = 10; 552 553} // namespace 554 555size_t GetSystemCommitCharge() { 556 // Synchronously reading files in /proc is safe. 557 base::ThreadRestrictions::ScopedAllowIO allow_io; 558 559 // Used memory is: total - free - buffers - caches 560 FilePath meminfo_file("/proc/meminfo"); 561 std::string meminfo_data; 562 if (!file_util::ReadFileToString(meminfo_file, &meminfo_data)) { 563 LOG(WARNING) << "Failed to open /proc/meminfo."; 564 return 0; 565 } 566 std::vector<std::string> meminfo_fields; 567 SplitStringAlongWhitespace(meminfo_data, &meminfo_fields); 568 569 if (meminfo_fields.size() < kMemCacheIndex) { 570 LOG(WARNING) << "Failed to parse /proc/meminfo. Only found " << 571 meminfo_fields.size() << " fields."; 572 return 0; 573 } 574 575 DCHECK_EQ(meminfo_fields[kMemTotalIndex-1], "MemTotal:"); 576 DCHECK_EQ(meminfo_fields[kMemFreeIndex-1], "MemFree:"); 577 DCHECK_EQ(meminfo_fields[kMemBuffersIndex-1], "Buffers:"); 578 DCHECK_EQ(meminfo_fields[kMemCacheIndex-1], "Cached:"); 579 580 int mem_total, mem_free, mem_buffers, mem_cache; 581 base::StringToInt(meminfo_fields[kMemTotalIndex], &mem_total); 582 base::StringToInt(meminfo_fields[kMemFreeIndex], &mem_free); 583 base::StringToInt(meminfo_fields[kMemBuffersIndex], &mem_buffers); 584 base::StringToInt(meminfo_fields[kMemCacheIndex], &mem_cache); 585 586 return mem_total - mem_free - mem_buffers - mem_cache; 587} 588 589namespace { 590 591void OnNoMemorySize(size_t size) { 592 if (size != 0) 593 LOG(FATAL) << "Out of memory, size = " << size; 594 LOG(FATAL) << "Out of memory."; 595} 596 597void OnNoMemory() { 598 OnNoMemorySize(0); 599} 600 601} // namespace 602 603extern "C" { 604#if !defined(USE_TCMALLOC) 605 606extern "C" { 607void* __libc_malloc(size_t size); 608void* __libc_realloc(void* ptr, size_t size); 609void* __libc_calloc(size_t nmemb, size_t size); 610void* __libc_valloc(size_t size); 611void* __libc_pvalloc(size_t size); 612void* __libc_memalign(size_t alignment, size_t size); 613} // extern "C" 614 615// Overriding the system memory allocation functions: 616// 617// For security reasons, we want malloc failures to be fatal. Too much code 618// doesn't check for a NULL return value from malloc and unconditionally uses 619// the resulting pointer. If the first offset that they try to access is 620// attacker controlled, then the attacker can direct the code to access any 621// part of memory. 622// 623// Thus, we define all the standard malloc functions here and mark them as 624// visibility 'default'. This means that they replace the malloc functions for 625// all Chromium code and also for all code in shared libraries. There are tests 626// for this in process_util_unittest.cc. 627// 628// If we are using tcmalloc, then the problem is moot since tcmalloc handles 629// this for us. Thus this code is in a !defined(USE_TCMALLOC) block. 630// 631// We call the real libc functions in this code by using __libc_malloc etc. 632// Previously we tried using dlsym(RTLD_NEXT, ...) but that failed depending on 633// the link order. Since ld.so needs calloc during symbol resolution, it 634// defines its own versions of several of these functions in dl-minimal.c. 635// Depending on the runtime library order, dlsym ended up giving us those 636// functions and bad things happened. See crbug.com/31809 637// 638// This means that any code which calls __libc_* gets the raw libc versions of 639// these functions. 640 641#define DIE_ON_OOM_1(function_name) \ 642 void* function_name(size_t) __attribute__ ((visibility("default"))); \ 643 \ 644 void* function_name(size_t size) { \ 645 void* ret = __libc_##function_name(size); \ 646 if (ret == NULL && size != 0) \ 647 OnNoMemorySize(size); \ 648 return ret; \ 649 } 650 651#define DIE_ON_OOM_2(function_name, arg1_type) \ 652 void* function_name(arg1_type, size_t) \ 653 __attribute__ ((visibility("default"))); \ 654 \ 655 void* function_name(arg1_type arg1, size_t size) { \ 656 void* ret = __libc_##function_name(arg1, size); \ 657 if (ret == NULL && size != 0) \ 658 OnNoMemorySize(size); \ 659 return ret; \ 660 } 661 662DIE_ON_OOM_1(malloc) 663DIE_ON_OOM_1(valloc) 664DIE_ON_OOM_1(pvalloc) 665 666DIE_ON_OOM_2(calloc, size_t) 667DIE_ON_OOM_2(realloc, void*) 668DIE_ON_OOM_2(memalign, size_t) 669 670// posix_memalign has a unique signature and doesn't have a __libc_ variant. 671int posix_memalign(void** ptr, size_t alignment, size_t size) 672 __attribute__ ((visibility("default"))); 673 674int posix_memalign(void** ptr, size_t alignment, size_t size) { 675 // This will use the safe version of memalign, above. 676 *ptr = memalign(alignment, size); 677 return 0; 678} 679 680#endif // !defined(USE_TCMALLOC) 681} // extern C 682 683void EnableTerminationOnOutOfMemory() { 684 // Set the new-out of memory handler. 685 std::set_new_handler(&OnNoMemory); 686 // If we're using glibc's allocator, the above functions will override 687 // malloc and friends and make them die on out of memory. 688} 689 690bool AdjustOOMScore(ProcessId process, int score) { 691 if (score < 0 || score > 15) 692 return false; 693 694 FilePath oom_adj("/proc"); 695 oom_adj = oom_adj.Append(base::Int64ToString(process)); 696 oom_adj = oom_adj.AppendASCII("oom_adj"); 697 698 if (!file_util::PathExists(oom_adj)) 699 return false; 700 701 std::string score_str = base::IntToString(score); 702 return (static_cast<int>(score_str.length()) == 703 file_util::WriteFile(oom_adj, score_str.c_str(), score_str.length())); 704} 705 706} // namespace base 707