Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,128 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/environment_internal.h"
#include <stddef.h>
#include <vector>
namespace base {
namespace internal {
namespace {
#if defined(OS_POSIX) || defined(OS_FUCHSIA) || defined(OS_WIN)
// Parses a null-terminated input string of an environment block. The key is
// placed into the given string, and the total length of the line, including
// the terminating null, is returned.
size_t ParseEnvLine(const NativeEnvironmentString::value_type* input,
NativeEnvironmentString* key) {
// Skip to the equals or end of the string, this is the key.
size_t cur = 0;
while (input[cur] && input[cur] != '=')
cur++;
*key = NativeEnvironmentString(&input[0], cur);
// Now just skip to the end of the string.
while (input[cur])
cur++;
return cur + 1;
}
#endif
} // namespace
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
std::unique_ptr<char* []> AlterEnvironment(const char* const* const env,
const EnvironmentMap& changes) {
std::string value_storage; // Holds concatenated null-terminated strings.
std::vector<size_t> result_indices; // Line indices into value_storage.
// First build up all of the unchanged environment strings. These are
// null-terminated of the form "key=value".
std::string key;
for (size_t i = 0; env[i]; i++) {
size_t line_length = ParseEnvLine(env[i], &key);
// Keep only values not specified in the change vector.
auto found_change = changes.find(key);
if (found_change == changes.end()) {
result_indices.push_back(value_storage.size());
value_storage.append(env[i], line_length);
}
}
// Now append all modified and new values.
for (const auto& i : changes) {
if (!i.second.empty()) {
result_indices.push_back(value_storage.size());
value_storage.append(i.first);
value_storage.push_back('=');
value_storage.append(i.second);
value_storage.push_back(0);
}
}
size_t pointer_count_required =
result_indices.size() + 1 + // Null-terminated array of pointers.
(value_storage.size() + sizeof(char*) - 1) / sizeof(char*); // Buffer.
std::unique_ptr<char*[]> result(new char*[pointer_count_required]);
// The string storage goes after the array of pointers.
char* storage_data =
reinterpret_cast<char*>(&result.get()[result_indices.size() + 1]);
if (!value_storage.empty())
memcpy(storage_data, value_storage.data(), value_storage.size());
// Fill array of pointers at the beginning of the result.
for (size_t i = 0; i < result_indices.size(); i++)
result[i] = &storage_data[result_indices[i]];
result[result_indices.size()] = 0; // Null terminator.
return result;
}
#elif defined(OS_WIN)
NativeEnvironmentString AlterEnvironment(const wchar_t* env,
const EnvironmentMap& changes) {
NativeEnvironmentString result;
// First build up all of the unchanged environment strings.
const wchar_t* ptr = env;
while (*ptr) {
std::wstring key;
size_t line_length = ParseEnvLine(ptr, &key);
// Keep only values not specified in the change vector.
if (changes.find(key) == changes.end()) {
result.append(ptr, line_length);
}
ptr += line_length;
}
// Now append all modified and new values.
for (const auto& i : changes) {
// Windows environment blocks cannot handle keys or values with NULs.
CHECK_EQ(std::wstring::npos, i.first.find(L'\0'));
CHECK_EQ(std::wstring::npos, i.second.find(L'\0'));
if (!i.second.empty()) {
result += i.first;
result.push_back('=');
result += i.second;
result.push_back('\0');
}
}
// Add the terminating NUL.
result.push_back('\0');
return result;
}
#endif // OS_POSIX || OS_FUCHSIA
} // namespace internal
} // namespace base

View file

@ -0,0 +1,52 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file contains internal routines that are called by other files in
// base/process/.
#ifndef BASE_PROCESS_ENVIRONMENT_INTERNAL_H_
#define BASE_PROCESS_ENVIRONMENT_INTERNAL_H_
#include <memory>
#include "base/environment.h"
#include "build/build_config.h"
namespace base {
namespace internal {
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
// Returns a modified environment vector constructed from the given environment
// and the list of changes given in |changes|. Each key in the environment is
// matched against the first element of the pairs. In the event of a match, the
// value is replaced by the second of the pair, unless the second is empty, in
// which case the key-value is removed.
//
// This POSIX version takes and returns a POSIX-style environment block, which
// is a null-terminated list of pointers to null-terminated strings. The
// returned array will have appended to it the storage for the array itself so
// there is only one pointer to manage, but this means that you can't copy the
// array without keeping the original around.
BASE_EXPORT std::unique_ptr<char*[]> AlterEnvironment(
const char* const* env,
const EnvironmentMap& changes);
#elif defined(OS_WIN)
// Returns a modified environment vector constructed from the given environment
// and the list of changes given in |changes|. Each key in the environment is
// matched against the first element of the pairs. In the event of a match, the
// value is replaced by the second of the pair, unless the second is empty, in
// which case the key-value is removed.
//
// This Windows version takes and returns a Windows-style environment block,
// which is a string containing several null-terminated strings followed by an
// extra terminating null character. So, e.g., the environment A=1 B=2 is
// represented as L"A=1\0B=2\0\0".
BASE_EXPORT NativeEnvironmentString
AlterEnvironment(const wchar_t* env, const EnvironmentMap& changes);
#endif // OS_*
} // namespace internal
} // namespace base
#endif // BASE_PROCESS_ENVIRONMENT_INTERNAL_H_

View file

@ -0,0 +1,153 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/internal_aix.h"
#include <sys/procfs.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <map>
#include <string>
#include <vector>
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
// Not defined on AIX by default.
#define NAME_MAX 255
namespace base {
namespace internalAIX {
const char kProcDir[] = "/proc";
const char kStatFile[] = "psinfo"; // AIX specific
FilePath GetProcPidDir(pid_t pid) {
return FilePath(kProcDir).Append(NumberToString(pid));
}
pid_t ProcDirSlotToPid(const char* d_name) {
int i;
for (i = 0; i < NAME_MAX && d_name[i]; ++i) {
if (!IsAsciiDigit(d_name[i])) {
return 0;
}
}
if (i == NAME_MAX)
return 0;
// Read the process's command line.
pid_t pid;
std::string pid_string(d_name);
if (!StringToInt(pid_string, &pid)) {
NOTREACHED();
return 0;
}
return pid;
}
bool ReadProcFile(const FilePath& file, struct psinfo* info) {
// Synchronously reading files in /proc is safe.
ThreadRestrictions::ScopedAllowIO allow_io;
int fileId;
if ((fileId = open(file.value().c_str(), O_RDONLY)) < 0) {
DPLOG(WARNING) << "Failed to open " << file.MaybeAsASCII();
return false;
}
if (read(fileId, info, sizeof(*info)) < 0) {
DPLOG(WARNING) << "Failed to read " << file.MaybeAsASCII();
return false;
}
return true;
}
bool ReadProcStats(pid_t pid, struct psinfo* info) {
FilePath stat_file = internalAIX::GetProcPidDir(pid).Append(kStatFile);
return ReadProcFile(stat_file, info);
}
bool ParseProcStats(struct psinfo& stats_data,
std::vector<std::string>* proc_stats) {
// The stat file is formatted as:
// struct psinfo
// see -
// https://www.ibm.com/support/knowledgecenter/ssw_aix_71/com.ibm.aix.files/proc.htm
proc_stats->clear();
// PID.
proc_stats->push_back(NumberToString(stats_data.pr_pid));
// Process name without parentheses. // 1
proc_stats->push_back(stats_data.pr_fname);
// Process State (Not available) // 2
proc_stats->push_back("0");
// Process id of parent // 3
proc_stats->push_back(NumberToString(stats_data.pr_ppid));
// Process group id // 4
proc_stats->push_back(NumberToString(stats_data.pr_pgid));
return true;
}
typedef std::map<std::string, std::string> ProcStatMap;
void ParseProcStat(const std::string& contents, ProcStatMap* output) {
StringPairs key_value_pairs;
SplitStringIntoKeyValuePairs(contents, ' ', '\n', &key_value_pairs);
for (size_t i = 0; i < key_value_pairs.size(); ++i) {
output->insert(key_value_pairs[i]);
}
}
int64_t GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats,
ProcStatsFields field_num) {
DCHECK_GE(field_num, VM_PPID);
CHECK_LT(static_cast<size_t>(field_num), proc_stats.size());
int64_t value;
return StringToInt64(proc_stats[field_num], &value) ? value : 0;
}
size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
ProcStatsFields field_num) {
DCHECK_GE(field_num, VM_PPID);
CHECK_LT(static_cast<size_t>(field_num), proc_stats.size());
size_t value;
return StringToSizeT(proc_stats[field_num], &value) ? value : 0;
}
int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num) {
struct psinfo stats_data;
if (!ReadProcStats(pid, &stats_data))
return 0;
std::vector<std::string> proc_stats;
if (!ParseProcStats(stats_data, &proc_stats))
return 0;
return GetProcStatsFieldAsInt64(proc_stats, field_num);
}
size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid, ProcStatsFields field_num) {
struct psinfo stats_data;
if (!ReadProcStats(pid, &stats_data))
return 0;
std::vector<std::string> proc_stats;
if (!ParseProcStats(stats_data, &proc_stats))
return 0;
return GetProcStatsFieldAsSizeT(proc_stats, field_num);
}
} // namespace internalAIX
} // namespace base

View file

@ -0,0 +1,84 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file contains internal routines that are called by other files in
// base/process/.
#ifndef BASE_PROCESS_INTERNAL_AIX_H_
#define BASE_PROCESS_INTERNAL_AIX_H_
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#include "base/files/file_path.h"
namespace base {
namespace internalAIX {
// "/proc"
extern const char kProcDir[];
// "psinfo"
extern const char kStatFile[];
// Returns a FilePath to "/proc/pid".
base::FilePath GetProcPidDir(pid_t pid);
// Take a /proc directory entry named |d_name|, and if it is the directory for
// a process, convert it to a pid_t.
// Returns 0 on failure.
// e.g. /proc/self/ will return 0, whereas /proc/1234 will return 1234.
pid_t ProcDirSlotToPid(const char* d_name);
// Reads /proc/<pid>/stat into |buffer|. Returns true if the file can be read
// and is non-empty.
bool ReadProcStats(pid_t pid, std::string* buffer);
// Takes |stats_data| and populates |proc_stats| with the values split by
// spaces. Taking into account the 2nd field may, in itself, contain spaces.
// Returns true if successful.
bool ParseProcStats(const std::string& stats_data,
std::vector<std::string>* proc_stats);
// Fields from /proc/<pid>/psinfo.
// If the ordering ever changes, carefully review functions that use these
// values.
// For AIX this is the bare minimum that we need. Most of the commented out
// fields can still be extracted but currently none of these are required.
enum ProcStatsFields {
VM_COMM = 1, // Filename of executable, without parentheses.
// VM_STATE = 2, // Letter indicating the state of the process.
VM_PPID = 3, // PID of the parent.
VM_PGRP = 4, // Process group id.
// VM_UTIME = 13, // Time scheduled in user mode in clock ticks.
// VM_STIME = 14, // Time scheduled in kernel mode in clock ticks.
// VM_NUMTHREADS = 19, // Number of threads.
// VM_STARTTIME = 21, // The time the process started in clock ticks.
// VM_VSIZE = 22, // Virtual memory size in bytes.
// VM_RSS = 23, // Resident Set Size in pages.
};
// Reads the |field_num|th field from |proc_stats|. Returns 0 on failure.
// This version does not handle the first 3 values, since the first value is
// simply |pid|, and the next two values are strings.
int64_t GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats,
ProcStatsFields field_num);
// Same as GetProcStatsFieldAsInt64(), but for size_t values.
size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
ProcStatsFields field_num);
// Convenience wrapper around GetProcStatsFieldAsInt64(), ParseProcStats() and
// ReadProcStats(). See GetProcStatsFieldAsInt64() for details.
int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num);
// Same as ReadProcStatsAndGetFieldAsInt64() but for size_t values.
size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid, ProcStatsFields field_num);
} // namespace internal
} // namespace base
#endif // BASE_PROCESS_INTERNAL_AIX_H_

View file

@ -0,0 +1,231 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/internal_linux.h"
#include <limits.h>
#include <unistd.h>
#include <map>
#include <string>
#include <vector>
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
// Not defined on AIX by default.
#if defined(OS_AIX)
#define NAME_MAX 255
#endif
namespace base {
namespace internal {
const char kProcDir[] = "/proc";
const char kStatFile[] = "stat";
FilePath GetProcPidDir(pid_t pid) {
return FilePath(kProcDir).Append(NumberToString(pid));
}
pid_t ProcDirSlotToPid(const char* d_name) {
int i;
for (i = 0; i < NAME_MAX && d_name[i]; ++i) {
if (!IsAsciiDigit(d_name[i])) {
return 0;
}
}
if (i == NAME_MAX)
return 0;
// Read the process's command line.
pid_t pid;
std::string pid_string(d_name);
if (!StringToInt(pid_string, &pid)) {
NOTREACHED();
return 0;
}
return pid;
}
bool ReadProcFile(const FilePath& file, std::string* buffer) {
buffer->clear();
// Synchronously reading files in /proc is safe.
ThreadRestrictions::ScopedAllowIO allow_io;
if (!ReadFileToString(file, buffer)) {
DLOG(WARNING) << "Failed to read " << file.MaybeAsASCII();
return false;
}
return !buffer->empty();
}
bool ReadProcStats(pid_t pid, std::string* buffer) {
FilePath stat_file = internal::GetProcPidDir(pid).Append(kStatFile);
return ReadProcFile(stat_file, buffer);
}
bool ParseProcStats(const std::string& stats_data,
std::vector<std::string>* proc_stats) {
// |stats_data| may be empty if the process disappeared somehow.
// e.g. http://crbug.com/145811
if (stats_data.empty())
return false;
// The stat file is formatted as:
// pid (process name) data1 data2 .... dataN
// Look for the closing paren by scanning backwards, to avoid being fooled by
// processes with ')' in the name.
size_t open_parens_idx = stats_data.find(" (");
size_t close_parens_idx = stats_data.rfind(") ");
if (open_parens_idx == std::string::npos ||
close_parens_idx == std::string::npos ||
open_parens_idx > close_parens_idx) {
DLOG(WARNING) << "Failed to find matched parens in '" << stats_data << "'";
NOTREACHED();
return false;
}
open_parens_idx++;
proc_stats->clear();
// PID.
proc_stats->push_back(stats_data.substr(0, open_parens_idx));
// Process name without parentheses.
proc_stats->push_back(
stats_data.substr(open_parens_idx + 1,
close_parens_idx - (open_parens_idx + 1)));
// Split the rest.
std::vector<std::string> other_stats = SplitString(
stats_data.substr(close_parens_idx + 2), " ",
base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
for (const auto& i : other_stats)
proc_stats->push_back(i);
return true;
}
typedef std::map<std::string, std::string> ProcStatMap;
void ParseProcStat(const std::string& contents, ProcStatMap* output) {
StringPairs key_value_pairs;
SplitStringIntoKeyValuePairs(contents, ' ', '\n', &key_value_pairs);
for (auto& i : key_value_pairs) {
output->insert(std::move(i));
}
}
int64_t GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats,
ProcStatsFields field_num) {
DCHECK_GE(field_num, VM_PPID);
CHECK_LT(static_cast<size_t>(field_num), proc_stats.size());
int64_t value;
return StringToInt64(proc_stats[field_num], &value) ? value : 0;
}
size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
ProcStatsFields field_num) {
DCHECK_GE(field_num, VM_PPID);
CHECK_LT(static_cast<size_t>(field_num), proc_stats.size());
size_t value;
return StringToSizeT(proc_stats[field_num], &value) ? value : 0;
}
int64_t ReadStatFileAndGetFieldAsInt64(const FilePath& stat_file,
ProcStatsFields field_num) {
std::string stats_data;
if (!ReadProcFile(stat_file, &stats_data))
return 0;
std::vector<std::string> proc_stats;
if (!ParseProcStats(stats_data, &proc_stats))
return 0;
return GetProcStatsFieldAsInt64(proc_stats, field_num);
}
int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num) {
FilePath stat_file = internal::GetProcPidDir(pid).Append(kStatFile);
return ReadStatFileAndGetFieldAsInt64(stat_file, field_num);
}
int64_t ReadProcSelfStatsAndGetFieldAsInt64(ProcStatsFields field_num) {
FilePath stat_file = FilePath(kProcDir).Append("self").Append(kStatFile);
return ReadStatFileAndGetFieldAsInt64(stat_file, field_num);
}
size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid,
ProcStatsFields field_num) {
std::string stats_data;
if (!ReadProcStats(pid, &stats_data))
return 0;
std::vector<std::string> proc_stats;
if (!ParseProcStats(stats_data, &proc_stats))
return 0;
return GetProcStatsFieldAsSizeT(proc_stats, field_num);
}
Time GetBootTime() {
FilePath path("/proc/stat");
std::string contents;
if (!ReadProcFile(path, &contents))
return Time();
ProcStatMap proc_stat;
ParseProcStat(contents, &proc_stat);
ProcStatMap::const_iterator btime_it = proc_stat.find("btime");
if (btime_it == proc_stat.end())
return Time();
int btime;
if (!StringToInt(btime_it->second, &btime))
return Time();
return Time::FromTimeT(btime);
}
TimeDelta GetUserCpuTimeSinceBoot() {
FilePath path("/proc/stat");
std::string contents;
if (!ReadProcFile(path, &contents))
return TimeDelta();
ProcStatMap proc_stat;
ParseProcStat(contents, &proc_stat);
ProcStatMap::const_iterator cpu_it = proc_stat.find("cpu");
if (cpu_it == proc_stat.end())
return TimeDelta();
std::vector<std::string> cpu = SplitString(
cpu_it->second, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
if (cpu.size() < 2 || cpu[0] != "cpu")
return TimeDelta();
uint64_t user;
uint64_t nice;
if (!StringToUint64(cpu[0], &user) || !StringToUint64(cpu[1], &nice))
return TimeDelta();
return ClockTicksToTimeDelta(user + nice);
}
TimeDelta ClockTicksToTimeDelta(int clock_ticks) {
// This queries the /proc-specific scaling factor which is
// conceptually the system hertz. To dump this value on another
// system, try
// od -t dL /proc/self/auxv
// and look for the number after 17 in the output; mine is
// 0000040 17 100 3 134512692
// which means the answer is 100.
// It may be the case that this value is always 100.
static const int kHertz = sysconf(_SC_CLK_TCK);
return TimeDelta::FromMicroseconds(
Time::kMicrosecondsPerSecond * clock_ticks / kHertz);
}
} // namespace internal
} // namespace base

View file

@ -0,0 +1,100 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file contains internal routines that are called by other files in
// base/process/.
#ifndef BASE_PROCESS_INTERNAL_LINUX_H_
#define BASE_PROCESS_INTERNAL_LINUX_H_
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#include "base/files/file_path.h"
namespace base {
class Time;
class TimeDelta;
namespace internal {
// "/proc"
extern const char kProcDir[];
// "stat"
extern const char kStatFile[];
// Returns a FilePath to "/proc/pid".
base::FilePath GetProcPidDir(pid_t pid);
// Take a /proc directory entry named |d_name|, and if it is the directory for
// a process, convert it to a pid_t.
// Returns 0 on failure.
// e.g. /proc/self/ will return 0, whereas /proc/1234 will return 1234.
pid_t ProcDirSlotToPid(const char* d_name);
// Reads /proc/<pid>/stat into |buffer|. Returns true if the file can be read
// and is non-empty.
bool ReadProcStats(pid_t pid, std::string* buffer);
// Takes |stats_data| and populates |proc_stats| with the values split by
// spaces. Taking into account the 2nd field may, in itself, contain spaces.
// Returns true if successful.
bool ParseProcStats(const std::string& stats_data,
std::vector<std::string>* proc_stats);
// Fields from /proc/<pid>/stat, 0-based. See man 5 proc.
// If the ordering ever changes, carefully review functions that use these
// values.
enum ProcStatsFields {
VM_COMM = 1, // Filename of executable, without parentheses.
VM_STATE = 2, // Letter indicating the state of the process.
VM_PPID = 3, // PID of the parent.
VM_PGRP = 4, // Process group id.
VM_MINFLT = 9, // Minor page fault count excluding children.
VM_MAJFLT = 11, // Major page fault count excluding children.
VM_UTIME = 13, // Time scheduled in user mode in clock ticks.
VM_STIME = 14, // Time scheduled in kernel mode in clock ticks.
VM_NUMTHREADS = 19, // Number of threads.
VM_STARTTIME = 21, // The time the process started in clock ticks.
VM_VSIZE = 22, // Virtual memory size in bytes.
VM_RSS = 23, // Resident Set Size in pages.
};
// Reads the |field_num|th field from |proc_stats|. Returns 0 on failure.
// This version does not handle the first 3 values, since the first value is
// simply |pid|, and the next two values are strings.
int64_t GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats,
ProcStatsFields field_num);
// Same as GetProcStatsFieldAsInt64(), but for size_t values.
size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
ProcStatsFields field_num);
// Convenience wrappers around GetProcStatsFieldAsInt64(), ParseProcStats() and
// ReadProcStats(). See GetProcStatsFieldAsInt64() for details.
int64_t ReadStatsFilendGetFieldAsInt64(const FilePath& stat_file,
ProcStatsFields field_num);
int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num);
int64_t ReadProcSelfStatsAndGetFieldAsInt64(ProcStatsFields field_num);
// Same as ReadProcStatsAndGetFieldAsInt64() but for size_t values.
size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid,
ProcStatsFields field_num);
// Returns the time that the OS started. Clock ticks are relative to this.
Time GetBootTime();
// Returns the amount of time spent in user space since boot across all CPUs.
TimeDelta GetUserCpuTimeSinceBoot();
// Converts Linux clock ticks to a wall time delta.
TimeDelta ClockTicksToTimeDelta(int clock_ticks);
} // namespace internal
} // namespace base
#endif // BASE_PROCESS_INTERNAL_LINUX_H_

View file

@ -0,0 +1,62 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/kill.h"
#include "base/bind.h"
#include "base/process/process_iterator.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
namespace base {
bool KillProcesses(const FilePath::StringType& executable_name,
int exit_code,
const ProcessFilter* filter) {
bool result = true;
NamedProcessIterator iter(executable_name, filter);
while (const ProcessEntry* entry = iter.NextProcessEntry()) {
Process process = Process::Open(entry->pid());
// Sometimes process open fails. This would cause a DCHECK in
// process.Terminate(). Maybe the process has killed itself between the
// time the process list was enumerated and the time we try to open the
// process?
if (!process.IsValid()) {
result = false;
continue;
}
result &= process.Terminate(exit_code, true);
}
return result;
}
#if defined(OS_WIN) || defined(OS_FUCHSIA)
// Common implementation for platforms under which |process| is a handle to
// the process, rather than an identifier that must be "reaped".
void EnsureProcessTerminated(Process process) {
DCHECK(!process.is_current());
if (process.WaitForExitWithTimeout(TimeDelta(), nullptr))
return;
ThreadPool::PostDelayedTask(
FROM_HERE,
{TaskPriority::BEST_EFFORT, TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
BindOnce(
[](Process process) {
if (process.WaitForExitWithTimeout(TimeDelta(), nullptr))
return;
#if defined(OS_WIN)
process.Terminate(win::kProcessKilledExitCode, false);
#else
process.Terminate(-1, false);
#endif
},
std::move(process)),
TimeDelta::FromSeconds(2));
}
#endif // defined(OS_WIN) || defined(OS_FUCHSIA)
} // namespace base

View file

@ -0,0 +1,162 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file contains routines to kill processes and get the exit code and
// termination status.
#ifndef BASE_PROCESS_KILL_H_
#define BASE_PROCESS_KILL_H_
#include "base/files/file_path.h"
#include "base/process/process.h"
#include "base/process/process_handle.h"
#include "base/time/time.h"
#include "build/build_config.h"
namespace base {
class ProcessFilter;
#if defined(OS_WIN)
namespace win {
// See definition in sandbox/win/src/sandbox_types.h
const DWORD kSandboxFatalMemoryExceeded = 7012;
// Exit codes with special meanings on Windows.
const DWORD kNormalTerminationExitCode = 0;
const DWORD kDebuggerInactiveExitCode = 0xC0000354;
const DWORD kKeyboardInterruptExitCode = 0xC000013A;
const DWORD kDebuggerTerminatedExitCode = 0x40010004;
const DWORD kStatusInvalidImageHashExitCode = 0xC0000428;
// This exit code is used by the Windows task manager when it kills a
// process. It's value is obviously not that unique, and it's
// surprising to me that the task manager uses this value, but it
// seems to be common practice on Windows to test for it as an
// indication that the task manager has killed something if the
// process goes away.
const DWORD kProcessKilledExitCode = 1;
} // namespace win
#endif // OS_WIN
// Return status values from GetTerminationStatus. Don't use these as
// exit code arguments to KillProcess*(), use platform/application
// specific values instead.
enum TerminationStatus {
// clang-format off
TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status
TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status
TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill
TERMINATION_STATUS_PROCESS_CRASHED, // e.g. Segmentation fault
TERMINATION_STATUS_STILL_RUNNING, // child hasn't exited yet
#if defined(OS_CHROMEOS)
// Used for the case when oom-killer kills a process on ChromeOS.
TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM,
#endif
#if defined(OS_ANDROID)
// On Android processes are spawned from the system Zygote and we do not get
// the termination status. We can't know if the termination was a crash or an
// oom kill for sure, but we can use status of the strong process bindings as
// a hint.
TERMINATION_STATUS_OOM_PROTECTED, // child was protected from oom kill
#endif
TERMINATION_STATUS_LAUNCH_FAILED, // child process never launched
TERMINATION_STATUS_OOM, // Process died due to oom
#if defined(OS_WIN)
// On Windows, the OS terminated process due to code integrity failure.
TERMINATION_STATUS_INTEGRITY_FAILURE,
#endif
TERMINATION_STATUS_MAX_ENUM
// clang-format on
};
// Attempts to kill all the processes on the current machine that were launched
// from the given executable name, ending them with the given exit code. If
// filter is non-null, then only processes selected by the filter are killed.
// Returns true if all processes were able to be killed off, false if at least
// one couldn't be killed.
BASE_EXPORT bool KillProcesses(const FilePath::StringType& executable_name,
int exit_code,
const ProcessFilter* filter);
#if defined(OS_POSIX)
// Attempts to kill the process group identified by |process_group_id|. Returns
// true on success.
BASE_EXPORT bool KillProcessGroup(ProcessHandle process_group_id);
#endif // defined(OS_POSIX)
// Get the termination status of the process by interpreting the
// circumstances of the child process' death. |exit_code| is set to
// the status returned by waitpid() on POSIX, and from GetExitCodeProcess() on
// Windows, and may not be null. Note that on Linux, this function
// will only return a useful result the first time it is called after
// the child exits (because it will reap the child and the information
// will no longer be available).
BASE_EXPORT TerminationStatus GetTerminationStatus(ProcessHandle handle,
int* exit_code);
#if defined(OS_POSIX)
// Send a kill signal to the process and then wait for the process to exit
// and get the termination status.
//
// This is used in situations where it is believed that the process is dead
// or dying (because communication with the child process has been cut).
// In order to avoid erroneously returning that the process is still running
// because the kernel is still cleaning it up, this will wait for the process
// to terminate. In order to avoid the risk of hanging while waiting for the
// process to terminate, send a SIGKILL to the process before waiting for the
// termination status.
//
// Note that it is not an option to call WaitForExitCode and then
// GetTerminationStatus as the child will be reaped when WaitForExitCode
// returns, and this information will be lost.
//
BASE_EXPORT TerminationStatus GetKnownDeadTerminationStatus(
ProcessHandle handle, int* exit_code);
#if defined(OS_LINUX)
// Spawns a thread to wait asynchronously for the child |process| to exit
// and then reaps it.
BASE_EXPORT void EnsureProcessGetsReaped(Process process);
#endif // defined(OS_LINUX)
#endif // defined(OS_POSIX)
// Registers |process| to be asynchronously monitored for termination, forcibly
// terminated if necessary, and reaped on exit. The caller should have signalled
// |process| to exit before calling this API. The API will allow a couple of
// seconds grace period before forcibly terminating |process|.
// TODO(https://crbug.com/806451): The Mac implementation currently blocks the
// calling thread for up to two seconds.
BASE_EXPORT void EnsureProcessTerminated(Process process);
// These are only sparingly used, and not needed on Fuchsia. They could be
// implemented if necessary.
#if !defined(OS_FUCHSIA)
// Wait for all the processes based on the named executable to exit. If filter
// is non-null, then only processes selected by the filter are waited on.
// Returns after all processes have exited or wait_milliseconds have expired.
// Returns true if all the processes exited, false otherwise.
BASE_EXPORT bool WaitForProcessesToExit(
const FilePath::StringType& executable_name,
base::TimeDelta wait,
const ProcessFilter* filter);
// Waits a certain amount of time (can be 0) for all the processes with a given
// executable name to exit, then kills off any of them that are still around.
// If filter is non-null, then only processes selected by the filter are waited
// on. Killed processes are ended with the given exit code. Returns false if
// any processes needed to be killed, true if they all exited cleanly within
// the wait_milliseconds delay.
BASE_EXPORT bool CleanupProcesses(const FilePath::StringType& executable_name,
base::TimeDelta wait,
int exit_code,
const ProcessFilter* filter);
#endif // !defined(OS_FUCHSIA)
} // namespace base
#endif // BASE_PROCESS_KILL_H_

View file

@ -0,0 +1,53 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/kill.h"
#include <zircon/syscalls.h>
#include "base/process/process_iterator.h"
#include "base/task/post_task.h"
#include "base/threading/platform_thread.h"
namespace base {
bool KillProcessGroup(ProcessHandle process_group_id) {
// |process_group_id| is really a job on Fuchsia.
zx_status_t status = zx_task_kill(process_group_id);
DLOG_IF(ERROR, status != ZX_OK)
<< "unable to terminate job " << process_group_id;
return status == ZX_OK;
}
TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
DCHECK(exit_code);
zx_info_process_t process_info;
zx_status_t status =
zx_object_get_info(handle, ZX_INFO_PROCESS, &process_info,
sizeof(process_info), nullptr, nullptr);
if (status != ZX_OK) {
DLOG(ERROR) << "unable to get termination status for " << handle;
*exit_code = 0;
return TERMINATION_STATUS_NORMAL_TERMINATION;
}
if (!process_info.started) {
*exit_code = 0;
return TERMINATION_STATUS_LAUNCH_FAILED;
}
if (!process_info.exited) {
*exit_code = 0;
return TERMINATION_STATUS_STILL_RUNNING;
}
// TODO(fuchsia): Is there more information about types of crashes, OOM, etc.
// available? https://crbug.com/706592.
*exit_code = process_info.return_code;
return process_info.return_code == 0
? TERMINATION_STATUS_NORMAL_TERMINATION
: TERMINATION_STATUS_ABNORMAL_TERMINATION;
}
} // namespace base

View file

@ -0,0 +1,173 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/kill.h"
#include <errno.h>
#include <signal.h>
#include <sys/event.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
namespace base {
namespace {
const int kWaitBeforeKillSeconds = 2;
// Reap |child| process. This call blocks until completion.
void BlockingReap(pid_t child) {
const pid_t result = HANDLE_EINTR(waitpid(child, NULL, 0));
if (result == -1) {
DPLOG(ERROR) << "waitpid(" << child << ", NULL, 0)";
}
}
// Waits for |timeout| seconds for the given |child| to exit and reap it. If
// the child doesn't exit within the time specified, kills it.
//
// This function takes two approaches: first, it tries to use kqueue to
// observe when the process exits. kevent can monitor a kqueue with a
// timeout, so this method is preferred to wait for a specified period of
// time. Once the kqueue indicates the process has exited, waitpid will reap
// the exited child. If the kqueue doesn't provide an exit event notification,
// before the timeout expires, or if the kqueue fails or misbehaves, the
// process will be mercilessly killed and reaped.
//
// A child process passed to this function may be in one of several states:
// running, terminated and not yet reaped, and (apparently, and unfortunately)
// terminated and already reaped. Normally, a process will at least have been
// asked to exit before this function is called, but this is not required.
// If a process is terminating and unreaped, there may be a window between the
// time that kqueue will no longer recognize it and when it becomes an actual
// zombie that a non-blocking (WNOHANG) waitpid can reap. This condition is
// detected when kqueue indicates that the process is not running and a
// non-blocking waitpid fails to reap the process but indicates that it is
// still running. In this event, a blocking attempt to reap the process
// collects the known-dying child, preventing zombies from congregating.
//
// In the event that the kqueue misbehaves entirely, as it might under a
// EMFILE condition ("too many open files", or out of file descriptors), this
// function will forcibly kill and reap the child without delay. This
// eliminates another potential zombie vector. (If you're out of file
// descriptors, you're probably deep into something else, but that doesn't
// mean that zombies be allowed to kick you while you're down.)
//
// The fact that this function seemingly can be called to wait on a child
// that's not only already terminated but already reaped is a bit of a
// problem: a reaped child's pid can be reclaimed and may refer to a distinct
// process in that case. The fact that this function can seemingly be called
// to wait on a process that's not even a child is also a problem: kqueue will
// work in that case, but waitpid won't, and killing a non-child might not be
// the best approach.
void WaitForChildToDie(pid_t child, int timeout) {
DCHECK_GT(child, 0);
DCHECK_GT(timeout, 0);
// DON'T ADD ANY EARLY RETURNS TO THIS FUNCTION without ensuring that
// |child| has been reaped. Specifically, even if a kqueue, kevent, or other
// call fails, this function should fall back to the last resort of trying
// to kill and reap the process. Not observing this rule will resurrect
// zombies.
int result;
ScopedFD kq(HANDLE_EINTR(kqueue()));
if (!kq.is_valid()) {
DPLOG(ERROR) << "kqueue()";
} else {
struct kevent change = {0};
EV_SET(&change, child, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL));
if (result == -1) {
if (errno != ESRCH) {
DPLOG(ERROR) << "kevent (setup " << child << ")";
} else {
// At this point, one of the following has occurred:
// 1. The process has died but has not yet been reaped.
// 2. The process has died and has already been reaped.
// 3. The process is in the process of dying. It's no longer
// kqueueable, but it may not be waitable yet either. Mark calls
// this case the "zombie death race".
result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
if (result != 0) {
// A positive result indicates case 1. waitpid succeeded and reaped
// the child. A result of -1 indicates case 2. The child has already
// been reaped. In both of these cases, no further action is
// necessary.
return;
}
// |result| is 0, indicating case 3. The process will be waitable in
// short order. Fall back out of the kqueue code to kill it (for good
// measure) and reap it.
}
} else {
// Keep track of the elapsed time to be able to restart kevent if it's
// interrupted.
TimeDelta remaining_delta = TimeDelta::FromSeconds(timeout);
TimeTicks deadline = TimeTicks::Now() + remaining_delta;
result = -1;
struct kevent event = {0};
while (remaining_delta.InMilliseconds() > 0) {
const struct timespec remaining_timespec = remaining_delta.ToTimeSpec();
result = kevent(kq.get(), NULL, 0, &event, 1, &remaining_timespec);
if (result == -1 && errno == EINTR) {
remaining_delta = deadline - TimeTicks::Now();
result = 0;
} else {
break;
}
}
if (result == -1) {
DPLOG(ERROR) << "kevent (wait " << child << ")";
} else if (result > 1) {
DLOG(ERROR) << "kevent (wait " << child << "): unexpected result "
<< result;
} else if (result == 1) {
if ((event.fflags & NOTE_EXIT) &&
(event.ident == static_cast<uintptr_t>(child))) {
// The process is dead or dying. This won't block for long, if at
// all.
BlockingReap(child);
return;
} else {
DLOG(ERROR) << "kevent (wait " << child
<< "): unexpected event: fflags=" << event.fflags
<< ", ident=" << event.ident;
}
}
}
}
// The child is still alive, or is very freshly dead. Be sure by sending it
// a signal. This is safe even if it's freshly dead, because it will be a
// zombie (or on the way to zombiedom) and kill will return 0 even if the
// signal is not delivered to a live process.
result = kill(child, SIGKILL);
if (result == -1) {
DPLOG(ERROR) << "kill(" << child << ", SIGKILL)";
} else {
// The child is definitely on the way out now. BlockingReap won't need to
// wait for long, if at all.
BlockingReap(child);
}
}
} // namespace
void EnsureProcessTerminated(Process process) {
WaitForChildToDie(process.Pid(), kWaitBeforeKillSeconds);
}
} // namespace base

View file

