Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
153
TMessagesProj/jni/voip/webrtc/base/process/internal_aix.cc
Normal file
153
TMessagesProj/jni/voip/webrtc/base/process/internal_aix.cc
Normal 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
|
||||
84
TMessagesProj/jni/voip/webrtc/base/process/internal_aix.h
Normal file
84
TMessagesProj/jni/voip/webrtc/base/process/internal_aix.h
Normal 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_
|
||||
231
TMessagesProj/jni/voip/webrtc/base/process/internal_linux.cc
Normal file
231
TMessagesProj/jni/voip/webrtc/base/process/internal_linux.cc
Normal 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
|
||||
100
TMessagesProj/jni/voip/webrtc/base/process/internal_linux.h
Normal file
100
TMessagesProj/jni/voip/webrtc/base/process/internal_linux.h
Normal 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_
|
||||
62
TMessagesProj/jni/voip/webrtc/base/process/kill.cc
Normal file
62
TMessagesProj/jni/voip/webrtc/base/process/kill.cc
Normal 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
|
||||
162
TMessagesProj/jni/voip/webrtc/base/process/kill.h
Normal file
162
TMessagesProj/jni/voip/webrtc/base/process/kill.h
Normal 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_
|
||||
53
TMessagesProj/jni/voip/webrtc/base/process/kill_fuchsia.cc
Normal file
53
TMessagesProj/jni/voip/webrtc/base/process/kill_fuchsia.cc
Normal 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
|
||||
173
TMessagesProj/jni/voip/webrtc/base/process/kill_mac.cc
Normal file
173
TMessagesProj/jni/voip/webrtc/base/process/kill_mac.cc
Normal 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
|
||||
187
TMessagesProj/jni/voip/webrtc/base/process/kill_posix.cc
Normal file
187
TMessagesProj/jni/voip/webrtc/base/process/kill_posix.cc
Normal 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
|
||||
122
TMessagesProj/jni/voip/webrtc/base/process/kill_win.cc
Normal file
122
TMessagesProj/jni/voip/webrtc/base/process/kill_win.cc
Normal 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
|
||||
27
TMessagesProj/jni/voip/webrtc/base/process/launch.cc
Normal file
27
TMessagesProj/jni/voip/webrtc/base/process/launch.cc
Normal 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
|
||||
426
TMessagesProj/jni/voip/webrtc/base/process/launch.h
Normal file
426
TMessagesProj/jni/voip/webrtc/base/process/launch.h
Normal 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_
|
||||
288
TMessagesProj/jni/voip/webrtc/base/process/launch_fuchsia.cc
Normal file
288
TMessagesProj/jni/voip/webrtc/base/process/launch_fuchsia.cc
Normal 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
|
||||
13
TMessagesProj/jni/voip/webrtc/base/process/launch_ios.cc
Normal file
13
TMessagesProj/jni/voip/webrtc/base/process/launch_ios.cc
Normal 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
|
||||
363
TMessagesProj/jni/voip/webrtc/base/process/launch_mac.cc
Normal file
363
TMessagesProj/jni/voip/webrtc/base/process/launch_mac.cc
Normal 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
|
||||
746
TMessagesProj/jni/voip/webrtc/base/process/launch_posix.cc
Normal file
746
TMessagesProj/jni/voip/webrtc/base/process/launch_posix.cc
Normal 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
|
||||
428
TMessagesProj/jni/voip/webrtc/base/process/launch_win.cc
Normal file
428
TMessagesProj/jni/voip/webrtc/base/process/launch_win.cc
Normal 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
|
||||
94
TMessagesProj/jni/voip/webrtc/base/process/memory.cc
Normal file
94
TMessagesProj/jni/voip/webrtc/base/process/memory.cc
Normal 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
|
||||
89
TMessagesProj/jni/voip/webrtc/base/process/memory.h
Normal file
89
TMessagesProj/jni/voip/webrtc/base/process/memory.h
Normal 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_
|
||||
24
TMessagesProj/jni/voip/webrtc/base/process/memory_fuchsia.cc
Normal file
24
TMessagesProj/jni/voip/webrtc/base/process/memory_fuchsia.cc
Normal 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
|
||||
145
TMessagesProj/jni/voip/webrtc/base/process/memory_linux.cc
Normal file
145
TMessagesProj/jni/voip/webrtc/base/process/memory_linux.cc
Normal 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
|
||||
49
TMessagesProj/jni/voip/webrtc/base/process/memory_mac.mm
Normal file
49
TMessagesProj/jni/voip/webrtc/base/process/memory_mac.mm
Normal 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
|
||||
44
TMessagesProj/jni/voip/webrtc/base/process/memory_stubs.cc
Normal file
44
TMessagesProj/jni/voip/webrtc/base/process/memory_stubs.cc
Normal 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
|
||||
82
TMessagesProj/jni/voip/webrtc/base/process/memory_win.cc
Normal file
82
TMessagesProj/jni/voip/webrtc/base/process/memory_win.cc
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
238
TMessagesProj/jni/voip/webrtc/base/process/process.h
Normal file
238
TMessagesProj/jni/voip/webrtc/base/process/process.h
Normal 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_
|
||||
224
TMessagesProj/jni/voip/webrtc/base/process/process_fuchsia.cc
Normal file
224
TMessagesProj/jni/voip/webrtc/base/process/process_fuchsia.cc
Normal 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
|
||||
40
TMessagesProj/jni/voip/webrtc/base/process/process_handle.cc
Normal file
40
TMessagesProj/jni/voip/webrtc/base/process/process_handle.cc
Normal 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
|
||||
142
TMessagesProj/jni/voip/webrtc/base/process/process_handle.h
Normal file
142
TMessagesProj/jni/voip/webrtc/base/process/process_handle.h
Normal 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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
33
TMessagesProj/jni/voip/webrtc/base/process/process_info.h
Normal file
33
TMessagesProj/jni/voip/webrtc/base/process/process_info.h
Normal 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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
153
TMessagesProj/jni/voip/webrtc/base/process/process_iterator.h
Normal file
153
TMessagesProj/jni/voip/webrtc/base/process/process_iterator.h
Normal 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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
206
TMessagesProj/jni/voip/webrtc/base/process/process_linux.cc
Normal file
206
TMessagesProj/jni/voip/webrtc/base/process/process_linux.cc
Normal 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
|
||||
98
TMessagesProj/jni/voip/webrtc/base/process/process_mac.cc
Normal file
98
TMessagesProj/jni/voip/webrtc/base/process/process_mac.cc
Normal 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
|
||||
167
TMessagesProj/jni/voip/webrtc/base/process/process_metrics.cc
Normal file
167
TMessagesProj/jni/voip/webrtc/base/process/process_metrics.cc
Normal 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
|
||||
580
TMessagesProj/jni/voip/webrtc/base/process/process_metrics.h
Normal file
580
TMessagesProj/jni/voip/webrtc/base/process/process_metrics.h
Normal 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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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], §ors_read);
|
||||
StringToUint64(disk_fields[kDiskReadTime], &read_time);
|
||||
StringToUint64(disk_fields[kDiskWrites], &writes);
|
||||
StringToUint64(disk_fields[kDiskWritesMerged], &writes_merged);
|
||||
StringToUint64(disk_fields[kDiskSectorsWritten], §ors_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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
386
TMessagesProj/jni/voip/webrtc/base/process/process_posix.cc
Normal file
386
TMessagesProj/jni/voip/webrtc/base/process/process_posix.cc
Normal 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
|
||||
281
TMessagesProj/jni/voip/webrtc/base/process/process_win.cc
Normal file
281
TMessagesProj/jni/voip/webrtc/base/process/process_win.cc
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue