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