@ -0,0 +1,187 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/kill.h"
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "base/debug/activity_tracker.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process/process_iterator.h"
#include "base/task/post_task.h"
#include "base/threading/platform_thread.h"
#include "build/build_config.h"
namespace base {
namespace {
TerminationStatus GetTerminationStatusImpl(ProcessHandle handle,
bool can_block,
int* exit_code) {
DCHECK(exit_code);
int status = 0;
const pid_t result = HANDLE_EINTR(waitpid(handle, &status,
can_block ? 0 : WNOHANG));
if (result == -1) {
DPLOG(ERROR) << "waitpid(" << handle << ")";
*exit_code = 0;
return TERMINATION_STATUS_NORMAL_TERMINATION;
}
if (result == 0) {
// the child hasn't exited yet.
*exit_code = 0;
return TERMINATION_STATUS_STILL_RUNNING;
}
*exit_code = status;
if (WIFSIGNALED(status)) {
switch (WTERMSIG(status)) {
case SIGABRT:
case SIGBUS:
case SIGFPE:
case SIGILL:
case SIGSEGV:
case SIGTRAP:
case SIGSYS:
return TERMINATION_STATUS_PROCESS_CRASHED;
case SIGKILL:
#if defined(OS_CHROMEOS)
// On ChromeOS, only way a process gets kill by SIGKILL
// is by oom-killer.
return TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM;
#endif
case SIGINT:
case SIGTERM:
return TERMINATION_STATUS_PROCESS_WAS_KILLED;
default:
break;
}
}
if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
return TERMINATION_STATUS_ABNORMAL_TERMINATION;
return TERMINATION_STATUS_NORMAL_TERMINATION;
}
} // namespace
#if !defined(OS_NACL_NONSFI)
bool KillProcessGroup(ProcessHandle process_group_id) {
bool result = kill(-1 * process_group_id, SIGKILL) == 0;
if (!result)
DPLOG(ERROR) << "Unable to terminate process group " << process_group_id;
return result;
}
#endif // !defined(OS_NACL_NONSFI)
TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
return GetTerminationStatusImpl(handle, false /* can_block */, exit_code);
}
TerminationStatus GetKnownDeadTerminationStatus(ProcessHandle handle,
int* exit_code) {
bool result = kill(handle, SIGKILL) == 0;
if (!result)
DPLOG(ERROR) << "Unable to terminate process " << handle;
return GetTerminationStatusImpl(handle, true /* can_block */, exit_code);
}
#if !defined(OS_NACL_NONSFI)
bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
TimeDelta wait,
const ProcessFilter* filter) {
bool result = false;
// TODO(port): This is inefficient, but works if there are multiple procs.
// TODO(port): use waitpid to avoid leaving zombies around
TimeTicks end_time = TimeTicks::Now() + wait;
do {
NamedProcessIterator iter(executable_name, filter);
if (!iter.NextProcessEntry()) {
result = true;
break;
}
PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
} while ((end_time - TimeTicks::Now()) > TimeDelta());
return result;
}
bool CleanupProcesses(const FilePath::StringType& executable_name,
TimeDelta wait,
int exit_code,
const ProcessFilter* filter) {
bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
if (!exited_cleanly)
KillProcesses(executable_name, exit_code, filter);
return exited_cleanly;
}
#if !defined(OS_MACOSX)
namespace {
class BackgroundReaper : public PlatformThread::Delegate {
public:
BackgroundReaper(base::Process child_process, const TimeDelta& wait_time)
: child_process_(std::move(child_process)), wait_time_(wait_time) {}
void ThreadMain() override {
if (!wait_time_.is_zero()) {
child_process_.WaitForExitWithTimeout(wait_time_, nullptr);
kill(child_process_.Handle(), SIGKILL);
}
child_process_.WaitForExit(nullptr);
delete this;
}
private:
Process child_process_;
const TimeDelta wait_time_;
DISALLOW_COPY_AND_ASSIGN(BackgroundReaper);
};
} // namespace
void EnsureProcessTerminated(Process process) {
DCHECK(!process.is_current());
if (process.WaitForExitWithTimeout(TimeDelta(), nullptr))
return;
PlatformThread::CreateNonJoinable(
0, new BackgroundReaper(std::move(process), TimeDelta::FromSeconds(2)));
}
#if defined(OS_LINUX)
void EnsureProcessGetsReaped(Process process) {
DCHECK(!process.is_current());
// If the child is already dead, then there's nothing to do.
if (process.WaitForExitWithTimeout(TimeDelta(), nullptr))
return;
PlatformThread::CreateNonJoinable(
0, new BackgroundReaper(std::move(process), TimeDelta()));
}
#endif // defined(OS_LINUX)
#endif // !defined(OS_MACOSX)
#endif // !defined(OS_NACL_NONSFI)
} // namespace base

View file

@ -0,0 +1,122 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/kill.h"
#include <algorithm>
#include <windows.h>
#include <io.h>
#include <stdint.h>
#include "base/logging.h"
#include "base/macros.h"
#include "base/process/memory.h"
#include "base/process/process_iterator.h"
namespace base {
TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
DCHECK(exit_code);
DWORD tmp_exit_code = 0;
if (!::GetExitCodeProcess(handle, &tmp_exit_code)) {
DPLOG(FATAL) << "GetExitCodeProcess() failed";
// This really is a random number. We haven't received any
// information about the exit code, presumably because this
// process doesn't have permission to get the exit code, or
// because of some other cause for GetExitCodeProcess to fail
// (MSDN docs don't give the possible failure error codes for
// this function, so it could be anything). But we don't want
// to leave exit_code uninitialized, since that could cause
// random interpretations of the exit code. So we assume it
// terminated "normally" in this case.
*exit_code = win::kNormalTerminationExitCode;
// Assume the child has exited normally if we can't get the exit
// code.
return TERMINATION_STATUS_NORMAL_TERMINATION;
}
if (tmp_exit_code == STILL_ACTIVE) {
DWORD wait_result = WaitForSingleObject(handle, 0);
if (wait_result == WAIT_TIMEOUT) {
*exit_code = wait_result;
return TERMINATION_STATUS_STILL_RUNNING;
}
if (wait_result == WAIT_FAILED) {
DPLOG(ERROR) << "WaitForSingleObject() failed";
} else {
DCHECK_EQ(WAIT_OBJECT_0, wait_result);
// Strange, the process used 0x103 (STILL_ACTIVE) as exit code.
NOTREACHED();
}
return TERMINATION_STATUS_ABNORMAL_TERMINATION;
}
*exit_code = tmp_exit_code;
// clang-format off
switch (tmp_exit_code) {
case win::kNormalTerminationExitCode:
return TERMINATION_STATUS_NORMAL_TERMINATION;
case win::kDebuggerInactiveExitCode: // STATUS_DEBUGGER_INACTIVE.
case win::kKeyboardInterruptExitCode: // Control-C/end session.
case win::kDebuggerTerminatedExitCode: // Debugger terminated process.
case win::kProcessKilledExitCode: // Task manager kill.
return TERMINATION_STATUS_PROCESS_WAS_KILLED;
case win::kSandboxFatalMemoryExceeded: // Terminated process due to
// exceeding the sandbox job
// object memory limits.
case win::kOomExceptionCode: // Ran out of memory.
return TERMINATION_STATUS_OOM;
// This exit code means the process failed an OS integrity check.
// This is tested in ProcessMitigationsTest.* in sandbox.
case win::kStatusInvalidImageHashExitCode:
return TERMINATION_STATUS_INTEGRITY_FAILURE;
default:
// All other exit codes indicate crashes.
return TERMINATION_STATUS_PROCESS_CRASHED;
}
// clang-format on
}
bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
TimeDelta wait,
const ProcessFilter* filter) {
bool result = true;
DWORD start_time = GetTickCount();
NamedProcessIterator iter(executable_name, filter);
for (const ProcessEntry* entry = iter.NextProcessEntry(); entry;
entry = iter.NextProcessEntry()) {
DWORD remaining_wait = static_cast<DWORD>(
std::max(static_cast<int64_t>(0),
wait.InMilliseconds() - (GetTickCount() - start_time)));
HANDLE process = OpenProcess(SYNCHRONIZE,
FALSE,
entry->th32ProcessID);
DWORD wait_result = WaitForSingleObject(process, remaining_wait);
CloseHandle(process);
result &= (wait_result == WAIT_OBJECT_0);
}
return result;
}
bool CleanupProcesses(const FilePath::StringType& executable_name,
TimeDelta wait,
int exit_code,
const ProcessFilter* filter) {
if (WaitForProcessesToExit(executable_name, wait, filter))
return true;
KillProcesses(executable_name, exit_code, filter);
return false;
}
} // namespace base

View file

@ -0,0 +1,27 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/launch.h"
#include "build/build_config.h"
namespace base {
LaunchOptions::LaunchOptions() = default;
LaunchOptions::LaunchOptions(const LaunchOptions& other) = default;
LaunchOptions::~LaunchOptions() = default;
LaunchOptions LaunchOptionsForTest() {
LaunchOptions options;
#if defined(OS_LINUX)
// To prevent accidental privilege sharing to an untrusted child, processes
// are started with PR_SET_NO_NEW_PRIVS. Do not set that here, since this
// new child will be used for testing only.
options.allow_new_privs = true;
#endif
return options;
}
} // namespace base

View file

@ -0,0 +1,426 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file contains functions for launching subprocesses.
#ifndef BASE_PROCESS_LAUNCH_H_
#define BASE_PROCESS_LAUNCH_H_
#include <stddef.h>
#include <string>
#include <utility>
#include <vector>
#include "base/base_export.h"
#include "base/command_line.h"
#include "base/environment.h"
#include "base/macros.h"
#include "base/process/process.h"
#include "base/process/process_handle.h"
#include "base/strings/string_piece.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#elif defined(OS_FUCHSIA)
#include <lib/fdio/spawn.h>
#include <zircon/types.h>
#endif
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
#include "base/posix/file_descriptor_shuffle.h"
#endif
#if defined(OS_MACOSX) && !defined(OS_IOS)
#include "base/mac/mach_port_rendezvous.h"
#endif
namespace base {
#if defined(OS_WIN)
typedef std::vector<HANDLE> HandlesToInheritVector;
#elif defined(OS_FUCHSIA)
struct PathToTransfer {
base::FilePath path;
zx_handle_t handle;
};
struct HandleToTransfer {
uint32_t id;
zx_handle_t handle;
};
typedef std::vector<HandleToTransfer> HandlesToTransferVector;
typedef std::vector<std::pair<int, int>> FileHandleMappingVector;
#elif defined(OS_POSIX)
typedef std::vector<std::pair<int, int>> FileHandleMappingVector;
#endif // defined(OS_WIN)
// Options for launching a subprocess that are passed to LaunchProcess().
// The default constructor constructs the object with default options.
struct BASE_EXPORT LaunchOptions {
#if (defined(OS_POSIX) || defined(OS_FUCHSIA)) && !defined(OS_MACOSX)
// Delegate to be run in between fork and exec in the subprocess (see
// pre_exec_delegate below)
class BASE_EXPORT PreExecDelegate {
public:
PreExecDelegate() = default;
virtual ~PreExecDelegate() = default;
// Since this is to be run between fork and exec, and fork may have happened
// while multiple threads were running, this function needs to be async
// safe.
virtual void RunAsyncSafe() = 0;
private:
DISALLOW_COPY_AND_ASSIGN(PreExecDelegate);
};
#endif // defined(OS_POSIX)
LaunchOptions();
LaunchOptions(const LaunchOptions&);
~LaunchOptions();
// If true, wait for the process to complete.
bool wait = false;
// If not empty, change to this directory before executing the new process.
base::FilePath current_directory;
#if defined(OS_WIN)
bool start_hidden = false;
// Sets STARTF_FORCEOFFFEEDBACK so that the feedback cursor is forced off
// while the process is starting.
bool feedback_cursor_off = false;
// Windows can inherit handles when it launches child processes.
// See https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873
// for a good overview of Windows handle inheritance.
//
// Implementation note: it might be nice to implement in terms of
// base::Optional<>, but then the natural default state (vector not present)
// would be "all inheritable handles" while we want "no inheritance."
enum class Inherit {
// Only those handles in |handles_to_inherit| vector are inherited. If the
// vector is empty, no handles are inherited. The handles in the vector must
// all be inheritable.
kSpecific,
// All handles in the current process which are inheritable are inherited.
// In production code this flag should be used only when running
// short-lived, trusted binaries, because open handles from other libraries
// and subsystems will leak to the child process, causing errors such as
// open socket hangs. There are also race conditions that can cause handle
// over-sharing.
//
// |handles_to_inherit| must be null.
//
// DEPRECATED. THIS SHOULD NOT BE USED. Explicitly map all handles that
// need to be shared in new code.
// TODO(brettw) bug 748258: remove this.
kAll
};
Inherit inherit_mode = Inherit::kSpecific;
HandlesToInheritVector handles_to_inherit;
// If non-null, runs as if the user represented by the token had launched it.
// Whether the application is visible on the interactive desktop depends on
// the token belonging to an interactive logon session.
//
// To avoid hard to diagnose problems, when specified this loads the
// environment variables associated with the user and if this operation fails
// the entire call fails as well.
UserTokenHandle as_user = nullptr;
// If true, use an empty string for the desktop name.
bool empty_desktop_name = false;
// If non-null, launches the application in that job object. The process will
// be terminated immediately and LaunchProcess() will fail if assignment to
// the job object fails.
HANDLE job_handle = nullptr;
// Handles for the redirection of stdin, stdout and stderr. The caller should
// either set all three of them or none (i.e. there is no way to redirect
// stderr without redirecting stdin).
//
// The handles must be inheritable. Pseudo handles are used when stdout and
// stderr redirect to the console. In that case, GetFileType() will return
// FILE_TYPE_CHAR and they're automatically inherited by child processes. See
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075.aspx
// Otherwise, the caller must ensure that the |inherit_mode| and/or
// |handles_to_inherit| set so that the handles are inherited.
HANDLE stdin_handle = nullptr;
HANDLE stdout_handle = nullptr;
HANDLE stderr_handle = nullptr;
// If set to true, ensures that the child process is launched with the
// CREATE_BREAKAWAY_FROM_JOB flag which allows it to breakout of the parent
// job if any.
bool force_breakaway_from_job_ = false;
// If set to true, permission to bring windows to the foreground is passed to
// the launched process if the current process has such permission.
bool grant_foreground_privilege = false;
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
// Remap file descriptors according to the mapping of src_fd->dest_fd to
// propagate FDs into the child process.
FileHandleMappingVector fds_to_remap;
#endif // defined(OS_WIN)
#if defined(OS_WIN) || defined(OS_POSIX) || defined(OS_FUCHSIA)
// Set/unset environment variables. These are applied on top of the parent
// process environment. Empty (the default) means to inherit the same
// environment. See internal::AlterEnvironment().
EnvironmentMap environment;
// Clear the environment for the new process before processing changes from
// |environment|.
bool clear_environment = false;
#endif // OS_WIN || OS_POSIX || OS_FUCHSIA
#if defined(OS_LINUX)
// If non-zero, start the process using clone(), using flags as provided.
// Unlike in clone, clone_flags may not contain a custom termination signal
// that is sent to the parent when the child dies. The termination signal will
// always be set to SIGCHLD.
int clone_flags = 0;
// By default, child processes will have the PR_SET_NO_NEW_PRIVS bit set. If
// true, then this bit will not be set in the new child process.
bool allow_new_privs = false;
// Sets parent process death signal to SIGKILL.
bool kill_on_parent_death = false;
#endif // defined(OS_LINUX)
#if defined(OS_MACOSX) && !defined(OS_IOS)
// Mach ports that will be accessible to the child process. These are not
// directly inherited across process creation, but they are stored by a Mach
// IPC server that a child process can communicate with to retrieve them.
//
// After calling LaunchProcess(), any rights that were transferred with MOVE
// dispositions will be consumed, even on failure.
//
// See base/mac/mach_port_rendezvous.h for details.
MachPortsForRendezvous mach_ports_for_rendezvous;
// When a child process is launched, the system tracks the parent process
// with a concept of "responsibility". The responsible process will be
// associated with any requests for private data stored on the system via
// the TCC subsystem. When launching processes that run foreign/third-party
// code, the responsibility for the child process should be disclaimed so
// that any TCC requests are not associated with the parent.
bool disclaim_responsibility = false;
#endif
#if defined(OS_FUCHSIA)
// If valid, launches the application in that job object.
zx_handle_t job_handle = ZX_HANDLE_INVALID;
// Specifies additional handles to transfer (not duplicate) to the child
// process. Each entry is an <id,handle> pair, with an |id| created using the
// PA_HND() macro. The child retrieves the handle
// |zx_take_startup_handle(id)|. The supplied handles are consumed by
// LaunchProcess() even on failure.
// Note that PA_USER1 ids are reserved for use by AddHandleToTransfer(), below
// and by convention PA_USER0 is reserved for use by the embedding
// application.
HandlesToTransferVector handles_to_transfer;
// Allocates a unique id for |handle| in |handles_to_transfer|, inserts it,
// and returns the generated id.
static uint32_t AddHandleToTransfer(
HandlesToTransferVector* handles_to_transfer,
zx_handle_t handle);
// Specifies which basic capabilities to grant to the child process.
// By default the child process will receive the caller's complete namespace,
// access to the current base::fuchsia::DefaultJob(), handles for stdio and
// access to the dynamic library loader.
// Note that the child is always provided access to the loader service.
uint32_t spawn_flags = FDIO_SPAWN_CLONE_NAMESPACE | FDIO_SPAWN_CLONE_STDIO |
FDIO_SPAWN_CLONE_JOB;
// Specifies paths to clone from the calling process' namespace into that of
// the child process. If |paths_to_clone| is empty then the process will
// receive either a full copy of the parent's namespace, or an empty one,
// depending on whether FDIO_SPAWN_CLONE_NAMESPACE is set.
std::vector<FilePath> paths_to_clone;
// Specifies handles which will be installed as files or directories in the
// child process' namespace. Paths installed by |paths_to_clone| will be
// overridden by these entries.
std::vector<PathToTransfer> paths_to_transfer;
// Suffix that will be added to the process name. When specified process name
// will be set to "<binary_name><process_suffix>".
std::string process_name_suffix;
#endif // defined(OS_FUCHSIA)
#if defined(OS_POSIX)
// If not empty, launch the specified executable instead of
// cmdline.GetProgram(). This is useful when it is necessary to pass a custom
// argv[0].
base::FilePath real_path;
#if !defined(OS_MACOSX)
// If non-null, a delegate to be run immediately prior to executing the new
// program in the child process.
//
// WARNING: If LaunchProcess is called in the presence of multiple threads,
// code running in this delegate essentially needs to be async-signal safe
// (see man 7 signal for a list of allowed functions).
PreExecDelegate* pre_exec_delegate = nullptr;
#endif // !defined(OS_MACOSX)
// Each element is an RLIMIT_* constant that should be raised to its
// rlim_max. This pointer is owned by the caller and must live through
// the call to LaunchProcess().
const std::vector<int>* maximize_rlimits = nullptr;
// If true, start the process in a new process group, instead of
// inheriting the parent's process group. The pgid of the child process
// will be the same as its pid.
bool new_process_group = false;
#endif // defined(OS_POSIX)
#if defined(OS_CHROMEOS)
// If non-negative, the specified file descriptor will be set as the launched
// process' controlling terminal.
int ctrl_terminal_fd = -1;
#endif // defined(OS_CHROMEOS)
};
// Launch a process via the command line |cmdline|.
// See the documentation of LaunchOptions for details on |options|.
//
// Returns a valid Process upon success.
//
// Unix-specific notes:
// - All file descriptors open in the parent process will be closed in the
// child process except for any preserved by options::fds_to_remap, and
// stdin, stdout, and stderr. If not remapped by options::fds_to_remap,
// stdin is reopened as /dev/null, and the child is allowed to inherit its
// parent's stdout and stderr.
// - If the first argument on the command line does not contain a slash,
// PATH will be searched. (See man execvp.)
BASE_EXPORT Process LaunchProcess(const CommandLine& cmdline,
const LaunchOptions& options);
#if defined(OS_WIN)
// Windows-specific LaunchProcess that takes the command line as a
// string. Useful for situations where you need to control the
// command line arguments directly, but prefer the CommandLine version
// if launching Chrome itself.
//
// The first command line argument should be the path to the process,
// and don't forget to quote it.
//
// Example (including literal quotes)
// cmdline = "c:\windows\explorer.exe" -foo "c:\bar\"
BASE_EXPORT Process LaunchProcess(const CommandLine::StringType& cmdline,
const LaunchOptions& options);
// Launches a process with elevated privileges. This does not behave exactly
// like LaunchProcess as it uses ShellExecuteEx instead of CreateProcess to
// create the process. This means the process will have elevated privileges
// and thus some common operations like OpenProcess will fail. Currently the
// only supported LaunchOptions are |start_hidden| and |wait|.
BASE_EXPORT Process LaunchElevatedProcess(const CommandLine& cmdline,
const LaunchOptions& options);
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
// A POSIX-specific version of LaunchProcess that takes an argv array
// instead of a CommandLine. Useful for situations where you need to
// control the command line arguments directly, but prefer the
// CommandLine version if launching Chrome itself.
BASE_EXPORT Process LaunchProcess(const std::vector<std::string>& argv,
const LaunchOptions& options);
#if !defined(OS_MACOSX)
// Close all file descriptors, except those which are a destination in the
// given multimap. Only call this function in a child process where you know
// that there aren't any other threads.
BASE_EXPORT void CloseSuperfluousFds(const InjectiveMultimap& saved_map);
#endif // defined(OS_MACOSX)
#endif // defined(OS_WIN)
#if defined(OS_WIN)
// Set |job_object|'s JOBOBJECT_EXTENDED_LIMIT_INFORMATION
// BasicLimitInformation.LimitFlags to |limit_flags|.
BASE_EXPORT bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags);
// Output multi-process printf, cout, cerr, etc to the cmd.exe console that ran
// chrome. This is not thread-safe: only call from main thread.
BASE_EXPORT void RouteStdioToConsole(bool create_console_if_not_found);
#endif // defined(OS_WIN)
// Executes the application specified by |cl| and wait for it to exit. Stores
// the output (stdout) in |output|. Redirects stderr to /dev/null. Returns true
// on success (application launched and exited cleanly, with exit code
// indicating success).
BASE_EXPORT bool GetAppOutput(const CommandLine& cl, std::string* output);
// Like GetAppOutput, but also includes stderr.
BASE_EXPORT bool GetAppOutputAndError(const CommandLine& cl,
std::string* output);
// A version of |GetAppOutput()| which also returns the exit code of the
// executed command. Returns true if the application runs and exits cleanly. If
// this is the case the exit code of the application is available in
// |*exit_code|.
BASE_EXPORT bool GetAppOutputWithExitCode(const CommandLine& cl,
std::string* output, int* exit_code);
#if defined(OS_WIN)
// A Windows-specific version of GetAppOutput that takes a command line string
// instead of a CommandLine object. Useful for situations where you need to
// control the command line arguments directly.
BASE_EXPORT bool GetAppOutput(CommandLine::StringPieceType cl,
std::string* output);
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
// A POSIX-specific version of GetAppOutput that takes an argv array
// instead of a CommandLine. Useful for situations where you need to
// control the command line arguments directly.
BASE_EXPORT bool GetAppOutput(const std::vector<std::string>& argv,
std::string* output);
// Like the above POSIX-specific version of GetAppOutput, but also includes
// stderr.
BASE_EXPORT bool GetAppOutputAndError(const std::vector<std::string>& argv,
std::string* output);
#endif // defined(OS_WIN)
// If supported on the platform, and the user has sufficent rights, increase
// the current process's scheduling priority to a high priority.
BASE_EXPORT void RaiseProcessToHighPriority();
// Creates a LaunchOptions object suitable for launching processes in a test
// binary. This should not be called in production/released code.
BASE_EXPORT LaunchOptions LaunchOptionsForTest();
#if defined(OS_LINUX) || defined(OS_NACL_NONSFI)
// A wrapper for clone with fork-like behavior, meaning that it returns the
// child's pid in the parent and 0 in the child. |flags|, |ptid|, and |ctid| are
// as in the clone system call (the CLONE_VM flag is not supported).
//
// This function uses the libc clone wrapper (which updates libc's pid cache)
// internally, so callers may expect things like getpid() to work correctly
// after in both the child and parent.
//
// As with fork(), callers should be extremely careful when calling this while
// multiple threads are running, since at the time the fork happened, the
// threads could have been in any state (potentially holding locks, etc.).
// Callers should most likely call execve() in the child soon after calling
// this.
//
// It is unsafe to use any pthread APIs after ForkWithFlags().
// However, performing an exec() will lift this restriction.
BASE_EXPORT pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid);
#endif
} // namespace base
#endif // BASE_PROCESS_LAUNCH_H_

View file

@ -0,0 +1,288 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/launch.h"
#include <lib/fdio/limits.h>
#include <lib/fdio/namespace.h>
#include <lib/fdio/spawn.h>
#include <lib/zx/job.h>
#include <stdint.h>
#include <unistd.h>
#include <zircon/processargs.h>
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/fuchsia/default_job.h"
#include "base/fuchsia/file_utils.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/process/environment_internal.h"
#include "base/scoped_generic.h"
namespace base {
namespace {
bool GetAppOutputInternal(const CommandLine& cmd_line,
bool include_stderr,
std::string* output,
int* exit_code) {
DCHECK(exit_code);
LaunchOptions options;
// LaunchProcess will automatically clone any stdio fd we do not explicitly
// map.
int pipe_fd[2];
if (pipe(pipe_fd) < 0)
return false;
options.fds_to_remap.emplace_back(pipe_fd[1], STDOUT_FILENO);
if (include_stderr)
options.fds_to_remap.emplace_back(pipe_fd[1], STDERR_FILENO);
Process process = LaunchProcess(cmd_line, options);
close(pipe_fd[1]);
if (!process.IsValid()) {
close(pipe_fd[0]);
return false;
}
output->clear();
for (;;) {
char buffer[256];
ssize_t bytes_read = read(pipe_fd[0], buffer, sizeof(buffer));
if (bytes_read <= 0)
break;
output->append(buffer, bytes_read);
}
close(pipe_fd[0]);
return process.WaitForExit(exit_code);
}
fdio_spawn_action_t FdioSpawnAction(uint32_t action) {
fdio_spawn_action_t new_action = {};
new_action.action = action;
return new_action;
}
fdio_spawn_action_t FdioSpawnActionCloneFd(int local_fd, int target_fd) {
fdio_spawn_action_t action = FdioSpawnAction(FDIO_SPAWN_ACTION_CLONE_FD);
action.fd.local_fd = local_fd;
action.fd.target_fd = target_fd;
return action;
}
fdio_spawn_action_t FdioSpawnActionAddNamespaceEntry(const char* prefix,
zx_handle_t handle) {
fdio_spawn_action_t action = FdioSpawnAction(FDIO_SPAWN_ACTION_ADD_NS_ENTRY);
action.ns.prefix = prefix;
action.ns.handle = handle;
return action;
}
fdio_spawn_action_t FdioSpawnActionAddHandle(uint32_t id, zx_handle_t handle) {
fdio_spawn_action_t action = FdioSpawnAction(FDIO_SPAWN_ACTION_ADD_HANDLE);
action.h.id = id;
action.h.handle = handle;
return action;
}
fdio_spawn_action_t FdioSpawnActionSetName(const char* name) {
fdio_spawn_action_t action = FdioSpawnAction(FDIO_SPAWN_ACTION_SET_NAME);
action.name.data = name;
return action;
}
} // namespace
// static
uint32_t LaunchOptions::AddHandleToTransfer(
HandlesToTransferVector* handles_to_transfer,
zx_handle_t handle) {
uint32_t handle_id = PA_HND(PA_USER1, handles_to_transfer->size());
handles_to_transfer->push_back({handle_id, handle});
return handle_id;
}
Process LaunchProcess(const CommandLine& cmdline,
const LaunchOptions& options) {
return LaunchProcess(cmdline.argv(), options);
}
// TODO(768416): Investigate whether we can make LaunchProcess() create
// unprivileged processes by default (no implicit capabilities are granted).
Process LaunchProcess(const std::vector<std::string>& argv,
const LaunchOptions& options) {
// fdio_spawn_etc() accepts an array of |fdio_spawn_action_t|, describing
// namespace entries, descriptors and handles to launch the child process
// with.
std::vector<fdio_spawn_action_t> spawn_actions;
// Handles to be transferred to the child are owned by this vector, so that
// they they are closed on early-exit, and can be release()d otherwise.
std::vector<zx::handle> transferred_handles;
// Add caller-supplied handles for transfer. We must do this first to ensure
// that the handles are consumed even if some later step fails.
for (const auto& id_and_handle : options.handles_to_transfer) {
spawn_actions.push_back(
FdioSpawnActionAddHandle(id_and_handle.id, id_and_handle.handle));
transferred_handles.emplace_back(id_and_handle.handle);
}
// Determine the job under which to launch the new process.
zx::unowned_job job = options.job_handle != ZX_HANDLE_INVALID
? zx::unowned_job(options.job_handle)
: GetDefaultJob();
DCHECK(job->is_valid());
// Construct an |argv| array of C-strings from the supplied std::strings.
std::vector<const char*> argv_cstr;
argv_cstr.reserve(argv.size() + 1);
for (const auto& arg : argv)
argv_cstr.push_back(arg.c_str());
argv_cstr.push_back(nullptr);
// Determine the environment to pass to the new process. If
// |clear_environment|, |environment| or |current_directory| are set then we
// construct a new (possibly empty) environment, otherwise we let fdio_spawn()
// clone the caller's environment into the new process.
uint32_t spawn_flags = FDIO_SPAWN_CLONE_LDSVC | options.spawn_flags;
EnvironmentMap environ_modifications = options.environment;
if (!options.current_directory.empty()) {
environ_modifications["PWD"] = options.current_directory.value();
} else {
FilePath cwd;
GetCurrentDirectory(&cwd);
environ_modifications["PWD"] = cwd.value();
}
std::unique_ptr<char* []> new_environ;
if (!environ_modifications.empty()) {
char* const empty_environ = nullptr;
char* const* old_environ =
options.clear_environment ? &empty_environ : environ;
new_environ =
internal::AlterEnvironment(old_environ, environ_modifications);
} else if (!options.clear_environment) {
spawn_flags |= FDIO_SPAWN_CLONE_ENVIRON;
}
// Add actions to clone handles for any specified paths into the new process'
// namespace.
if (!options.paths_to_clone.empty() || !options.paths_to_transfer.empty()) {
DCHECK((options.spawn_flags & FDIO_SPAWN_CLONE_NAMESPACE) == 0);
transferred_handles.reserve(transferred_handles.size() +
options.paths_to_clone.size() +
options.paths_to_transfer.size());
for (const auto& path_to_transfer : options.paths_to_transfer) {
zx::handle handle(path_to_transfer.handle);
spawn_actions.push_back(FdioSpawnActionAddNamespaceEntry(
path_to_transfer.path.value().c_str(), handle.get()));
transferred_handles.push_back(std::move(handle));
}
for (const auto& path_to_clone : options.paths_to_clone) {
fidl::InterfaceHandle<::fuchsia::io::Directory> directory =
base::fuchsia::OpenDirectory(path_to_clone);
if (!directory) {
LOG(WARNING) << "Could not open handle for path: " << path_to_clone;
return base::Process();
}
zx::handle handle = directory.TakeChannel();
spawn_actions.push_back(FdioSpawnActionAddNamespaceEntry(
path_to_clone.value().c_str(), handle.get()));
transferred_handles.push_back(std::move(handle));
}
}
// Add any file-descriptors to be cloned into the new process.
// Note that if FDIO_SPAWN_CLONE_STDIO is set, then any stdio entries in
// |fds_to_remap| will be used in place of the parent process' descriptors.
for (const auto& src_target : options.fds_to_remap) {
spawn_actions.push_back(
FdioSpawnActionCloneFd(src_target.first, src_target.second));
}
// If |process_name_suffix| is specified then set process name as
// "<file_name><suffix>", otherwise leave the default value.
std::string process_name;
if (!options.process_name_suffix.empty()) {
process_name = base::FilePath(argv[0]).BaseName().value() +
options.process_name_suffix;
spawn_actions.push_back(FdioSpawnActionSetName(process_name.c_str()));
}
zx::process process_handle;
// fdio_spawn_etc() will write a null-terminated scring to |error_message| in
// case of failure, so we avoid unnecessarily initializing it here.
char error_message[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
zx_status_t status = fdio_spawn_etc(
job->get(), spawn_flags, argv_cstr[0], argv_cstr.data(),
new_environ.get(), spawn_actions.size(), spawn_actions.data(),
process_handle.reset_and_get_address(), error_message);
// fdio_spawn_etc() will close all handles specified in add-handle actions,
// regardless of whether it succeeds or fails, so release our copies.
for (auto& transferred_handle : transferred_handles)
ignore_result(transferred_handle.release());
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "fdio_spawn: " << error_message;
return Process();
}
// Wrap the handle into a Process, and wait for it to terminate, if requested.
Process process(process_handle.release());
if (options.wait) {
status = zx_object_wait_one(process.Handle(), ZX_TASK_TERMINATED,
ZX_TIME_INFINITE, nullptr);
ZX_DCHECK(status == ZX_OK, status) << "zx_object_wait_one";
}
return process;
}
bool GetAppOutput(const CommandLine& cl, std::string* output) {
int exit_code;
bool result = GetAppOutputInternal(cl, false, output, &exit_code);
return result && exit_code == EXIT_SUCCESS;
}
bool GetAppOutput(const std::vector<std::string>& argv, std::string* output) {
return GetAppOutput(CommandLine(argv), output);
}
bool GetAppOutputAndError(const CommandLine& cl, std::string* output) {
int exit_code;
bool result = GetAppOutputInternal(cl, true, output, &exit_code);
return result && exit_code == EXIT_SUCCESS;
}
bool GetAppOutputAndError(const std::vector<std::string>& argv,
std::string* output) {
return GetAppOutputAndError(CommandLine(argv), output);
}
bool GetAppOutputWithExitCode(const CommandLine& cl,
std::string* output,
int* exit_code) {
// Contrary to GetAppOutput(), |true| return here means that the process was
// launched and the exit code was waited upon successfully, but not
// necessarily that the exit code was EXIT_SUCCESS.
return GetAppOutputInternal(cl, false, output, exit_code);
}
void RaiseProcessToHighPriority() {
// Fuchsia doesn't provide an API to change process priority.
}
} // namespace base

View file

@ -0,0 +1,13 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/launch.h"
namespace base {
void RaiseProcessToHighPriority() {
// Impossible on iOS. Do nothing.
}
} // namespace base

View file

