diff options
-rw-r--r-- | chrome/common/nacl_fork_delegate_linux.h | 21 | ||||
-rw-r--r-- | chrome/nacl/nacl_fork_delegate_linux.cc | 48 | ||||
-rw-r--r-- | content/browser/zygote_host_linux.cc | 35 | ||||
-rw-r--r-- | content/browser/zygote_main_linux.cc | 57 | ||||
-rw-r--r-- | content/common/zygote_fork_delegate_linux.h | 20 |
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 |