summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/common/nacl_fork_delegate_linux.h21
-rw-r--r--chrome/nacl/nacl_fork_delegate_linux.cc48
-rw-r--r--content/browser/zygote_host_linux.cc35
-rw-r--r--content/browser/zygote_main_linux.cc57
-rw-r--r--content/common/zygote_fork_delegate_linux.h20
5 files changed, 153 insertions, 28 deletions
diff --git a/chrome/common/nacl_fork_delegate_linux.h b/chrome/common/nacl_fork_delegate_linux.h
index 4409f27..b8ab0b0 100644
--- a/chrome/common/nacl_fork_delegate_linux.h
+++ b/chrome/common/nacl_fork_delegate_linux.h
@@ -23,13 +23,30 @@ class NaClForkDelegate : public ZygoteForkDelegate {
virtual void Init(bool sandboxed,
int browserdesc,
int sandboxdesc) OVERRIDE;
- virtual bool CanHelp(const std::string& process_type) OVERRIDE;
+ virtual void InitialUMA(std::string* uma_name,
+ int* uma_sample,
+ int* uma_boundary_value) OVERRIDE;
+ virtual bool CanHelp(const std::string& process_type, std::string* uma_name,
+ int* uma_sample, int* uma_boundary_value) OVERRIDE;
virtual pid_t Fork(const std::vector<int>& fds) OVERRIDE;
virtual bool AckChild(int fd,
const std::string& channel_switch) OVERRIDE;
private:
- bool ready_;
+ // These values are reported via UMA and hence they become permanent
+ // constants. Old values cannot be reused, only new ones added.
+ enum NaClHelperStatus {
+ kNaClHelperUnused = 0,
+ kNaClHelperMissing = 1,
+ kNaClHelperBootstrapMissing = 2,
+ kNaClHelperValgrind = 3,
+ kNaClHelperLaunchFailed = 4,
+ kNaClHelperAckFailed = 5,
+ kNaClHelperSuccess = 6,
+ kNaClHelperStatusBoundary // Must be one greater than highest value used.
+ };
+
+ NaClHelperStatus status_;
bool sandboxed_;
int fd_;
};
diff --git a/chrome/nacl/nacl_fork_delegate_linux.cc b/chrome/nacl/nacl_fork_delegate_linux.cc
index 828cb6e..0f389bd 100644
--- a/chrome/nacl/nacl_fork_delegate_linux.cc
+++ b/chrome/nacl/nacl_fork_delegate_linux.cc
@@ -23,7 +23,7 @@
#include "chrome/common/nacl_helper_linux.h"
NaClForkDelegate::NaClForkDelegate()
- : ready_(false),
+ : status_(kNaClHelperUnused),
sandboxed_(false),
fd_(-1) {}
@@ -45,25 +45,31 @@ void NaClForkDelegate::Init(const bool sandboxed,
base::file_handle_mapping_vector fds_to_map;
fds_to_map.push_back(std::make_pair(fds[1], kNaClZygoteDescriptor));
fds_to_map.push_back(std::make_pair(sandboxdesc, kNaClSandboxDescriptor));
- ready_ = false;
+
+ status_ = kNaClHelperUnused;
FilePath helper_exe;
FilePath helper_bootstrap_exe;
- if (PathService::Get(chrome::FILE_NACL_HELPER, &helper_exe) &&
- PathService::Get(chrome::FILE_NACL_HELPER_BOOTSTRAP,
- &helper_bootstrap_exe) &&
- !RunningOnValgrind()) {
+ if (!PathService::Get(chrome::FILE_NACL_HELPER, &helper_exe)) {
+ status_ = kNaClHelperMissing;
+ } else if (!PathService::Get(chrome::FILE_NACL_HELPER_BOOTSTRAP,
+ &helper_bootstrap_exe)) {
+ status_ = kNaClHelperBootstrapMissing;
+ } else if (RunningOnValgrind()) {
+ status_ = kNaClHelperValgrind;
+ } else {
CommandLine cmd_line(helper_bootstrap_exe);
cmd_line.AppendArgPath(helper_exe);
cmd_line.AppendArgNative(kNaClHelperAtZero);
base::LaunchOptions options;
options.fds_to_remap = &fds_to_map;
options.clone_flags = CLONE_FS | SIGCHLD;
- ready_ = base::LaunchProcess(cmd_line.argv(), options, NULL);
+ if (!base::LaunchProcess(cmd_line.argv(), options, NULL))
+ status_ = kNaClHelperLaunchFailed;
// parent and error cases are handled below
}
if (HANDLE_EINTR(close(fds[1])) != 0)
LOG(ERROR) << "close(fds[1]) failed";
- if (ready_) {
+ if (status_ == kNaClHelperUnused) {
const ssize_t kExpectedLength = strlen(kNaClHelperStartupAck);
char buf[kExpectedLength];
@@ -72,29 +78,47 @@ void NaClForkDelegate::Init(const bool sandboxed,
if (nread == kExpectedLength &&
memcmp(buf, kNaClHelperStartupAck, nread) == 0) {
// all is well
+ status_ = kNaClHelperSuccess;
fd_ = fds[0];
return;
}
+
+ status_ = kNaClHelperAckFailed;
LOG(ERROR) << "Bad NaCl helper startup ack (" << nread << " bytes)";
}
// TODO(bradchen): Make this LOG(ERROR) when the NaCl helper
// becomes the default.
- ready_ = false;
fd_ = -1;
if (HANDLE_EINTR(close(fds[0])) != 0)
LOG(ERROR) << "close(fds[0]) failed";
}
+void NaClForkDelegate::InitialUMA(std::string* uma_name,
+ int* uma_sample,
+ int* uma_boundary_value) {
+ *uma_name = "NaCl.Client.Helper.InitState";
+ *uma_sample = status_;
+ *uma_boundary_value = kNaClHelperStatusBoundary;
+}
+
NaClForkDelegate::~NaClForkDelegate() {
// side effect of close: delegate process will terminate
- if (ready_) {
+ if (status_ == kNaClHelperSuccess) {
if (HANDLE_EINTR(close(fd_)) != 0)
LOG(ERROR) << "close(fd_) failed";
}
}
-bool NaClForkDelegate::CanHelp(const std::string& process_type) {
- return (process_type == switches::kNaClLoaderProcess && ready_);
+bool NaClForkDelegate::CanHelp(const std::string& process_type,
+ std::string* uma_name,
+ int* uma_sample,
+ int* uma_boundary_value) {
+ if (process_type != switches::kNaClLoaderProcess)
+ return false;
+ *uma_name = "NaCl.Client.Helper.StateOnFork";
+ *uma_sample = status_;
+ *uma_boundary_value = kNaClHelperStatusBoundary;
+ return status_ == kNaClHelperSuccess;
}
pid_t NaClForkDelegate::Fork(const std::vector<int>& fds) {
diff --git a/content/browser/zygote_host_linux.cc b/content/browser/zygote_host_linux.cc
index 000ee29..4963dd7 100644
--- a/content/browser/zygote_host_linux.cc
+++ b/content/browser/zygote_host_linux.cc
@@ -17,6 +17,7 @@
#include "base/linux_util.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
#include "base/path_service.h"
#include "base/pickle.h"
#include "base/process_util.h"
@@ -267,8 +268,40 @@ pid_t ZygoteHost::ForkRequest(
fds))
return base::kNullProcessHandle;
- if (ReadReply(&pid, sizeof(pid)) != sizeof(pid))
+ // Read the reply, which pickles the PID and an optional UMA enumeration.
+ static const unsigned kMaxReplyLength = 2048;
+ char buf[kMaxReplyLength];
+ const ssize_t len = ReadReply(buf, sizeof(buf));
+
+ Pickle reply_pickle(buf, len);
+ void *iter = NULL;
+ if (len <= 0 || !reply_pickle.ReadInt(&iter, &pid))
return base::kNullProcessHandle;
+
+ // If there is a nonempty UMA name string, then there is a UMA
+ // enumeration to record.
+ std::string uma_name;
+ int uma_sample;
+ int uma_boundary_value;
+ if (reply_pickle.ReadString(&iter, &uma_name) &&
+ !uma_name.empty() &&
+ reply_pickle.ReadInt(&iter, &uma_sample) &&
+ reply_pickle.ReadInt(&iter, &uma_boundary_value)) {
+ // We cannot use the UMA_HISTOGRAM_ENUMERATION macro here,
+ // because that's only for when the name is the same every time.
+ // Here we're using whatever name we got from the other side.
+ // But since it's likely that the same one will be used repeatedly
+ // (even though it's not guaranteed), we cache it here.
+ static base::Histogram* uma_histogram;
+ if (!uma_histogram || uma_histogram->histogram_name() != uma_name) {
+ uma_histogram = base::LinearHistogram::FactoryGet(
+ uma_name, 1,
+ uma_boundary_value,
+ uma_boundary_value + 1, base::Histogram::kUmaTargetedHistogramFlag);
+ }
+ uma_histogram->Add(uma_sample);
+ }
+
if (pid <= 0)
return base::kNullProcessHandle;
}
diff --git a/content/browser/zygote_main_linux.cc b/content/browser/zygote_main_linux.cc
index aad5283..10c4983 100644
--- a/content/browser/zygote_main_linux.cc
+++ b/content/browser/zygote_main_linux.cc
@@ -96,6 +96,10 @@ class Zygote {
public:
Zygote(int sandbox_flags, ZygoteForkDelegate* helper)
: sandbox_flags_(sandbox_flags), helper_(helper) {
+ if (helper_)
+ helper_->InitialUMA(&initial_uma_name_,
+ &initial_uma_sample_,
+ &initial_uma_boundary_value_);
}
bool ProcessRequests() {
@@ -253,10 +257,16 @@ class Zygote {
// This is equivalent to fork(), except that, when using the SUID
// sandbox, it returns the real PID of the child process as it
// appears outside the sandbox, rather than returning the PID inside
- // the sandbox.
+ // the sandbox. Optionally, it fills in uma_name et al with a report
+ // the helper wants to make via UMA_HISTOGRAM_ENUMERATION.
int ForkWithRealPid(const std::string& process_type, std::vector<int>& fds,
- const std::string& channel_switch) {
- const bool use_helper = (helper_ && helper_->CanHelp(process_type));
+ const std::string& channel_switch,
+ std::string* uma_name,
+ int* uma_sample, int* uma_boundary_value) {
+ const bool use_helper = (helper_ && helper_->CanHelp(process_type,
+ uma_name,
+ uma_sample,
+ uma_boundary_value));
if (!(use_helper || g_suid_sandbox_active)) {
return fork();
}
@@ -382,7 +392,10 @@ class Zygote {
// process and the child process ID to the parent process, like fork().
base::ProcessId ReadArgsAndFork(const Pickle& pickle,
void* iter,
- std::vector<int>& fds) {
+ std::vector<int>& fds,
+ std::string* uma_name,
+ int* uma_sample,
+ int* uma_boundary_value) {
std::vector<std::string> args;
int argc = 0;
int numfds = 0;
@@ -422,7 +435,9 @@ class Zygote {
static_cast<uint32_t>(kSandboxIPCChannel), kMagicSandboxIPCDescriptor));
// Returns twice, once per process.
- base::ProcessId child_pid = ForkWithRealPid(process_type, fds, channel_id);
+ base::ProcessId child_pid = ForkWithRealPid(process_type, fds, channel_id,
+ uma_name, uma_sample,
+ uma_boundary_value);
if (!child_pid) {
// This is the child process.
#if defined(SECCOMP_SANDBOX)
@@ -470,14 +485,36 @@ class Zygote {
// child_pid of -1 on error.
bool HandleForkRequest(int fd, const Pickle& pickle,
void* iter, std::vector<int>& fds) {
- base::ProcessId child_pid = ReadArgsAndFork(pickle, iter, fds);
+ std::string uma_name;
+ int uma_sample;
+ int uma_boundary_value;
+ base::ProcessId child_pid = ReadArgsAndFork(pickle, iter, fds,
+ &uma_name, &uma_sample,
+ &uma_boundary_value);
if (child_pid == 0)
return true;
for (std::vector<int>::const_iterator
i = fds.begin(); i != fds.end(); ++i)
close(*i);
+ if (uma_name.empty()) {
+ // There is no UMA report from this particular fork.
+ // Use the initial UMA report if any, and clear that record for next time.
+ // Note the swap method here is the efficient way to do this, since
+ // we know uma_name is empty.
+ uma_name.swap(initial_uma_name_);
+ uma_sample = initial_uma_sample_;
+ uma_boundary_value = initial_uma_boundary_value_;
+ }
// Must always send reply, as ZygoteHost blocks while waiting for it.
- if (HANDLE_EINTR(write(fd, &child_pid, sizeof(child_pid))) < 0)
+ Pickle reply_pickle;
+ reply_pickle.WriteInt(child_pid);
+ reply_pickle.WriteString(uma_name);
+ if (!uma_name.empty()) {
+ reply_pickle.WriteInt(uma_sample);
+ reply_pickle.WriteInt(uma_boundary_value);
+ }
+ if (HANDLE_EINTR(write(fd, reply_pickle.data(), reply_pickle.size())) !=
+ static_cast<ssize_t> (reply_pickle.size()))
PLOG(ERROR) << "write";
return false;
}
@@ -499,6 +536,12 @@ class Zygote {
const int sandbox_flags_;
ZygoteForkDelegate* helper_;
+
+ // These might be set by helper_->InitialUMA. They supply a UMA
+ // enumeration sample we should report on the first fork.
+ std::string initial_uma_name_;
+ int initial_uma_sample_;
+ int initial_uma_boundary_value_;
};
// With SELinux we can carve out a precise sandbox, so we don't have to play
diff --git a/content/common/zygote_fork_delegate_linux.h b/content/common/zygote_fork_delegate_linux.h
index 26dfbc9..bae7df2 100644
--- a/content/common/zygote_fork_delegate_linux.h
+++ b/content/common/zygote_fork_delegate_linux.h
@@ -19,9 +19,9 @@
class ZygoteForkDelegate {
public:
// A ZygoteForkDelegate is created during Chrome linux zygote
- // initialization, and provides "fork()" functionality with
- // as an alternative to forking the zygote. A new delegate is
- // passed in as an argument to ZygoteMain().
+ // initialization, and provides "fork()" functionality as an
+ // alternative to forking the zygote. A new delegate is passed in
+ // as an argument to ZygoteMain().
ZygoteForkDelegate() {}
virtual ~ZygoteForkDelegate() {}
@@ -31,9 +31,17 @@ class ZygoteForkDelegate {
int browserdesc,
int sandboxdesc) = 0;
- // Returns 'true' if the delegate would like to handle a given
- // fork request. Otherwise returns false.
- virtual bool CanHelp(const std::string& process_type) = 0;
+ // After Init, supply a UMA_HISTOGRAM_ENUMERATION the delegate
+ // would like to supply on the first fork.
+ virtual void InitialUMA(std::string* uma_name,
+ int* uma_sample,
+ int* uma_boundary_value) = 0;
+
+ // Returns 'true' if the delegate would like to handle a given fork
+ // request. Otherwise returns false. Optionally, fills in uma_name et al
+ // with a report the helper wants to make via UMA_HISTOGRAM_ENUMERATION.
+ virtual bool CanHelp(const std::string& process_type, std::string* uma_name,
+ int* uma_sample, int* uma_boundary_value) = 0;
// Delegate forks, returning a -1 on failure. Outside the
// suid sandbox, Fork() returns the Linux process ID. Inside