@ -0,0 +1,363 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/launch.h"
#include <crt_externs.h>
#include <mach/mach.h>
#include <os/availability.h>
#include <spawn.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include "base/command_line.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process/environment_internal.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_restrictions.h"
#include "base/trace_event/trace_event.h"
extern "C" {
// Changes the current thread's directory to a path or directory file
// descriptor. libpthread only exposes a syscall wrapper starting in
// macOS 10.12, but the system call dates back to macOS 10.5. On older OSes,
// the syscall is issued directly.
int pthread_chdir_np(const char* dir) API_AVAILABLE(macosx(10.12));
int pthread_fchdir_np(int fd) API_AVAILABLE(macosx(10.12));
int responsibility_spawnattrs_setdisclaim(posix_spawnattr_t attrs, int disclaim)
API_AVAILABLE(macosx(10.14));
} // extern "C"
namespace base {
// Friend and derived class of ScopedAllowBaseSyncPrimitives which allows
// GetAppOutputInternal() to join a process. GetAppOutputInternal() can't itself
// be a friend of ScopedAllowBaseSyncPrimitives because it is in the anonymous
// namespace.
class GetAppOutputScopedAllowBaseSyncPrimitives
: public base::ScopedAllowBaseSyncPrimitives {};
namespace {
// DPSXCHECK is a Debug Posix Spawn Check macro. The posix_spawn* family of
// functions return an errno value, as opposed to setting errno directly. This
// macro emulates a DPCHECK().
#define DPSXCHECK(expr) \
do { \
int rv = (expr); \
DCHECK_EQ(rv, 0) << #expr << ": -" << rv << " " << strerror(rv); \
} while (0)
class PosixSpawnAttr {
public:
PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_init(&attr_)); }
~PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_destroy(&attr_)); }
posix_spawnattr_t* get() { return &attr_; }
private:
posix_spawnattr_t attr_;
};
class PosixSpawnFileActions {
public:
PosixSpawnFileActions() {
DPSXCHECK(posix_spawn_file_actions_init(&file_actions_));
}
~PosixSpawnFileActions() {
DPSXCHECK(posix_spawn_file_actions_destroy(&file_actions_));
}
void Open(int filedes, const char* path, int mode) {
DPSXCHECK(posix_spawn_file_actions_addopen(&file_actions_, filedes, path,
mode, 0));
}
void Dup2(int filedes, int newfiledes) {
DPSXCHECK(
posix_spawn_file_actions_adddup2(&file_actions_, filedes, newfiledes));
}
void Inherit(int filedes) {
DPSXCHECK(posix_spawn_file_actions_addinherit_np(&file_actions_, filedes));
}
const posix_spawn_file_actions_t* get() const { return &file_actions_; }
private:
posix_spawn_file_actions_t file_actions_;
DISALLOW_COPY_AND_ASSIGN(PosixSpawnFileActions);
};
int ChangeCurrentThreadDirectory(const char* path) {
if (__builtin_available(macOS 10.12, *)) {
return pthread_chdir_np(path);
} else {
return syscall(SYS___pthread_chdir, path);
}
}
// The recommended way to unset a per-thread cwd is to set a new value to an
// invalid file descriptor, per libpthread-218.1.3/private/private.h.
int ResetCurrentThreadDirectory() {
if (__builtin_available(macOS 10.12, *)) {
return pthread_fchdir_np(-1);
} else {
return syscall(SYS___pthread_fchdir, -1);
}
}
struct GetAppOutputOptions {
// Whether to pipe stderr to stdout in |output|.
bool include_stderr = false;
// Caller-supplied string poiter for the output.
std::string* output = nullptr;
// Result exit code of Process::Wait().
int exit_code = 0;
};
bool GetAppOutputInternal(const std::vector<std::string>& argv,
GetAppOutputOptions* gao_options) {
ScopedFD read_fd, write_fd;
{
int pipefds[2];
if (pipe(pipefds) != 0) {
DPLOG(ERROR) << "pipe";
return false;
}
read_fd.reset(pipefds[0]);
write_fd.reset(pipefds[1]);
}
LaunchOptions launch_options;
launch_options.fds_to_remap.emplace_back(write_fd.get(), STDOUT_FILENO);
if (gao_options->include_stderr) {
launch_options.fds_to_remap.emplace_back(write_fd.get(), STDERR_FILENO);
}
Process process = LaunchProcess(argv, launch_options);
// Close the parent process' write descriptor, so that EOF is generated in
// read loop below.
write_fd.reset();
// Read the child's output before waiting for its exit, otherwise the pipe
// buffer may fill up if the process is producing a lot of output.
std::string* output = gao_options->output;
output->clear();
const size_t kBufferSize = 1024;
size_t total_bytes_read = 0;
ssize_t read_this_pass = 0;
do {
output->resize(output->size() + kBufferSize);
read_this_pass = HANDLE_EINTR(
read(read_fd.get(), &(*output)[total_bytes_read], kBufferSize));
if (read_this_pass >= 0) {
total_bytes_read += read_this_pass;
output->resize(total_bytes_read);
}
} while (read_this_pass > 0);
// Reap the child process.
GetAppOutputScopedAllowBaseSyncPrimitives allow_wait;
if (!process.WaitForExit(&gao_options->exit_code)) {
return false;
}
return read_this_pass == 0;
}
} // namespace
Process LaunchProcess(const CommandLine& cmdline,
const LaunchOptions& options) {
return LaunchProcess(cmdline.argv(), options);
}
Process LaunchProcess(const std::vector<std::string>& argv,
const LaunchOptions& options) {
TRACE_EVENT0("base", "LaunchProcess");
PosixSpawnAttr attr;
short flags = POSIX_SPAWN_CLOEXEC_DEFAULT;
if (options.new_process_group) {
flags |= POSIX_SPAWN_SETPGROUP;
DPSXCHECK(posix_spawnattr_setpgroup(attr.get(), 0));
}
DPSXCHECK(posix_spawnattr_setflags(attr.get(), flags));
PosixSpawnFileActions file_actions;
// Process file descriptors for the child. By default, LaunchProcess will
// open stdin to /dev/null and inherit stdout and stderr.
bool inherit_stdout = true, inherit_stderr = true;
bool null_stdin = true;
for (const auto& dup2_pair : options.fds_to_remap) {
if (dup2_pair.second == STDIN_FILENO) {
null_stdin = false;
} else if (dup2_pair.second == STDOUT_FILENO) {
inherit_stdout = false;
} else if (dup2_pair.second == STDERR_FILENO) {
inherit_stderr = false;
}
if (dup2_pair.first == dup2_pair.second) {
file_actions.Inherit(dup2_pair.second);
} else {
file_actions.Dup2(dup2_pair.first, dup2_pair.second);
}
}
if (null_stdin) {
file_actions.Open(STDIN_FILENO, "/dev/null", O_RDONLY);
}
if (inherit_stdout) {
file_actions.Inherit(STDOUT_FILENO);
}
if (inherit_stderr) {
file_actions.Inherit(STDERR_FILENO);
}
if (options.disclaim_responsibility) {
if (__builtin_available(macOS 10.14, *)) {
DPSXCHECK(responsibility_spawnattrs_setdisclaim(attr.get(), 1));
}
}
std::vector<char*> argv_cstr;
argv_cstr.reserve(argv.size() + 1);
for (const auto& arg : argv)
argv_cstr.push_back(const_cast<char*>(arg.c_str()));
argv_cstr.push_back(nullptr);
std::unique_ptr<char*[]> owned_environ;
char* empty_environ = nullptr;
char** new_environ =
options.clear_environment ? &empty_environ : *_NSGetEnviron();
if (!options.environment.empty()) {
owned_environ =
internal::AlterEnvironment(new_environ, options.environment);
new_environ = owned_environ.get();
}
const char* executable_path = !options.real_path.empty()
? options.real_path.value().c_str()
: argv_cstr[0];
// If the new program has specified its PWD, change the thread-specific
// working directory. The new process will inherit it during posix_spawnp().
if (!options.current_directory.empty()) {
int rv =
ChangeCurrentThreadDirectory(options.current_directory.value().c_str());
if (rv != 0) {
DPLOG(ERROR) << "pthread_chdir_np";
return Process();
}
}
int rv;
pid_t pid;
{
// If |options.mach_ports_for_rendezvous| is specified : the server's lock
// must be held for the duration of posix_spawnp() so that new child's PID
// can be recorded with the set of ports.
const bool has_mac_ports_for_rendezvous =
!options.mach_ports_for_rendezvous.empty();
AutoLockMaybe rendezvous_lock(
has_mac_ports_for_rendezvous
? &MachPortRendezvousServer::GetInstance()->GetLock()
: nullptr);
// Use posix_spawnp as some callers expect to have PATH consulted.
rv = posix_spawnp(&pid, executable_path, file_actions.get(), attr.get(),
&argv_cstr[0], new_environ);
if (has_mac_ports_for_rendezvous) {
auto* rendezvous = MachPortRendezvousServer::GetInstance();
if (rv == 0) {
rendezvous->RegisterPortsForPid(pid, options.mach_ports_for_rendezvous);
} else {
// Because |options| is const-ref, the collection has to be copied here.
// The caller expects to relinquish ownership of any strong rights if
// LaunchProcess() were to succeed, so these rights should be manually
// destroyed on failure.
MachPortsForRendezvous ports = options.mach_ports_for_rendezvous;
for (auto& port : ports) {
port.second.Destroy();
}
}
}
}
// Restore the thread's working directory if it was changed.
if (!options.current_directory.empty()) {
ResetCurrentThreadDirectory();
}
if (rv != 0) {
DLOG(ERROR) << "posix_spawnp(" << executable_path << "): -" << rv << " "
<< strerror(rv);
return Process();
}
if (options.wait) {
// While this isn't strictly disk IO, waiting for another process to
// finish is the sort of thing ThreadRestrictions is trying to prevent.
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
pid_t ret = HANDLE_EINTR(waitpid(pid, nullptr, 0));
DPCHECK(ret > 0);
}
return Process(pid);
}
bool GetAppOutput(const CommandLine& cl, std::string* output) {
return GetAppOutput(cl.argv(), output);
}
bool GetAppOutputAndError(const CommandLine& cl, std::string* output) {
return GetAppOutputAndError(cl.argv(), output);
}
bool GetAppOutputWithExitCode(const CommandLine& cl,
std::string* output,
int* exit_code) {
GetAppOutputOptions options;
options.output = output;
bool rv = GetAppOutputInternal(cl.argv(), &options);
*exit_code = options.exit_code;
return rv;
}
bool GetAppOutput(const std::vector<std::string>& argv, std::string* output) {
GetAppOutputOptions options;
options.output = output;
return GetAppOutputInternal(argv, &options) &&
options.exit_code == EXIT_SUCCESS;
}
bool GetAppOutputAndError(const std::vector<std::string>& argv,
std::string* output) {
GetAppOutputOptions options;
options.include_stderr = true;
options.output = output;
return GetAppOutputInternal(argv, &options) &&
options.exit_code == EXIT_SUCCESS;
}
void RaiseProcessToHighPriority() {
// Historically this has not been implemented on POSIX and macOS. This could
// influence the Mach task policy in the future.
}
} // namespace base

View file

@ -0,0 +1,746 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/launch.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <setjmp.h>
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <iterator>
#include <limits>
#include <memory>
#include <set>
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/debug/debugger.h"
#include "base/debug/stack_trace.h"
#include "base/files/dir_reader_posix.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process/environment_internal.h"
#include "base/process/process.h"
#include "base/process/process_metrics.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "base/threading/platform_thread_internal_posix.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#if defined(OS_LINUX) || defined(OS_AIX)
#include <sys/prctl.h>
#endif
#if defined(OS_CHROMEOS)
#include <sys/ioctl.h>
#endif
#if defined(OS_FREEBSD)
#include <sys/event.h>
#include <sys/ucontext.h>
#endif
#if defined(OS_MACOSX)
#error "macOS should use launch_mac.cc"
#endif
extern char** environ;
namespace base {
// Friend and derived class of ScopedAllowBaseSyncPrimitives which allows
// GetAppOutputInternal() to join a process. GetAppOutputInternal() can't itself
// be a friend of ScopedAllowBaseSyncPrimitives because it is in the anonymous
// namespace.
class GetAppOutputScopedAllowBaseSyncPrimitives
: public base::ScopedAllowBaseSyncPrimitives {};
#if !defined(OS_NACL_NONSFI)
namespace {
// Get the process's "environment" (i.e. the thing that setenv/getenv
// work with).
char** GetEnvironment() {
return environ;
}
// Set the process's "environment" (i.e. the thing that setenv/getenv
// work with).
void SetEnvironment(char** env) {
environ = env;
}
// Set the calling thread's signal mask to new_sigmask and return
// the previous signal mask.
sigset_t SetSignalMask(const sigset_t& new_sigmask) {
sigset_t old_sigmask;
#if defined(OS_ANDROID)
// POSIX says pthread_sigmask() must be used in multi-threaded processes,
// but Android's pthread_sigmask() was broken until 4.1:
// https://code.google.com/p/android/issues/detail?id=15337
// http://stackoverflow.com/questions/13777109/pthread-sigmask-on-android-not-working
RAW_CHECK(sigprocmask(SIG_SETMASK, &new_sigmask, &old_sigmask) == 0);
#else
RAW_CHECK(pthread_sigmask(SIG_SETMASK, &new_sigmask, &old_sigmask) == 0);
#endif
return old_sigmask;
}
#if (!defined(OS_LINUX) && !defined(OS_AIX)) || \
(!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__))
void ResetChildSignalHandlersToDefaults() {
// The previous signal handlers are likely to be meaningless in the child's
// context so we reset them to the defaults for now. http://crbug.com/44953
// These signal handlers are set up at least in browser_main_posix.cc:
// BrowserMainPartsPosix::PreEarlyInitialization and stack_trace_posix.cc:
// EnableInProcessStackDumping.
signal(SIGHUP, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGILL, SIG_DFL);
signal(SIGABRT, SIG_DFL);
signal(SIGFPE, SIG_DFL);
signal(SIGBUS, SIG_DFL);
signal(SIGSEGV, SIG_DFL);
signal(SIGSYS, SIG_DFL);
signal(SIGTERM, SIG_DFL);
}
#else
// TODO(jln): remove the Linux special case once kernels are fixed.
// Internally the kernel makes sigset_t an array of long large enough to have
// one bit per signal.
typedef uint64_t kernel_sigset_t;
// This is what struct sigaction looks like to the kernel at least on X86 and
// ARM. MIPS, for instance, is very different.
struct kernel_sigaction {
void* k_sa_handler; // For this usage it only needs to be a generic pointer.
unsigned long k_sa_flags;
void* k_sa_restorer; // For this usage it only needs to be a generic pointer.
kernel_sigset_t k_sa_mask;
};
// glibc's sigaction() will prevent access to sa_restorer, so we need to roll
// our own.
int sys_rt_sigaction(int sig, const struct kernel_sigaction* act,
struct kernel_sigaction* oact) {
return syscall(SYS_rt_sigaction, sig, act, oact, sizeof(kernel_sigset_t));
}
// This function is intended to be used in between fork() and execve() and will
// reset all signal handlers to the default.
// The motivation for going through all of them is that sa_restorer can leak
// from parents and help defeat ASLR on buggy kernels. We reset it to null.
// See crbug.com/177956.
void ResetChildSignalHandlersToDefaults(void) {
for (int signum = 1; ; ++signum) {
struct kernel_sigaction act = {nullptr};
int sigaction_get_ret = sys_rt_sigaction(signum, nullptr, &act);
if (sigaction_get_ret && errno == EINVAL) {
#if !defined(NDEBUG)
// Linux supports 32 real-time signals from 33 to 64.
// If the number of signals in the Linux kernel changes, someone should
// look at this code.
const int kNumberOfSignals = 64;
RAW_CHECK(signum == kNumberOfSignals + 1);
#endif // !defined(NDEBUG)
break;
}
// All other failures are fatal.
if (sigaction_get_ret) {
RAW_LOG(FATAL, "sigaction (get) failed.");
}
// The kernel won't allow to re-set SIGKILL or SIGSTOP.
if (signum != SIGSTOP && signum != SIGKILL) {
act.k_sa_handler = reinterpret_cast<void*>(SIG_DFL);
act.k_sa_restorer = nullptr;
if (sys_rt_sigaction(signum, &act, nullptr)) {
RAW_LOG(FATAL, "sigaction (set) failed.");
}
}
#if !defined(NDEBUG)
// Now ask the kernel again and check that no restorer will leak.
if (sys_rt_sigaction(signum, nullptr, &act) || act.k_sa_restorer) {
RAW_LOG(FATAL, "Cound not fix sa_restorer.");
}
#endif // !defined(NDEBUG)
}
}
#endif // !defined(OS_LINUX) ||
// (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__))
} // anonymous namespace
// Functor for |ScopedDIR| (below).
struct ScopedDIRClose {
inline void operator()(DIR* x) const {
if (x)
closedir(x);
}
};
// Automatically closes |DIR*|s.
typedef std::unique_ptr<DIR, ScopedDIRClose> ScopedDIR;
#if defined(OS_LINUX) || defined(OS_AIX)
static const char kFDDir[] = "/proc/self/fd";
#elif defined(OS_SOLARIS)
static const char kFDDir[] = "/dev/fd";
#elif defined(OS_FREEBSD)
static const char kFDDir[] = "/dev/fd";
#elif defined(OS_OPENBSD)
static const char kFDDir[] = "/dev/fd";
#elif defined(OS_ANDROID)
static const char kFDDir[] = "/proc/self/fd";
#endif
void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
// DANGER: no calls to malloc or locks are allowed from now on:
// http://crbug.com/36678
// Get the maximum number of FDs possible.
size_t max_fds = GetMaxFds();
DirReaderPosix fd_dir(kFDDir);
if (!fd_dir.IsValid()) {
// Fallback case: Try every possible fd.
for (size_t i = 0; i < max_fds; ++i) {
const int fd = static_cast<int>(i);
if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
continue;
// Cannot use STL iterators here, since debug iterators use locks.
size_t j;
for (j = 0; j < saved_mapping.size(); j++) {
if (fd == saved_mapping[j].dest)
break;
}
if (j < saved_mapping.size())
continue;
// Since we're just trying to close anything we can find,
// ignore any error return values of close().
close(fd);
}
return;
}
const int dir_fd = fd_dir.fd();
for ( ; fd_dir.Next(); ) {
// Skip . and .. entries.
if (fd_dir.name()[0] == '.')
continue;
char *endptr;
errno = 0;
const long int fd = strtol(fd_dir.name(), &endptr, 10);
if (fd_dir.name()[0] == 0 || *endptr || fd < 0 || errno)
continue;
if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
continue;
// Cannot use STL iterators here, since debug iterators use locks.
size_t i;
for (i = 0; i < saved_mapping.size(); i++) {
if (fd == saved_mapping[i].dest)
break;
}
if (i < saved_mapping.size())
continue;
if (fd == dir_fd)
continue;
int ret = IGNORE_EINTR(close(fd));
DPCHECK(ret == 0);
}
}
Process LaunchProcess(const CommandLine& cmdline,
const LaunchOptions& options) {
return LaunchProcess(cmdline.argv(), options);
}
Process LaunchProcess(const std::vector<std::string>& argv,
const LaunchOptions& options) {
TRACE_EVENT0("base", "LaunchProcess");
InjectiveMultimap fd_shuffle1;
InjectiveMultimap fd_shuffle2;
fd_shuffle1.reserve(options.fds_to_remap.size());
fd_shuffle2.reserve(options.fds_to_remap.size());
std::vector<char*> argv_cstr;
argv_cstr.reserve(argv.size() + 1);
for (const auto& arg : argv)
argv_cstr.push_back(const_cast<char*>(arg.c_str()));
argv_cstr.push_back(nullptr);
std::unique_ptr<char* []> new_environ;
char* const empty_environ = nullptr;
char* const* old_environ = GetEnvironment();
if (options.clear_environment)
old_environ = &empty_environ;
if (!options.environment.empty())
new_environ = internal::AlterEnvironment(old_environ, options.environment);
sigset_t full_sigset;
sigfillset(&full_sigset);
const sigset_t orig_sigmask = SetSignalMask(full_sigset);
const char* current_directory = nullptr;
if (!options.current_directory.empty()) {
current_directory = options.current_directory.value().c_str();
}
pid_t pid;
base::TimeTicks before_fork = TimeTicks::Now();
#if defined(OS_LINUX) || defined(OS_AIX)
if (options.clone_flags) {
// Signal handling in this function assumes the creation of a new
// process, so we check that a thread is not being created by mistake
// and that signal handling follows the process-creation rules.
RAW_CHECK(
!(options.clone_flags & (CLONE_SIGHAND | CLONE_THREAD | CLONE_VM)));
// We specify a null ptid and ctid.
RAW_CHECK(
!(options.clone_flags &
(CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | CLONE_PARENT_SETTID)));
// Since we use waitpid, we do not support custom termination signals in the
// clone flags.
RAW_CHECK((options.clone_flags & 0xff) == 0);
pid = ForkWithFlags(options.clone_flags | SIGCHLD, nullptr, nullptr);
} else
#endif
{
pid = fork();
}
// Always restore the original signal mask in the parent.
if (pid != 0) {
base::TimeTicks after_fork = TimeTicks::Now();
SetSignalMask(orig_sigmask);
base::TimeDelta fork_time = after_fork - before_fork;
UMA_HISTOGRAM_TIMES("MPArch.ForkTime", fork_time);
}
if (pid < 0) {
DPLOG(ERROR) << "fork";
return Process();
}
if (pid == 0) {
// Child process
// DANGER: no calls to malloc or locks are allowed from now on:
// http://crbug.com/36678
// DANGER: fork() rule: in the child, if you don't end up doing exec*(),
// you call _exit() instead of exit(). This is because _exit() does not
// call any previously-registered (in the parent) exit handlers, which
// might do things like block waiting for threads that don't even exist
// in the child.
// If a child process uses the readline library, the process block forever.
// In BSD like OSes including OS X it is safe to assign /dev/null as stdin.
// See http://crbug.com/56596.
base::ScopedFD null_fd(HANDLE_EINTR(open("/dev/null", O_RDONLY)));
if (!null_fd.is_valid()) {
RAW_LOG(ERROR, "Failed to open /dev/null");
_exit(127);
}
int new_fd = HANDLE_EINTR(dup2(null_fd.get(), STDIN_FILENO));
if (new_fd != STDIN_FILENO) {
RAW_LOG(ERROR, "Failed to dup /dev/null for stdin");
_exit(127);
}
if (options.new_process_group) {
// Instead of inheriting the process group ID of the parent, the child
// starts off a new process group with pgid equal to its process ID.
if (setpgid(0, 0) < 0) {
RAW_LOG(ERROR, "setpgid failed");
_exit(127);
}
}
if (options.maximize_rlimits) {
// Some resource limits need to be maximal in this child.
for (auto resource : *options.maximize_rlimits) {
struct rlimit limit;
if (getrlimit(resource, &limit) < 0) {
RAW_LOG(WARNING, "getrlimit failed");
} else if (limit.rlim_cur < limit.rlim_max) {
limit.rlim_cur = limit.rlim_max;
if (setrlimit(resource, &limit) < 0) {
RAW_LOG(WARNING, "setrlimit failed");
}
}
}
}
ResetChildSignalHandlersToDefaults();
SetSignalMask(orig_sigmask);
#if 0
// When debugging it can be helpful to check that we really aren't making
// any hidden calls to malloc.
void *malloc_thunk =
reinterpret_cast<void*>(reinterpret_cast<intptr_t>(malloc) & ~4095);
mprotect(malloc_thunk, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
memset(reinterpret_cast<void*>(malloc), 0xff, 8);
#endif // 0
#if defined(OS_CHROMEOS)
if (options.ctrl_terminal_fd >= 0) {
// Set process' controlling terminal.
if (HANDLE_EINTR(setsid()) != -1) {
if (HANDLE_EINTR(
ioctl(options.ctrl_terminal_fd, TIOCSCTTY, nullptr)) == -1) {
RAW_LOG(WARNING, "ioctl(TIOCSCTTY), ctrl terminal not set");
}
} else {
RAW_LOG(WARNING, "setsid failed, ctrl terminal not set");
}
}
#endif // defined(OS_CHROMEOS)
// Cannot use STL iterators here, since debug iterators use locks.
// NOLINTNEXTLINE(modernize-loop-convert)
for (size_t i = 0; i < options.fds_to_remap.size(); ++i) {
const FileHandleMappingVector::value_type& value =
options.fds_to_remap[i];
fd_shuffle1.push_back(InjectionArc(value.first, value.second, false));
fd_shuffle2.push_back(InjectionArc(value.first, value.second, false));
}
if (!options.environment.empty() || options.clear_environment)
SetEnvironment(new_environ.get());
// fd_shuffle1 is mutated by this call because it cannot malloc.
if (!ShuffleFileDescriptors(&fd_shuffle1))
_exit(127);
CloseSuperfluousFds(fd_shuffle2);
// Set NO_NEW_PRIVS by default. Since NO_NEW_PRIVS only exists in kernel
// 3.5+, do not check the return value of prctl here.
#if defined(OS_LINUX) || defined(OS_AIX)
#ifndef PR_SET_NO_NEW_PRIVS
#define PR_SET_NO_NEW_PRIVS 38
#endif
if (!options.allow_new_privs) {
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) && errno != EINVAL) {
// Only log if the error is not EINVAL (i.e. not supported).
RAW_LOG(FATAL, "prctl(PR_SET_NO_NEW_PRIVS) failed");
}
}
if (options.kill_on_parent_death) {
if (prctl(PR_SET_PDEATHSIG, SIGKILL) != 0) {
RAW_LOG(ERROR, "prctl(PR_SET_PDEATHSIG) failed");
_exit(127);
}
}
#endif
if (current_directory != nullptr) {
RAW_CHECK(chdir(current_directory) == 0);
}
if (options.pre_exec_delegate != nullptr) {
options.pre_exec_delegate->RunAsyncSafe();
}
const char* executable_path = !options.real_path.empty() ?
options.real_path.value().c_str() : argv_cstr[0];
execvp(executable_path, argv_cstr.data());
RAW_LOG(ERROR, "LaunchProcess: failed to execvp:");
RAW_LOG(ERROR, argv_cstr[0]);
_exit(127);
} else {
// Parent process
if (options.wait) {
// While this isn't strictly disk IO, waiting for another process to
// finish is the sort of thing ThreadRestrictions is trying to prevent.
ScopedBlockingCall scoped_blocking_call(FROM_HERE,
BlockingType::MAY_BLOCK);
pid_t ret = HANDLE_EINTR(waitpid(pid, nullptr, 0));
DPCHECK(ret > 0);
}
}
return Process(pid);
}
void RaiseProcessToHighPriority() {
// On POSIX, we don't actually do anything here. We could try to nice() or
// setpriority() or sched_getscheduler, but these all require extra rights.
}
// Executes the application specified by |argv| and wait for it to exit. Stores
// the output (stdout) in |output|. If |do_search_path| is set, it searches the
// path for the application; in that case, |envp| must be null, and it will use
// the current environment. If |do_search_path| is false, |argv[0]| should fully
// specify the path of the application, and |envp| will be used as the
// environment. If |include_stderr| is true, includes stderr otherwise redirects
// it to /dev/null.
// The return value of the function indicates success or failure. In the case of
// success, the application exit code will be returned in |*exit_code|, which
// should be checked to determine if the application ran successfully.
static bool GetAppOutputInternal(
const std::vector<std::string>& argv,
char* const envp[],
bool include_stderr,
std::string* output,
bool do_search_path,
int* exit_code) {
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
// exit_code must be supplied so calling function can determine success.
DCHECK(exit_code);
*exit_code = EXIT_FAILURE;
// Declare and call reserve() here before calling fork() because the child
// process cannot allocate memory.
std::vector<char*> argv_cstr;
argv_cstr.reserve(argv.size() + 1);
InjectiveMultimap fd_shuffle1;
InjectiveMultimap fd_shuffle2;
fd_shuffle1.reserve(3);
fd_shuffle2.reserve(3);
// Either |do_search_path| should be false or |envp| should be null, but not
// both.
DCHECK(!do_search_path ^ !envp);
int pipe_fd[2];
if (pipe(pipe_fd) < 0)
return false;
pid_t pid = fork();
switch (pid) {
case -1: {
// error
close(pipe_fd[0]);
close(pipe_fd[1]);
return false;
}
case 0: {
// child
//
// DANGER: no calls to malloc or locks are allowed from now on:
// http://crbug.com/36678
// Obscure fork() rule: in the child, if you don't end up doing exec*(),
// you call _exit() instead of exit(). This is because _exit() does not
// call any previously-registered (in the parent) exit handlers, which
// might do things like block waiting for threads that don't even exist
// in the child.
int dev_null = open("/dev/null", O_WRONLY);
if (dev_null < 0)
_exit(127);
fd_shuffle1.push_back(InjectionArc(pipe_fd[1], STDOUT_FILENO, true));
fd_shuffle1.push_back(InjectionArc(include_stderr ? pipe_fd[1] : dev_null,
STDERR_FILENO, true));
fd_shuffle1.push_back(InjectionArc(dev_null, STDIN_FILENO, true));
// Adding another element here? Remeber to increase the argument to
// reserve(), above.
// Cannot use STL iterators here, since debug iterators use locks.
// NOLINTNEXTLINE(modernize-loop-convert)
for (size_t i = 0; i < fd_shuffle1.size(); ++i)
fd_shuffle2.push_back(fd_shuffle1[i]);
if (!ShuffleFileDescriptors(&fd_shuffle1))
_exit(127);
CloseSuperfluousFds(fd_shuffle2);
// Cannot use STL iterators here, since debug iterators use locks.
// NOLINTNEXTLINE(modernize-loop-convert)
for (size_t i = 0; i < argv.size(); ++i)
argv_cstr.push_back(const_cast<char*>(argv[i].c_str()));
argv_cstr.push_back(nullptr);
if (do_search_path)
execvp(argv_cstr[0], argv_cstr.data());
else
execve(argv_cstr[0], argv_cstr.data(), envp);
_exit(127);
}
default: {
// parent
//
// Close our writing end of pipe now. Otherwise later read would not
// be able to detect end of child's output (in theory we could still
// write to the pipe).
close(pipe_fd[1]);
output->clear();
while (true) {
char buffer[256];
ssize_t bytes_read =
HANDLE_EINTR(read(pipe_fd[0], buffer, sizeof(buffer)));
if (bytes_read <= 0)
break;
output->append(buffer, bytes_read);
}
close(pipe_fd[0]);
// Always wait for exit code (even if we know we'll declare
// GOT_MAX_OUTPUT).
Process process(pid);
// A process launched with GetAppOutput*() usually doesn't wait on the
// process that launched it and thus chances of deadlock are low.
GetAppOutputScopedAllowBaseSyncPrimitives allow_base_sync_primitives;
return process.WaitForExit(exit_code);
}
}
}
bool GetAppOutput(const CommandLine& cl, std::string* output) {
return GetAppOutput(cl.argv(), output);
}
bool GetAppOutput(const std::vector<std::string>& argv, std::string* output) {
// Run |execve()| with the current environment.
int exit_code;
bool result =
GetAppOutputInternal(argv, nullptr, false, output, true, &exit_code);
return result && exit_code == EXIT_SUCCESS;
}
bool GetAppOutputAndError(const CommandLine& cl, std::string* output) {
// Run |execve()| with the current environment.
int exit_code;
bool result =
GetAppOutputInternal(cl.argv(), nullptr, true, output, true, &exit_code);
return result && exit_code == EXIT_SUCCESS;
}
bool GetAppOutputAndError(const std::vector<std::string>& argv,
std::string* output) {
int exit_code;
bool result =
GetAppOutputInternal(argv, nullptr, true, output, true, &exit_code);
return result && exit_code == EXIT_SUCCESS;
}
bool GetAppOutputWithExitCode(const CommandLine& cl,
std::string* output,
int* exit_code) {
// Run |execve()| with the current environment.
return GetAppOutputInternal(cl.argv(), nullptr, false, output, true,
exit_code);
}
#endif // !defined(OS_NACL_NONSFI)
#if defined(OS_LINUX) || defined(OS_NACL_NONSFI) || defined(OS_AIX)
namespace {
// This function runs on the stack specified on the clone call. It uses longjmp
// to switch back to the original stack so the child can return from sys_clone.
int CloneHelper(void* arg) {
jmp_buf* env_ptr = reinterpret_cast<jmp_buf*>(arg);
longjmp(*env_ptr, 1);
// Should not be reached.
RAW_CHECK(false);
return 1;
}
// This function is noinline to ensure that stack_buf is below the stack pointer
// that is saved when setjmp is called below. This is needed because when
// compiled with FORTIFY_SOURCE, glibc's longjmp checks that the stack is moved
// upwards. See crbug.com/442912 for more details.
#if defined(ADDRESS_SANITIZER)
// Disable AddressSanitizer instrumentation for this function to make sure
// |stack_buf| is allocated on thread stack instead of ASan's fake stack.
// Under ASan longjmp() will attempt to clean up the area between the old and
// new stack pointers and print a warning that may confuse the user.
__attribute__((no_sanitize_address))
#endif
NOINLINE pid_t CloneAndLongjmpInChild(unsigned long flags,
pid_t* ptid,
pid_t* ctid,
jmp_buf* env) {
// We use the libc clone wrapper instead of making the syscall
// directly because making the syscall may fail to update the libc's
// internal pid cache. The libc interface unfortunately requires
// specifying a new stack, so we use setjmp/longjmp to emulate
// fork-like behavior.
alignas(16) char stack_buf[PTHREAD_STACK_MIN];
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
defined(ARCH_CPU_MIPS_FAMILY) || defined(ARCH_CPU_S390_FAMILY) || \
defined(ARCH_CPU_PPC64_FAMILY)
// The stack grows downward.
void* stack = stack_buf + sizeof(stack_buf);
#else
#error "Unsupported architecture"
#endif
return clone(&CloneHelper, stack, flags, env, ptid, nullptr, ctid);
}
} // anonymous namespace
pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid) {
const bool clone_tls_used = flags & CLONE_SETTLS;
const bool invalid_ctid =
(flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) && !ctid;
const bool invalid_ptid = (flags & CLONE_PARENT_SETTID) && !ptid;
// We do not support CLONE_VM.
const bool clone_vm_used = flags & CLONE_VM;
if (clone_tls_used || invalid_ctid || invalid_ptid || clone_vm_used) {
RAW_LOG(FATAL, "Invalid usage of ForkWithFlags");
}
jmp_buf env;
if (setjmp(env) == 0) {
return CloneAndLongjmpInChild(flags, ptid, ctid, &env);
}
#if defined(OS_LINUX)
// Since we use clone() directly, it does not call any pthread_aftork()
// callbacks, we explicitly clear tid cache here (normally this call is
// done as pthread_aftork() callback). See crbug.com/902514.
base::internal::ClearTidCache();
#endif // defined(OS_LINUX)
return 0;
}
#endif // defined(OS_LINUX) || defined(OS_NACL_NONSFI)
} // namespace base

View file

@ -0,0 +1,428 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/launch.h"
#include <fcntl.h>
#include <io.h>
#include <shellapi.h>
#include <windows.h>
#include <userenv.h>
#include <psapi.h>
#include <ios>
#include <limits>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/debug/activity_tracker.h"
#include "base/debug/stack_trace.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/process/environment_internal.h"
#include "base/process/kill.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
#include "base/threading/scoped_thread_priority.h"
#include "base/win/scoped_handle.h"
#include "base/win/scoped_process_information.h"
#include "base/win/startup_information.h"
#include "base/win/windows_version.h"
namespace base {
namespace {
bool GetAppOutputInternal(CommandLine::StringPieceType cl,
bool include_stderr,
std::string* output,
int* exit_code) {
HANDLE out_read = nullptr;
HANDLE out_write = nullptr;
SECURITY_ATTRIBUTES sa_attr;
// Set the bInheritHandle flag so pipe handles are inherited.
sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
sa_attr.bInheritHandle = TRUE;
sa_attr.lpSecurityDescriptor = nullptr;
// Create the pipe for the child process's STDOUT.
if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
NOTREACHED() << "Failed to create pipe";
return false;
}
// Ensure we don't leak the handles.
win::ScopedHandle scoped_out_read(out_read);
win::ScopedHandle scoped_out_write(out_write);
// Ensure the read handles to the pipes are not inherited.
if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
NOTREACHED() << "Failed to disabled pipe inheritance";
return false;
}
FilePath::StringType writable_command_line_string(cl);
STARTUPINFO start_info = {};
start_info.cb = sizeof(STARTUPINFO);
start_info.hStdOutput = out_write;
// Keep the normal stdin.
start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
if (include_stderr) {
start_info.hStdError = out_write;
} else {
start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
}
start_info.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
PROCESS_INFORMATION temp_process_info = {};
if (!CreateProcess(nullptr, data(writable_command_line_string), nullptr,
nullptr,
TRUE, // Handles are inherited.
0, nullptr, nullptr, &start_info, &temp_process_info)) {
NOTREACHED() << "Failed to start process";
return false;
}
win::ScopedProcessInformation proc_info(temp_process_info);
debug::GlobalActivityTracker* tracker = debug::GlobalActivityTracker::Get();
if (tracker)
tracker->RecordProcessLaunch(proc_info.process_id(), cl.as_string());
// Close our writing end of pipe now. Otherwise later read would not be able
// to detect end of child's output.
scoped_out_write.Close();
// Read output from the child process's pipe for STDOUT
const int kBufferSize = 1024;
char buffer[kBufferSize];
for (;;) {
DWORD bytes_read = 0;
BOOL success =
::ReadFile(out_read, buffer, kBufferSize, &bytes_read, nullptr);
if (!success || bytes_read == 0)
break;
output->append(buffer, bytes_read);
}
// Let's wait for the process to finish.
WaitForSingleObject(proc_info.process_handle(), INFINITE);
TerminationStatus status =
GetTerminationStatus(proc_info.process_handle(), exit_code);
debug::GlobalActivityTracker::RecordProcessExitIfEnabled(
proc_info.process_id(), *exit_code);
return status != TERMINATION_STATUS_PROCESS_CRASHED &&
status != TERMINATION_STATUS_ABNORMAL_TERMINATION;
}
} // namespace
void RouteStdioToConsole(bool create_console_if_not_found) {
// Don't change anything if stdout or stderr already point to a
// valid stream.
//
// If we are running under Buildbot or under Cygwin's default
// terminal (mintty), stderr and stderr will be pipe handles. In
// that case, we don't want to open CONOUT$, because its output
// likely does not go anywhere.
//
// We don't use GetStdHandle() to check stdout/stderr here because
// it can return dangling IDs of handles that were never inherited
// by this process. These IDs could have been reused by the time
// this function is called. The CRT checks the validity of
// stdout/stderr on startup (before the handle IDs can be reused).
// _fileno(stdout) will return -2 (_NO_CONSOLE_FILENO) if stdout was
// invalid.
if (_fileno(stdout) >= 0 || _fileno(stderr) >= 0) {
// _fileno was broken for SUBSYSTEM:WINDOWS from VS2010 to VS2012/2013.
// http://crbug.com/358267. Confirm that the underlying HANDLE is valid
// before aborting.
intptr_t stdout_handle = _get_osfhandle(_fileno(stdout));
intptr_t stderr_handle = _get_osfhandle(_fileno(stderr));
if (stdout_handle >= 0 || stderr_handle >= 0)
return;
}
if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
unsigned int result = GetLastError();
// Was probably already attached.
if (result == ERROR_ACCESS_DENIED)
return;
// Don't bother creating a new console for each child process if the
// parent process is invalid (eg: crashed).
if (result == ERROR_GEN_FAILURE)
return;
if (create_console_if_not_found) {
// Make a new console if attaching to parent fails with any other error.
// It should be ERROR_INVALID_HANDLE at this point, which means the
// browser was likely not started from a console.
AllocConsole();
} else {
return;
}
}
// Arbitrary byte count to use when buffering output lines. More
// means potential waste, less means more risk of interleaved
// log-lines in output.
enum { kOutputBufferSize = 64 * 1024 };
if (freopen("CONOUT$", "w", stdout)) {
setvbuf(stdout, nullptr, _IOLBF, kOutputBufferSize);
// Overwrite FD 1 for the benefit of any code that uses this FD
// directly. This is safe because the CRT allocates FDs 0, 1 and
// 2 at startup even if they don't have valid underlying Windows
// handles. This means we won't be overwriting an FD created by
// _open() after startup.
_dup2(_fileno(stdout), 1);
}
if (freopen("CONOUT$", "w", stderr)) {
setvbuf(stderr, nullptr, _IOLBF, kOutputBufferSize);
_dup2(_fileno(stderr), 2);
}
// Fix all cout, wcout, cin, wcin, cerr, wcerr, clog and wclog.
std::ios::sync_with_stdio();
}
Process LaunchProcess(const CommandLine& cmdline,
const LaunchOptions& options) {
return LaunchProcess(cmdline.GetCommandLineString(), options);
}
Process LaunchProcess(const CommandLine::StringType& cmdline,
const LaunchOptions& options) {
// Mitigate the issues caused by loading DLLs on a background thread
// (http://crbug/973868).
SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
win::StartupInformation startup_info_wrapper;
STARTUPINFO* startup_info = startup_info_wrapper.startup_info();
bool inherit_handles = options.inherit_mode == LaunchOptions::Inherit::kAll;
DWORD flags = 0;
if (!options.handles_to_inherit.empty()) {
DCHECK_EQ(options.inherit_mode, LaunchOptions::Inherit::kSpecific);
if (options.handles_to_inherit.size() >
std::numeric_limits<DWORD>::max() / sizeof(HANDLE)) {
DLOG(ERROR) << "Too many handles to inherit.";
return Process();
}
// Ensure the handles can be inherited.
for (HANDLE handle : options.handles_to_inherit) {
BOOL result = SetHandleInformation(handle, HANDLE_FLAG_INHERIT,
HANDLE_FLAG_INHERIT);
PCHECK(result);
}
if (!startup_info_wrapper.InitializeProcThreadAttributeList(1)) {
DPLOG(ERROR);
return Process();
}
if (!startup_info_wrapper.UpdateProcThreadAttribute(
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
const_cast<HANDLE*>(&options.handles_to_inherit[0]),
static_cast<DWORD>(options.handles_to_inherit.size() *
sizeof(HANDLE)))) {
DPLOG(ERROR);
return Process();
}
inherit_handles = true;
flags |= EXTENDED_STARTUPINFO_PRESENT;
}
if (options.feedback_cursor_off)
startup_info->dwFlags |= STARTF_FORCEOFFFEEDBACK;
if (options.empty_desktop_name)
startup_info->lpDesktop = const_cast<wchar_t*>(L"");
startup_info->dwFlags |= STARTF_USESHOWWINDOW;
startup_info->wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOWNORMAL;
if (options.stdin_handle || options.stdout_handle || options.stderr_handle) {
DCHECK(inherit_handles);
DCHECK(options.stdin_handle);
DCHECK(options.stdout_handle);
DCHECK(options.stderr_handle);
startup_info->dwFlags |= STARTF_USESTDHANDLES;
startup_info->hStdInput = options.stdin_handle;
startup_info->hStdOutput = options.stdout_handle;
startup_info->hStdError = options.stderr_handle;
}
if (options.job_handle) {
// If this code is run under a debugger, the launched process is
// automatically associated with a job object created by the debugger.
// The CREATE_BREAKAWAY_FROM_JOB flag is used to prevent this on Windows
// releases that do not support nested jobs.
if (win::GetVersion() < win::Version::WIN8)
flags |= CREATE_BREAKAWAY_FROM_JOB;
}
if (options.force_breakaway_from_job_)
flags |= CREATE_BREAKAWAY_FROM_JOB;
PROCESS_INFORMATION temp_process_info = {};
LPCTSTR current_directory = options.current_directory.empty()
? nullptr
: options.current_directory.value().c_str();
auto writable_cmdline(cmdline);
DCHECK(!(flags & CREATE_SUSPENDED))
<< "Creating a suspended process can lead to hung processes if the "
<< "launching process is killed before it assigns the process to the"
<< "job. https://crbug.com/820996";
if (options.as_user) {
flags |= CREATE_UNICODE_ENVIRONMENT;
void* environment_block = nullptr;
if (!CreateEnvironmentBlock(&environment_block, options.as_user, FALSE)) {
DPLOG(ERROR);
return Process();
}
// Environment options are not implemented for use with |as_user|.
DCHECK(!options.clear_environment);
DCHECK(options.environment.empty());
BOOL launched = CreateProcessAsUser(
options.as_user, nullptr, data(writable_cmdline), nullptr, nullptr,
inherit_handles, flags, environment_block, current_directory,
startup_info, &temp_process_info);
DestroyEnvironmentBlock(environment_block);
if (!launched) {
DPLOG(ERROR) << "Command line:" << std::endl
<< WideToUTF8(cmdline) << std::endl;
return Process();
}
} else {
wchar_t* new_environment = nullptr;
std::wstring env_storage;
if (options.clear_environment || !options.environment.empty()) {
if (options.clear_environment) {
static const wchar_t kEmptyEnvironment[] = {0};
env_storage =
internal::AlterEnvironment(kEmptyEnvironment, options.environment);
} else {
wchar_t* old_environment = GetEnvironmentStrings();
if (!old_environment) {
DPLOG(ERROR);
return Process();
}
env_storage =
internal::AlterEnvironment(old_environment, options.environment);
FreeEnvironmentStrings(old_environment);
}
new_environment = data(env_storage);
flags |= CREATE_UNICODE_ENVIRONMENT;
}
if (!CreateProcess(nullptr, data(writable_cmdline), nullptr, nullptr,
inherit_handles, flags, new_environment,
current_directory, startup_info, &temp_process_info)) {
DPLOG(ERROR) << "Command line:" << std::endl << cmdline << std::endl;
return Process();
}
}
win::ScopedProcessInformation process_info(temp_process_info);
if (options.job_handle &&
!AssignProcessToJobObject(options.job_handle,
process_info.process_handle())) {
DPLOG(ERROR) << "Could not AssignProcessToObject";
Process scoped_process(process_info.TakeProcessHandle());
scoped_process.Terminate(win::kProcessKilledExitCode, true);
return Process();
}
if (options.grant_foreground_privilege &&
!AllowSetForegroundWindow(GetProcId(process_info.process_handle()))) {
DPLOG(ERROR) << "Failed to grant foreground privilege to launched process";
}
if (options.wait)
WaitForSingleObject(process_info.process_handle(), INFINITE);
debug::GlobalActivityTracker::RecordProcessLaunchIfEnabled(
process_info.process_id(), cmdline);
return Process(process_info.TakeProcessHandle());
}
Process LaunchElevatedProcess(const CommandLine& cmdline,
const LaunchOptions& options) {
const FilePath::StringType file = cmdline.GetProgram().value();
const CommandLine::StringType arguments = cmdline.GetArgumentsString();
SHELLEXECUTEINFO shex_info = {};
shex_info.cbSize = sizeof(shex_info);
shex_info.fMask = SEE_MASK_NOCLOSEPROCESS;
shex_info.hwnd = GetActiveWindow();
shex_info.lpVerb = L"runas";
shex_info.lpFile = file.c_str();
shex_info.lpParameters = arguments.c_str();
shex_info.lpDirectory = nullptr;
shex_info.nShow = options.start_hidden ? SW_HIDE : SW_SHOWNORMAL;
shex_info.hInstApp = nullptr;
if (!ShellExecuteEx(&shex_info)) {
DPLOG(ERROR);
return Process();
}
if (options.wait)
WaitForSingleObject(shex_info.hProcess, INFINITE);
debug::GlobalActivityTracker::RecordProcessLaunchIfEnabled(
GetProcessId(shex_info.hProcess), file, arguments);
return Process(shex_info.hProcess);
}
bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags) {
JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {};
limit_info.BasicLimitInformation.LimitFlags = limit_flags;
return 0 != SetInformationJobObject(
job_object,
JobObjectExtendedLimitInformation,
&limit_info,
sizeof(limit_info));
}
bool GetAppOutput(const CommandLine& cl, std::string* output) {
return GetAppOutput(cl.GetCommandLineString(), output);
}
bool GetAppOutputAndError(const CommandLine& cl, std::string* output) {
int exit_code;
return GetAppOutputInternal(
cl.GetCommandLineString(), true, output, &exit_code);
}
bool GetAppOutputWithExitCode(const CommandLine& cl,
std::string* output,
int* exit_code) {
return GetAppOutputInternal(
cl.GetCommandLineString(), false, output, exit_code);
}
bool GetAppOutput(CommandLine::StringPieceType cl, std::string* output) {
int exit_code;
return GetAppOutputInternal(cl, false, output, &exit_code);
}
void RaiseProcessToHighPriority() {
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
}
} // namespace base

