process_util_linux.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
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/threading/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// Get the total CPU of a single process.  Return value is number of jiffies
72// on success or -1 on error.
73int GetProcessCPU(pid_t pid) {
74  // Synchronously reading files in /proc is safe.
75  base::ThreadRestrictions::ScopedAllowIO allow_io;
76
77  // Use /proc/<pid>/task to find all threads and parse their /stat file.
78  FilePath path = FilePath(StringPrintf("/proc/%d/task/", pid));
79
80  DIR* dir = opendir(path.value().c_str());
81  if (!dir) {
82    PLOG(ERROR) << "opendir(" << path.value() << ")";
83    return -1;
84  }
85
86  int total_cpu = 0;
87  while (struct dirent* ent = readdir(dir)) {
88    if (ent->d_name[0] == '.')
89      continue;
90
91    FilePath stat_path = path.AppendASCII(ent->d_name).AppendASCII("stat");
92    std::string stat;
93    if (file_util::ReadFileToString(stat_path, &stat)) {
94      int cpu = base::ParseProcStatCPU(stat);
95      if (cpu > 0)
96        total_cpu += cpu;
97    }
98  }
99  closedir(dir);
100
101  return total_cpu;
102}
103
104}  // namespace
105
106namespace base {
107
108ProcessId GetParentProcessId(ProcessHandle process) {
109  // Synchronously reading files in /proc is safe.
110  base::ThreadRestrictions::ScopedAllowIO allow_io;
111
112  FilePath stat_file("/proc");
113  stat_file = stat_file.Append(base::IntToString(process));
114  stat_file = stat_file.Append("status");
115  std::string status;
116  if (!file_util::ReadFileToString(stat_file, &status))
117    return -1;
118
119  StringTokenizer tokenizer(status, ":\n");
120  ParsingState state = KEY_NAME;
121  std::string last_key_name;
122  while (tokenizer.GetNext()) {
123    switch (state) {
124      case KEY_NAME:
125        last_key_name = tokenizer.token();
126        state = KEY_VALUE;
127        break;
128      case KEY_VALUE:
129        DCHECK(!last_key_name.empty());
130        if (last_key_name == "PPid") {
131          int ppid;
132          base::StringToInt(tokenizer.token(), &ppid);
133          return ppid;
134        }
135        state = KEY_NAME;
136        break;
137    }
138  }
139  NOTREACHED();
140  return -1;
141}
142
143FilePath GetProcessExecutablePath(ProcessHandle process) {
144  FilePath stat_file("/proc");
145  stat_file = stat_file.Append(base::IntToString(process));
146  stat_file = stat_file.Append("exe");
147  FilePath exe_name;
148  if (!file_util::ReadSymbolicLink(stat_file, &exe_name)) {
149    // No such process.  Happens frequently in e.g. TerminateAllChromeProcesses
150    return FilePath();
151  }
152  return exe_name;
153}
154
155ProcessIterator::ProcessIterator(const ProcessFilter* filter)
156    : filter_(filter) {
157  procfs_dir_ = opendir("/proc");
158}
159
160ProcessIterator::~ProcessIterator() {
161  if (procfs_dir_) {
162    closedir(procfs_dir_);
163    procfs_dir_ = NULL;
164  }
165}
166
167bool ProcessIterator::CheckForNextProcess() {
168  // TODO(port): skip processes owned by different UID
169
170  dirent* slot = 0;
171  const char* openparen;
172  const char* closeparen;
173  std::vector<std::string> cmd_line_args;
174
175  // Arbitrarily guess that there will never be more than 200 non-process
176  // files in /proc.  Hardy has 53.
177  int skipped = 0;
178  const int kSkipLimit = 200;
179  while (skipped < kSkipLimit) {
180    slot = readdir(procfs_dir_);
181    // all done looking through /proc?
182    if (!slot)
183      return false;
184
185    // If not a process, keep looking for one.
186    bool notprocess = false;
187    int i;
188    for (i = 0; i < NAME_MAX && slot->d_name[i]; ++i) {
189       if (!isdigit(slot->d_name[i])) {
190         notprocess = true;
191         break;
192       }
193    }
194    if (i == NAME_MAX || notprocess) {
195      skipped++;
196      continue;
197    }
198
199    // Read the process's command line.
200    std::string pid_string(slot->d_name);
201    int pid;
202    if (StringToInt(pid_string, &pid) && !GetProcCmdline(pid, &cmd_line_args))
203      continue;
204
205    // Read the process's status.
206    char buf[NAME_MAX + 12];
207    sprintf(buf, "/proc/%s/stat", slot->d_name);
208    FILE *fp = fopen(buf, "r");
209    if (!fp)
210      continue;
211    const char* result = fgets(buf, sizeof(buf), fp);
212    fclose(fp);
213    if (!result)
214      continue;
215
216    // Parse the status.  It is formatted like this:
217    // %d (%s) %c %d %d ...
218    // pid (name) runstate ppid gid
219    // To avoid being fooled by names containing a closing paren, scan
220    // backwards.
221    openparen = strchr(buf, '(');
222    closeparen = strrchr(buf, ')');
223    if (!openparen || !closeparen)
224      continue;
225    char runstate = closeparen[2];
226
227    // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped?
228    // Allowed values: D R S T Z
229    if (runstate != 'Z')
230      break;
231
232    // Nope, it's a zombie; somebody isn't cleaning up after their children.
233    // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.)
234    // There could be a lot of zombies, can't really decrement i here.
235  }
236  if (skipped >= kSkipLimit) {
237    NOTREACHED();
238    return false;
239  }
240
241  // This seems fragile.
242  entry_.pid_ = atoi(slot->d_name);
243  entry_.ppid_ = atoi(closeparen + 3);
244  entry_.gid_ = atoi(strchr(closeparen + 4, ' '));
245
246  entry_.cmd_line_args_.assign(cmd_line_args.begin(), cmd_line_args.end());
247
248  // TODO(port): read pid's commandline's $0, like killall does.  Using the
249  // short name between openparen and closeparen won't work for long names!
250  int len = closeparen - openparen - 1;
251  entry_.exe_file_.assign(openparen + 1, len);
252  return true;
253}
254
255bool NamedProcessIterator::IncludeEntry() {
256  if (executable_name_ != entry().exe_file())
257    return false;
258  return ProcessIterator::IncludeEntry();
259}
260
261
262// static
263ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
264  return new ProcessMetrics(process);
265}
266
267// On linux, we return vsize.
268size_t ProcessMetrics::GetPagefileUsage() const {
269  std::vector<std::string> proc_stats;
270  if (!GetProcStats(process_, &proc_stats))
271    LOG(WARNING) << "Failed to get process stats.";
272  const size_t kVmSize = 22;
273  if (proc_stats.size() > kVmSize) {
274    int vm_size;
275    base::StringToInt(proc_stats[kVmSize], &vm_size);
276    return static_cast<size_t>(vm_size);
277  }
278  return 0;
279}
280
281// On linux, we return the high water mark of vsize.
282size_t ProcessMetrics::GetPeakPagefileUsage() const {
283  std::vector<std::string> proc_stats;
284  if (!GetProcStats(process_, &proc_stats))
285    LOG(WARNING) << "Failed to get process stats.";
286  const size_t kVmPeak = 21;
287  if (proc_stats.size() > kVmPeak) {
288    int vm_peak;
289    if (base::StringToInt(proc_stats[kVmPeak], &vm_peak))
290      return vm_peak;
291  }
292  return 0;
293}
294
295// On linux, we return RSS.
296size_t ProcessMetrics::GetWorkingSetSize() const {
297  std::vector<std::string> proc_stats;
298  if (!GetProcStats(process_, &proc_stats))
299    LOG(WARNING) << "Failed to get process stats.";
300  const size_t kVmRss = 23;
301  if (proc_stats.size() > kVmRss) {
302    int num_pages;
303    if (base::StringToInt(proc_stats[kVmRss], &num_pages))
304      return static_cast<size_t>(num_pages) * getpagesize();
305  }
306  return 0;
307}
308
309// On linux, we return the high water mark of RSS.
310size_t ProcessMetrics::GetPeakWorkingSetSize() const {
311  std::vector<std::string> proc_stats;
312  if (!GetProcStats(process_, &proc_stats))
313    LOG(WARNING) << "Failed to get process stats.";
314  const size_t kVmHwm = 23;
315  if (proc_stats.size() > kVmHwm) {
316    int num_pages;
317    base::StringToInt(proc_stats[kVmHwm], &num_pages);
318    return static_cast<size_t>(num_pages) * getpagesize();
319  }
320  return 0;
321}
322
323bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
324                                    size_t* shared_bytes) {
325  WorkingSetKBytes ws_usage;
326  if (!GetWorkingSetKBytes(&ws_usage))
327    return false;
328
329  if (private_bytes)
330    *private_bytes = ws_usage.priv << 10;
331
332  if (shared_bytes)
333    *shared_bytes = ws_usage.shared * 1024;
334
335  return true;
336}
337
338// Private and Shared working set sizes are obtained from /proc/<pid>/smaps.
339// When that's not available, use the values from /proc<pid>/statm as a
340// close approximation.
341// See http://www.pixelbeat.org/scripts/ps_mem.py
342bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
343  // Synchronously reading files in /proc is safe.
344  base::ThreadRestrictions::ScopedAllowIO allow_io;
345
346  FilePath proc_dir = FilePath("/proc").Append(base::IntToString(process_));
347  std::string smaps;
348  int private_kb = 0;
349  int pss_kb = 0;
350  bool have_pss = false;
351  bool ret;
352
353  {
354    FilePath smaps_file = proc_dir.Append("smaps");
355    // Synchronously reading files in /proc is safe.
356    base::ThreadRestrictions::ScopedAllowIO allow_io;
357    ret = file_util::ReadFileToString(smaps_file, &smaps);
358  }
359  if (ret && smaps.length() > 0) {
360    const std::string private_prefix = "Private_";
361    const std::string pss_prefix = "Pss";
362    StringTokenizer tokenizer(smaps, ":\n");
363    StringPiece last_key_name;
364    ParsingState state = KEY_NAME;
365    while (tokenizer.GetNext()) {
366      switch (state) {
367        case KEY_NAME:
368          last_key_name = tokenizer.token_piece();
369          state = KEY_VALUE;
370          break;
371        case KEY_VALUE:
372          if (last_key_name.empty()) {
373            NOTREACHED();
374            return false;
375          }
376          if (last_key_name.starts_with(private_prefix)) {
377            int cur;
378            base::StringToInt(tokenizer.token(), &cur);
379            private_kb += cur;
380          } else if (last_key_name.starts_with(pss_prefix)) {
381            have_pss = true;
382            int cur;
383            base::StringToInt(tokenizer.token(), &cur);
384            pss_kb += cur;
385          }
386          state = KEY_NAME;
387          break;
388      }
389    }
390  } else {
391    // Try statm if smaps is empty because of the SUID sandbox.
392    // First we need to get the page size though.
393    int page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024;
394    if (page_size_kb <= 0)
395      return false;
396
397    std::string statm;
398    {
399      FilePath statm_file = proc_dir.Append("statm");
400      // Synchronously reading files in /proc is safe.
401      base::ThreadRestrictions::ScopedAllowIO allow_io;
402      ret = file_util::ReadFileToString(statm_file, &statm);
403    }
404    if (!ret || statm.length() == 0)
405      return false;
406
407    std::vector<std::string> statm_vec;
408    base::SplitString(statm, ' ', &statm_vec);
409    if (statm_vec.size() != 7)
410      return false;  // Not the format we expect.
411
412    int statm1, statm2;
413    base::StringToInt(statm_vec[1], &statm1);
414    base::StringToInt(statm_vec[2], &statm2);
415    private_kb = (statm1 - statm2) * page_size_kb;
416  }
417  ws_usage->priv = private_kb;
418  // Sharable is not calculated, as it does not provide interesting data.
419  ws_usage->shareable = 0;
420
421  ws_usage->shared = 0;
422  if (have_pss)
423    ws_usage->shared = pss_kb;
424  return true;
425}
426
427double ProcessMetrics::GetCPUUsage() {
428  // This queries the /proc-specific scaling factor which is
429  // conceptually the system hertz.  To dump this value on another
430  // system, try
431  //   od -t dL /proc/self/auxv
432  // and look for the number after 17 in the output; mine is
433  //   0000040          17         100           3   134512692
434  // which means the answer is 100.
435  // It may be the case that this value is always 100.
436  static const int kHertz = sysconf(_SC_CLK_TCK);
437
438  struct timeval now;
439  int retval = gettimeofday(&now, NULL);
440  if (retval)
441    return 0;
442  int64 time = TimeValToMicroseconds(now);
443
444  if (last_time_ == 0) {
445    // First call, just set the last values.
446    last_time_ = time;
447    last_cpu_ = GetProcessCPU(process_);
448    return 0;
449  }
450
451  int64 time_delta = time - last_time_;
452  DCHECK_NE(time_delta, 0);
453  if (time_delta == 0)
454    return 0;
455
456  int cpu = GetProcessCPU(process_);
457
458  // We have the number of jiffies in the time period.  Convert to percentage.
459  // Note this means we will go *over* 100 in the case where multiple threads
460  // are together adding to more than one CPU's worth.
461  int percentage = 100 * (cpu - last_cpu_) /
462      (kHertz * TimeDelta::FromMicroseconds(time_delta).InSecondsF());
463
464  last_time_ = time;
465  last_cpu_ = cpu;
466
467  return percentage;
468}
469
470// To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING
471// in your kernel configuration.
472bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
473  // Synchronously reading files in /proc is safe.
474  base::ThreadRestrictions::ScopedAllowIO allow_io;
475
476  std::string proc_io_contents;
477  FilePath io_file("/proc");
478  io_file = io_file.Append(base::IntToString(process_));
479  io_file = io_file.Append("io");
480  if (!file_util::ReadFileToString(io_file, &proc_io_contents))
481    return false;
482
483  (*io_counters).OtherOperationCount = 0;
484  (*io_counters).OtherTransferCount = 0;
485
486  StringTokenizer tokenizer(proc_io_contents, ": \n");
487  ParsingState state = KEY_NAME;
488  std::string last_key_name;
489  while (tokenizer.GetNext()) {
490    switch (state) {
491      case KEY_NAME:
492        last_key_name = tokenizer.token();
493        state = KEY_VALUE;
494        break;
495      case KEY_VALUE:
496        DCHECK(!last_key_name.empty());
497        if (last_key_name == "syscr") {
498          base::StringToInt64(tokenizer.token(),
499              reinterpret_cast<int64*>(&(*io_counters).ReadOperationCount));
500        } else if (last_key_name == "syscw") {
501          base::StringToInt64(tokenizer.token(),
502              reinterpret_cast<int64*>(&(*io_counters).WriteOperationCount));
503        } else if (last_key_name == "rchar") {
504          base::StringToInt64(tokenizer.token(),
505              reinterpret_cast<int64*>(&(*io_counters).ReadTransferCount));
506        } else if (last_key_name == "wchar") {
507          base::StringToInt64(tokenizer.token(),
508              reinterpret_cast<int64*>(&(*io_counters).WriteTransferCount));
509        }
510        state = KEY_NAME;
511        break;
512    }
513  }
514  return true;
515}
516
517ProcessMetrics::ProcessMetrics(ProcessHandle process)
518    : process_(process),
519      last_time_(0),
520      last_system_time_(0),
521      last_cpu_(0) {
522  processor_count_ = base::SysInfo::NumberOfProcessors();
523}
524
525
526// Exposed for testing.
527int ParseProcStatCPU(const std::string& input) {
528  // /proc/<pid>/stat contains the process name in parens.  In case the
529  // process name itself contains parens, skip past them.
530  std::string::size_type rparen = input.rfind(')');
531  if (rparen == std::string::npos)
532    return -1;
533
534  // From here, we expect a bunch of space-separated fields, where the
535  // 0-indexed 11th and 12th are utime and stime.  On two different machines
536  // I found 42 and 39 fields, so let's just expect the ones we need.
537  std::vector<std::string> fields;
538  base::SplitString(input.substr(rparen + 2), ' ', &fields);
539  if (fields.size() < 13)
540    return -1;  // Output not in the format we expect.
541
542  int fields11, fields12;
543  base::StringToInt(fields[11], &fields11);
544  base::StringToInt(fields[12], &fields12);
545  return fields11 + fields12;
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