View file

@ -0,0 +1,94 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/memory.h"
#if defined(OS_WIN)
#include <windows.h>
#endif // defined(OS_WIN)
#include "base/debug/alias.h"
#include "base/logging.h"
#include "base/partition_alloc_buildflags.h"
#if BUILDFLAG(USE_PARTITION_ALLOC)
#include "base/allocator/partition_allocator/page_allocator.h"
#endif
#include "build/build_config.h"
namespace base {
namespace internal {
NOINLINE void OnNoMemoryInternal(size_t size) {
#if defined(OS_WIN)
// Kill the process. This is important for security since most of code
// does not check the result of memory allocation.
// https://msdn.microsoft.com/en-us/library/het71c37.aspx
// Pass the size of the failed request in an exception argument.
ULONG_PTR exception_args[] = {size};
::RaiseException(base::win::kOomExceptionCode, EXCEPTION_NONCONTINUABLE,
base::size(exception_args), exception_args);
// Safety check, make sure process exits here.
_exit(win::kOomExceptionCode);
#else
size_t tmp_size = size;
base::debug::Alias(&tmp_size);
LOG(FATAL) << "Out of memory. size=" << tmp_size;
#endif // defined(OS_WIN)
}
} // namespace internal
// Defined in memory_win.cc for Windows.
#if !defined(OS_WIN)
namespace {
// Breakpad server classifies base::`anonymous namespace'::OnNoMemory as
// out-of-memory crash.
NOINLINE void OnNoMemory(size_t size) {
internal::OnNoMemoryInternal(size);
}
} // namespace
void TerminateBecauseOutOfMemory(size_t size) {
OnNoMemory(size);
}
#endif // !defined(OS_WIN)
// Defined in memory_mac.mm for Mac.
#if !defined(OS_MACOSX)
bool UncheckedCalloc(size_t num_items, size_t size, void** result) {
const size_t alloc_size = num_items * size;
// Overflow check
if (size && ((alloc_size / size) != num_items)) {
*result = nullptr;
return false;
}
if (!UncheckedMalloc(alloc_size, result))
return false;
memset(*result, 0, alloc_size);
return true;
}
#endif // defined(OS_MACOSX)
namespace internal {
bool ReleaseAddressSpaceReservation() {
#if BUILDFLAG(USE_PARTITION_ALLOC)
return ReleaseReservation();
#else
return false;
#endif
}
} // namespace internal
} // namespace base

View file

@ -0,0 +1,89 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_PROCESS_MEMORY_H_
#define BASE_PROCESS_MEMORY_H_
#include <stddef.h>
#include "base/base_export.h"
#include "base/process/process_handle.h"
#include "build/build_config.h"
namespace base {
// Enables 'terminate on heap corruption' flag. Helps protect against heap
// overflow. Has no effect if the OS doesn't provide the necessary facility.
BASE_EXPORT void EnableTerminationOnHeapCorruption();
// Turns on process termination if memory runs out.
BASE_EXPORT void EnableTerminationOnOutOfMemory();
// Terminates process. Should be called only for out of memory errors.
// Crash reporting classifies such crashes as OOM.
BASE_EXPORT void TerminateBecauseOutOfMemory(size_t size);
#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX)
BASE_EXPORT extern size_t g_oom_size;
// The maximum allowed value for the OOM score.
const int kMaxOomScore = 1000;
// This adjusts /proc/<pid>/oom_score_adj so the Linux OOM killer will
// prefer to kill certain process types over others. The range for the
// adjustment is [-1000, 1000], with [0, 1000] being user accessible.
// If the Linux system doesn't support the newer oom_score_adj range
// of [0, 1000], then we revert to using the older oom_adj, and
// translate the given value into [0, 15]. Some aliasing of values
// may occur in that case, of course.
BASE_EXPORT bool AdjustOOMScore(ProcessId process, int score);
#endif
namespace internal {
// Returns true if address-space was released. Some configurations reserve part
// of the process address-space for special allocations (e.g. WASM).
bool ReleaseAddressSpaceReservation();
} // namespace internal
#if defined(OS_WIN)
namespace win {
// Custom Windows exception code chosen to indicate an out of memory error.
// See https://msdn.microsoft.com/en-us/library/het71c37.aspx.
// "To make sure that you do not define a code that conflicts with an existing
// exception code" ... "The resulting error code should therefore have the
// highest four bits set to hexadecimal E."
// 0xe0000008 was chosen arbitrarily, as 0x00000008 is ERROR_NOT_ENOUGH_MEMORY.
const DWORD kOomExceptionCode = 0xe0000008;
} // namespace win
#endif
namespace internal {
// Handles out of memory, with the failed allocation |size|, or 0 when it is not
// known.
BASE_EXPORT NOINLINE void OnNoMemoryInternal(size_t size);
} // namespace internal
// Special allocator functions for callers that want to check for OOM.
// These will not abort if the allocation fails even if
// EnableTerminationOnOutOfMemory has been called.
// This can be useful for huge and/or unpredictable size memory allocations.
// Please only use this if you really handle the case when the allocation
// fails. Doing otherwise would risk security.
// These functions may still crash on OOM when running under memory tools,
// specifically ASan and other sanitizers.
// Return value tells whether the allocation succeeded. If it fails |result| is
// set to NULL, otherwise it holds the memory address.
BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedMalloc(size_t size,
void** result);
BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedCalloc(size_t num_items,
size_t size,
void** result);
} // namespace base
#endif // BASE_PROCESS_MEMORY_H_

View file

@ -0,0 +1,24 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/memory.h"
#include <stdlib.h>
namespace base {
void EnableTerminationOnOutOfMemory() {
// Nothing to be done here.
}
void EnableTerminationOnHeapCorruption() {
// Nothing to be done here.
}
bool UncheckedMalloc(size_t size, void** result) {
*result = malloc(size);
return *result != nullptr;
}
} // namespace base

View file

@ -0,0 +1,145 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/memory.h"
#include <stddef.h>
#include <new>
#include "base/allocator/allocator_shim.h"
#include "base/allocator/buildflags.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/process/internal_linux.h"
#include "base/strings/string_number_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#if BUILDFLAG(USE_TCMALLOC)
#include "third_party/tcmalloc/chromium/src/config.h"
#include "third_party/tcmalloc/chromium/src/gperftools/tcmalloc.h"
#endif
namespace base {
size_t g_oom_size = 0U;
namespace {
void OnNoMemorySize(size_t size) {
g_oom_size = size;
if (size != 0)
LOG(FATAL) << "Out of memory, size = " << size;
LOG(FATAL) << "Out of memory.";
}
// NOINLINE as base::`anonymous namespace`::OnNoMemory() is recognized by the
// crash server.
NOINLINE void OnNoMemory() {
OnNoMemorySize(0);
}
void ReleaseReservationOrTerminate() {
if (internal::ReleaseAddressSpaceReservation())
return;
OnNoMemory();
}
} // namespace
void EnableTerminationOnHeapCorruption() {
// On Linux, there nothing to do AFAIK.
}
void EnableTerminationOnOutOfMemory() {
// Set the new-out of memory handler.
std::set_new_handler(&ReleaseReservationOrTerminate);
// If we're using glibc's allocator, the above functions will override
// malloc and friends and make them die on out of memory.
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
allocator::SetCallNewHandlerOnMallocFailure(true);
#elif defined(USE_TCMALLOC)
// For tcmalloc, we need to tell it to behave like new.
tc_set_new_mode(1);
#endif
}
// ScopedAllowBlocking() has private constructor and it can only be used in
// friend classes/functions. Declaring a class is easier in this situation to
// avoid adding more dependency to thread_restrictions.h because of the
// parameter used in AdjustOOMScore(). Specifically, ProcessId is a typedef
// and we'll need to include another header file in thread_restrictions.h
// without the class.
class AdjustOOMScoreHelper {
public:
static bool AdjustOOMScore(ProcessId process, int score);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(AdjustOOMScoreHelper);
};
// static.
bool AdjustOOMScoreHelper::AdjustOOMScore(ProcessId process, int score) {
if (score < 0 || score > kMaxOomScore)
return false;
FilePath oom_path(internal::GetProcPidDir(process));
// Temporarily allowing blocking since oom paths are pseudo-filesystem paths.
base::ScopedAllowBlocking allow_blocking;
// Attempt to write the newer oom_score_adj file first.
FilePath oom_file = oom_path.AppendASCII("oom_score_adj");
if (PathExists(oom_file)) {
std::string score_str = NumberToString(score);
DVLOG(1) << "Adjusting oom_score_adj of " << process << " to "
<< score_str;
int score_len = static_cast<int>(score_str.length());
return (score_len == WriteFile(oom_file, score_str.c_str(), score_len));
}
// If the oom_score_adj file doesn't exist, then we write the old
// style file and translate the oom_adj score to the range 0-15.
oom_file = oom_path.AppendASCII("oom_adj");
if (PathExists(oom_file)) {
// Max score for the old oom_adj range. Used for conversion of new
// values to old values.
const int kMaxOldOomScore = 15;
int converted_score = score * kMaxOldOomScore / kMaxOomScore;
std::string score_str = NumberToString(converted_score);
DVLOG(1) << "Adjusting oom_adj of " << process << " to " << score_str;
int score_len = static_cast<int>(score_str.length());
return (score_len == WriteFile(oom_file, score_str.c_str(), score_len));
}
return false;
}
// NOTE: This is not the only version of this function in the source:
// the setuid sandbox (in process_util_linux.c, in the sandbox source)
// also has its own C version.
bool AdjustOOMScore(ProcessId process, int score) {
return AdjustOOMScoreHelper::AdjustOOMScore(process, score);
}
bool UncheckedMalloc(size_t size, void** result) {
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
*result = allocator::UncheckedAlloc(size);
#elif defined(MEMORY_TOOL_REPLACES_ALLOCATOR) || \
(!defined(LIBC_GLIBC) && !defined(USE_TCMALLOC))
*result = malloc(size);
#elif defined(LIBC_GLIBC) && !defined(USE_TCMALLOC)
*result = __libc_malloc(size);
#elif defined(USE_TCMALLOC)
*result = tc_malloc_skip_new_handler(size);
#endif
return *result != nullptr;
}
} // namespace base

View file

@ -0,0 +1,49 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/memory.h"
#include "base/allocator/allocator_interception_mac.h"
#include "base/allocator/allocator_shim.h"
#include "base/allocator/buildflags.h"
#include "build/build_config.h"
namespace base {
namespace {
void oom_killer_new() {
TerminateBecauseOutOfMemory(0);
}
} // namespace
void EnableTerminationOnHeapCorruption() {
#if !ARCH_CPU_64_BITS
DLOG(WARNING) << "EnableTerminationOnHeapCorruption only works on 64-bit";
#endif
}
bool UncheckedMalloc(size_t size, void** result) {
return allocator::UncheckedMallocMac(size, result);
}
bool UncheckedCalloc(size_t num_items, size_t size, void** result) {
return allocator::UncheckedCallocMac(num_items, size, result);
}
void EnableTerminationOnOutOfMemory() {
// Step 1: Enable OOM killer on C++ failures.
std::set_new_handler(oom_killer_new);
// Step 2: Enable OOM killer on C-malloc failures for the default zone (if we
// have a shim).
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
allocator::SetCallNewHandlerOnMallocFailure(true);
#endif
// Step 3: Enable OOM killer on all other malloc zones (or just "all" without
// "other" if shim is disabled).
allocator::InterceptAllocationsMac();
}
} // namespace base

View file

@ -0,0 +1,44 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/memory.h"
#include <stddef.h>
#include <stdlib.h>
namespace base {
void EnableTerminationOnOutOfMemory() {
}
void EnableTerminationOnHeapCorruption() {
}
bool AdjustOOMScore(ProcessId process, int score) {
return false;
}
void TerminateBecauseOutOfMemory(size_t size) {
abort();
}
// UncheckedMalloc and Calloc exist so that platforms making use of
// EnableTerminationOnOutOfMemory have a way to allocate memory without
// crashing. This _stubs.cc file is for platforms that do not support
// EnableTerminationOnOutOfMemory (note the empty implementation above). As
// such, these two Unchecked.alloc functions need only trivially pass-through to
// their respective stdlib function since those functions will return null on a
// failure to allocate.
bool UncheckedMalloc(size_t size, void** result) {
*result = malloc(size);
return *result != nullptr;
}
bool UncheckedCalloc(size_t num_items, size_t size, void** result) {
*result = calloc(num_items, size);
return *result != nullptr;
}
} // namespace base

View file

@ -0,0 +1,82 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/memory.h"
#include "base/stl_util.h"
#include <windows.h> // Must be in front of other Windows header files.
#include <new.h>
#include <psapi.h>
#include <stddef.h>
#if defined(__clang__)
// This global constructor is trivial and non-racy (per being const).
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
// malloc_unchecked is required to implement UncheckedMalloc properly.
// It's provided by allocator_shim_win.cc but since that's not always present,
// we provide a default that falls back to regular malloc.
typedef void* (*MallocFn)(size_t);
extern "C" void* (*const malloc_unchecked)(size_t);
extern "C" void* (*const malloc_default)(size_t) = &malloc;
#if defined(__clang__)
#pragma clang diagnostic pop // -Wglobal-constructors
#endif
#if defined(_M_IX86)
#pragma comment(linker, "/alternatename:_malloc_unchecked=_malloc_default")
#elif defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64)
#pragma comment(linker, "/alternatename:malloc_unchecked=malloc_default")
#else
#error Unsupported platform
#endif
namespace base {
namespace {
// Return a non-0 value to retry the allocation.
int ReleaseReservationOrTerminate(size_t size) {
constexpr int kRetryAllocation = 1;
if (internal::ReleaseAddressSpaceReservation())
return kRetryAllocation;
internal::OnNoMemoryInternal(size);
return 0;
}
// TODO(crbug.com/1062949): Remove the NOINLINE once the crash servers handle
// the |OnNoMemoryInternal()| signature..
NOINLINE int OnNoMemory(size_t size) {
internal::OnNoMemoryInternal(size);
return 0;
}
} // namespace
void TerminateBecauseOutOfMemory(size_t size) {
OnNoMemory(size);
}
void EnableTerminationOnHeapCorruption() {
// Ignore the result code. Supported on XP SP3 and Vista.
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
}
void EnableTerminationOnOutOfMemory() {
constexpr int kCallNewHandlerOnAllocationFailure = 1;
_set_new_handler(&ReleaseReservationOrTerminate);
_set_new_mode(kCallNewHandlerOnAllocationFailure);
}
// Implemented using a weak symbol.
bool UncheckedMalloc(size_t size, void** result) {
*result = malloc_unchecked(size);
return *result != NULL;
}
} // namespace base

View file

@ -0,0 +1,28 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/port_provider_mac.h"
namespace base {
PortProvider::PortProvider() : lock_(), observer_list_() {}
PortProvider::~PortProvider() {}
void PortProvider::AddObserver(Observer* observer) {
base::AutoLock l(lock_);
observer_list_.AddObserver(observer);
}
void PortProvider::RemoveObserver(Observer* observer) {
base::AutoLock l(lock_);
observer_list_.RemoveObserver(observer);
}
void PortProvider::NotifyObservers(ProcessHandle process) {
base::AutoLock l(lock_);
for (auto& observer : observer_list_)
observer.OnReceivedTaskPort(process);
}
} // namespace base

View file

@ -0,0 +1,61 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_PROCESS_PORT_PROVIDER_MAC_H_
#define BASE_PROCESS_PORT_PROVIDER_MAC_H_
#include <mach/mach.h>
#include "base/base_export.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/process/process_handle.h"
#include "base/synchronization/lock.h"
namespace base {
// Abstract base class that provides a mapping from ProcessHandle (pid_t) to the
// Mach task port. This replicates task_for_pid(), which requires root
// privileges.
class BASE_EXPORT PortProvider {
public:
PortProvider();
virtual ~PortProvider();
class Observer {
public:
virtual ~Observer() {}
// Called by the PortProvider to notify observers that the task port was
// received for a given process.
// No guarantees are made about the thread on which this notification will
// be sent.
// Observers must not call AddObserver() or RemoveObserver() in this
// callback, as doing so will deadlock.
virtual void OnReceivedTaskPort(ProcessHandle process) = 0;
};
// Returns the mach task port for |process| if possible, or else
// |MACH_PORT_NULL|.
virtual mach_port_t TaskForPid(ProcessHandle process) const = 0;
// Observer interface.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
protected:
// Called by subclasses to send a notification to observers.
void NotifyObservers(ProcessHandle process);
private:
// ObserverList is not thread-safe, so |lock_| ensures consistency of
// |observer_list_|.
base::Lock lock_;
base::ObserverList<Observer>::Unchecked observer_list_;
DISALLOW_COPY_AND_ASSIGN(PortProvider);
};
} // namespace base
#endif // BASE_PROCESS_PORT_PROVIDER_MAC_H_

View file

@ -0,0 +1,238 @@
// Copyright 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_PROCESS_PROCESS_H_
#define BASE_PROCESS_PROCESS_H_
#include "base/base_export.h"
#include "base/macros.h"
#include "base/process/process_handle.h"
#include "base/time/time.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include "base/win/scoped_handle.h"
#endif
#if defined(OS_FUCHSIA)
#include <lib/zx/process.h>
#endif
#if defined(OS_MACOSX)
#include "base/feature_list.h"
#include "base/process/port_provider_mac.h"
#endif
namespace base {
#if defined(OS_MACOSX)
extern const Feature kMacAllowBackgroundingProcesses;
#endif
// Provides a move-only encapsulation of a process.
//
// This object is not tied to the lifetime of the underlying process: the
// process may be killed and this object may still around, and it will still
// claim to be valid. The actual behavior in that case is OS dependent like so:
//
// Windows: The underlying ProcessHandle will be valid after the process dies
// and can be used to gather some information about that process, but most
// methods will obviously fail.
//
// POSIX: The underlying ProcessHandle is not guaranteed to remain valid after
// the process dies, and it may be reused by the system, which means that it may
// end up pointing to the wrong process.
class BASE_EXPORT Process {
public:
// On Windows, this takes ownership of |handle|. On POSIX, this does not take
// ownership of |handle|.
explicit Process(ProcessHandle handle = kNullProcessHandle);
Process(Process&& other);
// The destructor does not terminate the process.
~Process();
Process& operator=(Process&& other);
// Returns an object for the current process.
static Process Current();
// Returns a Process for the given |pid|.
static Process Open(ProcessId pid);
// Returns a Process for the given |pid|. On Windows the handle is opened
// with more access rights and must only be used by trusted code (can read the
// address space and duplicate handles).
static Process OpenWithExtraPrivileges(ProcessId pid);
#if defined(OS_WIN)
// Returns a Process for the given |pid|, using some |desired_access|.
// See ::OpenProcess documentation for valid |desired_access|.
static Process OpenWithAccess(ProcessId pid, DWORD desired_access);
#endif
// Creates an object from a |handle| owned by someone else.
// Don't use this for new code. It is only intended to ease the migration to
// a strict ownership model.
// TODO(rvargas) crbug.com/417532: Remove this code.
static Process DeprecatedGetProcessFromHandle(ProcessHandle handle);
// Returns true if processes can be backgrounded.
static bool CanBackgroundProcesses();
// Terminates the current process immediately with |exit_code|.
[[noreturn]] static void TerminateCurrentProcessImmediately(int exit_code);
// Returns true if this objects represents a valid process.
bool IsValid() const;
// Returns a handle for this process. There is no guarantee about when that
// handle becomes invalid because this object retains ownership.
ProcessHandle Handle() const;
// Returns a second object that represents this process.
Process Duplicate() const;
// Get the PID for this process.
ProcessId Pid() const;
#if !defined(OS_ANDROID)
// Get the creation time for this process. Since the Pid can be reused after a
// process dies, it is useful to use both the Pid and the creation time to
// uniquely identify a process.
//
// Not available on Android because /proc/stat/ cannot be accessed on O+.
// https://issuetracker.google.com/issues/37140047
Time CreationTime() const;
#endif // !defined(OS_ANDROID)
// Returns true if this process is the current process.
bool is_current() const;
// Close the process handle. This will not terminate the process.
void Close();
// Returns true if this process is still running. This is only safe on Windows
// (and maybe Fuchsia?), because the ProcessHandle will keep the zombie
// process information available until itself has been released. But on Posix,
// the OS may reuse the ProcessId.
#if defined(OS_WIN)
bool IsRunning() const {
return !WaitForExitWithTimeout(base::TimeDelta(), nullptr);
}
#endif
// Terminates the process with extreme prejudice. The given |exit_code| will
// be the exit code of the process. If |wait| is true, this method will wait
// for up to one minute for the process to actually terminate.
// Returns true if the process terminates within the allowed time.
// NOTE: On POSIX |exit_code| is ignored.
bool Terminate(int exit_code, bool wait) const;
#if defined(OS_WIN)
enum class WaitExitStatus {
PROCESS_EXITED,
STOP_EVENT_SIGNALED,
FAILED,
};
// Waits for the process to exit, or the specified |stop_event_handle| to be
// set. Returns value indicating which event was set. The given |exit_code|
// will be the exit code of the process.
WaitExitStatus WaitForExitOrEvent(
const base::win::ScopedHandle& stop_event_handle,
int* exit_code) const;
#endif // OS_WIN
// Waits for the process to exit. Returns true on success.
// On POSIX, if the process has been signaled then |exit_code| is set to -1.
// On Linux this must be a child process, however on Mac and Windows it can be
// any process.
// NOTE: |exit_code| is optional, nullptr can be passed if the exit code is
// not required.
bool WaitForExit(int* exit_code) const;
// Same as WaitForExit() but only waits for up to |timeout|.
// NOTE: |exit_code| is optional, nullptr can be passed if the exit code
// is not required.
bool WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const;
// Indicates that the process has exited with the specified |exit_code|.
// This should be called if process exit is observed outside of this class.
// (i.e. Not because Terminate or WaitForExit, above, was called.)
// Note that nothing prevents this being called multiple times for a dead
// process though that should be avoided.
void Exited(int exit_code) const;
#if defined(OS_MACOSX)
// The Mac needs a Mach port in order to manipulate a process's priority,
// and there's no good way to get that from base given the pid. These Mac
// variants of the IsProcessBackgrounded and SetProcessBackgrounded API take
// a port provider for this reason. See crbug.com/460102
//
// A process is backgrounded when its task priority is
// |TASK_BACKGROUND_APPLICATION|.
//
// Returns true if the port_provider can locate a task port for the process
// and it is backgrounded. If port_provider is null, returns false.
bool IsProcessBackgrounded(PortProvider* port_provider) const;
// Set the process as backgrounded. If value is
// true, the priority of the associated task will be set to
// TASK_BACKGROUND_APPLICATION. If value is false, the
// priority of the process will be set to TASK_FOREGROUND_APPLICATION.
//
// Returns true if the priority was changed, false otherwise. If
// |port_provider| is null, this is a no-op and it returns false.
bool SetProcessBackgrounded(PortProvider* port_provider, bool value);
#else
// A process is backgrounded when it's priority is lower than normal.
// Return true if this process is backgrounded, false otherwise.
bool IsProcessBackgrounded() const;
// Set a process as backgrounded. If value is true, the priority of the
// process will be lowered. If value is false, the priority of the process
// will be made "normal" - equivalent to default process priority.
// Returns true if the priority was changed, false otherwise.
bool SetProcessBackgrounded(bool value);
#endif // defined(OS_MACOSX)
// Returns an integer representing the priority of a process. The meaning
// of this value is OS dependent.
int GetPriority() const;
#if defined(OS_CHROMEOS)
// Get the PID in its PID namespace.
// If the process is not in a PID namespace or /proc/<pid>/status does not
// report NSpid, kNullProcessId is returned.
ProcessId GetPidInNamespace() const;
#endif
private:
#if defined(OS_WIN)
win::ScopedHandle process_;
#elif defined(OS_FUCHSIA)
zx::process process_;
#else
ProcessHandle process_;
#endif
#if defined(OS_WIN) || defined(OS_FUCHSIA)
bool is_current_process_;
#endif
DISALLOW_COPY_AND_ASSIGN(Process);
};
#if defined(OS_CHROMEOS)
// Exposed for testing.
// Given the contents of the /proc/<pid>/cgroup file, determine whether the
// process is backgrounded or not.
BASE_EXPORT bool IsProcessBackgroundedCGroup(
const StringPiece& cgroup_contents);
#endif // defined(OS_CHROMEOS)
} // namespace base
#endif // BASE_PROCESS_PROCESS_H_

View file

@ -0,0 +1,224 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process.h"
#include <lib/zx/process.h>
#include <zircon/process.h>
#include <zircon/syscalls.h>
#include "base/clang_profiling_buildflags.h"
#include "base/debug/activity_tracker.h"
#include "base/fuchsia/default_job.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/strings/stringprintf.h"
#if BUILDFLAG(CLANG_PROFILING)
#include "base/test/clang_profiling.h"
#endif
namespace base {
Process::Process(ProcessHandle handle)
: process_(handle), is_current_process_(false) {
CHECK_NE(handle, zx_process_self());
}
Process::~Process() {
Close();
}
Process::Process(Process&& other)
: process_(std::move(other.process_)),
is_current_process_(other.is_current_process_) {
other.is_current_process_ = false;
}
Process& Process::operator=(Process&& other) {
process_ = std::move(other.process_);
is_current_process_ = other.is_current_process_;
other.is_current_process_ = false;
return *this;
}
// static
Process Process::Current() {
Process process;
process.is_current_process_ = true;
return process;
}
// static
Process Process::Open(ProcessId pid) {
if (pid == GetCurrentProcId())
return Current();
// While a process with object id |pid| might exist, the job returned by
// zx::job::default_job() might not contain it, so this call can fail.
zx::process process;
zx_status_t status =
GetDefaultJob()->get_child(pid, ZX_RIGHT_SAME_RIGHTS, &process);
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "zx_object_get_child";
return Process();
}
return Process(process.release());
}
// static
Process Process::OpenWithExtraPrivileges(ProcessId pid) {
// No privileges to set.
return Open(pid);
}
// static
Process Process::DeprecatedGetProcessFromHandle(ProcessHandle handle) {
DCHECK_NE(handle, GetCurrentProcessHandle());
zx::process out;
zx_status_t result =
zx::unowned_process(handle)->duplicate(ZX_RIGHT_SAME_RIGHTS, &out);
if (result != ZX_OK) {
ZX_DLOG(ERROR, result) << "zx_handle_duplicate(from_handle)";
return Process();
}
return Process(out.release());
}
// static
bool Process::CanBackgroundProcesses() {
return false;
}
// static
void Process::TerminateCurrentProcessImmediately(int exit_code) {
#if BUILDFLAG(CLANG_PROFILING)
WriteClangProfilingProfile();
#endif
_exit(exit_code);
}
bool Process::IsValid() const {
return process_.is_valid() || is_current();
}
ProcessHandle Process::Handle() const {
return is_current_process_ ? zx_process_self() : process_.get();
}
Process Process::Duplicate() const {
if (is_current())
return Current();
if (!IsValid())
return Process();
zx::process out;
zx_status_t result = process_.duplicate(ZX_RIGHT_SAME_RIGHTS, &out);
if (result != ZX_OK) {
ZX_DLOG(ERROR, result) << "zx_handle_duplicate";
return Process();
}
return Process(out.release());
}
ProcessId Process::Pid() const {
DCHECK(IsValid());
return GetProcId(Handle());
}
Time Process::CreationTime() const {
// TODO(https://crbug.com/726484): There is no syscall providing this data.
NOTIMPLEMENTED();
return Time();
}
bool Process::is_current() const {
return is_current_process_;
}
void Process::Close() {
is_current_process_ = false;
process_.reset();
}
bool Process::Terminate(int exit_code, bool wait) const {
// exit_code isn't supportable. https://crbug.com/753490.
zx_status_t status = zx_task_kill(Handle());
if (status == ZX_OK && wait) {
zx_signals_t signals;
status = zx_object_wait_one(Handle(), ZX_TASK_TERMINATED,
zx_deadline_after(ZX_SEC(60)), &signals);
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "zx_object_wait_one(terminate)";
} else {
CHECK(signals & ZX_TASK_TERMINATED);
}
} else if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "zx_task_kill";
}
return status >= 0;
}
bool Process::WaitForExit(int* exit_code) const {
return WaitForExitWithTimeout(TimeDelta::Max(), exit_code);
}
bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const {
if (is_current_process_)
return false;
// Record the event that this thread is blocking upon (for hang diagnosis).
base::debug::ScopedProcessWaitActivity process_activity(this);
zx_time_t deadline = timeout == TimeDelta::Max()
? ZX_TIME_INFINITE
: (TimeTicks::Now() + timeout).ToZxTime();
zx_signals_t signals_observed = 0;
zx_status_t status = zx_object_wait_one(process_.get(), ZX_TASK_TERMINATED,
deadline, &signals_observed);
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "zx_object_wait_one";
return false;
}
zx_info_process_t proc_info;
status = zx_object_get_info(process_.get(), ZX_INFO_PROCESS, &proc_info,
sizeof(proc_info), nullptr, nullptr);
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "zx_object_get_info";
if (exit_code)
*exit_code = -1;
return false;
}
if (exit_code)
*exit_code = proc_info.return_code;
return true;
}
void Process::Exited(int exit_code) const {}
bool Process::IsProcessBackgrounded() const {
// See SetProcessBackgrounded().
DCHECK(IsValid());
return false;
}
bool Process::SetProcessBackgrounded(bool value) {
// No process priorities on Fuchsia. TODO(fuchsia): See MG-783, and update
// this later if priorities are implemented.
return false;
}
int Process::GetPriority() const {
DCHECK(IsValid());
// No process priorities on Fuchsia.
return 0;
}
} // namespace base

View file

@ -0,0 +1,40 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_handle.h"
#include <stdint.h>
#include "base/logging.h"
#include "build/build_config.h"
namespace base {
namespace {
ProcessId g_pid_outside_of_namespace = kNullProcessId;
} // namespace
std::ostream& operator<<(std::ostream& os, const UniqueProcId& obj) {
os << obj.GetUnsafeValue();
return os;
}
UniqueProcId GetUniqueIdForProcess() {
// Used for logging. Must not use LogMessage or any of the macros that call
// into it.
return (g_pid_outside_of_namespace != kNullProcessId)
? UniqueProcId(g_pid_outside_of_namespace)
: UniqueProcId(GetCurrentProcId());
}
#if defined(OS_LINUX) || defined(OS_AIX)
void InitUniqueIdForProcessInPidNamespace(ProcessId pid_outside_of_namespace) {
DCHECK(pid_outside_of_namespace != kNullProcessId);
g_pid_outside_of_namespace = pid_outside_of_namespace;
}
#endif
} // namespace base

View file

@ -0,0 +1,142 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_PROCESS_PROCESS_HANDLE_H_
#define BASE_PROCESS_PROCESS_HANDLE_H_
#include <stdint.h>
#include <sys/types.h>
#include "base/base_export.h"
#include "base/files/file_path.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include "base/win/windows_types.h"
#endif
#if defined(OS_FUCHSIA)
#include <zircon/types.h>
#endif
namespace base {
// ProcessHandle is a platform specific type which represents the underlying OS
// handle to a process.
// ProcessId is a number which identifies the process in the OS.
#if defined(OS_WIN)
typedef HANDLE ProcessHandle;
typedef DWORD ProcessId;
typedef HANDLE UserTokenHandle;
const ProcessHandle kNullProcessHandle = NULL;
const ProcessId kNullProcessId = 0;
#elif defined(OS_FUCHSIA)
typedef zx_handle_t ProcessHandle;
typedef zx_koid_t ProcessId;
const ProcessHandle kNullProcessHandle = ZX_HANDLE_INVALID;
const ProcessId kNullProcessId = ZX_KOID_INVALID;
#elif defined(OS_POSIX)
// On POSIX, our ProcessHandle will just be the PID.
typedef pid_t ProcessHandle;
typedef pid_t ProcessId;
const ProcessHandle kNullProcessHandle = 0;
const ProcessId kNullProcessId = 0;
#endif // defined(OS_WIN)
// To print ProcessIds portably use CrPRIdPid (based on PRIuS and friends from
// C99 and format_macros.h) like this:
// base::StringPrintf("PID is %" CrPRIdPid ".\n", pid);
#if defined(OS_WIN) || defined(OS_FUCHSIA)
#define CrPRIdPid "ld"
#else
#define CrPRIdPid "d"
#endif
class UniqueProcId {
public:
explicit UniqueProcId(ProcessId value) : value_(value) {}
UniqueProcId(const UniqueProcId& other) = default;
UniqueProcId& operator=(const UniqueProcId& other) = default;
// Returns the process PID. WARNING: On some platforms, the pid may not be
// valid within the current process sandbox.
ProcessId GetUnsafeValue() const { return value_; }
bool operator==(const UniqueProcId& other) const {
return value_ == other.value_;
}
bool operator!=(const UniqueProcId& other) const {
return value_ != other.value_;
}
bool operator<(const UniqueProcId& other) const {
return value_ < other.value_;
}
bool operator<=(const UniqueProcId& other) const {
return value_ <= other.value_;
}
bool operator>(const UniqueProcId& other) const {
return value_ > other.value_;
}
bool operator>=(const UniqueProcId& other) const {
return value_ >= other.value_;
}
private:
ProcessId value_;
};
std::ostream& operator<<(std::ostream& os, const UniqueProcId& obj);
// Returns the id of the current process.
// Note that on some platforms, this is not guaranteed to be unique across
// processes (use GetUniqueIdForProcess if uniqueness is required).
BASE_EXPORT ProcessId GetCurrentProcId();
// Returns a unique ID for the current process. The ID will be unique across all
// currently running processes within the chrome session, but IDs of terminated
// processes may be reused.
BASE_EXPORT UniqueProcId GetUniqueIdForProcess();
#if defined(OS_LINUX)
// When a process is started in a different PID namespace from the browser
// process, this function must be called with the process's PID in the browser's
// PID namespace in order to initialize its unique ID. Not thread safe.
// WARNING: To avoid inconsistent results from GetUniqueIdForProcess, this
// should only be called very early after process startup - ideally as soon
// after process creation as possible.
BASE_EXPORT void InitUniqueIdForProcessInPidNamespace(
ProcessId pid_outside_of_namespace);
#endif
// Returns the ProcessHandle of the current process.
BASE_EXPORT ProcessHandle GetCurrentProcessHandle();
// Returns the process ID for the specified process. This is functionally the
// same as Windows' GetProcessId(), but works on versions of Windows before Win
// XP SP1 as well.
// DEPRECATED. New code should be using Process::Pid() instead.
// Note that on some platforms, this is not guaranteed to be unique across
// processes.
BASE_EXPORT ProcessId GetProcId(ProcessHandle process);
#if !defined(OS_FUCHSIA)
// Returns the ID for the parent of the given process. Not available on Fuchsia.
// Returning a negative value indicates an error, such as if the |process| does
// not exist. Returns 0 when |process| has no parent process.
BASE_EXPORT ProcessId GetParentProcessId(ProcessHandle process);
#endif // !defined(OS_FUCHSIA)
#if defined(OS_POSIX)
// Returns the path to the executable of the given process.
BASE_EXPORT FilePath GetProcessExecutablePath(ProcessHandle process);
#endif
} // namespace base
#endif // BASE_PROCESS_PROCESS_HANDLE_H_

View file

@ -0,0 +1,43 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_handle.h"
#include "base/stl_util.h"
#include <limits.h>
#include <stddef.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/user.h>
#include <unistd.h>
namespace base {
ProcessId GetParentProcessId(ProcessHandle process) {
struct kinfo_proc info;
size_t length;
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process };
if (sysctl(mib, base::size(mib), &info, &length, NULL, 0) < 0)
return -1;
return info.ki_ppid;
}
FilePath GetProcessExecutablePath(ProcessHandle process) {
char pathname[PATH_MAX];
size_t length;
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, process };
length = sizeof(pathname);
if (sysctl(mib, base::size(mib), pathname, &length, NULL, 0) < 0 ||
length == 0) {
return FilePath();
}
return FilePath(std::string(pathname));
}
} // namespace base

View file

@ -0,0 +1,37 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_handle.h"
#include <zircon/process.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include "base/logging.h"
namespace base {
ProcessId GetCurrentProcId() {
return GetProcId(GetCurrentProcessHandle());
}
ProcessHandle GetCurrentProcessHandle() {
// Note that zx_process_self() returns a real handle, and ownership is not
// transferred to the caller (i.e. this should never be closed).
return zx_process_self();
}
ProcessId GetProcId(ProcessHandle process) {
zx_info_handle_basic_t basic;
zx_status_t status = zx_object_get_info(process, ZX_INFO_HANDLE_BASIC, &basic,
sizeof(basic), nullptr, nullptr);
if (status != ZX_OK) {
DLOG(ERROR) << "zx_object_get_info failed: "
<< zx_status_get_string(status);
return ZX_KOID_INVALID;
}
return basic.koid;
}
} // namespace base

View file

@ -0,0 +1,39 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_handle.h"
#include "base/files/file_util.h"
#include "base/process/internal_linux.h"
#if defined(OS_AIX)
#include "base/process/internal_aix.h"
#endif
namespace base {
ProcessId GetParentProcessId(ProcessHandle process) {
ProcessId pid =
#if defined(OS_AIX)
internalAIX::ReadProcStatsAndGetFieldAsInt64(process,
internalAIX::VM_PPID);
#else
internal::ReadProcStatsAndGetFieldAsInt64(process, internal::VM_PPID);
#endif
// TODO(zijiehe): Returns 0 if |process| does not have a parent process.
if (pid)
return pid;
return -1;
}
FilePath GetProcessExecutablePath(ProcessHandle process) {
FilePath stat_file = internal::GetProcPidDir(process).Append("exe");
FilePath exe_name;
if (!ReadSymbolicLink(stat_file, &exe_name)) {
// No such process. Happens frequently in e.g. TerminateAllChromeProcesses
return FilePath();
}
return exe_name;
}
} // namespace base

View file

@ -0,0 +1,37 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_handle.h"
#include <libproc.h>
#include <stddef.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include "base/logging.h"
namespace base {
ProcessId GetParentProcessId(ProcessHandle process) {
struct kinfo_proc info;
size_t length = sizeof(struct kinfo_proc);
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process };
if (sysctl(mib, 4, &info, &length, NULL, 0) < 0) {
DPLOG(ERROR) << "sysctl";
return -1;
}
if (length == 0)
return -1;
return info.kp_eproc.e_ppid;
}
FilePath GetProcessExecutablePath(ProcessHandle process) {
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
if (!proc_pidpath(process, pathbuf, sizeof(pathbuf)))
return FilePath();
return FilePath(pathbuf);
}
} // namespace base

View file

@ -0,0 +1,51 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_handle.h"
#include "base/stl_util.h"
#include <stddef.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>
namespace base {
ProcessId GetParentProcessId(ProcessHandle process) {
struct kinfo_proc info;
size_t length;
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process,
sizeof(struct kinfo_proc), 0 };
if (sysctl(mib, base::size(mib), NULL, &length, NULL, 0) < 0)
return -1;
mib[5] = (length / sizeof(struct kinfo_proc));
if (sysctl(mib, base::size(mib), &info, &length, NULL, 0) < 0)
return -1;
return info.p_ppid;
}
FilePath GetProcessExecutablePath(ProcessHandle process) {
struct kinfo_proc kp;
size_t len;
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process,
sizeof(struct kinfo_proc), 0 };
if (sysctl(mib, base::size(mib), NULL, &len, NULL, 0) == -1)
return FilePath();
mib[5] = (len / sizeof(struct kinfo_proc));
if (sysctl(mib, base::size(mib), &kp, &len, NULL, 0) < 0)
return FilePath();
if ((kp.p_flag & P_SYSTEM) != 0)
return FilePath();
if (strcmp(kp.p_comm, "chrome") == 0)
return FilePath(kp.p_comm);
return FilePath();
}
} // namespace base

View file

@ -0,0 +1,23 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_handle.h"
#include <unistd.h>
namespace base {
ProcessId GetCurrentProcId() {
return getpid();
}
ProcessHandle GetCurrentProcessHandle() {
return GetCurrentProcId();
}
ProcessId GetProcId(ProcessHandle process) {
return process;
}
} // namespace base

View file

@ -0,0 +1,52 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_handle.h"
#include <windows.h>
#include <tlhelp32.h>
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
namespace base {
ProcessId GetCurrentProcId() {
return ::GetCurrentProcessId();
}
ProcessHandle GetCurrentProcessHandle() {
return ::GetCurrentProcess();
}
ProcessId GetProcId(ProcessHandle process) {
if (process == base::kNullProcessHandle)
return 0;
// This returns 0 if we have insufficient rights to query the process handle.
// Invalid handles or non-process handles will cause a hard failure.
ProcessId result = GetProcessId(process);
CHECK(result != 0 || GetLastError() != ERROR_INVALID_HANDLE)
<< "process handle = " << process;
return result;
}
ProcessId GetParentProcessId(ProcessHandle process) {
ProcessId child_pid = GetProcId(process);
PROCESSENTRY32 process_entry;
process_entry.dwSize = sizeof(PROCESSENTRY32);
win::ScopedHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
if (snapshot.IsValid() && Process32First(snapshot.Get(), &process_entry)) {
do {
if (process_entry.th32ProcessID == child_pid)
return process_entry.th32ParentProcessID;
} while (Process32Next(snapshot.Get(), &process_entry));
}
// TODO(zijiehe): To match other platforms, -1 (UINT32_MAX) should be returned
// if |child_id| cannot be found in the |snapshot|.
return 0u;
}
} // namespace base

View file

@ -0,0 +1,33 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_PROCESS_PROCESS_INFO_H_
#define BASE_PROCESS_PROCESS_INFO_H_
#include "base/base_export.h"
#include "build/build_config.h"
namespace base {
#if defined(OS_WIN)
enum IntegrityLevel {
INTEGRITY_UNKNOWN,
UNTRUSTED_INTEGRITY,
LOW_INTEGRITY,
MEDIUM_INTEGRITY,
HIGH_INTEGRITY,
};
// Returns the integrity level of the process. Returns INTEGRITY_UNKNOWN in the
// case of an underlying system failure.
BASE_EXPORT IntegrityLevel GetCurrentProcessIntegrityLevel();
// Determines whether the current process is elevated.
BASE_EXPORT bool IsCurrentProcessElevated();
#endif // defined(OS_WIN)
} // namespace base
#endif // BASE_PROCESS_PROCESS_INFO_H_

View file

@ -0,0 +1,86 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_info.h"
#include <windows.h>
#include <memory>
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/time/time.h"
#include "base/win/scoped_handle.h"
namespace base {
namespace {
HANDLE GetCurrentProcessToken() {
HANDLE process_token;
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token);
DCHECK(process_token != NULL && process_token != INVALID_HANDLE_VALUE);
return process_token;
}
} // namespace
IntegrityLevel GetCurrentProcessIntegrityLevel() {
HANDLE process_token(GetCurrentProcessToken());
DWORD token_info_length = 0;
if (::GetTokenInformation(process_token, TokenIntegrityLevel, nullptr, 0,
&token_info_length) ||
::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
NOTREACHED();
return INTEGRITY_UNKNOWN;
}
auto token_label_bytes = std::make_unique<char[]>(token_info_length);
TOKEN_MANDATORY_LABEL* token_label =
reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_label_bytes.get());
if (!::GetTokenInformation(process_token, TokenIntegrityLevel, token_label,
token_info_length, &token_info_length)) {
NOTREACHED();
return INTEGRITY_UNKNOWN;
}
DWORD integrity_level = *::GetSidSubAuthority(
token_label->Label.Sid,
static_cast<DWORD>(*::GetSidSubAuthorityCount(token_label->Label.Sid) -
1));
if (integrity_level < SECURITY_MANDATORY_LOW_RID)
return UNTRUSTED_INTEGRITY;
if (integrity_level < SECURITY_MANDATORY_MEDIUM_RID)
return LOW_INTEGRITY;
if (integrity_level >= SECURITY_MANDATORY_MEDIUM_RID &&
integrity_level < SECURITY_MANDATORY_HIGH_RID) {
return MEDIUM_INTEGRITY;
}
if (integrity_level >= SECURITY_MANDATORY_HIGH_RID)
return HIGH_INTEGRITY;
NOTREACHED();
return INTEGRITY_UNKNOWN;
}
bool IsCurrentProcessElevated() {
HANDLE process_token(GetCurrentProcessToken());
// Unlike TOKEN_ELEVATION_TYPE which returns TokenElevationTypeDefault when
// UAC is turned off, TOKEN_ELEVATION returns whether the process is elevated.
DWORD size;
TOKEN_ELEVATION elevation;
if (!GetTokenInformation(process_token, TokenElevation, &elevation,
sizeof(elevation), &size)) {
PLOG(ERROR) << "GetTokenInformation() failed";
return false;
}
return !!elevation.TokenIsElevated;
}
} // namespace base

View file

@ -0,0 +1,66 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_iterator.h"
#include "build/build_config.h"
namespace base {
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
ProcessEntry::ProcessEntry() : pid_(0), ppid_(0), gid_(0) {}
ProcessEntry::ProcessEntry(const ProcessEntry& other) = default;
ProcessEntry::~ProcessEntry() = default;
#endif
const ProcessEntry* ProcessIterator::NextProcessEntry() {
bool result = false;
do {
result = CheckForNextProcess();
} while (result && !IncludeEntry());
if (result)
return &entry_;
return nullptr;
}
ProcessIterator::ProcessEntries ProcessIterator::Snapshot() {
ProcessEntries found;
while (const ProcessEntry* process_entry = NextProcessEntry()) {
found.push_back(*process_entry);
}
return found;
}
bool ProcessIterator::IncludeEntry() {
return !filter_ || filter_->Includes(entry_);
}
NamedProcessIterator::NamedProcessIterator(
const FilePath::StringType& executable_name,
const ProcessFilter* filter) : ProcessIterator(filter),
executable_name_(executable_name) {
#if defined(OS_ANDROID)
// On Android, the process name contains only the last 15 characters, which
// is in file /proc/<pid>/stat, the string between open parenthesis and close
// parenthesis. Please See ProcessIterator::CheckForNextProcess for details.
// Now if the length of input process name is greater than 15, only save the
// last 15 characters.
if (executable_name_.size() > 15) {
executable_name_ = FilePath::StringType(executable_name_,
executable_name_.size() - 15, 15);
}
#endif
}
NamedProcessIterator::~NamedProcessIterator() = default;
int GetProcessCount(const FilePath::StringType& executable_name,
const ProcessFilter* filter) {
int count = 0;
NamedProcessIterator iter(executable_name, filter);
while (iter.NextProcessEntry())
++count;
return count;
}
} // namespace base

View file

@ -0,0 +1,153 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file contains methods to iterate over processes on the system.
#ifndef BASE_PROCESS_PROCESS_ITERATOR_H_
#define BASE_PROCESS_PROCESS_ITERATOR_H_
#include <stddef.h>
#include <list>
#include <string>
#include <vector>
#include "base/base_export.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/process/process.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#include <tlhelp32.h>
#elif defined(OS_MACOSX) || defined(OS_OPENBSD)
#include <sys/sysctl.h>
#elif defined(OS_FREEBSD)
#include <sys/user.h>
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
#include <dirent.h>
#endif
namespace base {
#if defined(OS_WIN)
struct ProcessEntry : public PROCESSENTRY32 {
ProcessId pid() const { return th32ProcessID; }
ProcessId parent_pid() const { return th32ParentProcessID; }
const wchar_t* exe_file() const { return szExeFile; }
};
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
struct BASE_EXPORT ProcessEntry {
ProcessEntry();
ProcessEntry(const ProcessEntry& other);
~ProcessEntry();
ProcessId pid() const { return pid_; }
ProcessId parent_pid() const { return ppid_; }
ProcessId gid() const { return gid_; }
const char* exe_file() const { return exe_file_.c_str(); }
const std::vector<std::string>& cmd_line_args() const {
return cmd_line_args_;
}
ProcessId pid_;
ProcessId ppid_;
ProcessId gid_;
std::string exe_file_;
std::vector<std::string> cmd_line_args_;
};
#endif // defined(OS_WIN)
// Used to filter processes by process ID.
class ProcessFilter {
public:
// Returns true to indicate set-inclusion and false otherwise. This method
// should not have side-effects and should be idempotent.
virtual bool Includes(const ProcessEntry& entry) const = 0;
protected:
virtual ~ProcessFilter() = default;
};
// This class provides a way to iterate through a list of processes on the
// current machine with a specified filter.
// To use, create an instance and then call NextProcessEntry() until it returns
// false.
class BASE_EXPORT ProcessIterator {
public:
typedef std::list<ProcessEntry> ProcessEntries;
explicit ProcessIterator(const ProcessFilter* filter);
virtual ~ProcessIterator();
// If there's another process that matches the given executable name,
// returns a const pointer to the corresponding PROCESSENTRY32.
// If there are no more matching processes, returns NULL.
// The returned pointer will remain valid until NextProcessEntry()
// is called again or this NamedProcessIterator goes out of scope.
const ProcessEntry* NextProcessEntry();
// Takes a snapshot of all the ProcessEntry found.
ProcessEntries Snapshot();
protected:
virtual bool IncludeEntry();
const ProcessEntry& entry() { return entry_; }
private:
// Determines whether there's another process (regardless of executable)
// left in the list of all processes. Returns true and sets entry_ to
// that process's info if there is one, false otherwise.
bool CheckForNextProcess();
// Initializes a PROCESSENTRY32 data structure so that it's ready for
// use with Process32First/Process32Next.
void InitProcessEntry(ProcessEntry* entry);
#if defined(OS_WIN)
HANDLE snapshot_;
bool started_iteration_;
#elif defined(OS_MACOSX) || defined(OS_BSD)
std::vector<kinfo_proc> kinfo_procs_;
size_t index_of_kinfo_proc_;
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
DIR* procfs_dir_;
#endif
ProcessEntry entry_;
const ProcessFilter* filter_;
DISALLOW_COPY_AND_ASSIGN(ProcessIterator);
};
// This class provides a way to iterate through the list of processes
// on the current machine that were started from the given executable
// name. To use, create an instance and then call NextProcessEntry()
// until it returns false.
class BASE_EXPORT NamedProcessIterator : public ProcessIterator {
public:
NamedProcessIterator(const FilePath::StringType& executable_name,
const ProcessFilter* filter);
~NamedProcessIterator() override;
protected:
bool IncludeEntry() override;
private:
FilePath::StringType executable_name_;
DISALLOW_COPY_AND_ASSIGN(NamedProcessIterator);
};
// Returns the number of processes on the machine that are running from the
// given executable name. If filter is non-null, then only processes selected
// by the filter will be counted.
BASE_EXPORT int GetProcessCount(const FilePath::StringType& executable_name,
const ProcessFilter* filter);
} // namespace base
#endif // BASE_PROCESS_PROCESS_ITERATOR_H_

View file

@ -0,0 +1,131 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_iterator.h"
#include <errno.h>
#include <sys/types.h>
#include <stddef.h>
#include <sys/sysctl.h>
#include <unistd.h>
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
namespace base {
ProcessIterator::ProcessIterator(const ProcessFilter* filter)
: index_of_kinfo_proc_(),
filter_(filter) {
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, getuid() };
bool done = false;
int try_num = 1;
const int max_tries = 10;
do {
size_t len = 0;
if (sysctl(mib, base::size(mib), NULL, &len, NULL, 0) < 0) {
LOG(ERROR) << "failed to get the size needed for the process list";
kinfo_procs_.resize(0);
done = true;
} else {
size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
// Leave some spare room for process table growth (more could show up
// between when we check and now)
num_of_kinfo_proc += 16;
kinfo_procs_.resize(num_of_kinfo_proc);
len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
if (sysctl(mib, base::size(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
// If we get a mem error, it just means we need a bigger buffer, so
// loop around again. Anything else is a real error and give up.
if (errno != ENOMEM) {
LOG(ERROR) << "failed to get the process list";
kinfo_procs_.resize(0);
done = true;
}
} else {
// Got the list, just make sure we're sized exactly right
size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
kinfo_procs_.resize(num_of_kinfo_proc);
done = true;
}
}
} while (!done && (try_num++ < max_tries));
if (!done) {
LOG(ERROR) << "failed to collect the process list in a few tries";
kinfo_procs_.resize(0);
}
}
ProcessIterator::~ProcessIterator() {
}
bool ProcessIterator::CheckForNextProcess() {
std::string data;
for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) {
size_t length;
struct kinfo_proc kinfo = kinfo_procs_[index_of_kinfo_proc_];
int mib[] = { CTL_KERN, KERN_PROC_ARGS, kinfo.ki_pid };
if ((kinfo.ki_pid > 0) && (kinfo.ki_stat == SZOMB))
continue;
length = 0;
if (sysctl(mib, base::size(mib), NULL, &length, NULL, 0) < 0) {
LOG(ERROR) << "failed to figure out the buffer size for a command line";
continue;
}
data.resize(length);
if (sysctl(mib, base::size(mib), &data[0], &length, NULL, 0) < 0) {
LOG(ERROR) << "failed to fetch a commandline";
continue;
}
std::string delimiters;
delimiters.push_back('\0');
entry_.cmd_line_args_ = SplitString(data, delimiters,
KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
size_t exec_name_end = data.find('\0');
if (exec_name_end == std::string::npos) {
LOG(ERROR) << "command line data didn't match expected format";
continue;
}
entry_.pid_ = kinfo.ki_pid;
entry_.ppid_ = kinfo.ki_ppid;
entry_.gid_ = kinfo.ki_pgid;
size_t last_slash = data.rfind('/', exec_name_end);
if (last_slash == std::string::npos) {
entry_.exe_file_.assign(data, 0, exec_name_end);
} else {
entry_.exe_file_.assign(data, last_slash + 1,
exec_name_end - last_slash - 1);
}
// Start w/ the next entry next time through
++index_of_kinfo_proc_;
return true;
}
return false;
}
bool NamedProcessIterator::IncludeEntry() {
if (executable_name_ != entry().exe_file())
return false;
return ProcessIterator::IncludeEntry();
}
} // namespace base

View file

@ -0,0 +1,26 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_iterator.h"
namespace base {
ProcessIterator::ProcessIterator(const ProcessFilter* filter) {
// TODO(fuchsia): There's no Fuchsia API to iterate processes currently.
NOTREACHED();
}
ProcessIterator::~ProcessIterator() {}
bool ProcessIterator::CheckForNextProcess() {
// TODO(fuchsia): There's no Fuchsia API to iterate processes currently.
return false;
}
bool NamedProcessIterator::IncludeEntry() {
// TODO(fuchsia): There's no Fuchsia API to iterate processes currently.
return false;
}
} // namespace base

View file

@ -0,0 +1,151 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_iterator.h"
#include <stddef.h>
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/process/internal_linux.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
namespace base {
namespace {
// Reads the |field_num|th field from |proc_stats|.
// Returns an empty string on failure.
// This version only handles VM_COMM and VM_STATE, which are the only fields
// that are strings.
std::string GetProcStatsFieldAsString(
const std::vector<std::string>& proc_stats,
internal::ProcStatsFields field_num) {
if (field_num < internal::VM_COMM || field_num > internal::VM_STATE) {
NOTREACHED();
return std::string();
}
if (proc_stats.size() > static_cast<size_t>(field_num))
return proc_stats[field_num];
NOTREACHED();
return std::string();
}
// Reads /proc/<pid>/cmdline and populates |proc_cmd_line_args| with the command
// line arguments. Returns true if successful.
// Note: /proc/<pid>/cmdline contains command line arguments separated by single
// null characters. We tokenize it into a vector of strings using '\0' as a
// delimiter.
bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) {
// Synchronously reading files in /proc is safe.
ThreadRestrictions::ScopedAllowIO allow_io;
FilePath cmd_line_file = internal::GetProcPidDir(pid).Append("cmdline");
std::string cmd_line;
if (!ReadFileToString(cmd_line_file, &cmd_line))
return false;
std::string delimiters;
delimiters.push_back('\0');
*proc_cmd_line_args = SplitString(cmd_line, delimiters, KEEP_WHITESPACE,
SPLIT_WANT_NONEMPTY);
return true;
}
} // namespace
ProcessIterator::ProcessIterator(const ProcessFilter* filter)
: filter_(filter) {
procfs_dir_ = opendir(internal::kProcDir);
if (!procfs_dir_) {
// On Android, SELinux may prevent reading /proc. See
// https://crbug.com/581517 for details.
PLOG(ERROR) << "opendir " << internal::kProcDir;
}
}
ProcessIterator::~ProcessIterator() {
if (procfs_dir_) {
closedir(procfs_dir_);
procfs_dir_ = nullptr;
}
}
bool ProcessIterator::CheckForNextProcess() {
// TODO(port): skip processes owned by different UID
if (!procfs_dir_) {
DLOG(ERROR) << "Skipping CheckForNextProcess(), no procfs_dir_";
return false;
}
pid_t pid = kNullProcessId;
std::vector<std::string> cmd_line_args;
std::string stats_data;
std::vector<std::string> proc_stats;
// Arbitrarily guess that there will never be more than 200 non-process
// files in /proc. Hardy has 53 and Lucid has 61.
int skipped = 0;
const int kSkipLimit = 200;
while (skipped < kSkipLimit) {
dirent* slot = readdir(procfs_dir_);
// all done looking through /proc?
if (!slot)
return false;
// If not a process, keep looking for one.
pid = internal::ProcDirSlotToPid(slot->d_name);
if (!pid) {
skipped++;
continue;
}
if (!GetProcCmdline(pid, &cmd_line_args))
continue;
if (!internal::ReadProcStats(pid, &stats_data))
continue;
if (!internal::ParseProcStats(stats_data, &proc_stats))
continue;
std::string runstate =
GetProcStatsFieldAsString(proc_stats, internal::VM_STATE);
if (runstate.size() != 1) {
NOTREACHED();
continue;
}
// Is the process in 'Zombie' state, i.e. dead but waiting to be reaped?
// Allowed values: D R S T Z
if (runstate[0] != 'Z')
break;
// Nope, it's a zombie; somebody isn't cleaning up after their children.
// (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.)
// There could be a lot of zombies, can't really decrement i here.
}
if (skipped >= kSkipLimit) {
NOTREACHED();
return false;
}
entry_.pid_ = pid;
entry_.ppid_ = GetProcStatsFieldAsInt64(proc_stats, internal::VM_PPID);
entry_.gid_ = GetProcStatsFieldAsInt64(proc_stats, internal::VM_PGRP);
entry_.cmd_line_args_.assign(cmd_line_args.begin(), cmd_line_args.end());
entry_.exe_file_ = GetProcessExecutablePath(pid).BaseName().value();
return true;
}
bool NamedProcessIterator::IncludeEntry() {
if (executable_name_ != entry().exe_file())
return false;
return ProcessIterator::IncludeEntry();
}
} // namespace base

View file

@ -0,0 +1,139 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_iterator.h"
#include <errno.h>
#include <stddef.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
namespace base {
ProcessIterator::ProcessIterator(const ProcessFilter* filter)
: index_of_kinfo_proc_(0),
filter_(filter) {
// Get a snapshot of all of my processes (yes, as we loop it can go stale, but
// but trying to find where we were in a constantly changing list is basically
// impossible.
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID,
static_cast<int>(geteuid()) };
// Since more processes could start between when we get the size and when
// we get the list, we do a loop to keep trying until we get it.
bool done = false;
int try_num = 1;
const int max_tries = 10;
do {
// Get the size of the buffer
size_t len = 0;
if (sysctl(mib, base::size(mib), NULL, &len, NULL, 0) < 0) {
DLOG(ERROR) << "failed to get the size needed for the process list";
kinfo_procs_.resize(0);
done = true;
} else {
size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
// Leave some spare room for process table growth (more could show up
// between when we check and now)
num_of_kinfo_proc += 16;
kinfo_procs_.resize(num_of_kinfo_proc);
len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
// Load the list of processes
if (sysctl(mib, base::size(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
// If we get a mem error, it just means we need a bigger buffer, so
// loop around again. Anything else is a real error and give up.
if (errno != ENOMEM) {
DLOG(ERROR) << "failed to get the process list";
kinfo_procs_.resize(0);
done = true;
}
} else {
// Got the list, just make sure we're sized exactly right
kinfo_procs_.resize(len / sizeof(struct kinfo_proc));
done = true;
}
}
} while (!done && (try_num++ < max_tries));
if (!done) {
DLOG(ERROR) << "failed to collect the process list in a few tries";
kinfo_procs_.resize(0);
}
}
ProcessIterator::~ProcessIterator() {
}
bool ProcessIterator::CheckForNextProcess() {
std::string data;
for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) {
kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_];
// Skip processes just awaiting collection
if ((kinfo.kp_proc.p_pid > 0) && (kinfo.kp_proc.p_stat == SZOMB))
continue;
int mib[] = { CTL_KERN, KERN_PROCARGS, kinfo.kp_proc.p_pid };
// Find out what size buffer we need.
size_t data_len = 0;
if (sysctl(mib, base::size(mib), NULL, &data_len, NULL, 0) < 0) {
DVPLOG(1) << "failed to figure out the buffer size for a commandline";
continue;
}
data.resize(data_len);
if (sysctl(mib, base::size(mib), &data[0], &data_len, NULL, 0) < 0) {
DVPLOG(1) << "failed to fetch a commandline";
continue;
}
// |data| contains all the command line parameters of the process, separated
// by blocks of one or more null characters. We tokenize |data| into a
// vector of strings using '\0' as a delimiter and populate
// |entry_.cmd_line_args_|.
std::string delimiters;
delimiters.push_back('\0');
entry_.cmd_line_args_ = SplitString(data, delimiters,
KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
// |data| starts with the full executable path followed by a null character.
// We search for the first instance of '\0' and extract everything before it
// to populate |entry_.exe_file_|.
size_t exec_name_end = data.find('\0');
if (exec_name_end == std::string::npos) {
DLOG(ERROR) << "command line data didn't match expected format";
continue;
}
entry_.pid_ = kinfo.kp_proc.p_pid;
entry_.ppid_ = kinfo.kp_eproc.e_ppid;
entry_.gid_ = kinfo.kp_eproc.e_pgid;
size_t last_slash = data.rfind('/', exec_name_end);
if (last_slash == std::string::npos)
entry_.exe_file_.assign(data, 0, exec_name_end);
else
entry_.exe_file_.assign(data, last_slash + 1,
exec_name_end - last_slash - 1);
// Start w/ the next entry next time through
++index_of_kinfo_proc_;
// Done
return true;
}
return false;
}
bool NamedProcessIterator::IncludeEntry() {
return (executable_name_ == entry().exe_file() &&
ProcessIterator::IncludeEntry());
}
} // namespace base

View file

@ -0,0 +1,132 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_iterator.h"
#include <errno.h>
#include <stddef.h>
#include <sys/sysctl.h>
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
namespace base {
ProcessIterator::ProcessIterator(const ProcessFilter* filter)
: index_of_kinfo_proc_(),
filter_(filter) {
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, getuid(),
sizeof(struct kinfo_proc), 0 };
bool done = false;
int try_num = 1;
const int max_tries = 10;
do {
size_t len = 0;
if (sysctl(mib, base::size(mib), NULL, &len, NULL, 0) < 0) {
DLOG(ERROR) << "failed to get the size needed for the process list";
kinfo_procs_.resize(0);
done = true;
} else {
size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
// Leave some spare room for process table growth (more could show up
// between when we check and now)
num_of_kinfo_proc += 16;
kinfo_procs_.resize(num_of_kinfo_proc);
len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
if (sysctl(mib, base::size(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
// If we get a mem error, it just means we need a bigger buffer, so
// loop around again. Anything else is a real error and give up.
if (errno != ENOMEM) {
DLOG(ERROR) << "failed to get the process list";
kinfo_procs_.resize(0);
done = true;
}
} else {
// Got the list, just make sure we're sized exactly right
size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
kinfo_procs_.resize(num_of_kinfo_proc);
done = true;
}
}
} while (!done && (try_num++ < max_tries));
if (!done) {
DLOG(ERROR) << "failed to collect the process list in a few tries";
kinfo_procs_.resize(0);
}
}
ProcessIterator::~ProcessIterator() {
}
bool ProcessIterator::CheckForNextProcess() {
std::string data;
for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) {
kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_];
// Skip processes just awaiting collection
if ((kinfo.p_pid > 0) && (kinfo.p_stat == SZOMB))
continue;
int mib[] = { CTL_KERN, KERN_PROC_ARGS, kinfo.p_pid };
// Find out what size buffer we need.
size_t data_len = 0;
if (sysctl(mib, base::size(mib), NULL, &data_len, NULL, 0) < 0) {
DVPLOG(1) << "failed to figure out the buffer size for a commandline";
continue;
}
data.resize(data_len);
if (sysctl(mib, base::size(mib), &data[0], &data_len, NULL, 0) < 0) {
DVPLOG(1) << "failed to fetch a commandline";
continue;
}
// |data| contains all the command line parameters of the process, separated
// by blocks of one or more null characters. We tokenize |data| into a
// vector of strings using '\0' as a delimiter and populate
// |entry_.cmd_line_args_|.
std::string delimiters;
delimiters.push_back('\0');
entry_.cmd_line_args_ = SplitString(data, delimiters, KEEP_WHITESPACE,
SPLIT_WANT_NONEMPTY);
// |data| starts with the full executable path followed by a null character.
// We search for the first instance of '\0' and extract everything before it
// to populate |entry_.exe_file_|.
size_t exec_name_end = data.find('\0');
if (exec_name_end == std::string::npos) {
DLOG(ERROR) << "command line data didn't match expected format";
continue;
}
entry_.pid_ = kinfo.p_pid;
entry_.ppid_ = kinfo.p_ppid;
entry_.gid_ = kinfo.p__pgid;
size_t last_slash = data.rfind('/', exec_name_end);
if (last_slash == std::string::npos)
entry_.exe_file_.assign(data, 0, exec_name_end);
else
entry_.exe_file_.assign(data, last_slash + 1,
exec_name_end - last_slash - 1);
// Start w/ the next entry next time through
++index_of_kinfo_proc_;
// Done
return true;
}
return false;
}
bool NamedProcessIterator::IncludeEntry() {
return (executable_name_ == entry().exe_file() &&
ProcessIterator::IncludeEntry());
}
} // namespace base

View file

@ -0,0 +1,43 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_iterator.h"
#include "base/strings/string_util.h"
namespace base {
ProcessIterator::ProcessIterator(const ProcessFilter* filter)
: started_iteration_(false),
filter_(filter) {
snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
}
ProcessIterator::~ProcessIterator() {
CloseHandle(snapshot_);
}
bool ProcessIterator::CheckForNextProcess() {
InitProcessEntry(&entry_);
if (!started_iteration_) {
started_iteration_ = true;
return !!Process32First(snapshot_, &entry_);
}
return !!Process32Next(snapshot_, &entry_);
}
void ProcessIterator::InitProcessEntry(ProcessEntry* entry) {
memset(entry, 0, sizeof(*entry));
entry->dwSize = sizeof(*entry);
}
bool NamedProcessIterator::IncludeEntry() {
// Case insensitive.
return !_wcsicmp(executable_name_.c_str(), entry().exe_file()) &&
ProcessIterator::IncludeEntry();
}
} // namespace base

View file

@ -0,0 +1,206 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process.h"
#include <errno.h>
#include <sys/resource.h>
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/posix/can_lower_nice_to.h"
#include "base/process/internal_linux.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
namespace base {
namespace {
const int kForegroundPriority = 0;
#if defined(OS_CHROMEOS)
// We are more aggressive in our lowering of background process priority
// for chromeos as we have much more control over other processes running
// on the machine.
//
// TODO(davemoore) Refactor this by adding support for higher levels to set
// the foregrounding / backgrounding process so we don't have to keep
// chrome / chromeos specific logic here.
const int kBackgroundPriority = 19;
const char kControlPath[] = "/sys/fs/cgroup/cpu%s/cgroup.procs";
const char kForeground[] = "/chrome_renderers/foreground";
const char kBackground[] = "/chrome_renderers/background";
const char kProcPath[] = "/proc/%d/cgroup";
struct CGroups {
// Check for cgroups files. ChromeOS supports these by default. It creates
// a cgroup mount in /sys/fs/cgroup and then configures two cpu task groups,
// one contains at most a single foreground renderer and the other contains
// all background renderers. This allows us to limit the impact of background
// renderers on foreground ones to a greater level than simple renicing.
bool enabled;
base::FilePath foreground_file;
base::FilePath background_file;
CGroups() {
foreground_file =
base::FilePath(base::StringPrintf(kControlPath, kForeground));
background_file =
base::FilePath(base::StringPrintf(kControlPath, kBackground));
base::FileSystemType foreground_type;
base::FileSystemType background_type;
enabled =
base::GetFileSystemType(foreground_file, &foreground_type) &&
base::GetFileSystemType(background_file, &background_type) &&
foreground_type == FILE_SYSTEM_CGROUP &&
background_type == FILE_SYSTEM_CGROUP;
}
static CGroups& Get() {
static auto& groups = *new CGroups;
return groups;
}
};
#else
const int kBackgroundPriority = 5;
#endif // defined(OS_CHROMEOS)
} // namespace
Time Process::CreationTime() const {
int64_t start_ticks = is_current()
? internal::ReadProcSelfStatsAndGetFieldAsInt64(
internal::VM_STARTTIME)
: internal::ReadProcStatsAndGetFieldAsInt64(
Pid(), internal::VM_STARTTIME);
if (!start_ticks)
return Time();
TimeDelta start_offset = internal::ClockTicksToTimeDelta(start_ticks);
Time boot_time = internal::GetBootTime();
if (boot_time.is_null())
return Time();
return Time(boot_time + start_offset);
}
// static
bool Process::CanBackgroundProcesses() {
#if defined(OS_CHROMEOS)
if (CGroups::Get().enabled)
return true;
#endif // defined(OS_CHROMEOS)
static const bool can_reraise_priority =
internal::CanLowerNiceTo(kForegroundPriority);
return can_reraise_priority;
}
bool Process::IsProcessBackgrounded() const {
DCHECK(IsValid());
#if defined(OS_CHROMEOS)
if (CGroups::Get().enabled) {
// Used to allow reading the process priority from proc on thread launch.
base::ThreadRestrictions::ScopedAllowIO allow_io;
std::string proc;
if (base::ReadFileToString(
base::FilePath(StringPrintf(kProcPath, process_)), &proc)) {
return IsProcessBackgroundedCGroup(proc);
}
return false;
}
#endif // defined(OS_CHROMEOS)
return GetPriority() == kBackgroundPriority;
}
bool Process::SetProcessBackgrounded(bool background) {
DCHECK(IsValid());
#if defined(OS_CHROMEOS)
if (CGroups::Get().enabled) {
std::string pid = NumberToString(process_);
const base::FilePath file = background ? CGroups::Get().background_file
: CGroups::Get().foreground_file;
return base::WriteFile(file, pid.c_str(), pid.size()) > 0;
}
#endif // defined(OS_CHROMEOS)
if (!CanBackgroundProcesses())
return false;
int priority = background ? kBackgroundPriority : kForegroundPriority;
int result = setpriority(PRIO_PROCESS, process_, priority);
DPCHECK(result == 0);
return result == 0;
}
#if defined(OS_CHROMEOS)
bool IsProcessBackgroundedCGroup(const StringPiece& cgroup_contents) {
// The process can be part of multiple control groups, and for each cgroup
// hierarchy there's an entry in the file. We look for a control group
// named "/chrome_renderers/background" to determine if the process is
// backgrounded. crbug.com/548818.
std::vector<StringPiece> lines = SplitStringPiece(
cgroup_contents, "\n", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
for (const auto& line : lines) {
std::vector<StringPiece> fields =
SplitStringPiece(line, ":", TRIM_WHITESPACE, SPLIT_WANT_ALL);
if (fields.size() != 3U) {
NOTREACHED();
continue;
}
if (fields[2] == kBackground)
return true;
}
return false;
}
#endif // defined(OS_CHROMEOS)
#if defined(OS_CHROMEOS)
// Reads /proc/<pid>/status and returns the PID in its PID namespace.
// If the process is not in a PID namespace or /proc/<pid>/status does not
// report NSpid, kNullProcessId is returned.
ProcessId Process::GetPidInNamespace() const {
std::string status;
{
// Synchronously reading files in /proc does not hit the disk.
ThreadRestrictions::ScopedAllowIO allow_io;
FilePath status_file =
FilePath("/proc").Append(NumberToString(process_)).Append("status");
if (!ReadFileToString(status_file, &status)) {
return kNullProcessId;
}
}
StringPairs pairs;
SplitStringIntoKeyValuePairs(status, ':', '\n', &pairs);
for (const auto& pair : pairs) {
const std::string& key = pair.first;
const std::string& value_str = pair.second;
if (key == "NSpid") {
std::vector<StringPiece> split_value_str = SplitStringPiece(
value_str, "\t", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
if (split_value_str.size() <= 1) {
return kNullProcessId;
}
int value;
// The last value in the list is the PID in the namespace.
if (!StringToInt(split_value_str.back(), &value)) {
NOTREACHED();
return kNullProcessId;
}
return value;
}
}
return kNullProcessId;
}
#endif // defined(OS_CHROMEOS)
} // namespace base

View file

@ -0,0 +1,98 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process.h"
#include <mach/mach.h>
#include <stddef.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <unistd.h>
#include <memory>
#include "base/feature_list.h"
#include "base/mac/mach_logging.h"
#include "base/memory/free_deleter.h"
#include "base/stl_util.h"
namespace base {
// Enables backgrounding hidden renderers on Mac.
const Feature kMacAllowBackgroundingProcesses{"MacAllowBackgroundingProcesses",
FEATURE_DISABLED_BY_DEFAULT};
Time Process::CreationTime() const {
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, Pid()};
size_t len = 0;
if (sysctl(mib, size(mib), NULL, &len, NULL, 0) < 0)
return Time();
std::unique_ptr<struct kinfo_proc, base::FreeDeleter> proc(
static_cast<struct kinfo_proc*>(malloc(len)));
if (sysctl(mib, size(mib), proc.get(), &len, NULL, 0) < 0)
return Time();
return Time::FromTimeVal(proc->kp_proc.p_un.__p_starttime);
}
bool Process::CanBackgroundProcesses() {
return FeatureList::IsEnabled(kMacAllowBackgroundingProcesses);
}
bool Process::IsProcessBackgrounded(PortProvider* port_provider) const {
DCHECK(IsValid());
if (port_provider == nullptr || !CanBackgroundProcesses())
return false;
mach_port_t task_port = port_provider->TaskForPid(Pid());
if (task_port == TASK_NULL)
return false;
task_category_policy_data_t category_policy;
mach_msg_type_number_t task_info_count = TASK_CATEGORY_POLICY_COUNT;
boolean_t get_default = FALSE;
kern_return_t result =
task_policy_get(task_port, TASK_CATEGORY_POLICY,
reinterpret_cast<task_policy_t>(&category_policy),
&task_info_count, &get_default);
MACH_LOG_IF(ERROR, result != KERN_SUCCESS, result)
<< "task_policy_get TASK_CATEGORY_POLICY";
if (result == KERN_SUCCESS && get_default == FALSE) {
return category_policy.role == TASK_BACKGROUND_APPLICATION;
}
return false;
}
bool Process::SetProcessBackgrounded(PortProvider* port_provider,
bool background) {
DCHECK(IsValid());
if (port_provider == nullptr || !CanBackgroundProcesses())
return false;
mach_port_t task_port = port_provider->TaskForPid(Pid());
if (task_port == TASK_NULL)
return false;
if (IsProcessBackgrounded(port_provider) == background)
return true;
task_category_policy category_policy;
category_policy.role =
background ? TASK_BACKGROUND_APPLICATION : TASK_FOREGROUND_APPLICATION;
kern_return_t result =
task_policy_set(task_port, TASK_CATEGORY_POLICY,
reinterpret_cast<task_policy_t>(&category_policy),
TASK_CATEGORY_POLICY_COUNT);
if (result != KERN_SUCCESS) {
MACH_LOG(ERROR, result) << "task_policy_set TASK_CATEGORY_POLICY";
return false;
}
return true;
}
} // namespace base

View file

@ -0,0 +1,167 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_metrics.h"
#include <utility>
#include "base/logging.h"
#include "base/values.h"
#include "build/build_config.h"
namespace {
int CalculateEventsPerSecond(uint64_t event_count,
uint64_t* last_event_count,
base::TimeTicks* last_calculated) {
base::TimeTicks time = base::TimeTicks::Now();
if (*last_event_count == 0) {
// First call, just set the last values.
*last_calculated = time;
*last_event_count = event_count;
return 0;
}
int64_t events_delta = event_count - *last_event_count;
int64_t time_delta = (time - *last_calculated).InMicroseconds();
if (time_delta == 0) {
NOTREACHED();
return 0;
}
*last_calculated = time;
*last_event_count = event_count;
int64_t events_delta_for_ms =
events_delta * base::Time::kMicrosecondsPerSecond;
// Round the result up by adding 1/2 (the second term resolves to 1/2 without
// dropping down into floating point).
return (events_delta_for_ms + time_delta / 2) / time_delta;
}
} // namespace
namespace base {
SystemMemoryInfoKB::SystemMemoryInfoKB() = default;
SystemMemoryInfoKB::SystemMemoryInfoKB(const SystemMemoryInfoKB& other) =
default;
SystemMetrics::SystemMetrics() {
committed_memory_ = 0;
}
SystemMetrics SystemMetrics::Sample() {
SystemMetrics system_metrics;
system_metrics.committed_memory_ = GetSystemCommitCharge();
#if defined(OS_LINUX) || defined(OS_ANDROID)
GetSystemMemoryInfo(&system_metrics.memory_info_);
GetVmStatInfo(&system_metrics.vmstat_info_);
GetSystemDiskInfo(&system_metrics.disk_info_);
#endif
#if defined(OS_CHROMEOS)
GetSwapInfo(&system_metrics.swap_info_);
#endif
#if defined(OS_WIN)
GetSystemPerformanceInfo(&system_metrics.performance_);
#endif
return system_metrics;
}
std::unique_ptr<Value> SystemMetrics::ToValue() const {
std::unique_ptr<DictionaryValue> res(new DictionaryValue());
res->SetIntKey("committed_memory", static_cast<int>(committed_memory_));
#if defined(OS_LINUX) || defined(OS_ANDROID)
std::unique_ptr<DictionaryValue> meminfo = memory_info_.ToValue();
std::unique_ptr<DictionaryValue> vmstat = vmstat_info_.ToValue();
meminfo->MergeDictionary(vmstat.get());
res->Set("meminfo", std::move(meminfo));
res->Set("diskinfo", disk_info_.ToValue());
#endif
#if defined(OS_CHROMEOS)
res->Set("swapinfo", swap_info_.ToValue());
#endif
#if defined(OS_WIN)
res->Set("perfinfo", performance_.ToValue());
#endif
return std::move(res);
}
std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateCurrentProcessMetrics() {
#if !defined(OS_MACOSX) || defined(OS_IOS)
return CreateProcessMetrics(base::GetCurrentProcessHandle());
#else
return CreateProcessMetrics(base::GetCurrentProcessHandle(), nullptr);
#endif // !defined(OS_MACOSX) || defined(OS_IOS)
}
#if !defined(OS_FREEBSD) || !defined(OS_POSIX)
double ProcessMetrics::GetPlatformIndependentCPUUsage() {
TimeDelta cumulative_cpu = GetCumulativeCPUUsage();
TimeTicks time = TimeTicks::Now();
if (last_cumulative_cpu_.is_zero()) {
// First call, just set the last values.
last_cumulative_cpu_ = cumulative_cpu;
last_cpu_time_ = time;
return 0;
}
TimeDelta system_time_delta = cumulative_cpu - last_cumulative_cpu_;
TimeDelta time_delta = time - last_cpu_time_;
DCHECK(!time_delta.is_zero());
if (time_delta.is_zero())
return 0;
last_cumulative_cpu_ = cumulative_cpu;
last_cpu_time_ = time;
return 100.0 * system_time_delta.InMicrosecondsF() /
time_delta.InMicrosecondsF();
}
#endif
#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_AIX)
int ProcessMetrics::CalculateIdleWakeupsPerSecond(
uint64_t absolute_idle_wakeups) {
return CalculateEventsPerSecond(absolute_idle_wakeups,
&last_absolute_idle_wakeups_,
&last_idle_wakeups_time_);
}
#else
int ProcessMetrics::GetIdleWakeupsPerSecond() {
NOTIMPLEMENTED(); // http://crbug.com/120488
return 0;
}
#endif // defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_AIX)
#if defined(OS_MACOSX)
int ProcessMetrics::CalculatePackageIdleWakeupsPerSecond(
uint64_t absolute_package_idle_wakeups) {
return CalculateEventsPerSecond(absolute_package_idle_wakeups,
&last_absolute_package_idle_wakeups_,
&last_package_idle_wakeups_time_);
}
#endif // defined(OS_MACOSX)
#if !defined(OS_WIN)
uint64_t ProcessMetrics::GetCumulativeDiskUsageInBytes() {
// Not implemented.
return 0;
}
#endif
uint64_t ProcessMetrics::GetDiskUsageBytesPerSecond() {
uint64_t cumulative_disk_usage = GetCumulativeDiskUsageInBytes();
return CalculateEventsPerSecond(cumulative_disk_usage,
&last_cumulative_disk_usage_,
&last_disk_usage_time_);
}
} // namespace base

View file

@ -0,0 +1,580 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file contains routines for gathering resource statistics for processes
// running on the system.
#ifndef BASE_PROCESS_PROCESS_METRICS_H_
#define BASE_PROCESS_PROCESS_METRICS_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>
#include "base/base_export.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/process/process_handle.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
#if defined(OS_MACOSX)
#include <mach/mach.h>
#include "base/process/port_provider_mac.h"
#if !defined(OS_IOS)
#include <mach/mach_vm.h>
#endif
#endif
#if defined(OS_WIN)
#include "base/win/scoped_handle.h"
#include "base/win/windows_types.h"
#endif
namespace base {
// Full declaration is in process_metrics_iocounters.h.
struct IoCounters;
#if defined(OS_LINUX) || defined(OS_ANDROID)
// Minor and major page fault counts since the process creation.
// Both counts are process-wide, and exclude child processes.
//
// minor: Number of page faults that didn't require disk IO.
// major: Number of page faults that required disk IO.
struct PageFaultCounts {
int64_t minor;
int64_t major;
};
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
// Convert a POSIX timeval to microseconds.
BASE_EXPORT int64_t TimeValToMicroseconds(const struct timeval& tv);
// Provides performance metrics for a specified process (CPU usage and IO
// counters). Use CreateCurrentProcessMetrics() to get an instance for the
// current process, or CreateProcessMetrics() to get an instance for an
// arbitrary process. Then, access the information with the different get
// methods.
//
// This class exposes a few platform-specific APIs for parsing memory usage, but
// these are not intended to generalize to other platforms, since the memory
// models differ substantially.
//
// To obtain consistent memory metrics, use the memory_instrumentation service.
//
// For further documentation on memory, see
// https://chromium.googlesource.com/chromium/src/+/HEAD/docs/README.md
class BASE_EXPORT ProcessMetrics {
public:
~ProcessMetrics();
// Creates a ProcessMetrics for the specified process.
#if !defined(OS_MACOSX) || defined(OS_IOS)
static std::unique_ptr<ProcessMetrics> CreateProcessMetrics(
ProcessHandle process);
#else
// The port provider needs to outlive the ProcessMetrics object returned by
// this function. If NULL is passed as provider, the returned object
// only returns valid metrics if |process| is the current process.
static std::unique_ptr<ProcessMetrics> CreateProcessMetrics(
ProcessHandle process,
PortProvider* port_provider);
#endif // !defined(OS_MACOSX) || defined(OS_IOS)
// Creates a ProcessMetrics for the current process. This a cross-platform
// convenience wrapper for CreateProcessMetrics().
static std::unique_ptr<ProcessMetrics> CreateCurrentProcessMetrics();
#if defined(OS_LINUX) || defined(OS_ANDROID)
// Resident Set Size is a Linux/Android specific memory concept. Do not
// attempt to extend this to other platforms.
BASE_EXPORT size_t GetResidentSetSize() const;
#endif
// Returns the percentage of time spent executing, across all threads of the
// process, in the interval since the last time the method was called. Since
// this considers the total execution time across all threads in a process,
// the result can easily exceed 100% in multi-thread processes running on
// multi-core systems. In general the result is therefore a value in the
// range 0% to SysInfo::NumberOfProcessors() * 100%.
//
// To obtain the percentage of total available CPU resources consumed by this
// process over the interval, the caller must divide by NumberOfProcessors().
//
// Since this API measures usage over an interval, it will return zero on the
// first call, and an actual value only on the second and subsequent calls.
double GetPlatformIndependentCPUUsage();
// Returns the cumulative CPU usage across all threads of the process since
// process start. In case of multi-core processors, a process can consume CPU
// at a rate higher than wall-clock time, e.g. two cores at full utilization
// will result in a time delta of 2 seconds/per 1 wall-clock second.
TimeDelta GetCumulativeCPUUsage();
// Returns the number of average idle cpu wakeups per second since the last
// call.
int GetIdleWakeupsPerSecond();
#if defined(OS_MACOSX)
// Returns the number of average "package idle exits" per second, which have
// a higher energy impact than a regular wakeup, since the last call.
//
// From the powermetrics man page:
// "With the exception of some Mac Pro systems, Mac and
// iOS systems are typically single package systems, wherein all CPUs are
// part of a single processor complex (typically a single IC die) with shared
// logic that can include (depending on system specifics) shared last level
// caches, an integrated memory controller etc. When all CPUs in the package
// are idle, the hardware can power-gate significant portions of the shared
// logic in addition to each individual processor's logic, as well as take
// measures such as placing DRAM in to self-refresh (also referred to as
// auto-refresh), place interconnects into lower-power states etc"
int GetPackageIdleWakeupsPerSecond();
// Returns "Energy Impact", a synthetic power estimation metric displayed by
// macOS in Activity Monitor and the battery menu.
int GetEnergyImpact();
#endif
// Retrieves accounting information for all I/O operations performed by the
// process.
// If IO information is retrieved successfully, the function returns true
// and fills in the IO_COUNTERS passed in. The function returns false
// otherwise.
bool GetIOCounters(IoCounters* io_counters) const;
// Returns the number of bytes transferred to/from disk per second, across all
// threads of the process, in the interval since the last time the method was
// called.
//
// Since this API measures usage over an interval, it will return zero on the
// first call, and an actual value only on the second and subsequent calls.
uint64_t GetDiskUsageBytesPerSecond();
// Returns the cumulative disk usage in bytes across all threads of the
// process since process start.
uint64_t GetCumulativeDiskUsageInBytes();
#if defined(OS_POSIX)
// Returns the number of file descriptors currently open by the process, or
// -1 on error.
int GetOpenFdCount() const;
// Returns the soft limit of file descriptors that can be opened by the
// process, or -1 on error.
int GetOpenFdSoftLimit() const;
#endif // defined(OS_POSIX)
#if defined(OS_LINUX) || defined(OS_ANDROID)
// Bytes of swap as reported by /proc/[pid]/status.
uint64_t GetVmSwapBytes() const;
// Minor and major page fault count as reported by /proc/[pid]/stat.
// Returns true for success.
bool GetPageFaultCounts(PageFaultCounts* counts) const;
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
// Returns total memory usage of malloc.
size_t GetMallocUsage();
private:
#if !defined(OS_MACOSX) || defined(OS_IOS)
explicit ProcessMetrics(ProcessHandle process);
#else
ProcessMetrics(ProcessHandle process, PortProvider* port_provider);
#endif // !defined(OS_MACOSX) || defined(OS_IOS)
#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_AIX)
int CalculateIdleWakeupsPerSecond(uint64_t absolute_idle_wakeups);
#endif
#if defined(OS_MACOSX)
// The subset of wakeups that cause a "package exit" can be tracked on macOS.
// See |GetPackageIdleWakeupsForSecond| comment for more info.
int CalculatePackageIdleWakeupsPerSecond(
uint64_t absolute_package_idle_wakeups);
#endif
#if defined(OS_WIN)
win::ScopedHandle process_;
#else
ProcessHandle process_;
#endif
// Used to store the previous times and CPU usage counts so we can
// compute the CPU usage between calls.
TimeTicks last_cpu_time_;
#if !defined(OS_FREEBSD) || !defined(OS_POSIX)
TimeDelta last_cumulative_cpu_;
#endif
// Used to store the previous times and disk usage counts so we can
// compute the disk usage between calls.
TimeTicks last_disk_usage_time_;
// Number of bytes transferred to/from disk in bytes.
uint64_t last_cumulative_disk_usage_ = 0;
#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_AIX)
// Same thing for idle wakeups.
TimeTicks last_idle_wakeups_time_;
uint64_t last_absolute_idle_wakeups_;
#endif
#if defined(OS_MACOSX)
// And same thing for package idle exit wakeups.
TimeTicks last_package_idle_wakeups_time_;
uint64_t last_absolute_package_idle_wakeups_;
double last_energy_impact_;
// In mach_absolute_time units.
uint64_t last_energy_impact_time_;
#endif
#if !defined(OS_IOS)
#if defined(OS_MACOSX)
// Queries the port provider if it's set.
mach_port_t TaskForPid(ProcessHandle process) const;
PortProvider* port_provider_;
#endif // defined(OS_MACOSX)
#endif // !defined(OS_IOS)
DISALLOW_COPY_AND_ASSIGN(ProcessMetrics);
};
// Returns the memory committed by the system in KBytes.
// Returns 0 if it can't compute the commit charge.
BASE_EXPORT size_t GetSystemCommitCharge();
// Returns the number of bytes in a memory page. Do not use this to compute
// the number of pages in a block of memory for calling mincore(). On some
// platforms, e.g. iOS, mincore() uses a different page size from what is
// returned by GetPageSize().
BASE_EXPORT size_t GetPageSize();
// Returns the maximum number of file descriptors that can be open by a process
// at once. If the number is unavailable, a conservative best guess is returned.
BASE_EXPORT size_t GetMaxFds();
// Returns the maximum number of handles that can be open at once per process.
BASE_EXPORT size_t GetHandleLimit();
#if defined(OS_POSIX)
// Increases the file descriptor soft limit to |max_descriptors| or the OS hard
// limit, whichever is lower. If the limit is already higher than
// |max_descriptors|, then nothing happens.
BASE_EXPORT void IncreaseFdLimitTo(unsigned int max_descriptors);
#endif // defined(OS_POSIX)
#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
defined(OS_ANDROID) || defined(OS_AIX) || defined(OS_FUCHSIA)
// Data about system-wide memory consumption. Values are in KB. Available on
// Windows, Mac, Linux, Android and Chrome OS.
//
// Total memory are available on all platforms that implement
// GetSystemMemoryInfo(). Total/free swap memory are available on all platforms
// except on Mac. Buffers/cached/active_anon/inactive_anon/active_file/
// inactive_file/dirty/reclaimable/pswpin/pswpout/pgmajfault are available on
// Linux/Android/Chrome OS. Shmem/slab/gem_objects/gem_size are Chrome OS only.
// Speculative/file_backed/purgeable are Mac and iOS only.
// Free is absent on Windows (see "avail_phys" below).
struct BASE_EXPORT SystemMemoryInfoKB {
SystemMemoryInfoKB();
SystemMemoryInfoKB(const SystemMemoryInfoKB& other);
// Serializes the platform specific fields to value.
std::unique_ptr<DictionaryValue> ToValue() const;
int total = 0;
#if !defined(OS_WIN)
int free = 0;
#endif
#if defined(OS_WIN)
// "This is the amount of physical memory that can be immediately reused
// without having to write its contents to disk first. It is the sum of the
// size of the standby, free, and zero lists." (MSDN).
// Standby: not modified pages of physical ram (file-backed memory) that are
// not actively being used.
int avail_phys = 0;
#endif
#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX)
// This provides an estimate of available memory as described here:
// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
// NOTE: this is ONLY valid in kernels 3.14 and up. Its value will always
// be 0 in earlier kernel versions.
// Note: it includes _all_ file-backed memory (active + inactive).
int available = 0;
#endif
#if !defined(OS_MACOSX)
int swap_total = 0;
int swap_free = 0;
#endif
#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_AIX) || \
defined(OS_FUCHSIA)
int buffers = 0;
int cached = 0;
int active_anon = 0;
int inactive_anon = 0;
int active_file = 0;
int inactive_file = 0;
int dirty = 0;
int reclaimable = 0;
#endif // defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_AIX) ||
// defined(OS_FUCHSIA)
#if defined(OS_CHROMEOS)
int shmem = 0;
int slab = 0;
// Gem data will be -1 if not supported.
int gem_objects = -1;
long long gem_size = -1;
#endif // defined(OS_CHROMEOS)
#if defined(OS_MACOSX)
int speculative = 0;
int file_backed = 0;
int purgeable = 0;
#endif // defined(OS_MACOSX)
};
// On Linux/Android/Chrome OS, system-wide memory consumption data is parsed
// from /proc/meminfo and /proc/vmstat. On Windows/Mac, it is obtained using
// system API calls.
//
// Fills in the provided |meminfo| structure. Returns true on success.
// Exposed for memory debugging widget.
BASE_EXPORT bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo);
#endif // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) ||
// defined(OS_ANDROID) || defined(OS_AIX) || defined(OS_FUCHSIA)
#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX)
// Parse the data found in /proc/<pid>/stat and return the sum of the
// CPU-related ticks. Returns -1 on parse error.
// Exposed for testing.
BASE_EXPORT int ParseProcStatCPU(StringPiece input);
// Get the number of threads of |process| as available in /proc/<pid>/stat.
// This should be used with care as no synchronization with running threads is
// done. This is mostly useful to guarantee being single-threaded.
// Returns 0 on failure.
BASE_EXPORT int GetNumberOfThreads(ProcessHandle process);
// /proc/self/exe refers to the current executable.
BASE_EXPORT extern const char kProcSelfExe[];
// Parses a string containing the contents of /proc/meminfo
// returns true on success or false for a parsing error
// Exposed for testing.
BASE_EXPORT bool ParseProcMeminfo(StringPiece input,
SystemMemoryInfoKB* meminfo);
// Data from /proc/vmstat.
struct BASE_EXPORT VmStatInfo {
// Serializes the platform specific fields to value.
std::unique_ptr<DictionaryValue> ToValue() const;
unsigned long pswpin = 0;
unsigned long pswpout = 0;
unsigned long pgmajfault = 0;
};
// Retrieves data from /proc/vmstat about system-wide vm operations.
// Fills in the provided |vmstat| structure. Returns true on success.
BASE_EXPORT bool GetVmStatInfo(VmStatInfo* vmstat);
// Parses a string containing the contents of /proc/vmstat
// returns true on success or false for a parsing error
// Exposed for testing.
BASE_EXPORT bool ParseProcVmstat(StringPiece input, VmStatInfo* vmstat);
// Data from /proc/diskstats about system-wide disk I/O.
struct BASE_EXPORT SystemDiskInfo {
SystemDiskInfo();
SystemDiskInfo(const SystemDiskInfo& other);
// Serializes the platform specific fields to value.
std::unique_ptr<Value> ToValue() const;
uint64_t reads = 0;
uint64_t reads_merged = 0;
uint64_t sectors_read = 0;
uint64_t read_time = 0;
uint64_t writes = 0;
uint64_t writes_merged = 0;
uint64_t sectors_written = 0;
uint64_t write_time = 0;
uint64_t io = 0;
uint64_t io_time = 0;
uint64_t weighted_io_time = 0;
};
// Checks whether the candidate string is a valid disk name, [hsv]d[a-z]+
// for a generic disk or mmcblk[0-9]+ for the MMC case.
// Names of disk partitions (e.g. sda1) are not valid.
BASE_EXPORT bool IsValidDiskName(StringPiece candidate);
// Retrieves data from /proc/diskstats about system-wide disk I/O.
// Fills in the provided |diskinfo| structure. Returns true on success.
BASE_EXPORT bool GetSystemDiskInfo(SystemDiskInfo* diskinfo);
// Returns the amount of time spent in user space since boot across all CPUs.
BASE_EXPORT TimeDelta GetUserCpuTimeSinceBoot();
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
#if defined(OS_CHROMEOS)
// Data from files in directory /sys/block/zram0 about ZRAM usage.
struct BASE_EXPORT SwapInfo {
SwapInfo()
: num_reads(0),
num_writes(0),
compr_data_size(0),
orig_data_size(0),
mem_used_total(0) {
}
// Serializes the platform specific fields to value.
std::unique_ptr<Value> ToValue() const;
uint64_t num_reads = 0;
uint64_t num_writes = 0;
uint64_t compr_data_size = 0;
uint64_t orig_data_size = 0;
uint64_t mem_used_total = 0;
};
// Parses a string containing the contents of /sys/block/zram0/mm_stat.
// This should be used for the new ZRAM sysfs interfaces.
// Returns true on success or false for a parsing error.
// Exposed for testing.
BASE_EXPORT bool ParseZramMmStat(StringPiece mm_stat_data, SwapInfo* swap_info);
// Parses a string containing the contents of /sys/block/zram0/stat
// This should be used for the new ZRAM sysfs interfaces.
// Returns true on success or false for a parsing error.
// Exposed for testing.
BASE_EXPORT bool ParseZramStat(StringPiece stat_data, SwapInfo* swap_info);
// In ChromeOS, reads files from /sys/block/zram0 that contain ZRAM usage data.
// Fills in the provided |swap_data| structure.
// Returns true on success or false for a parsing error.
BASE_EXPORT bool GetSwapInfo(SwapInfo* swap_info);
#endif // defined(OS_CHROMEOS)
struct BASE_EXPORT SystemPerformanceInfo {
SystemPerformanceInfo();
SystemPerformanceInfo(const SystemPerformanceInfo& other);
// Serializes the platform specific fields to value.
std::unique_ptr<Value> ToValue() const;
// Total idle time of all processes in the system (units of 100 ns).
uint64_t idle_time = 0;
// Number of bytes read.
uint64_t read_transfer_count = 0;
// Number of bytes written.
uint64_t write_transfer_count = 0;
// Number of bytes transferred (e.g. DeviceIoControlFile)
uint64_t other_transfer_count = 0;
// The amount of read operations.
uint64_t read_operation_count = 0;
// The amount of write operations.
uint64_t write_operation_count = 0;
// The amount of other operations.
uint64_t other_operation_count = 0;
// The number of pages written to the system's pagefiles.
uint64_t pagefile_pages_written = 0;
// The number of write operations performed on the system's pagefiles.
uint64_t pagefile_pages_write_ios = 0;
// The number of pages of physical memory available to processes running on
// the system.
uint64_t available_pages = 0;
// The number of pages read from disk to resolve page faults.
uint64_t pages_read = 0;
// The number of read operations initiated to resolve page faults.
uint64_t page_read_ios = 0;
};
// Retrieves performance counters from the operating system.
// Fills in the provided |info| structure. Returns true on success.
BASE_EXPORT bool GetSystemPerformanceInfo(SystemPerformanceInfo* info);
// Collects and holds performance metrics for system memory and disk.
// Provides functionality to retrieve the data on various platforms and
// to serialize the stored data.
class BASE_EXPORT SystemMetrics {
public:
SystemMetrics();
static SystemMetrics Sample();
// Serializes the system metrics to value.
std::unique_ptr<Value> ToValue() const;
private:
FRIEND_TEST_ALL_PREFIXES(SystemMetricsTest, SystemMetrics);
size_t committed_memory_;
#if defined(OS_LINUX) || defined(OS_ANDROID)
SystemMemoryInfoKB memory_info_;
VmStatInfo vmstat_info_;
SystemDiskInfo disk_info_;
#endif
#if defined(OS_CHROMEOS)
SwapInfo swap_info_;
#endif
#if defined(OS_WIN)
SystemPerformanceInfo performance_;
#endif
};
#if defined(OS_MACOSX) && !defined(OS_IOS)
enum class MachVMRegionResult {
// There were no more memory regions between |address| and the end of the
// virtual address space.
Finished,
// All output parameters are invalid.
Error,
// All output parameters are filled in.
Success
};
// Returns info on the first memory region at or after |address|, including
// resident memory and share mode. On Success, |size| reflects the size of the
// memory region.
// |size| and |info| are output parameters, only valid on Success.
// |address| is an in-out parameter, than represents both the address to start
// looking, and the start address of the memory region.
BASE_EXPORT MachVMRegionResult GetTopInfo(mach_port_t task,
mach_vm_size_t* size,
mach_vm_address_t* address,
vm_region_top_info_data_t* info);
// Returns info on the first memory region at or after |address|, including
// protection values. On Success, |size| reflects the size of the
// memory region.
// Returns info on the first memory region at or after |address|, including
// resident memory and share mode.
// |size| and |info| are output parameters, only valid on Success.
BASE_EXPORT MachVMRegionResult GetBasicInfo(mach_port_t task,
mach_vm_size_t* size,
mach_vm_address_t* address,
vm_region_basic_info_64* info);
#endif // defined(OS_MACOSX) && !defined(OS_IOS)
} // namespace base
#endif // BASE_PROCESS_PROCESS_METRICS_H_

View file

@ -0,0 +1,72 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_metrics.h"
#include <stddef.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <unistd.h>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/process/process_metrics_iocounters.h"
#include "base/stl_util.h"
namespace base {
ProcessMetrics::ProcessMetrics(ProcessHandle process)
: process_(process),
last_cpu_(0) {}
// static
std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
ProcessHandle process) {
return WrapUnique(new ProcessMetrics(process));
}
double ProcessMetrics::GetPlatformIndependentCPUUsage() {
struct kinfo_proc info;
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, process_};
size_t length = sizeof(info);
if (sysctl(mib, base::size(mib), &info, &length, NULL, 0) < 0)
return 0;
return (info.ki_pctcpu / FSCALE) * 100.0;
}
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
NOTREACHED();
return TimeDelta();
}
bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
return false;
}
size_t GetSystemCommitCharge() {
int mib[2], pagesize;
unsigned long mem_total, mem_free, mem_inactive;
size_t length = sizeof(mem_total);
if (sysctl(mib, base::size(mib), &mem_total, &length, NULL, 0) < 0)
return 0;
length = sizeof(mem_free);
if (sysctlbyname("vm.stats.vm.v_free_count", &mem_free, &length, NULL, 0) < 0)
return 0;
length = sizeof(mem_inactive);
if (sysctlbyname("vm.stats.vm.v_inactive_count", &mem_inactive, &length,
NULL, 0) < 0) {
return 0;
}
pagesize = getpagesize();
return mem_total - (mem_free*pagesize) - (mem_inactive*pagesize);
}
} // namespace base

View file

@ -0,0 +1,43 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_metrics.h"
#include <lib/fdio/limits.h>
namespace base {
size_t GetMaxFds() {
return FDIO_MAX_FD;
}
size_t GetHandleLimit() {
// Duplicated from the internal Magenta kernel constant kMaxHandleCount
// (zircon/kernel/object/handle.cc).
return 256 * 1024u;
}
size_t GetSystemCommitCharge() {
// TODO(https://crbug.com/926581): Fuchsia does not support this.
return 0;
}
// static
std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
ProcessHandle process) {
// TODO(https://crbug.com/926581).
return nullptr;
}
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
// TODO(https://crbug.com/926581).
return TimeDelta();
}
bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
// TODO(https://crbug.com/926581).
return false;
}
} // namespace base

View file

@ -0,0 +1,37 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This is a separate file so that users of process metrics don't need to
// include windows.h unless they need IoCounters.
#ifndef BASE_PROCESS_PROCESS_METRICS_IOCOUNTERS_H_
#define BASE_PROCESS_PROCESS_METRICS_IOCOUNTERS_H_
#include <stdint.h>
#include "base/process/process_metrics.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#endif
namespace base {
#if defined(OS_WIN)
struct IoCounters : public IO_COUNTERS {};
#elif defined(OS_POSIX)
struct IoCounters {
uint64_t ReadOperationCount;
uint64_t WriteOperationCount;
uint64_t OtherOperationCount;
uint64_t ReadTransferCount;
uint64_t WriteTransferCount;
uint64_t OtherTransferCount;
};
#endif
} // namespace base
#endif // BASE_PROCESS_PROCESS_METRICS_IOCOUNTERS_H_

View file

@ -0,0 +1,100 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_metrics.h"
#include <limits.h>
#include <mach/task.h>
#include <stddef.h>
#include "base/logging.h"
#include "base/mac/scoped_mach_port.h"
#include "base/memory/ptr_util.h"
#include "base/numerics/safe_conversions.h"
namespace base {
ProcessMetrics::ProcessMetrics(ProcessHandle process) {}
ProcessMetrics::~ProcessMetrics() {}
// static
std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
ProcessHandle process) {
return WrapUnique(new ProcessMetrics(process));
}
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
NOTIMPLEMENTED();
return TimeDelta();
}
size_t GetMaxFds() {
static const rlim_t kSystemDefaultMaxFds = 256;
rlim_t max_fds;
struct rlimit nofile;
if (getrlimit(RLIMIT_NOFILE, &nofile)) {
// Error case: Take a best guess.
max_fds = kSystemDefaultMaxFds;
} else {
max_fds = nofile.rlim_cur;
}
if (max_fds > INT_MAX)
max_fds = INT_MAX;
return static_cast<size_t>(max_fds);
}
void IncreaseFdLimitTo(unsigned int max_descriptors) {
// Unimplemented.
}
size_t GetPageSize() {
return getpagesize();
}
// Bytes committed by the system.
size_t GetSystemCommitCharge() {
NOTIMPLEMENTED();
return 0;
}
bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
struct host_basic_info hostinfo;
mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
base::mac::ScopedMachSendRight host(mach_host_self());
int result = host_info(host.get(), HOST_BASIC_INFO,
reinterpret_cast<host_info_t>(&hostinfo), &count);
if (result != KERN_SUCCESS)
return false;
DCHECK_EQ(HOST_BASIC_INFO_COUNT, count);
meminfo->total = static_cast<int>(hostinfo.max_mem / 1024);
vm_statistics64_data_t vm_info;
count = HOST_VM_INFO64_COUNT;
if (host_statistics64(host.get(), HOST_VM_INFO64,
reinterpret_cast<host_info64_t>(&vm_info),
&count) != KERN_SUCCESS) {
return false;
}
DCHECK_EQ(HOST_VM_INFO64_COUNT, count);
// Check that PAGE_SIZE is divisible by 1024 (2^10).
CHECK_EQ(PAGE_SIZE, (PAGE_SIZE >> 10) << 10);
meminfo->free = saturated_cast<int>(
PAGE_SIZE / 1024 * (vm_info.free_count - vm_info.speculative_count));
meminfo->speculative =
saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.speculative_count);
meminfo->file_backed =
saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.external_page_count);
meminfo->purgeable =
saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.purgeable_count);
return true;
}
} // namespace base

View file

@ -0,0 +1,915 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_metrics.h"
#include <dirent.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <utility>
#include "base/files/dir_reader_posix.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/optional.h"
#include "base/process/internal_linux.h"
#include "base/process/process_metrics_iocounters.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
namespace base {
namespace {
void TrimKeyValuePairs(StringPairs* pairs) {
for (auto& pair : *pairs) {
TrimWhitespaceASCII(pair.first, TRIM_ALL, &pair.first);
TrimWhitespaceASCII(pair.second, TRIM_ALL, &pair.second);
}
}
#if defined(OS_CHROMEOS)
// Read a file with a single number string and return the number as a uint64_t.
uint64_t ReadFileToUint64(const FilePath& file) {
std::string file_contents;
if (!ReadFileToString(file, &file_contents))
return 0;
TrimWhitespaceASCII(file_contents, TRIM_ALL, &file_contents);
uint64_t file_contents_uint64 = 0;
if (!StringToUint64(file_contents, &file_contents_uint64))
return 0;
return file_contents_uint64;
}
#endif
// Read |filename| in /proc/<pid>/, split the entries into key/value pairs, and
// trim the key and value. On success, return true and write the trimmed
// key/value pairs into |key_value_pairs|.
bool ReadProcFileToTrimmedStringPairs(pid_t pid,
StringPiece filename,
StringPairs* key_value_pairs) {
std::string status_data;
{
// Synchronously reading files in /proc does not hit the disk.
ThreadRestrictions::ScopedAllowIO allow_io;
FilePath status_file = internal::GetProcPidDir(pid).Append(filename);
if (!ReadFileToString(status_file, &status_data))
return false;
}
SplitStringIntoKeyValuePairs(status_data, ':', '\n', key_value_pairs);
TrimKeyValuePairs(key_value_pairs);
return true;
}
// Read /proc/<pid>/status and return the value for |field|, or 0 on failure.
// Only works for fields in the form of "Field: value kB".
size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, StringPiece field) {
StringPairs pairs;
if (!ReadProcFileToTrimmedStringPairs(pid, "status", &pairs))
return 0;
for (const auto& pair : pairs) {
const std::string& key = pair.first;
const std::string& value_str = pair.second;
if (key != field)
continue;
std::vector<StringPiece> split_value_str =
SplitStringPiece(value_str, " ", TRIM_WHITESPACE, SPLIT_WANT_ALL);
if (split_value_str.size() != 2 || split_value_str[1] != "kB") {
NOTREACHED();
return 0;
}
size_t value;
if (!StringToSizeT(split_value_str[0], &value)) {
NOTREACHED();
return 0;
}
return value;
}
// This can be reached if the process dies when proc is read -- in that case,
// the kernel can return missing fields.
return 0;
}
#if defined(OS_LINUX) || defined(OS_AIX)
// Read /proc/<pid>/status and look for |field|. On success, return true and
// write the value for |field| into |result|.
// Only works for fields in the form of "field : uint_value"
bool ReadProcStatusAndGetFieldAsUint64(pid_t pid,
StringPiece field,
uint64_t* result) {
StringPairs pairs;
if (!ReadProcFileToTrimmedStringPairs(pid, "status", &pairs))
return false;
for (const auto& pair : pairs) {
const std::string& key = pair.first;
const std::string& value_str = pair.second;
if (key != field)
continue;
uint64_t value;
if (!StringToUint64(value_str, &value))
return false;
*result = value;
return true;
}
return false;
}
#endif // defined(OS_LINUX) || defined(OS_AIX)
// Get the total CPU of a single process. Return value is number of jiffies
// on success or -1 on error.
int64_t GetProcessCPU(pid_t pid) {
std::string buffer;
std::vector<std::string> proc_stats;
if (!internal::ReadProcStats(pid, &buffer) ||
!internal::ParseProcStats(buffer, &proc_stats)) {
return -1;
}
int64_t total_cpu =
internal::GetProcStatsFieldAsInt64(proc_stats, internal::VM_UTIME) +
internal::GetProcStatsFieldAsInt64(proc_stats, internal::VM_STIME);
return total_cpu;
}
#if defined(OS_CHROMEOS)
// Report on Chrome OS GEM object graphics memory. /run/debugfs_gpu is a
// bind mount into /sys/kernel/debug and synchronously reading the in-memory
// files in /sys is fast.
void ReadChromeOSGraphicsMemory(SystemMemoryInfoKB* meminfo) {
#if defined(ARCH_CPU_ARM_FAMILY)
FilePath geminfo_file("/run/debugfs_gpu/exynos_gem_objects");
#else
FilePath geminfo_file("/run/debugfs_gpu/i915_gem_objects");
#endif
std::string geminfo_data;
meminfo->gem_objects = -1;
meminfo->gem_size = -1;
if (ReadFileToString(geminfo_file, &geminfo_data)) {
int gem_objects = -1;
long long gem_size = -1;
int num_res = sscanf(geminfo_data.c_str(), "%d objects, %lld bytes",
&gem_objects, &gem_size);
if (num_res == 2) {
meminfo->gem_objects = gem_objects;
meminfo->gem_size = gem_size;
}
}
#if defined(ARCH_CPU_ARM_FAMILY)
// Incorporate Mali graphics memory if present.
FilePath mali_memory_file("/sys/class/misc/mali0/device/memory");
std::string mali_memory_data;
if (ReadFileToString(mali_memory_file, &mali_memory_data)) {
long long mali_size = -1;
int num_res = sscanf(mali_memory_data.c_str(), "%lld bytes", &mali_size);
if (num_res == 1)
meminfo->gem_size += mali_size;
}
#endif // defined(ARCH_CPU_ARM_FAMILY)
}
#endif // defined(OS_CHROMEOS)
} // namespace
// static
std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
ProcessHandle process) {
return WrapUnique(new ProcessMetrics(process));
}
size_t ProcessMetrics::GetResidentSetSize() const {
return internal::ReadProcStatsAndGetFieldAsSizeT(process_, internal::VM_RSS) *
getpagesize();
}
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
return internal::ClockTicksToTimeDelta(GetProcessCPU(process_));
}
// For the /proc/self/io file to exist, the Linux kernel must have
// CONFIG_TASK_IO_ACCOUNTING enabled.
bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
StringPairs pairs;
if (!ReadProcFileToTrimmedStringPairs(process_, "io", &pairs))
return false;
io_counters->OtherOperationCount = 0;
io_counters->OtherTransferCount = 0;
for (const auto& pair : pairs) {
const std::string& key = pair.first;
const std::string& value_str = pair.second;
uint64_t* target_counter = nullptr;
if (key == "syscr")
target_counter = &io_counters->ReadOperationCount;
else if (key == "syscw")
target_counter = &io_counters->WriteOperationCount;
else if (key == "rchar")
target_counter = &io_counters->ReadTransferCount;
else if (key == "wchar")
target_counter = &io_counters->WriteTransferCount;
if (!target_counter)
continue;
bool converted = StringToUint64(value_str, target_counter);
DCHECK(converted);
}
return true;
}
#if defined(OS_LINUX) || defined(OS_ANDROID)
uint64_t ProcessMetrics::GetVmSwapBytes() const {
return ReadProcStatusAndGetFieldAsSizeT(process_, "VmSwap") * 1024;
}
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
#if defined(OS_LINUX) || defined(OS_ANDROID)
bool ProcessMetrics::GetPageFaultCounts(PageFaultCounts* counts) const {
// We are not using internal::ReadStatsFileAndGetFieldAsInt64(), since it
// would read the file twice, and return inconsistent numbers.
std::string stats_data;
if (!internal::ReadProcStats(process_, &stats_data))
return false;
std::vector<std::string> proc_stats;
if (!internal::ParseProcStats(stats_data, &proc_stats))
return false;
counts->minor =
internal::GetProcStatsFieldAsInt64(proc_stats, internal::VM_MINFLT);
counts->major =
internal::GetProcStatsFieldAsInt64(proc_stats, internal::VM_MAJFLT);
return true;
}
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
int ProcessMetrics::GetOpenFdCount() const {
// Use /proc/<pid>/fd to count the number of entries there.
FilePath fd_path = internal::GetProcPidDir(process_).Append("fd");
DirReaderPosix dir_reader(fd_path.value().c_str());
if (!dir_reader.IsValid())
return -1;
int total_count = 0;
for (; dir_reader.Next(); ) {
const char* name = dir_reader.name();
if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0)
++total_count;
}
return total_count;
}
int ProcessMetrics::GetOpenFdSoftLimit() const {
// Use /proc/<pid>/limits to read the open fd limit.
FilePath fd_path = internal::GetProcPidDir(process_).Append("limits");
std::string limits_contents;
if (!ReadFileToString(fd_path, &limits_contents))
return -1;
for (const auto& line : SplitStringPiece(
limits_contents, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
if (!line.starts_with("Max open files"))
continue;
auto tokens =
SplitStringPiece(line, " ", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
if (tokens.size() > 3) {
int limit = -1;
if (!StringToInt(tokens[3], &limit))
return -1;
return limit;
}
}
return -1;
}
#if defined(OS_LINUX) || defined(OS_AIX)
ProcessMetrics::ProcessMetrics(ProcessHandle process)
: process_(process), last_absolute_idle_wakeups_(0) {}
#else
ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process) {}
#endif
size_t GetSystemCommitCharge() {
SystemMemoryInfoKB meminfo;
if (!GetSystemMemoryInfo(&meminfo))
return 0;
return meminfo.total - meminfo.free - meminfo.buffers - meminfo.cached;
}
int ParseProcStatCPU(StringPiece input) {
// |input| may be empty if the process disappeared somehow.
// e.g. http://crbug.com/145811.
if (input.empty())
return -1;
size_t start = input.find_last_of(')');
if (start == input.npos)
return -1;
// Number of spaces remaining until reaching utime's index starting after the
// last ')'.
int num_spaces_remaining = internal::VM_UTIME - 1;
size_t i = start;
while ((i = input.find(' ', i + 1)) != input.npos) {
// Validate the assumption that there aren't any contiguous spaces
// in |input| before utime.
DCHECK_NE(input[i - 1], ' ');
if (--num_spaces_remaining == 0) {
int utime = 0;
int stime = 0;
if (sscanf(&input.data()[i], "%d %d", &utime, &stime) != 2)
return -1;
return utime + stime;
}
}
return -1;
}
int GetNumberOfThreads(ProcessHandle process) {
return internal::ReadProcStatsAndGetFieldAsInt64(process,
internal::VM_NUMTHREADS);
}
const char kProcSelfExe[] = "/proc/self/exe";
namespace {
// The format of /proc/diskstats is:
// Device major number
// Device minor number
// Device name
// Field 1 -- # of reads completed
// This is the total number of reads completed successfully.
// Field 2 -- # of reads merged, field 6 -- # of writes merged
// Reads and writes which are adjacent to each other may be merged for
// efficiency. Thus two 4K reads may become one 8K read before it is
// ultimately handed to the disk, and so it will be counted (and queued)
// as only one I/O. This field lets you know how often this was done.
// Field 3 -- # of sectors read
// This is the total number of sectors read successfully.
// Field 4 -- # of milliseconds spent reading
// This is the total number of milliseconds spent by all reads (as
// measured from __make_request() to end_that_request_last()).
// Field 5 -- # of writes completed
// This is the total number of writes completed successfully.
// Field 6 -- # of writes merged
// See the description of field 2.
// Field 7 -- # of sectors written
// This is the total number of sectors written successfully.
// Field 8 -- # of milliseconds spent writing
// This is the total number of milliseconds spent by all writes (as
// measured from __make_request() to end_that_request_last()).
// Field 9 -- # of I/Os currently in progress
// The only field that should go to zero. Incremented as requests are
// given to appropriate struct request_queue and decremented as they
// finish.
// Field 10 -- # of milliseconds spent doing I/Os
// This field increases so long as field 9 is nonzero.
// Field 11 -- weighted # of milliseconds spent doing I/Os
// This field is incremented at each I/O start, I/O completion, I/O
// merge, or read of these stats by the number of I/Os in progress
// (field 9) times the number of milliseconds spent doing I/O since the
// last update of this field. This can provide an easy measure of both
// I/O completion time and the backlog that may be accumulating.
const size_t kDiskDriveName = 2;
const size_t kDiskReads = 3;
const size_t kDiskReadsMerged = 4;
const size_t kDiskSectorsRead = 5;
const size_t kDiskReadTime = 6;
const size_t kDiskWrites = 7;
const size_t kDiskWritesMerged = 8;
const size_t kDiskSectorsWritten = 9;
const size_t kDiskWriteTime = 10;
const size_t kDiskIO = 11;
const size_t kDiskIOTime = 12;
const size_t kDiskWeightedIOTime = 13;
} // namespace
std::unique_ptr<DictionaryValue> SystemMemoryInfoKB::ToValue() const {
auto res = std::make_unique<DictionaryValue>();
res->SetIntKey("total", total);
res->SetIntKey("free", free);
res->SetIntKey("available", available);
res->SetIntKey("buffers", buffers);
res->SetIntKey("cached", cached);
res->SetIntKey("active_anon", active_anon);
res->SetIntKey("inactive_anon", inactive_anon);
res->SetIntKey("active_file", active_file);
res->SetIntKey("inactive_file", inactive_file);
res->SetIntKey("swap_total", swap_total);
res->SetIntKey("swap_free", swap_free);
res->SetIntKey("swap_used", swap_total - swap_free);
res->SetIntKey("dirty", dirty);
res->SetIntKey("reclaimable", reclaimable);
#ifdef OS_CHROMEOS
res->SetIntKey("shmem", shmem);
res->SetIntKey("slab", slab);
res->SetIntKey("gem_objects", gem_objects);
res->SetIntKey("gem_size", gem_size);
#endif
return res;
}
bool ParseProcMeminfo(StringPiece meminfo_data, SystemMemoryInfoKB* meminfo) {
// The format of /proc/meminfo is:
//
// MemTotal: 8235324 kB
// MemFree: 1628304 kB
// Buffers: 429596 kB
// Cached: 4728232 kB
// ...
// There is no guarantee on the ordering or position
// though it doesn't appear to change very often
// As a basic sanity check at the end, make sure the MemTotal value will be at
// least non-zero. So start off with a zero total.
meminfo->total = 0;
for (const StringPiece& line : SplitStringPiece(
meminfo_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
std::vector<StringPiece> tokens = SplitStringPiece(
line, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
// HugePages_* only has a number and no suffix so there may not be exactly 3
// tokens.
if (tokens.size() <= 1) {
DLOG(WARNING) << "meminfo: tokens: " << tokens.size()
<< " malformed line: " << line.as_string();
continue;
}
int* target = nullptr;
if (tokens[0] == "MemTotal:")
target = &meminfo->total;
else if (tokens[0] == "MemFree:")
target = &meminfo->free;
else if (tokens[0] == "MemAvailable:")
target = &meminfo->available;
else if (tokens[0] == "Buffers:")
target = &meminfo->buffers;
else if (tokens[0] == "Cached:")
target = &meminfo->cached;
else if (tokens[0] == "Active(anon):")
target = &meminfo->active_anon;
else if (tokens[0] == "Inactive(anon):")
target = &meminfo->inactive_anon;
else if (tokens[0] == "Active(file):")
target = &meminfo->active_file;
else if (tokens[0] == "Inactive(file):")
target = &meminfo->inactive_file;
else if (tokens[0] == "SwapTotal:")
target = &meminfo->swap_total;
else if (tokens[0] == "SwapFree:")
target = &meminfo->swap_free;
else if (tokens[0] == "Dirty:")
target = &meminfo->dirty;
else if (tokens[0] == "SReclaimable:")
target = &meminfo->reclaimable;
#if defined(OS_CHROMEOS)
// Chrome OS has a tweaked kernel that allows querying Shmem, which is
// usually video memory otherwise invisible to the OS.
else if (tokens[0] == "Shmem:")
target = &meminfo->shmem;
else if (tokens[0] == "Slab:")
target = &meminfo->slab;
#endif
if (target)
StringToInt(tokens[1], target);
}
// Make sure the MemTotal is valid.
return meminfo->total > 0;
}
bool ParseProcVmstat(StringPiece vmstat_data, VmStatInfo* vmstat) {
// The format of /proc/vmstat is:
//
// nr_free_pages 299878
// nr_inactive_anon 239863
// nr_active_anon 1318966
// nr_inactive_file 2015629
// ...
//
// Iterate through the whole file because the position of the
// fields are dependent on the kernel version and configuration.
bool has_pswpin = false;
bool has_pswpout = false;
bool has_pgmajfault = false;
for (const StringPiece& line : SplitStringPiece(
vmstat_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
std::vector<StringPiece> tokens = SplitStringPiece(
line, " ", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
if (tokens.size() != 2)
continue;
uint64_t val;
if (!StringToUint64(tokens[1], &val))
continue;
if (tokens[0] == "pswpin") {
vmstat->pswpin = val;
DCHECK(!has_pswpin);
has_pswpin = true;
} else if (tokens[0] == "pswpout") {
vmstat->pswpout = val;
DCHECK(!has_pswpout);
has_pswpout = true;
} else if (tokens[0] == "pgmajfault") {
vmstat->pgmajfault = val;
DCHECK(!has_pgmajfault);
has_pgmajfault = true;
}
if (has_pswpin && has_pswpout && has_pgmajfault)
return true;
}
return false;
}
bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
// Synchronously reading files in /proc and /sys are safe.
ThreadRestrictions::ScopedAllowIO allow_io;
// Used memory is: total - free - buffers - caches
FilePath meminfo_file("/proc/meminfo");
std::string meminfo_data;
if (!ReadFileToString(meminfo_file, &meminfo_data)) {
DLOG(WARNING) << "Failed to open " << meminfo_file.value();
return false;
}
if (!ParseProcMeminfo(meminfo_data, meminfo)) {
DLOG(WARNING) << "Failed to parse " << meminfo_file.value();
return false;
}
#if defined(OS_CHROMEOS)
ReadChromeOSGraphicsMemory(meminfo);
#endif
return true;
}
std::unique_ptr<DictionaryValue> VmStatInfo::ToValue() const {
auto res = std::make_unique<DictionaryValue>();
res->SetIntKey("pswpin", pswpin);
res->SetIntKey("pswpout", pswpout);
res->SetIntKey("pgmajfault", pgmajfault);
return res;
}
bool GetVmStatInfo(VmStatInfo* vmstat) {
// Synchronously reading files in /proc and /sys are safe.
ThreadRestrictions::ScopedAllowIO allow_io;
FilePath vmstat_file("/proc/vmstat");
std::string vmstat_data;
if (!ReadFileToString(vmstat_file, &vmstat_data)) {
DLOG(WARNING) << "Failed to open " << vmstat_file.value();
return false;
}
if (!ParseProcVmstat(vmstat_data, vmstat)) {
DLOG(WARNING) << "Failed to parse " << vmstat_file.value();
return false;
}
return true;
}
SystemDiskInfo::SystemDiskInfo() {
reads = 0;
reads_merged = 0;
sectors_read = 0;
read_time = 0;
writes = 0;
writes_merged = 0;
sectors_written = 0;
write_time = 0;
io = 0;
io_time = 0;
weighted_io_time = 0;
}
SystemDiskInfo::SystemDiskInfo(const SystemDiskInfo& other) = default;
std::unique_ptr<Value> SystemDiskInfo::ToValue() const {
auto res = std::make_unique<DictionaryValue>();
// Write out uint64_t variables as doubles.
// Note: this may discard some precision, but for JS there's no other option.
res->SetDouble("reads", static_cast<double>(reads));
res->SetDouble("reads_merged", static_cast<double>(reads_merged));
res->SetDouble("sectors_read", static_cast<double>(sectors_read));
res->SetDouble("read_time", static_cast<double>(read_time));
res->SetDouble("writes", static_cast<double>(writes));
res->SetDouble("writes_merged", static_cast<double>(writes_merged));
res->SetDouble("sectors_written", static_cast<double>(sectors_written));
res->SetDouble("write_time", static_cast<double>(write_time));
res->SetDouble("io", static_cast<double>(io));
res->SetDouble("io_time", static_cast<double>(io_time));
res->SetDouble("weighted_io_time", static_cast<double>(weighted_io_time));
return std::move(res);
}
bool IsValidDiskName(StringPiece candidate) {
if (candidate.length() < 3)
return false;
if (candidate[1] == 'd' &&
(candidate[0] == 'h' || candidate[0] == 's' || candidate[0] == 'v')) {
// [hsv]d[a-z]+ case
for (size_t i = 2; i < candidate.length(); ++i) {
if (!islower(candidate[i]))
return false;
}
return true;
}
const char kMMCName[] = "mmcblk";
if (!candidate.starts_with(kMMCName))
return false;
// mmcblk[0-9]+ case
for (size_t i = strlen(kMMCName); i < candidate.length(); ++i) {
if (!isdigit(candidate[i]))
return false;
}
return true;
}
bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) {
// Synchronously reading files in /proc does not hit the disk.
ThreadRestrictions::ScopedAllowIO allow_io;
FilePath diskinfo_file("/proc/diskstats");
std::string diskinfo_data;
if (!ReadFileToString(diskinfo_file, &diskinfo_data)) {
DLOG(WARNING) << "Failed to open " << diskinfo_file.value();
return false;
}
std::vector<StringPiece> diskinfo_lines = SplitStringPiece(
diskinfo_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
if (diskinfo_lines.empty()) {
DLOG(WARNING) << "No lines found";
return false;
}
diskinfo->reads = 0;
diskinfo->reads_merged = 0;
diskinfo->sectors_read = 0;
diskinfo->read_time = 0;
diskinfo->writes = 0;
diskinfo->writes_merged = 0;
diskinfo->sectors_written = 0;
diskinfo->write_time = 0;
diskinfo->io = 0;
diskinfo->io_time = 0;
diskinfo->weighted_io_time = 0;
uint64_t reads = 0;
uint64_t reads_merged = 0;
uint64_t sectors_read = 0;
uint64_t read_time = 0;
uint64_t writes = 0;
uint64_t writes_merged = 0;
uint64_t sectors_written = 0;
uint64_t write_time = 0;
uint64_t io = 0;
uint64_t io_time = 0;
uint64_t weighted_io_time = 0;
for (const StringPiece& line : diskinfo_lines) {
std::vector<StringPiece> disk_fields = SplitStringPiece(
line, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
// Fields may have overflowed and reset to zero.
if (!IsValidDiskName(disk_fields[kDiskDriveName].as_string()))
continue;
StringToUint64(disk_fields[kDiskReads], &reads);
StringToUint64(disk_fields[kDiskReadsMerged], &reads_merged);
StringToUint64(disk_fields[kDiskSectorsRead], &sectors_read);
StringToUint64(disk_fields[kDiskReadTime], &read_time);
StringToUint64(disk_fields[kDiskWrites], &writes);
StringToUint64(disk_fields[kDiskWritesMerged], &writes_merged);
StringToUint64(disk_fields[kDiskSectorsWritten], &sectors_written);
StringToUint64(disk_fields[kDiskWriteTime], &write_time);
StringToUint64(disk_fields[kDiskIO], &io);
StringToUint64(disk_fields[kDiskIOTime], &io_time);
StringToUint64(disk_fields[kDiskWeightedIOTime], &weighted_io_time);
diskinfo->reads += reads;
diskinfo->reads_merged += reads_merged;
diskinfo->sectors_read += sectors_read;
diskinfo->read_time += read_time;
diskinfo->writes += writes;
diskinfo->writes_merged += writes_merged;
diskinfo->sectors_written += sectors_written;
diskinfo->write_time += write_time;
diskinfo->io += io;
diskinfo->io_time += io_time;
diskinfo->weighted_io_time += weighted_io_time;
}
return true;
}
TimeDelta GetUserCpuTimeSinceBoot() {
return internal::GetUserCpuTimeSinceBoot();
}
#if defined(OS_CHROMEOS)
std::unique_ptr<Value> SwapInfo::ToValue() const {
auto res = std::make_unique<DictionaryValue>();
// Write out uint64_t variables as doubles.
// Note: this may discard some precision, but for JS there's no other option.
res->SetDouble("num_reads", static_cast<double>(num_reads));
res->SetDouble("num_writes", static_cast<double>(num_writes));
res->SetDouble("orig_data_size", static_cast<double>(orig_data_size));
res->SetDouble("compr_data_size", static_cast<double>(compr_data_size));
res->SetDouble("mem_used_total", static_cast<double>(mem_used_total));
double ratio = compr_data_size ? static_cast<double>(orig_data_size) /
static_cast<double>(compr_data_size)
: 0;
res->SetDouble("compression_ratio", ratio);
return std::move(res);
}
bool ParseZramMmStat(StringPiece mm_stat_data, SwapInfo* swap_info) {
// There are 7 columns in /sys/block/zram0/mm_stat,
// split by several spaces. The first three columns
// are orig_data_size, compr_data_size and mem_used_total.
// Example:
// 17715200 5008166 566062 0 1225715712 127 183842
//
// For more details:
// https://www.kernel.org/doc/Documentation/blockdev/zram.txt
std::vector<StringPiece> tokens = SplitStringPiece(
mm_stat_data, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
if (tokens.size() < 7) {
DLOG(WARNING) << "zram mm_stat: tokens: " << tokens.size()
<< " malformed line: " << mm_stat_data.as_string();
return false;
}
if (!StringToUint64(tokens[0], &swap_info->orig_data_size))
return false;
if (!StringToUint64(tokens[1], &swap_info->compr_data_size))
return false;
if (!StringToUint64(tokens[2], &swap_info->mem_used_total))
return false;
return true;
}
bool ParseZramStat(StringPiece stat_data, SwapInfo* swap_info) {
// There are 11 columns in /sys/block/zram0/stat,
// split by several spaces. The first column is read I/Os
// and fifth column is write I/Os.
// Example:
// 299 0 2392 0 1 0 8 0 0 0 0
//
// For more details:
// https://www.kernel.org/doc/Documentation/blockdev/zram.txt
std::vector<StringPiece> tokens = SplitStringPiece(
stat_data, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
if (tokens.size() < 11) {
DLOG(WARNING) << "zram stat: tokens: " << tokens.size()
<< " malformed line: " << stat_data.as_string();
return false;
}
if (!StringToUint64(tokens[0], &swap_info->num_reads))
return false;
if (!StringToUint64(tokens[4], &swap_info->num_writes))
return false;
return true;
}
namespace {
bool IgnoreZramFirstPage(uint64_t orig_data_size, SwapInfo* swap_info) {
if (orig_data_size <= 4096) {
// A single page is compressed at startup, and has a high compression
// ratio. Ignore this as it doesn't indicate any real swapping.
swap_info->orig_data_size = 0;
swap_info->num_reads = 0;
swap_info->num_writes = 0;
swap_info->compr_data_size = 0;
swap_info->mem_used_total = 0;
return true;
}
return false;
}
void ParseZramPath(SwapInfo* swap_info) {
FilePath zram_path("/sys/block/zram0");
uint64_t orig_data_size =
ReadFileToUint64(zram_path.Append("orig_data_size"));
if (IgnoreZramFirstPage(orig_data_size, swap_info))
return;
swap_info->orig_data_size = orig_data_size;
swap_info->num_reads = ReadFileToUint64(zram_path.Append("num_reads"));
swap_info->num_writes = ReadFileToUint64(zram_path.Append("num_writes"));
swap_info->compr_data_size =
ReadFileToUint64(zram_path.Append("compr_data_size"));
swap_info->mem_used_total =
ReadFileToUint64(zram_path.Append("mem_used_total"));
}
bool GetSwapInfoImpl(SwapInfo* swap_info) {
// Synchronously reading files in /sys/block/zram0 does not hit the disk.
ThreadRestrictions::ScopedAllowIO allow_io;
// Since ZRAM update, it shows the usage data in different places.
// If file "/sys/block/zram0/mm_stat" exists, use the new way, otherwise,
// use the old way.
static Optional<bool> use_new_zram_interface;
FilePath zram_mm_stat_file("/sys/block/zram0/mm_stat");
if (!use_new_zram_interface.has_value()) {
use_new_zram_interface = PathExists(zram_mm_stat_file);
}
if (!use_new_zram_interface.value()) {
ParseZramPath(swap_info);
return true;
}
std::string mm_stat_data;
if (!ReadFileToString(zram_mm_stat_file, &mm_stat_data)) {
DLOG(WARNING) << "Failed to open " << zram_mm_stat_file.value();
return false;
}
if (!ParseZramMmStat(mm_stat_data, swap_info)) {
DLOG(WARNING) << "Failed to parse " << zram_mm_stat_file.value();
return false;
}
if (IgnoreZramFirstPage(swap_info->orig_data_size, swap_info))
return true;
FilePath zram_stat_file("/sys/block/zram0/stat");
std::string stat_data;
if (!ReadFileToString(zram_stat_file, &stat_data)) {
DLOG(WARNING) << "Failed to open " << zram_stat_file.value();
return false;
}
if (!ParseZramStat(stat_data, swap_info)) {
DLOG(WARNING) << "Failed to parse " << zram_stat_file.value();
return false;
}
return true;
}
} // namespace
bool GetSwapInfo(SwapInfo* swap_info) {
if (!GetSwapInfoImpl(swap_info)) {
*swap_info = SwapInfo();
return false;
}
return true;
}
#endif // defined(OS_CHROMEOS)
#if defined(OS_LINUX) || defined(OS_AIX)
int ProcessMetrics::GetIdleWakeupsPerSecond() {
uint64_t num_switches;
static const char kSwitchStat[] = "voluntary_ctxt_switches";
return ReadProcStatusAndGetFieldAsUint64(process_, kSwitchStat, &num_switches)
? CalculateIdleWakeupsPerSecond(num_switches)
: 0;
}
#endif // defined(OS_LINUX) || defined(OS_AIX)
} // namespace base

View file

@ -0,0 +1,345 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_metrics.h"
#include <libproc.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <mach/mach_vm.h>
#include <mach/shared_region.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/sysctl.h>
#include "base/logging.h"
#include "base/mac/mac_util.h"
#include "base/mac/mach_logging.h"
#include "base/mac/scoped_mach_port.h"
#include "base/memory/ptr_util.h"
#include "base/numerics/safe_conversions.h"
#include "base/numerics/safe_math.h"
#include "base/process/process_metrics_iocounters.h"
#include "base/time/time.h"
namespace {
// This is a standin for the private pm_task_energy_data_t struct.
struct OpaquePMTaskEnergyData {
// Empirical size of the private struct.
uint8_t data[384];
};
// Sample everything but network usage, since fetching network
// usage can hang.
static constexpr uint8_t kPMSampleFlags = 0xff & ~0x8;
} // namespace
extern "C" {
// From libpmsample.dylib
int pm_sample_task(mach_port_t task,
OpaquePMTaskEnergyData* pm_energy,
uint64_t mach_time,
uint8_t flags);
// From libpmenergy.dylib
double pm_energy_impact(OpaquePMTaskEnergyData* pm_energy);
} // extern "C"
namespace base {
namespace {
bool GetTaskInfo(mach_port_t task, task_basic_info_64* task_info_data) {
if (task == MACH_PORT_NULL)
return false;
mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT;
kern_return_t kr = task_info(task,
TASK_BASIC_INFO_64,
reinterpret_cast<task_info_t>(task_info_data),
&count);
// Most likely cause for failure: |task| is a zombie.
return kr == KERN_SUCCESS;
}
MachVMRegionResult ParseOutputFromMachVMRegion(kern_return_t kr) {
if (kr == KERN_INVALID_ADDRESS) {
// We're at the end of the address space.
return MachVMRegionResult::Finished;
} else if (kr != KERN_SUCCESS) {
return MachVMRegionResult::Error;
}
return MachVMRegionResult::Success;
}
bool GetPowerInfo(mach_port_t task, task_power_info* power_info_data) {
if (task == MACH_PORT_NULL)
return false;
mach_msg_type_number_t power_info_count = TASK_POWER_INFO_COUNT;
kern_return_t kr = task_info(task, TASK_POWER_INFO,
reinterpret_cast<task_info_t>(power_info_data),
&power_info_count);
// Most likely cause for failure: |task| is a zombie.
return kr == KERN_SUCCESS;
}
double GetEnergyImpactInternal(mach_port_t task, uint64_t mach_time) {
OpaquePMTaskEnergyData energy_info{};
if (pm_sample_task(task, &energy_info, mach_time, kPMSampleFlags) != 0)
return 0.0;
return pm_energy_impact(&energy_info);
}
} // namespace
// Getting a mach task from a pid for another process requires permissions in
// general, so there doesn't really seem to be a way to do these (and spinning
// up ps to fetch each stats seems dangerous to put in a base api for anyone to
// call). Child processes ipc their port, so return something if available,
// otherwise return 0.
// static
std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
ProcessHandle process,
PortProvider* port_provider) {
return WrapUnique(new ProcessMetrics(process, port_provider));
}
#define TIME_VALUE_TO_TIMEVAL(a, r) do { \
(r)->tv_sec = (a)->seconds; \
(r)->tv_usec = (a)->microseconds; \
} while (0)
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
mach_port_t task = TaskForPid(process_);
if (task == MACH_PORT_NULL)
return TimeDelta();
// Libtop explicitly loops over the threads (libtop_pinfo_update_cpu_usage()
// in libtop.c), but this is more concise and gives the same results:
task_thread_times_info thread_info_data;
mach_msg_type_number_t thread_info_count = TASK_THREAD_TIMES_INFO_COUNT;
kern_return_t kr = task_info(task,
TASK_THREAD_TIMES_INFO,
reinterpret_cast<task_info_t>(&thread_info_data),
&thread_info_count);
if (kr != KERN_SUCCESS) {
// Most likely cause: |task| is a zombie.
return TimeDelta();
}
task_basic_info_64 task_info_data;
if (!GetTaskInfo(task, &task_info_data))
return TimeDelta();
/* Set total_time. */
// thread info contains live time...
struct timeval user_timeval, system_timeval, task_timeval;
TIME_VALUE_TO_TIMEVAL(&thread_info_data.user_time, &user_timeval);
TIME_VALUE_TO_TIMEVAL(&thread_info_data.system_time, &system_timeval);
timeradd(&user_timeval, &system_timeval, &task_timeval);
// ... task info contains terminated time.
TIME_VALUE_TO_TIMEVAL(&task_info_data.user_time, &user_timeval);
TIME_VALUE_TO_TIMEVAL(&task_info_data.system_time, &system_timeval);
timeradd(&user_timeval, &task_timeval, &task_timeval);
timeradd(&system_timeval, &task_timeval, &task_timeval);
return TimeDelta::FromMicroseconds(TimeValToMicroseconds(task_timeval));
}
int ProcessMetrics::GetPackageIdleWakeupsPerSecond() {
mach_port_t task = TaskForPid(process_);
task_power_info power_info_data;
GetPowerInfo(task, &power_info_data);
// The task_power_info struct contains two wakeup counters:
// task_interrupt_wakeups and task_platform_idle_wakeups.
// task_interrupt_wakeups is the total number of wakeups generated by the
// process, and is the number that Activity Monitor reports.
// task_platform_idle_wakeups is a subset of task_interrupt_wakeups that
// tallies the number of times the processor was taken out of its low-power
// idle state to handle a wakeup. task_platform_idle_wakeups therefore result
// in a greater power increase than the other interrupts which occur while the
// CPU is already working, and reducing them has a greater overall impact on
// power usage. See the powermetrics man page for more info.
return CalculatePackageIdleWakeupsPerSecond(
power_info_data.task_platform_idle_wakeups);
}
int ProcessMetrics::GetIdleWakeupsPerSecond() {
mach_port_t task = TaskForPid(process_);
task_power_info power_info_data;
GetPowerInfo(task, &power_info_data);
return CalculateIdleWakeupsPerSecond(power_info_data.task_interrupt_wakeups);
}
int ProcessMetrics::GetEnergyImpact() {
uint64_t now = mach_absolute_time();
if (last_energy_impact_ == 0) {
last_energy_impact_ = GetEnergyImpactInternal(TaskForPid(process_), now);
last_energy_impact_time_ = now;
return 0;
}
double total_energy_impact =
GetEnergyImpactInternal(TaskForPid(process_), now);
uint64_t delta = now - last_energy_impact_time_;
if (delta == 0)
return 0;
// Scale by 100 since the histogram is integral.
double seconds_since_last_measurement =
base::TimeTicks::FromMachAbsoluteTime(delta).since_origin().InSecondsF();
int energy_impact = 100 * (total_energy_impact - last_energy_impact_) /
seconds_since_last_measurement;
last_energy_impact_ = total_energy_impact;
last_energy_impact_time_ = now;
return energy_impact;
}
int ProcessMetrics::GetOpenFdCount() const {
// In order to get a true count of the open number of FDs, PROC_PIDLISTFDS
// is used. This is done twice: first to get the appropriate size of a
// buffer, and then secondly to fill the buffer with the actual FD info.
//
// The buffer size returned in the first call is an estimate, based on the
// number of allocated fileproc structures in the kernel. This number can be
// greater than the actual number of open files, since the structures are
// allocated in slabs. The value returned in proc_bsdinfo::pbi_nfiles is
// also the number of allocated fileprocs, not the number in use.
//
// However, the buffer size returned in the second call is an accurate count
// of the open number of descriptors. The contents of the buffer are unused.
int rv = proc_pidinfo(process_, PROC_PIDLISTFDS, 0, nullptr, 0);
if (rv < 0)
return -1;
std::unique_ptr<char[]> buffer(new char[rv]);
rv = proc_pidinfo(process_, PROC_PIDLISTFDS, 0, buffer.get(), rv);
if (rv < 0)
return -1;
return rv / PROC_PIDLISTFD_SIZE;
}
int ProcessMetrics::GetOpenFdSoftLimit() const {
return GetMaxFds();
}
bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
return false;
}
ProcessMetrics::ProcessMetrics(ProcessHandle process,
PortProvider* port_provider)
: process_(process),
last_absolute_idle_wakeups_(0),
last_absolute_package_idle_wakeups_(0),
last_energy_impact_(0),
port_provider_(port_provider) {}
mach_port_t ProcessMetrics::TaskForPid(ProcessHandle process) const {
mach_port_t task = MACH_PORT_NULL;
if (port_provider_)
task = port_provider_->TaskForPid(process_);
if (task == MACH_PORT_NULL && process_ == getpid())
task = mach_task_self();
return task;
}
// Bytes committed by the system.
size_t GetSystemCommitCharge() {
base::mac::ScopedMachSendRight host(mach_host_self());
mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
vm_statistics_data_t data;
kern_return_t kr = host_statistics(host.get(), HOST_VM_INFO,
reinterpret_cast<host_info_t>(&data),
&count);
if (kr != KERN_SUCCESS) {
MACH_DLOG(WARNING, kr) << "host_statistics";
return 0;
}
return (data.active_count * PAGE_SIZE) / 1024;
}
bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
struct host_basic_info hostinfo;
mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
base::mac::ScopedMachSendRight host(mach_host_self());
int result = host_info(host.get(), HOST_BASIC_INFO,
reinterpret_cast<host_info_t>(&hostinfo), &count);
if (result != KERN_SUCCESS)
return false;
DCHECK_EQ(HOST_BASIC_INFO_COUNT, count);
meminfo->total = static_cast<int>(hostinfo.max_mem / 1024);
vm_statistics64_data_t vm_info;
count = HOST_VM_INFO64_COUNT;
if (host_statistics64(host.get(), HOST_VM_INFO64,
reinterpret_cast<host_info64_t>(&vm_info),
&count) != KERN_SUCCESS) {
return false;
}
DCHECK_EQ(HOST_VM_INFO64_COUNT, count);
static_assert(PAGE_SIZE % 1024 == 0, "Invalid page size");
meminfo->free = saturated_cast<int>(
PAGE_SIZE / 1024 * (vm_info.free_count - vm_info.speculative_count));
meminfo->speculative =
saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.speculative_count);
meminfo->file_backed =
saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.external_page_count);
meminfo->purgeable =
saturated_cast<int>(PAGE_SIZE / 1024 * vm_info.purgeable_count);
return true;
}
// Both |size| and |address| are in-out parameters.
// |info| is an output parameter, only valid on Success.
MachVMRegionResult GetTopInfo(mach_port_t task,
mach_vm_size_t* size,
mach_vm_address_t* address,
vm_region_top_info_data_t* info) {
mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT;
mach_port_t object_name;
kern_return_t kr = mach_vm_region(task, address, size, VM_REGION_TOP_INFO,
reinterpret_cast<vm_region_info_t>(info),
&info_count, &object_name);
// The kernel always returns a null object for VM_REGION_TOP_INFO, but
// balance it with a deallocate in case this ever changes. See 10.9.2
// xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region.
mach_port_deallocate(task, object_name);
return ParseOutputFromMachVMRegion(kr);
}
MachVMRegionResult GetBasicInfo(mach_port_t task,
mach_vm_size_t* size,
mach_vm_address_t* address,
vm_region_basic_info_64* info) {
mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
mach_port_t object_name;
kern_return_t kr = mach_vm_region(
task, address, size, VM_REGION_BASIC_INFO_64,
reinterpret_cast<vm_region_info_t>(info), &info_count, &object_name);
// The kernel always returns a null object for VM_REGION_BASIC_INFO_64, but
// balance it with a deallocate in case this ever changes. See 10.9.2
// xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region.
mach_port_deallocate(task, object_name);
return ParseOutputFromMachVMRegion(kr);
}
} // namespace base

View file

@ -0,0 +1,16 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_metrics.h"
#include <stddef.h>
#include <unistd.h>
namespace base {
size_t GetPageSize() {
return getpagesize();
}
} // namespace base

View file

@ -0,0 +1,90 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_metrics.h"
#include <stddef.h>
#include <stdint.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include "base/memory/ptr_util.h"
#include "base/process/process_metrics_iocounters.h"
#include "base/stl_util.h"
namespace base {
// static
std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
ProcessHandle process) {
return WrapUnique(new ProcessMetrics(process));
}
bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
return false;
}
static int GetProcessCPU(pid_t pid) {
struct kinfo_proc info;
size_t length;
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid,
sizeof(struct kinfo_proc), 0 };
if (sysctl(mib, base::size(mib), NULL, &length, NULL, 0) < 0)
return -1;
mib[5] = (length / sizeof(struct kinfo_proc));
if (sysctl(mib, base::size(mib), &info, &length, NULL, 0) < 0)
return 0;
return info.p_pctcpu;
}
double ProcessMetrics::GetPlatformIndependentCPUUsage() {
TimeTicks time = TimeTicks::Now();
if (last_cpu_time_.is_zero()) {
// First call, just set the last values.
last_cpu_time_ = time;
return 0;
}
int cpu = GetProcessCPU(process_);
last_cpu_time_ = time;
double percentage = static_cast<double>((cpu * 100.0) / FSCALE);
return percentage;
}
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
NOTREACHED();
return TimeDelta();
}
ProcessMetrics::ProcessMetrics(ProcessHandle process)
: process_(process),
last_cpu_(0) {}
size_t GetSystemCommitCharge() {
int mib[] = { CTL_VM, VM_METER };
int pagesize;
struct vmtotal vmtotal;
unsigned long mem_total, mem_free, mem_inactive;
size_t len = sizeof(vmtotal);
if (sysctl(mib, base::size(mib), &vmtotal, &len, NULL, 0) < 0)
return 0;
mem_total = vmtotal.t_vm;
mem_free = vmtotal.t_free;
mem_inactive = vmtotal.t_vm - vmtotal.t_avm;
pagesize = getpagesize();
return mem_total - (mem_free*pagesize) - (mem_inactive*pagesize);
}
} // namespace base

View file

@ -0,0 +1,126 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_metrics.h"
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/time.h>
#include <unistd.h>
#include "base/allocator/buildflags.h"
#include "base/logging.h"
#include "build/build_config.h"
#if !defined(OS_FUCHSIA)
#include <sys/resource.h>
#endif
#if defined(OS_MACOSX)
#include <malloc/malloc.h>
#else
#include <malloc.h>
#endif
namespace base {
int64_t TimeValToMicroseconds(const struct timeval& tv) {
int64_t ret = tv.tv_sec; // Avoid (int * int) integer overflow.
ret *= Time::kMicrosecondsPerSecond;
ret += tv.tv_usec;
return ret;
}
ProcessMetrics::~ProcessMetrics() = default;
#if !defined(OS_FUCHSIA)
#if defined(OS_LINUX)
static const rlim_t kSystemDefaultMaxFds = 8192;
#elif defined(OS_MACOSX)
static const rlim_t kSystemDefaultMaxFds = 256;
#elif defined(OS_SOLARIS)
static const rlim_t kSystemDefaultMaxFds = 8192;
#elif defined(OS_FREEBSD)
static const rlim_t kSystemDefaultMaxFds = 8192;
#elif defined(OS_NETBSD)
static const rlim_t kSystemDefaultMaxFds = 1024;
#elif defined(OS_OPENBSD)
static const rlim_t kSystemDefaultMaxFds = 256;
#elif defined(OS_ANDROID)
static const rlim_t kSystemDefaultMaxFds = 1024;
#elif defined(OS_AIX)
static const rlim_t kSystemDefaultMaxFds = 8192;
#endif
size_t GetMaxFds() {
rlim_t max_fds;
struct rlimit nofile;
if (getrlimit(RLIMIT_NOFILE, &nofile)) {
// getrlimit failed. Take a best guess.
max_fds = kSystemDefaultMaxFds;
RAW_LOG(ERROR, "getrlimit(RLIMIT_NOFILE) failed");
} else {
max_fds = nofile.rlim_cur;
}
if (max_fds > INT_MAX)
max_fds = INT_MAX;
return static_cast<size_t>(max_fds);
}
size_t GetHandleLimit() {
#if defined(OS_MACOSX)
// Taken from a small test that allocated ports in a loop.
return static_cast<size_t>(1 << 18);
#else
return GetMaxFds();
#endif
}
void IncreaseFdLimitTo(unsigned int max_descriptors) {
struct rlimit limits;
if (getrlimit(RLIMIT_NOFILE, &limits) == 0) {
unsigned int new_limit = max_descriptors;
if (max_descriptors <= limits.rlim_cur)
return;
if (limits.rlim_max > 0 && limits.rlim_max < max_descriptors) {
new_limit = limits.rlim_max;
}
limits.rlim_cur = new_limit;
if (setrlimit(RLIMIT_NOFILE, &limits) != 0) {
PLOG(INFO) << "Failed to set file descriptor limit";
}
} else {
PLOG(INFO) << "Failed to get file descriptor limit";
}
}
#endif // !defined(OS_FUCHSIA)
size_t GetPageSize() {
return getpagesize();
}
size_t ProcessMetrics::GetMallocUsage() {
#if defined(OS_MACOSX) || defined(OS_IOS)
malloc_statistics_t stats = {0};
malloc_zone_statistics(nullptr, &stats);
return stats.size_in_use;
#elif defined(OS_LINUX) || defined(OS_ANDROID)
struct mallinfo minfo = mallinfo();
#if BUILDFLAG(USE_TCMALLOC)
return minfo.uordblks;
#else
return minfo.hblkhd + minfo.arena;
#endif
#elif defined(OS_FUCHSIA)
// TODO(fuchsia): Not currently exposed. https://crbug.com/735087.
return 0;
#endif
}
} // namespace base

View file

@ -0,0 +1,305 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_metrics.h"
#include <windows.h> // Must be in front of other Windows header files.
#include <psapi.h>
#include <stddef.h>
#include <stdint.h>
#include <winternl.h>
#include <algorithm>
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/process/process_metrics_iocounters.h"
#include "base/system/sys_info.h"
#include "base/threading/scoped_blocking_call.h"
namespace base {
namespace {
// System pagesize. This value remains constant on x86/64 architectures.
const int PAGESIZE_KB = 4;
// ntstatus.h conflicts with windows.h so define this locally.
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
// Definition of this struct is taken from the book:
// Windows NT/200, Native API reference, Gary Nebbett
struct SYSTEM_PERFORMANCE_INFORMATION {
// Total idle time of all processes in the system (units of 100 ns).
LARGE_INTEGER IdleTime;
// Number of bytes read (by all call to ZwReadFile).
LARGE_INTEGER ReadTransferCount;
// Number of bytes written (by all call to ZwWriteFile).
LARGE_INTEGER WriteTransferCount;
// Number of bytes transferred (e.g. DeviceIoControlFile)
LARGE_INTEGER OtherTransferCount;
// The amount of read operations.
ULONG ReadOperationCount;
// The amount of write operations.
ULONG WriteOperationCount;
// The amount of other operations.
ULONG OtherOperationCount;
// The number of pages of physical memory available to processes running on
// the system.
ULONG AvailablePages;
ULONG TotalCommittedPages;
ULONG TotalCommitLimit;
ULONG PeakCommitment;
ULONG PageFaults;
ULONG WriteCopyFaults;
ULONG TransitionFaults;
ULONG CacheTransitionFaults;
ULONG DemandZeroFaults;
// The number of pages read from disk to resolve page faults.
ULONG PagesRead;
// The number of read operations initiated to resolve page faults.
ULONG PageReadIos;
ULONG CacheReads;
ULONG CacheIos;
// The number of pages written to the system's pagefiles.
ULONG PagefilePagesWritten;
// The number of write operations performed on the system's pagefiles.
ULONG PagefilePageWriteIos;
ULONG MappedFilePagesWritten;
ULONG MappedFilePageWriteIos;
ULONG PagedPoolUsage;
ULONG NonPagedPoolUsage;
ULONG PagedPoolAllocs;
ULONG PagedPoolFrees;
ULONG NonPagedPoolAllocs;
ULONG NonPagedPoolFrees;
ULONG TotalFreeSystemPtes;
ULONG SystemCodePage;
ULONG TotalSystemDriverPages;
ULONG TotalSystemCodePages;
ULONG SmallNonPagedLookasideListAllocateHits;
ULONG SmallPagedLookasideListAllocateHits;
ULONG Reserved3;
ULONG MmSystemCachePage;
ULONG PagedPoolPage;
ULONG SystemDriverPage;
ULONG FastReadNoWait;
ULONG FastReadWait;
ULONG FastReadResourceMiss;
ULONG FastReadNotPossible;
ULONG FastMdlReadNoWait;
ULONG FastMdlReadWait;
ULONG FastMdlReadResourceMiss;
ULONG FastMdlReadNotPossible;
ULONG MapDataNoWait;
ULONG MapDataWait;
ULONG MapDataNoWaitMiss;
ULONG MapDataWaitMiss;
ULONG PinMappedDataCount;
ULONG PinReadNoWait;
ULONG PinReadWait;
ULONG PinReadNoWaitMiss;
ULONG PinReadWaitMiss;
ULONG CopyReadNoWait;
ULONG CopyReadWait;
ULONG CopyReadNoWaitMiss;
ULONG CopyReadWaitMiss;
ULONG MdlReadNoWait;
ULONG MdlReadWait;
ULONG MdlReadNoWaitMiss;
ULONG MdlReadWaitMiss;
ULONG ReadAheadIos;
ULONG LazyWriteIos;
ULONG LazyWritePages;
ULONG DataFlushes;
ULONG DataPages;
ULONG ContextSwitches;
ULONG FirstLevelTbFills;
ULONG SecondLevelTbFills;
ULONG SystemCalls;
};
} // namespace
ProcessMetrics::~ProcessMetrics() { }
size_t GetMaxFds() {
// Windows is only limited by the amount of physical memory.
return std::numeric_limits<size_t>::max();
}
size_t GetHandleLimit() {
// Rounded down from value reported here:
// http://blogs.technet.com/b/markrussinovich/archive/2009/09/29/3283844.aspx
return static_cast<size_t>(1 << 23);
}
// static
std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
ProcessHandle process) {
return WrapUnique(new ProcessMetrics(process));
}
TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
FILETIME creation_time;
FILETIME exit_time;
FILETIME kernel_time;
FILETIME user_time;
if (!process_.IsValid())
return TimeDelta();
if (!GetProcessTimes(process_.Get(), &creation_time, &exit_time, &kernel_time,
&user_time)) {
// This should never fail because we duplicate the handle to guarantee it
// will remain valid.
DCHECK(false);
return TimeDelta();
}
return TimeDelta::FromFileTime(kernel_time) +
TimeDelta::FromFileTime(user_time);
}
bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
if (!process_.IsValid())
return false;
return GetProcessIoCounters(process_.Get(), io_counters) != FALSE;
}
uint64_t ProcessMetrics::GetCumulativeDiskUsageInBytes() {
IoCounters counters;
if (!GetIOCounters(&counters))
return 0;
return counters.ReadTransferCount + counters.WriteTransferCount +
counters.OtherTransferCount;
}
ProcessMetrics::ProcessMetrics(ProcessHandle process) {
if (process) {
HANDLE duplicate_handle = INVALID_HANDLE_VALUE;
BOOL result = ::DuplicateHandle(::GetCurrentProcess(), process,
::GetCurrentProcess(), &duplicate_handle,
PROCESS_QUERY_INFORMATION, FALSE, 0);
DPCHECK(result);
process_.Set(duplicate_handle);
}
}
size_t GetSystemCommitCharge() {
// Get the System Page Size.
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
PERFORMANCE_INFORMATION info;
if (!GetPerformanceInfo(&info, sizeof(info))) {
DLOG(ERROR) << "Failed to fetch internal performance info.";
return 0;
}
return (info.CommitTotal * system_info.dwPageSize) / 1024;
}
size_t GetPageSize() {
return PAGESIZE_KB * 1024;
}
// This function uses the following mapping between MEMORYSTATUSEX and
// SystemMemoryInfoKB:
// ullTotalPhys ==> total
// ullAvailPhys ==> avail_phys
// ullTotalPageFile ==> swap_total
// ullAvailPageFile ==> swap_free
bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
MEMORYSTATUSEX mem_status;
mem_status.dwLength = sizeof(mem_status);
if (!::GlobalMemoryStatusEx(&mem_status))
return false;
meminfo->total = mem_status.ullTotalPhys / 1024;
meminfo->avail_phys = mem_status.ullAvailPhys / 1024;
meminfo->swap_total = mem_status.ullTotalPageFile / 1024;
meminfo->swap_free = mem_status.ullAvailPageFile / 1024;
return true;
}
size_t ProcessMetrics::GetMallocUsage() {
// Unsupported as getting malloc usage on Windows requires iterating through
// the heap which is slow and crashes.
return 0;
}
SystemPerformanceInfo::SystemPerformanceInfo() = default;
SystemPerformanceInfo::SystemPerformanceInfo(
const SystemPerformanceInfo& other) = default;
std::unique_ptr<Value> SystemPerformanceInfo::ToValue() const {
std::unique_ptr<DictionaryValue> result(new DictionaryValue());
// Write out uint64_t variables as doubles.
// Note: this may discard some precision, but for JS there's no other option.
result->SetDouble("idle_time", strict_cast<double>(idle_time));
result->SetDouble("read_transfer_count",
strict_cast<double>(read_transfer_count));
result->SetDouble("write_transfer_count",
strict_cast<double>(write_transfer_count));
result->SetDouble("other_transfer_count",
strict_cast<double>(other_transfer_count));
result->SetDouble("read_operation_count",
strict_cast<double>(read_operation_count));
result->SetDouble("write_operation_count",
strict_cast<double>(write_operation_count));
result->SetDouble("other_operation_count",
strict_cast<double>(other_operation_count));
result->SetDouble("pagefile_pages_written",
strict_cast<double>(pagefile_pages_written));
result->SetDouble("pagefile_pages_write_ios",
strict_cast<double>(pagefile_pages_write_ios));
result->SetDouble("available_pages", strict_cast<double>(available_pages));
result->SetDouble("pages_read", strict_cast<double>(pages_read));
result->SetDouble("page_read_ios", strict_cast<double>(page_read_ios));
return result;
}
// Retrieves performance counters from the operating system.
// Fills in the provided |info| structure. Returns true on success.
BASE_EXPORT bool GetSystemPerformanceInfo(SystemPerformanceInfo* info) {
static const auto query_system_information_ptr =
reinterpret_cast<decltype(&::NtQuerySystemInformation)>(GetProcAddress(
GetModuleHandle(L"ntdll.dll"), "NtQuerySystemInformation"));
if (!query_system_information_ptr)
return false;
SYSTEM_PERFORMANCE_INFORMATION counters = {};
{
// The call to NtQuerySystemInformation might block on a lock.
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
BlockingType::MAY_BLOCK);
if (query_system_information_ptr(::SystemPerformanceInformation, &counters,
sizeof(SYSTEM_PERFORMANCE_INFORMATION),
nullptr) != STATUS_SUCCESS) {
return false;
}
}
info->idle_time = counters.IdleTime.QuadPart;
info->read_transfer_count = counters.ReadTransferCount.QuadPart;
info->write_transfer_count = counters.WriteTransferCount.QuadPart;
info->other_transfer_count = counters.OtherTransferCount.QuadPart;
info->read_operation_count = counters.ReadOperationCount;
info->write_operation_count = counters.WriteOperationCount;
info->other_operation_count = counters.OtherOperationCount;
info->pagefile_pages_written = counters.PagefilePagesWritten;
info->pagefile_pages_write_ios = counters.PagefilePageWriteIos;
info->available_pages = counters.AvailablePages;
info->pages_read = counters.PagesRead;
info->page_read_ios = counters.PageReadIos;
return true;
}
} // namespace base

View file

@ -0,0 +1,386 @@
// Copyright 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process.h"
#include <errno.h>
#include <signal.h>
#include <stdint.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include "base/clang_profiling_buildflags.h"
#include "base/debug/activity_tracker.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process/kill.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#if defined(OS_MACOSX)
#include <sys/event.h>
#endif
#if BUILDFLAG(CLANG_PROFILING)
#include "base/test/clang_profiling.h"
#endif
namespace {
#if !defined(OS_NACL_NONSFI)
bool WaitpidWithTimeout(base::ProcessHandle handle,
int* status,
base::TimeDelta wait) {
// This POSIX version of this function only guarantees that we wait no less
// than |wait| for the process to exit. The child process may
// exit sometime before the timeout has ended but we may still block for up
// to 256 milliseconds after the fact.
//
// waitpid() has no direct support on POSIX for specifying a timeout, you can
// either ask it to block indefinitely or return immediately (WNOHANG).
// When a child process terminates a SIGCHLD signal is sent to the parent.
// Catching this signal would involve installing a signal handler which may
// affect other parts of the application and would be difficult to debug.
//
// Our strategy is to call waitpid() once up front to check if the process
// has already exited, otherwise to loop for |wait|, sleeping for
// at most 256 milliseconds each time using usleep() and then calling
// waitpid(). The amount of time we sleep starts out at 1 milliseconds, and
// we double it every 4 sleep cycles.
//
// usleep() is speced to exit if a signal is received for which a handler
// has been installed. This means that when a SIGCHLD is sent, it will exit
// depending on behavior external to this function.
//
// This function is used primarily for unit tests, if we want to use it in
// the application itself it would probably be best to examine other routes.
if (wait == base::TimeDelta::Max()) {
return HANDLE_EINTR(waitpid(handle, status, 0)) > 0;
}
pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG));
static const int64_t kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds.
int64_t max_sleep_time_usecs = 1 << 10; // ~1 milliseconds.
int64_t double_sleep_time = 0;
// If the process hasn't exited yet, then sleep and try again.
base::TimeTicks wakeup_time = base::TimeTicks::Now() + wait;
while (ret_pid == 0) {
base::TimeTicks now = base::TimeTicks::Now();
if (now > wakeup_time)
break;
// Guaranteed to be non-negative!
int64_t sleep_time_usecs = (wakeup_time - now).InMicroseconds();
// Sleep for a bit while we wait for the process to finish.
if (sleep_time_usecs > max_sleep_time_usecs)
sleep_time_usecs = max_sleep_time_usecs;
// usleep() will return 0 and set errno to EINTR on receipt of a signal
// such as SIGCHLD.
usleep(sleep_time_usecs);
ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG));
if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) &&
(double_sleep_time++ % 4 == 0)) {
max_sleep_time_usecs *= 2;
}
}
return ret_pid > 0;
}
#if defined(OS_MACOSX)
// Using kqueue on Mac so that we can wait on non-child processes.
// We can't use kqueues on child processes because we need to reap
// our own children using wait.
bool WaitForSingleNonChildProcess(base::ProcessHandle handle,
base::TimeDelta wait) {
DCHECK_GT(handle, 0);
base::ScopedFD kq(kqueue());
if (!kq.is_valid()) {
DPLOG(ERROR) << "kqueue";
return false;
}
struct kevent change = {0};
EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL));
if (result == -1) {
if (errno == ESRCH) {
// If the process wasn't found, it must be dead.
return true;
}
DPLOG(ERROR) << "kevent (setup " << handle << ")";
return false;
}
// Keep track of the elapsed time to be able to restart kevent if it's
// interrupted.
bool wait_forever = (wait == base::TimeDelta::Max());
base::TimeDelta remaining_delta;
base::TimeTicks deadline;
if (!wait_forever) {
remaining_delta = wait;
deadline = base::TimeTicks::Now() + remaining_delta;
}
result = -1;
struct kevent event = {0};
do {
struct timespec remaining_timespec;
struct timespec* remaining_timespec_ptr;
if (wait_forever) {
remaining_timespec_ptr = NULL;
} else {
remaining_timespec = remaining_delta.ToTimeSpec();
remaining_timespec_ptr = &remaining_timespec;
}
result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr);
if (result == -1 && errno == EINTR) {
if (!wait_forever) {
remaining_delta = deadline - base::TimeTicks::Now();
}
result = 0;
} else {
break;
}
} while (wait_forever || remaining_delta > base::TimeDelta());
if (result < 0) {
DPLOG(ERROR) << "kevent (wait " << handle << ")";
return false;
} else if (result > 1) {
DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result "
<< result;
return false;
} else if (result == 0) {
// Timed out.
return false;
}
DCHECK_EQ(result, 1);
if (event.filter != EVFILT_PROC ||
(event.fflags & NOTE_EXIT) == 0 ||
event.ident != static_cast<uintptr_t>(handle)) {
DLOG(ERROR) << "kevent (wait " << handle
<< "): unexpected event: filter=" << event.filter
<< ", fflags=" << event.fflags
<< ", ident=" << event.ident;
return false;
}
return true;
}
#endif // OS_MACOSX
bool WaitForExitWithTimeoutImpl(base::ProcessHandle handle,
int* exit_code,
base::TimeDelta timeout) {
const base::ProcessHandle our_pid = base::GetCurrentProcessHandle();
if (handle == our_pid) {
// We won't be able to wait for ourselves to exit.
return false;
}
const base::ProcessHandle parent_pid = base::GetParentProcessId(handle);
const bool exited = (parent_pid < 0);
if (!exited && parent_pid != our_pid) {
#if defined(OS_MACOSX)
// On Mac we can wait on non child processes.
return WaitForSingleNonChildProcess(handle, timeout);
#else
// Currently on Linux we can't handle non child processes.
NOTIMPLEMENTED();
#endif // OS_MACOSX
}
int status;
if (!WaitpidWithTimeout(handle, &status, timeout))
return exited;
if (WIFSIGNALED(status)) {
if (exit_code)
*exit_code = -1;
return true;
}
if (WIFEXITED(status)) {
if (exit_code)
*exit_code = WEXITSTATUS(status);
return true;
}
return exited;
}
#endif // !defined(OS_NACL_NONSFI)
} // namespace
namespace base {
Process::Process(ProcessHandle handle) : process_(handle) {
}
Process::~Process() = default;
Process::Process(Process&& other) : process_(other.process_) {
other.Close();
}
Process& Process::operator=(Process&& other) {
process_ = other.process_;
other.Close();
return *this;
}
// static
Process Process::Current() {
return Process(GetCurrentProcessHandle());
}
// static
Process Process::Open(ProcessId pid) {
if (pid == GetCurrentProcId())
return Current();
// On POSIX process handles are the same as PIDs.
return Process(pid);
}
// static
Process Process::OpenWithExtraPrivileges(ProcessId pid) {
// On POSIX there are no privileges to set.
return Open(pid);
}
// static
Process Process::DeprecatedGetProcessFromHandle(ProcessHandle handle) {
DCHECK_NE(handle, GetCurrentProcessHandle());
return Process(handle);
}
#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_AIX)
// static
bool Process::CanBackgroundProcesses() {
return false;
}
#endif // !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_AIX)
// static
void Process::TerminateCurrentProcessImmediately(int exit_code) {
#if BUILDFLAG(CLANG_PROFILING)
WriteClangProfilingProfile();
#endif
_exit(exit_code);
}
bool Process::IsValid() const {
return process_ != kNullProcessHandle;
}
ProcessHandle Process::Handle() const {
return process_;
}
Process Process::Duplicate() const {
if (is_current())
return Current();
return Process(process_);
}
ProcessId Process::Pid() const {
DCHECK(IsValid());
return GetProcId(process_);
}
bool Process::is_current() const {
return process_ == GetCurrentProcessHandle();
}
void Process::Close() {
process_ = kNullProcessHandle;
// if the process wasn't terminated (so we waited) or the state
// wasn't already collected w/ a wait from process_utils, we're gonna
// end up w/ a zombie when it does finally exit.
}
#if !defined(OS_NACL_NONSFI)
bool Process::Terminate(int exit_code, bool wait) const {
// exit_code isn't supportable.
DCHECK(IsValid());
CHECK_GT(process_, 0);
bool did_terminate = kill(process_, SIGTERM) == 0;
if (wait && did_terminate) {
if (WaitForExitWithTimeout(TimeDelta::FromSeconds(60), nullptr))
return true;
did_terminate = kill(process_, SIGKILL) == 0;
if (did_terminate)
return WaitForExit(nullptr);
}
if (!did_terminate)
DPLOG(ERROR) << "Unable to terminate process " << process_;
return did_terminate;
}
#endif // !defined(OS_NACL_NONSFI)
bool Process::WaitForExit(int* exit_code) const {
return WaitForExitWithTimeout(TimeDelta::Max(), exit_code);
}
bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const {
// Intentionally avoid instantiating ScopedBlockingCallWithBaseSyncPrimitives.
// In some cases, this function waits on a child Process doing CPU work.
// http://crbug.com/905788
if (!timeout.is_zero())
internal::AssertBaseSyncPrimitivesAllowed();
// Record the event that this thread is blocking upon (for hang diagnosis).
base::debug::ScopedProcessWaitActivity process_activity(this);
int local_exit_code = 0;
bool exited = WaitForExitWithTimeoutImpl(Handle(), &local_exit_code, timeout);
if (exited) {
Exited(local_exit_code);
if (exit_code)
*exit_code = local_exit_code;
}
return exited;
}
void Process::Exited(int exit_code) const {}
#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_AIX)
bool Process::IsProcessBackgrounded() const {
// See SetProcessBackgrounded().
DCHECK(IsValid());
return false;
}
bool Process::SetProcessBackgrounded(bool value) {
// Not implemented for POSIX systems other than Linux and Mac. With POSIX, if
// we were to lower the process priority we wouldn't be able to raise it back
// to its initial priority.
NOTIMPLEMENTED();
return false;
}
#endif // !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_AIX)
int Process::GetPriority() const {
DCHECK(IsValid());
return getpriority(PRIO_PROCESS, process_);
}
} // namespace base

View file

@ -0,0 +1,281 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process.h"
#include "base/clang_profiling_buildflags.h"
#include "base/debug/activity_tracker.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/process/kill.h"
#include "base/threading/thread_restrictions.h"
#include <windows.h>
#if BUILDFLAG(CLANG_PROFILING)
#include "base/test/clang_profiling.h"
#endif
namespace {
DWORD kBasicProcessAccess =
PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | SYNCHRONIZE;
} // namespace
namespace base {
Process::Process(ProcessHandle handle)
: process_(handle), is_current_process_(false) {
CHECK_NE(handle, ::GetCurrentProcess());
}
Process::Process(Process&& other)
: process_(other.process_.Take()),
is_current_process_(other.is_current_process_) {
other.Close();
}
Process::~Process() {
}
Process& Process::operator=(Process&& other) {
DCHECK_NE(this, &other);
process_.Set(other.process_.Take());
is_current_process_ = other.is_current_process_;
other.Close();
return *this;
}
// static
Process Process::Current() {
Process process;
process.is_current_process_ = true;
return process;
}
// static
Process Process::Open(ProcessId pid) {
return Process(::OpenProcess(kBasicProcessAccess, FALSE, pid));
}
// static
Process Process::OpenWithExtraPrivileges(ProcessId pid) {
DWORD access = kBasicProcessAccess | PROCESS_DUP_HANDLE | PROCESS_VM_READ;
return Process(::OpenProcess(access, FALSE, pid));
}
// static
Process Process::OpenWithAccess(ProcessId pid, DWORD desired_access) {
return Process(::OpenProcess(desired_access, FALSE, pid));
}
// static
Process Process::DeprecatedGetProcessFromHandle(ProcessHandle handle) {
DCHECK_NE(handle, ::GetCurrentProcess());
ProcessHandle out_handle;
if (!::DuplicateHandle(GetCurrentProcess(), handle,
GetCurrentProcess(), &out_handle,
0, FALSE, DUPLICATE_SAME_ACCESS)) {
return Process();
}
return Process(out_handle);
}
// static
bool Process::CanBackgroundProcesses() {
return true;
}
// static
void Process::TerminateCurrentProcessImmediately(int exit_code) {
#if BUILDFLAG(CLANG_PROFILING)
WriteClangProfilingProfile();
#endif
::TerminateProcess(GetCurrentProcess(), exit_code);
// There is some ambiguity over whether the call above can return. Rather than
// hitting confusing crashes later on we should crash right here.
IMMEDIATE_CRASH();
}
bool Process::IsValid() const {
return process_.IsValid() || is_current();
}
ProcessHandle Process::Handle() const {
return is_current_process_ ? GetCurrentProcess() : process_.Get();
}
Process Process::Duplicate() const {
if (is_current())
return Current();
ProcessHandle out_handle;
if (!IsValid() || !::DuplicateHandle(GetCurrentProcess(),
Handle(),
GetCurrentProcess(),
&out_handle,
0,
FALSE,
DUPLICATE_SAME_ACCESS)) {
return Process();
}
return Process(out_handle);
}
ProcessId Process::Pid() const {
DCHECK(IsValid());
return GetProcId(Handle());
}
Time Process::CreationTime() const {
FILETIME creation_time = {};
FILETIME ignore1 = {};
FILETIME ignore2 = {};
FILETIME ignore3 = {};
if (!::GetProcessTimes(Handle(), &creation_time, &ignore1, &ignore2,
&ignore3)) {
return Time();
}
return Time::FromFileTime(creation_time);
}
bool Process::is_current() const {
return is_current_process_;
}
void Process::Close() {
is_current_process_ = false;
if (!process_.IsValid())
return;
process_.Close();
}
bool Process::Terminate(int exit_code, bool wait) const {
constexpr DWORD kWaitMs = 60 * 1000;
// exit_code cannot be implemented.
DCHECK(IsValid());
bool result = (::TerminateProcess(Handle(), exit_code) != FALSE);
if (result) {
// The process may not end immediately due to pending I/O
if (wait && ::WaitForSingleObject(Handle(), kWaitMs) != WAIT_OBJECT_0)
DPLOG(ERROR) << "Error waiting for process exit";
Exited(exit_code);
} else {
// The process can't be terminated, perhaps because it has already exited or
// is in the process of exiting. An error code of ERROR_ACCESS_DENIED is the
// undocumented-but-expected result if the process has already exited or
// started exiting when TerminateProcess is called, so don't print an error
// message in that case.
if (GetLastError() != ERROR_ACCESS_DENIED)
DPLOG(ERROR) << "Unable to terminate process";
// A non-zero timeout is necessary here for the same reasons as above.
if (::WaitForSingleObject(Handle(), kWaitMs) == WAIT_OBJECT_0) {
DWORD actual_exit;
Exited(::GetExitCodeProcess(Handle(), &actual_exit) ? actual_exit
: exit_code);
result = true;
}
}
return result;
}
Process::WaitExitStatus Process::WaitForExitOrEvent(
const base::win::ScopedHandle& stop_event_handle,
int* exit_code) const {
// Record the event that this thread is blocking upon (for hang diagnosis).
base::debug::ScopedProcessWaitActivity process_activity(this);
HANDLE events[] = {Handle(), stop_event_handle.Get()};
DWORD wait_result =
::WaitForMultipleObjects(base::size(events), events, FALSE, INFINITE);
if (wait_result == WAIT_OBJECT_0) {
DWORD temp_code; // Don't clobber out-parameters in case of failure.
if (!::GetExitCodeProcess(Handle(), &temp_code))
return Process::WaitExitStatus::FAILED;
if (exit_code)
*exit_code = temp_code;
Exited(temp_code);
return Process::WaitExitStatus::PROCESS_EXITED;
}
if (wait_result == WAIT_OBJECT_0 + 1) {
return Process::WaitExitStatus::STOP_EVENT_SIGNALED;
}
return Process::WaitExitStatus::FAILED;
}
bool Process::WaitForExit(int* exit_code) const {
return WaitForExitWithTimeout(TimeDelta::FromMilliseconds(INFINITE),
exit_code);
}
bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const {
// Intentionally avoid instantiating ScopedBlockingCallWithBaseSyncPrimitives.
// In some cases, this function waits on a child Process doing CPU work.
// http://crbug.com/905788
if (!timeout.is_zero())
internal::AssertBaseSyncPrimitivesAllowed();
// Record the event that this thread is blocking upon (for hang diagnosis).
base::debug::ScopedProcessWaitActivity process_activity(this);
// Limit timeout to INFINITE.
DWORD timeout_ms = saturated_cast<DWORD>(timeout.InMilliseconds());
if (::WaitForSingleObject(Handle(), timeout_ms) != WAIT_OBJECT_0)
return false;
DWORD temp_code; // Don't clobber out-parameters in case of failure.
if (!::GetExitCodeProcess(Handle(), &temp_code))
return false;
if (exit_code)
*exit_code = temp_code;
Exited(temp_code);
return true;
}
void Process::Exited(int exit_code) const {
base::debug::GlobalActivityTracker::RecordProcessExitIfEnabled(Pid(),
exit_code);
}
bool Process::IsProcessBackgrounded() const {
DCHECK(IsValid());
DWORD priority = GetPriority();
if (priority == 0)
return false; // Failure case.
return ((priority == BELOW_NORMAL_PRIORITY_CLASS) ||
(priority == IDLE_PRIORITY_CLASS));
}
bool Process::SetProcessBackgrounded(bool value) {
DCHECK(IsValid());
// Vista and above introduce a real background mode, which not only
// sets the priority class on the threads but also on the IO generated
// by it. Unfortunately it can only be set for the calling process.
DWORD priority;
if (is_current()) {
priority = value ? PROCESS_MODE_BACKGROUND_BEGIN :
PROCESS_MODE_BACKGROUND_END;
} else {
priority = value ? IDLE_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS;
}
return (::SetPriorityClass(Handle(), priority) != 0);
}
int Process::GetPriority() const {
DCHECK(IsValid());
return ::GetPriorityClass(Handle());
}
} // namespace base