diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-02-13 15:05:36 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-02-14 10:33:47 +0000 |
commit | e684a3455bcc29a6e3e66a004e352dea4e1141e7 (patch) | |
tree | d55b4003bde34d7d05f558f02cfd82b2a66a7aac /chromium/base/metrics | |
parent | 2b94bfe47ccb6c08047959d1c26e392919550e86 (diff) | |
download | qtwebengine-chromium-e684a3455bcc29a6e3e66a004e352dea4e1141e7.tar.gz |
BASELINE: Update Chromium to 72.0.3626.110 and Ninja to 1.9.0
Change-Id: Ic57220b00ecc929a893c91f5cc552f5d3e99e922
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/base/metrics')
20 files changed, 670 insertions, 141 deletions
diff --git a/chromium/base/metrics/OWNERS b/chromium/base/metrics/OWNERS index 4cc69ff0630..5ed8a4a263c 100644 --- a/chromium/base/metrics/OWNERS +++ b/chromium/base/metrics/OWNERS @@ -1,10 +1,11 @@ asvitkine@chromium.org bcwhite@chromium.org -gayane@chromium.org holte@chromium.org isherman@chromium.org jwd@chromium.org mpearson@chromium.org rkaplow@chromium.org +per-file field_trial_memory_mac*=rsesek@chromium.org + # COMPONENT: Internals>Metrics diff --git a/chromium/base/metrics/bucket_ranges.cc b/chromium/base/metrics/bucket_ranges.cc index 39b379320e6..2723c3eb35a 100644 --- a/chromium/base/metrics/bucket_ranges.cc +++ b/chromium/base/metrics/bucket_ranges.cc @@ -74,7 +74,8 @@ const uint32_t kCrcTable[256] = { // the CRC correct for big-endian vs little-ending calculations. All we need is // a nice hash, that tends to depend on all the bits of the sample, with very // little chance of changes in one place impacting changes in another place. -static uint32_t Crc32(uint32_t sum, HistogramBase::Sample value) { +// Temporary non-static for https://crbug.com/836238 +/*static*/ uint32_t Crc32(uint32_t sum, HistogramBase::Sample value) { union { HistogramBase::Sample range; unsigned char bytes[sizeof(HistogramBase::Sample)]; diff --git a/chromium/base/metrics/bucket_ranges.h b/chromium/base/metrics/bucket_ranges.h index 1b6d069bdfa..476d2dfad43 100644 --- a/chromium/base/metrics/bucket_ranges.h +++ b/chromium/base/metrics/bucket_ranges.h @@ -99,6 +99,7 @@ class BASE_EXPORT BucketRanges { ////////////////////////////////////////////////////////////////////////////// // Expose only for test. BASE_EXPORT extern const uint32_t kCrcTable[256]; +uint32_t Crc32(uint32_t sum, HistogramBase::Sample value); } // namespace base diff --git a/chromium/base/metrics/field_trial.cc b/chromium/base/metrics/field_trial.cc index ffb4744e7e7..f4835249944 100644 --- a/chromium/base/metrics/field_trial.cc +++ b/chromium/base/metrics/field_trial.cc @@ -26,7 +26,9 @@ #include "base/unguessable_token.h" // On POSIX, the fd is shared using the mapping in GlobalDescriptors. -#if defined(OS_POSIX) && !defined(OS_NACL) +#if defined(OS_MACOSX) && !defined(OS_IOS) +#include "base/metrics/field_trial_memory_mac.h" +#elif defined(OS_POSIX) && !defined(OS_NACL) #include "base/posix/global_descriptors.h" #endif @@ -43,20 +45,6 @@ const char kPersistentStringSeparator = '/'; // Currently a slash. // command line which forces its activation. const char kActivationMarker = '*'; -// Use shared memory to communicate field trial (experiment) state. Set to false -// for now while the implementation is fleshed out (e.g. data format, single -// shared memory segment). See https://codereview.chromium.org/2365273004/ and -// crbug.com/653874 -// The browser is the only process that has write access to the shared memory. -// This is safe from race conditions because MakeIterable is a release operation -// and GetNextOfType is an acquire operation, so memory writes before -// MakeIterable happen before memory reads after GetNextOfType. -#if defined(OS_FUCHSIA) // TODO(752368): Not yet supported on Fuchsia. -const bool kUseSharedMemoryForFieldTrials = false; -#else -const bool kUseSharedMemoryForFieldTrials = true; -#endif - // Constants for the field trial allocator. const char kAllocatorName[] = "FieldTrialAllocator"; @@ -240,10 +228,10 @@ SharedMemoryHandle GetSharedMemoryReadOnlyHandle(SharedMemory* shared_memory) { // writable mapping attempts, but the original one in |shm| survives // and is still usable in the current process. result.SetRegionReadOnly(); -#endif // OS_ANDROID +#endif // defined(OS_ANDROID) return result; } -#endif // !OS_NACL +#endif // !defined(OS_NACL) } // namespace @@ -457,7 +445,7 @@ void FieldTrial::FinalizeGroupChoiceImpl(bool is_locked) { SetGroupChoice(default_group_name_, kDefaultGroupNumber); // Add the field trial to shared memory. - if (kUseSharedMemoryForFieldTrials && trial_registered_) + if (trial_registered_) FieldTrialList::OnGroupFinalized(is_locked, this); } @@ -829,7 +817,8 @@ void FieldTrialList::CreateTrialsFromCommandLine( int fd_key) { global_->create_trials_from_command_line_called_ = true; -#if defined(OS_WIN) || defined(OS_FUCHSIA) +#if defined(OS_WIN) || defined(OS_FUCHSIA) || \ + (defined(OS_MACOSX) && !defined(OS_IOS)) if (cmd_line.HasSwitch(field_trial_handle_switch)) { std::string switch_value = cmd_line.GetSwitchValueASCII(field_trial_handle_switch); @@ -869,8 +858,7 @@ void FieldTrialList::CreateFeaturesFromCommandLine( const char* disable_features_switch, FeatureList* feature_list) { // Fallback to command line if not using shared memory. - if (!kUseSharedMemoryForFieldTrials || - !global_->field_trial_allocator_.get()) { + if (!global_->field_trial_allocator_.get()) { return feature_list->InitializeFromCommandLine( command_line.GetSwitchValueASCII(enable_features_switch), command_line.GetSwitchValueASCII(disable_features_switch)); @@ -886,18 +874,25 @@ void FieldTrialList::AppendFieldTrialHandleIfNeeded( HandlesToInheritVector* handles) { if (!global_) return; - if (kUseSharedMemoryForFieldTrials) { - InstantiateFieldTrialAllocatorIfNeeded(); - if (global_->readonly_allocator_handle_.IsValid()) - handles->push_back(global_->readonly_allocator_handle_.GetHandle()); - } + InstantiateFieldTrialAllocatorIfNeeded(); + if (global_->readonly_allocator_handle_.IsValid()) + handles->push_back(global_->readonly_allocator_handle_.GetHandle()); } #elif defined(OS_FUCHSIA) // TODO(fuchsia): Implement shared-memory configuration (crbug.com/752368). +#elif defined(OS_MACOSX) && !defined(OS_IOS) +// static +FieldTrialMemoryServer* FieldTrialList::GetFieldTrialMemoryServer() { + if (global_) { + InstantiateFieldTrialAllocatorIfNeeded(); + return global_->field_trial_server_.get(); + } + return nullptr; +} #elif defined(OS_POSIX) && !defined(OS_NACL) // static SharedMemoryHandle FieldTrialList::GetFieldTrialHandle() { - if (global_ && kUseSharedMemoryForFieldTrials) { + if (global_) { InstantiateFieldTrialAllocatorIfNeeded(); // We check for an invalid handle where this gets called. return global_->readonly_allocator_handle_; @@ -912,53 +907,37 @@ void FieldTrialList::CopyFieldTrialStateToFlags( const char* enable_features_switch, const char* disable_features_switch, CommandLine* cmd_line) { - // TODO(lawrencewu): Ideally, having the global would be guaranteed. However, - // content browser tests currently don't create a FieldTrialList because they - // don't run ChromeBrowserMainParts code where it's done for Chrome. - // Some tests depend on the enable and disable features flag switch, though, - // so we can still add those even though AllStatesToString() will be a no-op. - if (!global_) { +#if !defined(OS_FUCHSIA) // TODO(752368): Not yet supported on Fuchsia. + // Use shared memory to communicate field trial state to child processes. + // The browser is the only process that has write access to the shared memory. + InstantiateFieldTrialAllocatorIfNeeded(); +#endif // !defined(OS_FUCHSIA) + + // If the readonly handle did not get created, fall back to flags. + if (!global_ || !global_->readonly_allocator_handle_.IsValid()) { AddFeatureAndFieldTrialFlags(enable_features_switch, disable_features_switch, cmd_line); return; } - // Use shared memory to pass the state if the feature is enabled, otherwise - // fallback to passing it via the command line as a string. - if (kUseSharedMemoryForFieldTrials) { - InstantiateFieldTrialAllocatorIfNeeded(); - // If the readonly handle didn't get duplicated properly, then fallback to - // original behavior. - if (!global_->readonly_allocator_handle_.IsValid()) { - AddFeatureAndFieldTrialFlags(enable_features_switch, - disable_features_switch, cmd_line); - return; - } + global_->field_trial_allocator_->UpdateTrackingHistograms(); + std::string switch_value = + SerializeSharedMemoryHandleMetadata(global_->readonly_allocator_handle_); + cmd_line->AppendSwitchASCII(field_trial_handle_switch, switch_value); - global_->field_trial_allocator_->UpdateTrackingHistograms(); - std::string switch_value = SerializeSharedMemoryHandleMetadata( - global_->readonly_allocator_handle_); - cmd_line->AppendSwitchASCII(field_trial_handle_switch, switch_value); - - // Append --enable-features and --disable-features switches corresponding - // to the features enabled on the command-line, so that child and browser - // process command lines match and clearly show what has been specified - // explicitly by the user. - std::string enabled_features; - std::string disabled_features; - FeatureList::GetInstance()->GetCommandLineFeatureOverrides( - &enabled_features, &disabled_features); - - if (!enabled_features.empty()) - cmd_line->AppendSwitchASCII(enable_features_switch, enabled_features); - if (!disabled_features.empty()) - cmd_line->AppendSwitchASCII(disable_features_switch, disabled_features); - - return; - } + // Append --enable-features and --disable-features switches corresponding + // to the features enabled on the command-line, so that child and browser + // process command lines match and clearly show what has been specified + // explicitly by the user. + std::string enabled_features; + std::string disabled_features; + FeatureList::GetInstance()->GetCommandLineFeatureOverrides( + &enabled_features, &disabled_features); - AddFeatureAndFieldTrialFlags(enable_features_switch, disable_features_switch, - cmd_line); + if (!enabled_features.empty()) + cmd_line->AppendSwitchASCII(enable_features_switch, enabled_features); + if (!disabled_features.empty()) + cmd_line->AppendSwitchASCII(disable_features_switch, disabled_features); } // static @@ -1042,8 +1021,7 @@ void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { if (!field_trial->enable_field_trial_) return; - if (kUseSharedMemoryForFieldTrials) - ActivateFieldTrialEntryWhileLocked(field_trial); + ActivateFieldTrialEntryWhileLocked(field_trial); } // Recording for stability debugging has to be done inline as a task posted @@ -1215,6 +1193,10 @@ std::string FieldTrialList::SerializeSharedMemoryHandleMetadata( ss << uintptr_handle << ","; #elif defined(OS_FUCHSIA) ss << shm.GetHandle() << ","; +#elif defined(OS_MACOSX) && !defined(OS_IOS) + // The handle on Mac is looked up directly by the child, rather than being + // transferred to the child over the command line. + ss << "0,"; #elif !defined(OS_POSIX) #error Unsupported OS #endif @@ -1225,7 +1207,8 @@ std::string FieldTrialList::SerializeSharedMemoryHandleMetadata( return ss.str(); } -#if defined(OS_WIN) || defined(OS_FUCHSIA) +#if defined(OS_WIN) || defined(OS_FUCHSIA) || \ + (defined(OS_MACOSX) && !defined(OS_IOS)) // static SharedMemoryHandle FieldTrialList::DeserializeSharedMemoryHandleMetadata( @@ -1252,7 +1235,12 @@ SharedMemoryHandle FieldTrialList::DeserializeSharedMemoryHandleMetadata( FALSE, DUPLICATE_SAME_ACCESS); CloseHandle(parent_handle); } -#endif // defined(OS_WIN) +#elif defined(OS_MACOSX) && !defined(OS_IOS) + mac::ScopedMachSendRight scoped_handle = + FieldTrialMemoryClient::AcquireMemoryObject(); + if (scoped_handle == MACH_PORT_NULL) + return SharedMemoryHandle(); +#endif base::UnguessableToken guid; if (!DeserializeGUIDFromStringPieces(tokens[1], tokens[2], &guid)) @@ -1262,6 +1250,11 @@ SharedMemoryHandle FieldTrialList::DeserializeSharedMemoryHandleMetadata( if (!base::StringToInt(tokens[3], &size)) return SharedMemoryHandle(); +#if defined(OS_MACOSX) && !defined(OS_IOS) + // Transfer ownership to SharedMemoryHandle. + mach_port_t handle = scoped_handle.release(); +#endif + return SharedMemoryHandle(handle, static_cast<size_t>(size), guid); } @@ -1291,7 +1284,8 @@ SharedMemoryHandle FieldTrialList::DeserializeSharedMemoryHandleMetadata( #endif -#if defined(OS_WIN) || defined(OS_FUCHSIA) +#if defined(OS_WIN) || defined(OS_FUCHSIA) || \ + (defined(OS_MACOSX) && !defined(OS_IOS)) // static bool FieldTrialList::CreateTrialsFromSwitchValue( const std::string& switch_value) { @@ -1305,9 +1299,6 @@ bool FieldTrialList::CreateTrialsFromSwitchValue( bool FieldTrialList::CreateTrialsFromDescriptor( int fd_key, const std::string& switch_value) { - if (!kUseSharedMemoryForFieldTrials) - return false; - if (fd_key == -1) return false; @@ -1374,6 +1365,7 @@ bool FieldTrialList::CreateTrialsFromSharedMemory( void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { if (!global_) return; + AutoLock auto_lock(global_->lock_); // Create the allocator if not already created and add all existing trials. if (global_->field_trial_allocator_ != nullptr) @@ -1382,9 +1374,6 @@ void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { SharedMemoryCreateOptions options; options.size = kFieldTrialAllocationSize; options.share_read_only = true; -#if defined(OS_MACOSX) && !defined(OS_IOS) - options.type = SharedMemoryHandle::POSIX; -#endif std::unique_ptr<SharedMemory> shm(new SharedMemory()); if (!shm->Create(options)) @@ -1411,6 +1400,13 @@ void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { global_->readonly_allocator_handle_ = GetSharedMemoryReadOnlyHandle( global_->field_trial_allocator_->shared_memory()); #endif + +#if defined(OS_MACOSX) && !defined(OS_IOS) + global_->field_trial_server_ = std::make_unique<FieldTrialMemoryServer>( + global_->readonly_allocator_handle_.GetMemoryObject()); + bool ok = global_->field_trial_server_->Start(); + DCHECK(ok); +#endif } // static diff --git a/chromium/base/metrics/field_trial.h b/chromium/base/metrics/field_trial.h index d439fccee95..95f646fe2eb 100644 --- a/chromium/base/metrics/field_trial.h +++ b/chromium/base/metrics/field_trial.h @@ -85,6 +85,7 @@ namespace base { class FieldTrialList; +class FieldTrialMemoryServer; class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> { public: @@ -588,6 +589,10 @@ class BASE_EXPORT FieldTrialList { base::HandlesToInheritVector* handles); #elif defined(OS_FUCHSIA) // TODO(fuchsia): Implement shared-memory configuration (crbug.com/752368). +#elif defined(OS_MACOSX) && !defined(OS_IOS) + // On Mac, the field trial shared memory is accessed via a Mach server, which + // the child looks up directly. + static FieldTrialMemoryServer* GetFieldTrialMemoryServer(); #elif defined(OS_POSIX) && !defined(OS_NACL) // On POSIX, we also need to explicitly pass down this file descriptor that // should be shared with the child process. Returns an invalid handle if it @@ -682,6 +687,7 @@ class BASE_EXPORT FieldTrialList { FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, ClearParamsFromSharedMemory); FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, SerializeSharedMemoryHandleMetadata); + friend int SerializeSharedMemoryHandleMetadata(void); FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, CheckReadOnlySharedMemoryHandle); // Serialization is used to pass information about the handle to child @@ -690,7 +696,8 @@ class BASE_EXPORT FieldTrialList { // underlying OS resource - that must be done by the Process launcher. static std::string SerializeSharedMemoryHandleMetadata( const SharedMemoryHandle& shm); -#if defined(OS_WIN) || defined(OS_FUCHSIA) +#if defined(OS_WIN) || defined(OS_FUCHSIA) || \ + (defined(OS_MACOSX) && !defined(OS_IOS)) static SharedMemoryHandle DeserializeSharedMemoryHandleMetadata( const std::string& switch_value); #elif defined(OS_POSIX) && !defined(OS_NACL) @@ -699,7 +706,8 @@ class BASE_EXPORT FieldTrialList { const std::string& switch_value); #endif -#if defined(OS_WIN) || defined(OS_FUCHSIA) +#if defined(OS_WIN) || defined(OS_FUCHSIA) || \ + (defined(OS_MACOSX) && !defined(OS_IOS)) // Takes in |handle_switch| from the command line which represents the shared // memory handle for field trials, parses it, and creates the field trials. // Returns true on success, false on failure. @@ -793,6 +801,12 @@ class BASE_EXPORT FieldTrialList { // AppendFieldTrialHandleIfNeeded(). base::SharedMemoryHandle readonly_allocator_handle_; +#if defined(OS_MACOSX) && !defined(OS_IOS) + // Mach message server that handles requests to acquire the shared memory + // object. + std::unique_ptr<FieldTrialMemoryServer> field_trial_server_; +#endif + // Tracks whether CreateTrialsFromCommandLine() has been called. bool create_trials_from_command_line_called_ = false; diff --git a/chromium/base/metrics/field_trial_memory_mac.cc b/chromium/base/metrics/field_trial_memory_mac.cc new file mode 100644 index 00000000000..be1adb2e664 --- /dev/null +++ b/chromium/base/metrics/field_trial_memory_mac.cc @@ -0,0 +1,206 @@ +// 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. + +#include "base/metrics/field_trial_memory_mac.h" + +#include <bsm/libbsm.h> +#include <libproc.h> +#include <mach/mig.h> +#include <servers/bootstrap.h> +#include <unistd.h> + +#include "base/logging.h" +#include "base/mac/foundation_util.h" +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_msg_destroy.h" +#include "base/strings/stringprintf.h" + +namespace base { + +namespace { + +// The name to use in the bootstrap server, formatted with the BaseBundleID and +// PID of the server. +const char kBootstrapNameFormat[] = "%s.FieldTrialMemoryServer.%d"; + +enum FieldTrialMsgId : mach_msg_id_t { + kFieldTrialMsgIdRequest = 'FTrq', + kFieldTrialMsgIdResponse = 'FTsp', +}; + +// Message received by the server for handling lookup lookup requests. +struct FieldTrialMemoryRequestMessage : public mach_msg_base_t { + // The size of the message excluding the trailer, used for msgh_size. + static const mach_msg_size_t kSendSize; + + mach_msg_audit_trailer_t trailer; +}; + +const mach_msg_size_t FieldTrialMemoryRequestMessage::kSendSize = + sizeof(FieldTrialMemoryRequestMessage) - sizeof(trailer); + +// Message used for sending and receiving the memory object handle. +struct FieldTrialMemoryResponseMessage : public mach_msg_base_t { + // The size of the message excluding the trailer, used for msgh_size. + static const mach_msg_size_t kSendSize; + + mach_msg_port_descriptor_t port; + mach_msg_trailer_t trailer; +}; + +const mach_msg_size_t FieldTrialMemoryResponseMessage::kSendSize = + sizeof(FieldTrialMemoryResponseMessage) - sizeof(trailer); + +} // namespace + +FieldTrialMemoryServer::FieldTrialMemoryServer(mach_port_t memory_object) + : memory_object_(memory_object), server_pid_(getpid()) { + DCHECK(memory_object != MACH_PORT_NULL); +} + +FieldTrialMemoryServer::~FieldTrialMemoryServer() {} + +bool FieldTrialMemoryServer::Start() { + std::string bootstrap_name = GetBootstrapName(); + kern_return_t kr = bootstrap_check_in( + bootstrap_port, bootstrap_name.c_str(), + mac::ScopedMachReceiveRight::Receiver(server_port_).get()); + if (kr != KERN_SUCCESS) { + BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_check_in " << bootstrap_name; + return false; + } + + dispatch_source_ = std::make_unique<DispatchSourceMach>( + "org.chromium.base.FieldTrialMemoryServer", server_port_.get(), ^{ + HandleRequest(); + }); + dispatch_source_->Resume(); + return true; +} + +// static +std::string FieldTrialMemoryServer::GetBootstrapName() { + return StringPrintf(kBootstrapNameFormat, mac::BaseBundleID(), getpid()); +} + +void FieldTrialMemoryServer::HandleRequest() { + // Receive the request message, using the kernel audit token to ascertain the + // PID of the sender. + FieldTrialMemoryRequestMessage request{}; + request.header.msgh_size = sizeof(request); + request.header.msgh_local_port = server_port_.get(); + + const mach_msg_option_t options = + MACH_RCV_MSG | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) | + MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT); + + kern_return_t kr = + mach_msg(&request.header, options, 0, sizeof(request), server_port_.get(), + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "mach_msg receive"; + return; + } + + // Destroy the message in case of an early return, which will release + // any rights from a bad message. In the case of a disallowed sender, + // the destruction of the reply port will break them out of a mach_msg. + ScopedMachMsgDestroy scoped_message(&request.header); + + if (request.header.msgh_id != kFieldTrialMsgIdRequest || + request.header.msgh_size != request.kSendSize) { + // Do not reply to messages that are unexpected. + return; + } + + // A client is allowed to look up the object if the sending process is a + // direct child of this server's process. + pid_t sender_pid = audit_token_to_pid(request.trailer.msgh_audit); + proc_bsdshortinfo sender{}; + int rv = proc_pidinfo(sender_pid, PROC_PIDT_SHORTBSDINFO, 0, &sender, + PROC_PIDT_SHORTBSDINFO_SIZE); + if (rv != PROC_PIDT_SHORTBSDINFO_SIZE || + sender.pbsi_ppid != static_cast<uint32_t>(server_pid_)) { + return; + } + + FieldTrialMemoryResponseMessage response{}; + response.header.msgh_bits = + MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE) | + MACH_MSGH_BITS_COMPLEX; + response.header.msgh_size = response.kSendSize; + response.header.msgh_remote_port = request.header.msgh_remote_port; + response.header.msgh_id = kFieldTrialMsgIdResponse; + response.body.msgh_descriptor_count = 1; + response.port.name = memory_object_; + response.port.disposition = MACH_MSG_TYPE_COPY_SEND; + response.port.type = MACH_MSG_PORT_DESCRIPTOR; + + scoped_message.Disarm(); + + kr = mach_msg(&response.header, MACH_SEND_MSG, response.header.msgh_size, 0, + MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_msg send"; +} + +// static +mac::ScopedMachSendRight FieldTrialMemoryClient::AcquireMemoryObject() { + mac::ScopedMachSendRight server_port; + std::string bootstrap_name = GetBootstrapName(); + kern_return_t kr = bootstrap_look_up( + bootstrap_port, const_cast<char*>(bootstrap_name.c_str()), + mac::ScopedMachSendRight::Receiver(server_port).get()); + if (kr != KERN_SUCCESS) { + BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << bootstrap_name; + return mac::ScopedMachSendRight(); + } + + return ChildSendRequest(std::move(server_port)); +} + +// static +std::string FieldTrialMemoryClient::GetBootstrapName() { + return StringPrintf(kBootstrapNameFormat, mac::BaseBundleID(), getppid()); +} + +// static +mac::ScopedMachSendRight FieldTrialMemoryClient::ChildSendRequest( + mac::ScopedMachSendRight server_port) { + // Perform a send and receive mach_msg. + union { + FieldTrialMemoryRequestMessage request; + FieldTrialMemoryResponseMessage response; + } msg{}; + msg.request.header.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); + // The size of |msg| is used for receiving since it includes space for the + // trailer, but for the request being sent, the size is just the base message. + msg.request.header.msgh_size = msg.request.kSendSize; + msg.request.header.msgh_remote_port = server_port.release(); + msg.request.header.msgh_local_port = mig_get_reply_port(); + msg.request.header.msgh_id = kFieldTrialMsgIdRequest; + + kern_return_t kr = + mach_msg(&msg.request.header, MACH_SEND_MSG | MACH_RCV_MSG, + msg.request.header.msgh_size, sizeof(msg.response), + msg.request.header.msgh_local_port, MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "mach_msg"; + return mac::ScopedMachSendRight(); + } + + if (msg.response.header.msgh_id != kFieldTrialMsgIdResponse || + msg.response.header.msgh_size != msg.response.kSendSize) { + return mac::ScopedMachSendRight(); + } + + return mac::ScopedMachSendRight(msg.response.port.name); +} + +FieldTrialMemoryClient::FieldTrialMemoryClient() = default; + +FieldTrialMemoryClient::~FieldTrialMemoryClient() = default; + +} // namespace base diff --git a/chromium/base/metrics/field_trial_memory_mac.h b/chromium/base/metrics/field_trial_memory_mac.h new file mode 100644 index 00000000000..3a323760216 --- /dev/null +++ b/chromium/base/metrics/field_trial_memory_mac.h @@ -0,0 +1,85 @@ +// 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. + +#ifndef BASE_METRICS_FIELD_TRIAL_MEMORY_MAC_H_ +#define BASE_METRICS_FIELD_TRIAL_MEMORY_MAC_H_ + +#include <mach/port.h> +#include <sys/types.h> + +#include <memory> +#include <string> + +#include "base/base_export.h" +#include "base/mac/dispatch_source_mach.h" +#include "base/mac/scoped_mach_port.h" +#include "base/macros.h" + +namespace base { + +// FieldTrialMemoryServer services requests for the FieldTrial shared memory +// region on Mac. Shared memory on Mac uses Mach ports, which cannot be +// transferred across process creation. Instead, this class publishes an +// endpoint in the bootstrap server. Child processes look up the server and then +// send requests to acquire the shared memory object. Only processes that are +// direct children of the process that is running this server are allowed to +// acquire the memory object send right. +class BASE_EXPORT FieldTrialMemoryServer { + public: + // Creates a server that will vend access to the passed |memory_object|. + // This does not change the user refcount of the object. Start() must be + // called before requests will be processed. + explicit FieldTrialMemoryServer(mach_port_t memory_object); + ~FieldTrialMemoryServer(); + + // Starts processing requests for the server. Returns false if the server + // could not be started and true on success. + bool Start(); + + private: + friend class FieldTrialMemoryServerTest; + + // Exposed for testing. + void set_server_pid(pid_t pid) { server_pid_ = pid; } + + // Returns the name of the server to publish in the bootstrap namespace. + static std::string GetBootstrapName(); + + // The server-side Mach message handler. + void HandleRequest(); + + mach_port_t memory_object_; // weak + pid_t server_pid_; // PPID used for access control checks. + mac::ScopedMachReceiveRight server_port_; + std::unique_ptr<DispatchSourceMach> dispatch_source_; + + DISALLOW_COPY_AND_ASSIGN(FieldTrialMemoryServer); +}; + +// Client class for accessing the memory object exposed by the +// FieldTrialMemoryServer. +class BASE_EXPORT FieldTrialMemoryClient { + public: + // Called by children of the process running the FieldTrialMemoryServer, this + // attempts to acquire the port for the |memory_object_|. Returns the port + // on success or MACH_PORT_NULL on error or failure. + static mac::ScopedMachSendRight AcquireMemoryObject(); + + // Returns the name of the server to look up in the bootstrap namespace. + static std::string GetBootstrapName(); + + private: + // Sends the Mach message to |server_port| to acquire the memory object. + static mac::ScopedMachSendRight ChildSendRequest( + mac::ScopedMachSendRight server_port); + + FieldTrialMemoryClient(); + ~FieldTrialMemoryClient(); + + DISALLOW_COPY_AND_ASSIGN(FieldTrialMemoryClient); +}; + +} // namespace base + +#endif // BASE_METRICS_FIELD_TRIAL_MEMORY_MAC_H_ diff --git a/chromium/base/metrics/field_trial_memory_mac_unittest.cc b/chromium/base/metrics/field_trial_memory_mac_unittest.cc new file mode 100644 index 00000000000..536aa3a232f --- /dev/null +++ b/chromium/base/metrics/field_trial_memory_mac_unittest.cc @@ -0,0 +1,118 @@ +// 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. + +#include "base/metrics/field_trial_memory_mac.h" + +#include <mach/mach.h> +#include <mach/mach_vm.h> + +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_vm.h" +#include "base/test/multiprocess_test.h" +#include "base/test/test_timeouts.h" +#include "testing/multiprocess_func_list.h" + +namespace base { + +namespace { + +enum ChildExitCode { + kChildExitInvalid, + kChildExitNoPort, + kChildExitMapFailed, + kChildExitBadPattern, + kChildExitSuccess, +}; + +constexpr char kMemoryTestPattern[] = "Hello there, bear"; + +constexpr mach_vm_size_t kMemoryAllocationSize = 1024; + +} // namespace + +class FieldTrialMemoryServerTest : public MultiProcessTest { + public: + void SetUp() override { + mach_vm_address_t address = 0; + mach_vm_size_t size = mach_vm_round_page(kMemoryAllocationSize); + kern_return_t kr = + mach_vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE); + ASSERT_EQ(kr, KERN_SUCCESS) << "mach_vm_allocate"; + memory_.reset(address, size); + + kr = mach_make_memory_entry_64( + mach_task_self(), &size, address, VM_PROT_READ, + mac::ScopedMachSendRight::Receiver(memory_object_).get(), + MACH_PORT_NULL); + ASSERT_EQ(kr, KERN_SUCCESS) << "mach_make_memory_entry_64"; + + memcpy(reinterpret_cast<void*>(address), kMemoryTestPattern, + sizeof(kMemoryTestPattern)); + } + + void SetServerPid(FieldTrialMemoryServer* server, pid_t server_pid) { + server->set_server_pid(server_pid); + } + + mach_port_t memory_object() { return memory_object_.get(); } + + private: + mac::ScopedMachVM memory_; + mac::ScopedMachSendRight memory_object_; +}; + +MULTIPROCESS_TEST_MAIN(AcquireMemoryObjectAndMap) { + mac::ScopedMachSendRight memory_object = + FieldTrialMemoryClient::AcquireMemoryObject(); + if (memory_object == MACH_PORT_NULL) + return kChildExitNoPort; + + mach_vm_address_t address = 0; + kern_return_t kr = + mach_vm_map(mach_task_self(), &address, kMemoryAllocationSize, 0, + VM_FLAGS_ANYWHERE, memory_object.get(), 0, false, + VM_PROT_READ, VM_PROT_READ, VM_INHERIT_NONE); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "mach_vm_map"; + return kChildExitMapFailed; + } + + if (memcmp(kMemoryTestPattern, reinterpret_cast<void*>(address), + sizeof(kMemoryTestPattern)) != 0) { + return kChildExitBadPattern; + } + + return kChildExitSuccess; +} + +TEST_F(FieldTrialMemoryServerTest, AllowedPid) { + FieldTrialMemoryServer server(memory_object()); + ASSERT_TRUE(server.Start()); + + Process child = SpawnChild("AcquireMemoryObjectAndMap"); + + int exit_code; + ASSERT_TRUE(WaitForMultiprocessTestChildExit( + child, TestTimeouts::action_timeout(), &exit_code)); + + EXPECT_EQ(kChildExitSuccess, exit_code); +} + +TEST_F(FieldTrialMemoryServerTest, BlockedPid) { + FieldTrialMemoryServer server(memory_object()); + // Override the server's PID so that the request does not look like it is + // coming from a process that is the child of the server. + SetServerPid(&server, 1); + ASSERT_TRUE(server.Start()); + + Process child = SpawnChild("AcquireMemoryObjectAndMap"); + + int exit_code; + ASSERT_TRUE(WaitForMultiprocessTestChildExit( + child, TestTimeouts::action_timeout(), &exit_code)); + + EXPECT_EQ(kChildExitNoPort, exit_code); +} + +} // namespace base diff --git a/chromium/base/metrics/field_trial_unittest.cc b/chromium/base/metrics/field_trial_unittest.cc index 7dbe737d8a8..dce2dd6d817 100644 --- a/chromium/base/metrics/field_trial_unittest.cc +++ b/chromium/base/metrics/field_trial_unittest.cc @@ -11,7 +11,6 @@ #include "base/feature_list.h" #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" #include "base/metrics/field_trial_param_associator.h" #include "base/rand_util.h" #include "base/run_loop.h" @@ -19,10 +18,18 @@ #include "base/strings/stringprintf.h" #include "base/test/gtest_util.h" #include "base/test/mock_entropy_provider.h" +#include "base/test/multiprocess_test.h" #include "base/test/scoped_feature_list.h" +#include "base/test/scoped_task_environment.h" #include "base/test/test_shared_memory_util.h" +#include "base/test/test_timeouts.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" +#include "testing/multiprocess_func_list.h" + +#if defined(OS_MACOSX) && !defined(OS_IOS) +#include "base/metrics/field_trial_memory_mac.h" +#endif namespace base { @@ -100,7 +107,7 @@ class FieldTrialTest : public ::testing::Test { FieldTrialTest() : trial_list_(nullptr) {} private: - MessageLoop message_loop_; + test::ScopedTaskEnvironment scoped_task_environment_; FieldTrialList trial_list_; DISALLOW_COPY_AND_ASSIGN(FieldTrialTest); @@ -1426,33 +1433,66 @@ TEST(FieldTrialListTest, DumpAndFetchFromSharedMemory) { EXPECT_EQ("value2", shm_params["key2"]); } -#if !defined(OS_NACL) -TEST(FieldTrialListTest, SerializeSharedMemoryHandleMetadata) { - std::unique_ptr<SharedMemory> shm(new SharedMemory()); - shm->CreateAndMapAnonymous(4 << 10); - +#if !defined(OS_NACL) && !defined(OS_IOS) +MULTIPROCESS_TEST_MAIN(SerializeSharedMemoryHandleMetadata) { std::string serialized = - FieldTrialList::SerializeSharedMemoryHandleMetadata(shm->handle()); -#if defined(OS_WIN) || defined(OS_FUCHSIA) + CommandLine::ForCurrentProcess()->GetSwitchValueASCII("field_trials"); + std::string guid_string = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII("guid"); + +#if defined(OS_WIN) || defined(OS_FUCHSIA) || \ + (defined(OS_MACOSX) && !defined(OS_IOS)) SharedMemoryHandle deserialized = FieldTrialList::DeserializeSharedMemoryHandleMetadata(serialized); #else - // Use a valid-looking arbitrary number for the file descriptor. It's not - // being used in this unittest, but needs to pass sanity checks in the - // handle's constructor. + // Use the arbitrary value selected below. SharedMemoryHandle deserialized = FieldTrialList::DeserializeSharedMemoryHandleMetadata(42, serialized); #endif - EXPECT_EQ(deserialized.GetGUID(), shm->handle().GetGUID()); - EXPECT_FALSE(deserialized.GetGUID().is_empty()); + CHECK_EQ(deserialized.GetGUID().ToString(), guid_string); + CHECK(!deserialized.GetGUID().is_empty()); + + return 0; +} + +TEST(FieldTrialListTest, SerializeSharedMemoryHandleMetadata) { + std::unique_ptr<SharedMemory> shm(new SharedMemory()); + shm->CreateAndMapAnonymous(4 << 10); + +#if defined(OS_MACOSX) && !defined(OS_IOS) + FieldTrialMemoryServer mach_server(shm->handle().GetMemoryObject()); + ASSERT_TRUE(mach_server.Start()); +#endif + + std::string serialized = + FieldTrialList::SerializeSharedMemoryHandleMetadata(shm->handle()); + + LaunchOptions options; +#if defined(OS_POSIX) && (!defined(OS_MACOSX) && !defined(OS_IOS)) + // Pick an arbitrary FD number to use for the shmem FD in the child. + options.fds_to_remap.emplace_back( + std::make_pair(shm->handle().GetHandle(), 42)); +#endif + CommandLine cmd_line = GetMultiProcessTestChildBaseCommandLine(); + cmd_line.AppendSwitchASCII("field_trials", serialized); + cmd_line.AppendSwitchASCII("guid", shm->handle().GetGUID().ToString()); + + Process process = SpawnMultiProcessTestChild( + "SerializeSharedMemoryHandleMetadata", cmd_line, options); + + int exit_code; + EXPECT_TRUE(WaitForMultiprocessTestChildExit( + process, TestTimeouts::action_timeout(), &exit_code)); + EXPECT_EQ(0, exit_code); } #endif // !defined(OS_NACL) // Verify that the field trial shared memory handle is really read-only, and -// does not allow writable mappings. Test disabled on NaCl, Windows and Fuchsia -// which don't support/implement GetFieldTrialHandle(). For Fuchsia, see -// crbug.com/752368 -#if !defined(OS_NACL) && !defined(OS_WIN) && !defined(OS_FUCHSIA) +// does not allow writable mappings. Test disabled on NaCl, Windows, Fuchsia, +// and Mac, which don't support/implement GetFieldTrialHandle(). For Fuchsia, +// see crbug.com/752368 +#if !defined(OS_NACL) && !defined(OS_WIN) && !defined(OS_FUCHSIA) && \ + (!defined(OS_MACOSX) && !defined(OS_IOS)) TEST(FieldTrialListTest, CheckReadOnlySharedMemoryHandle) { FieldTrialList field_trial_list(nullptr); FieldTrialList::CreateFieldTrial("Trial1", "Group1"); diff --git a/chromium/base/metrics/histogram.cc b/chromium/base/metrics/histogram.cc index 1b80abbd6bd..0dc4bbfe707 100644 --- a/chromium/base/metrics/histogram.cc +++ b/chromium/base/metrics/histogram.cc @@ -93,7 +93,7 @@ typedef HistogramBase::Count Count; typedef HistogramBase::Sample Sample; // static -const uint32_t Histogram::kBucketCount_MAX = 16384u; +const uint32_t Histogram::kBucketCount_MAX = 10002u; class Histogram::Factory { public: @@ -341,6 +341,7 @@ void Histogram::InitializeBucketRanges(Sample minimum, while (bucket_count > ++bucket_index) { double log_current; log_current = log(static_cast<double>(current)); + debug::Alias(&log_current); // Calculate the count'th root of the range. log_ratio = (log_max - log_current) / (bucket_count - bucket_index); // See where the next bucket would start. @@ -421,27 +422,37 @@ bool Histogram::InspectConstructionArguments(StringPiece name, Sample* minimum, Sample* maximum, uint32_t* bucket_count) { + bool check_okay = true; + + // Checks below must be done after any min/max swap. + if (*minimum > *maximum) { + check_okay = false; + std::swap(*minimum, *maximum); + } + // Defensive code for backward compatibility. if (*minimum < 1) { DVLOG(1) << "Histogram: " << name << " has bad minimum: " << *minimum; *minimum = 1; + if (*maximum < 1) + *maximum = 1; } if (*maximum >= kSampleType_MAX) { DVLOG(1) << "Histogram: " << name << " has bad maximum: " << *maximum; *maximum = kSampleType_MAX - 1; } if (*bucket_count >= kBucketCount_MAX) { + check_okay = false; DVLOG(1) << "Histogram: " << name << " has bad bucket_count: " << *bucket_count; *bucket_count = kBucketCount_MAX - 1; } - - bool check_okay = true; - - if (*minimum > *maximum) { - check_okay = false; - std::swap(*minimum, *maximum); + if (*bucket_count > 1002) { + UmaHistogramSparse("Histogram.TooManyBuckets.1000", + static_cast<Sample>(HashMetricName(name))); } + + // Ensure parameters are sane. if (*maximum == *minimum) { check_okay = false; *maximum = *minimum + 1; @@ -450,13 +461,6 @@ bool Histogram::InspectConstructionArguments(StringPiece name, check_okay = false; *bucket_count = 3; } - // Very high bucket counts are wasteful. Use a sparse histogram instead. - // Value of 10002 equals a user-supplied value of 10k + 2 overflow buckets. - constexpr uint32_t kMaxBucketCount = 10002; - if (*bucket_count > kMaxBucketCount) { - check_okay = false; - *bucket_count = kMaxBucketCount; - } if (*bucket_count > static_cast<uint32_t>(*maximum - *minimum + 2)) { check_okay = false; *bucket_count = static_cast<uint32_t>(*maximum - *minimum + 2); @@ -921,6 +925,18 @@ HistogramBase* LinearHistogram::FactoryGetWithRangeDescription( uint32_t bucket_count, int32_t flags, const DescriptionPair descriptions[]) { + // Originally, histograms were required to have at least one sample value + // plus underflow and overflow buckets. For single-entry enumerations, + // that one value is usually zero (which IS the underflow bucket) + // resulting in a |maximum| value of 1 (the exclusive upper-bound) and only + // the two outlier buckets. Handle this by making max==2 and buckets==3. + // This usually won't have any cost since the single-value-optimization + // will be used until the count exceeds 16 bits. + if (maximum == 1 && bucket_count == 2) { + maximum = 2; + bucket_count = 3; + } + bool valid_arguments = Histogram::InspectConstructionArguments( name, &minimum, &maximum, &bucket_count); DCHECK(valid_arguments); diff --git a/chromium/base/metrics/histogram_flattener.h b/chromium/base/metrics/histogram_flattener.h index 6a5e3f42988..ed81154364b 100644 --- a/chromium/base/metrics/histogram_flattener.h +++ b/chromium/base/metrics/histogram_flattener.h @@ -19,12 +19,13 @@ class HistogramSamples; // handles the logistics of gathering up available histograms for recording. class BASE_EXPORT HistogramFlattener { public: + virtual ~HistogramFlattener() = default; + virtual void RecordDelta(const HistogramBase& histogram, const HistogramSamples& snapshot) = 0; protected: HistogramFlattener() = default; - virtual ~HistogramFlattener() = default; private: DISALLOW_COPY_AND_ASSIGN(HistogramFlattener); diff --git a/chromium/base/metrics/histogram_functions.cc b/chromium/base/metrics/histogram_functions.cc index 31bf219e5af..e0d7b2ddfcf 100644 --- a/chromium/base/metrics/histogram_functions.cc +++ b/chromium/base/metrics/histogram_functions.cc @@ -89,6 +89,22 @@ void UmaHistogramLongTimes(const std::string& name, TimeDelta sample) { TimeDelta::FromHours(1), 50); } +void UmaHistogramCustomMicrosecondsTimes(const std::string& name, + TimeDelta sample, + TimeDelta min, + TimeDelta max, + int buckets) { + HistogramBase* histogram = Histogram::FactoryTimeGet( + name, min, max, buckets, HistogramBase::kUmaTargetedHistogramFlag); + histogram->AddTimeMicrosecondsGranularity(sample); +} + +void UmaHistogramMicrosecondsTimes(const std::string& name, TimeDelta sample) { + UmaHistogramCustomMicrosecondsTimes(name, sample, + TimeDelta::FromMicroseconds(1), + TimeDelta::FromSeconds(10), 50); +} + void UmaHistogramMemoryKB(const std::string& name, int sample) { UmaHistogramCustomCounts(name, sample, 1000, 500000, 50); } diff --git a/chromium/base/metrics/histogram_functions.h b/chromium/base/metrics/histogram_functions.h index 60c005726f5..3d95464f555 100644 --- a/chromium/base/metrics/histogram_functions.h +++ b/chromium/base/metrics/histogram_functions.h @@ -101,7 +101,7 @@ BASE_EXPORT void UmaHistogramCounts100000(const std::string& name, int sample); BASE_EXPORT void UmaHistogramCounts1M(const std::string& name, int sample); BASE_EXPORT void UmaHistogramCounts10M(const std::string& name, int sample); -// For histograms storing times. +// For histograms storing times. It uses milliseconds granularity. BASE_EXPORT void UmaHistogramCustomTimes(const std::string& name, TimeDelta sample, TimeDelta min, @@ -116,6 +116,17 @@ BASE_EXPORT void UmaHistogramMediumTimes(const std::string& name, BASE_EXPORT void UmaHistogramLongTimes(const std::string& name, TimeDelta sample); +// For histograms storing times with microseconds granularity. +BASE_EXPORT void UmaHistogramCustomMicrosecondsTimes(const std::string& name, + TimeDelta sample, + TimeDelta min, + TimeDelta max, + int buckets); + +// For microseconds timings from 1 microsecond up to 10 seconds (50 buckets). +BASE_EXPORT void UmaHistogramMicrosecondsTimes(const std::string& name, + TimeDelta sample); + // For recording memory related histograms. // Used to measure common KB-granularity memory stats. Range is up to 500M. BASE_EXPORT void UmaHistogramMemoryKB(const std::string& name, int sample); diff --git a/chromium/base/metrics/histogram_macros.h b/chromium/base/metrics/histogram_macros.h index 93bd4bdff7c..892f1a6cba2 100644 --- a/chromium/base/metrics/histogram_macros.h +++ b/chromium/base/metrics/histogram_macros.h @@ -43,8 +43,9 @@ // having to handle a sentinel no-op value. // // Sample usage: -// // These values are persisted to logs. Entries should not be renumbered and -// // numeric values should never be reused. +// // These values are logged to UMA. Entries should not be renumbered and +// // numeric values should never be reused. Please keep in sync with "MyEnum" +// // in src/tools/metrics/histograms/enums.xml. // enum class MyEnum { // kFirstValue = 0, // kSecondValue = 1, @@ -59,8 +60,9 @@ // greater than any other enumerator that will be sampled. // // Sample usage: -// // These values are persisted to logs. Entries should not be renumbered and -// // numeric values should never be reused. +// // These values are logged to UMA. Entries should not be renumbered and +// // numeric values should never be reused. Please keep in sync with "MyEnum" +// // in src/tools/metrics/histograms/enums.xml. // enum class MyEnum { // FIRST_VALUE = 0, // SECOND_VALUE = 1, @@ -77,10 +79,10 @@ // enum to an arithmetic type and adding one. Instead, prefer the two argument // version of the macro which automatically deduces the boundary from kMaxValue. #define UMA_HISTOGRAM_ENUMERATION(name, ...) \ - CR_EXPAND_ARG(INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \ + INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \ __VA_ARGS__, INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY, \ - INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY)( \ - name, __VA_ARGS__, base::HistogramBase::kUmaTargetedHistogramFlag)) + INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY) \ + (name, __VA_ARGS__, base::HistogramBase::kUmaTargetedHistogramFlag) // As above but "scaled" count to avoid overflows caused by increments of // large amounts. See UMA_HISTOGRAM_SCALED_EXACT_LINEAR for more information. @@ -257,10 +259,10 @@ name, min, max, bucket_count, \ base::HistogramBase::kUmaTargetedHistogramFlag)) -// Scoped class which logs its time on this earth as a UMA statistic. This is -// recommended for when you want a histogram which measures the time it takes -// for a method to execute. This measures up to 10 seconds. It uses -// UMA_HISTOGRAM_TIMES under the hood. +// Scoped class which logs its time on this earth in milliseconds as a UMA +// statistic. This is recommended for when you want a histogram which measures +// the time it takes for a method to execute. This measures up to 10 seconds. It +// uses UMA_HISTOGRAM_TIMES under the hood. // Sample usage: // void Function() { diff --git a/chromium/base/metrics/histogram_macros_local.h b/chromium/base/metrics/histogram_macros_local.h index c4d333b5019..38a2d785202 100644 --- a/chromium/base/metrics/histogram_macros_local.h +++ b/chromium/base/metrics/histogram_macros_local.h @@ -19,10 +19,10 @@ // For usage details, see the equivalents in histogram_macros.h. #define LOCAL_HISTOGRAM_ENUMERATION(name, ...) \ - CR_EXPAND_ARG(INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \ + INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \ __VA_ARGS__, INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY, \ - INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY)( \ - name, __VA_ARGS__, base::HistogramBase::kNoFlags)) + INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY) \ + (name, __VA_ARGS__, base::HistogramBase::kNoFlags) #define LOCAL_HISTOGRAM_BOOLEAN(name, sample) \ STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \ diff --git a/chromium/base/metrics/histogram_unittest.cc b/chromium/base/metrics/histogram_unittest.cc index e516acbe2d1..0eec32b0ee2 100644 --- a/chromium/base/metrics/histogram_unittest.cc +++ b/chromium/base/metrics/histogram_unittest.cc @@ -295,6 +295,25 @@ TEST_P(HistogramTest, LinearRangesTest) { EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges())); } +TEST_P(HistogramTest, SingleValueEnumerationHistogram) { + // Make sure its possible to construct a linear histogram with only the two + // required outlier buckets (underflow and overflow). + HistogramBase* histogram = LinearHistogram::FactoryGet( + "SingleValueEnum", 1, 1, 2, HistogramBase::kNoFlags); + EXPECT_TRUE(histogram); + + // Make sure the macros work properly. This can only be run when + // there is no persistent allocator which can be discarded and leave + // dangling pointers. + if (!use_persistent_histogram_allocator_) { + enum EnumWithMax { + kSomething = 0, + kMaxValue = kSomething, + }; + UMA_HISTOGRAM_ENUMERATION("h1", kSomething); + } +} + TEST_P(HistogramTest, ArrayToCustomEnumRangesTest) { const HistogramBase::Sample ranges[3] = {5, 10, 20}; std::vector<HistogramBase::Sample> ranges_vec = diff --git a/chromium/base/metrics/persistent_histogram_storage.cc b/chromium/base/metrics/persistent_histogram_storage.cc index e2a56d7df6c..0676e47ba18 100644 --- a/chromium/base/metrics/persistent_histogram_storage.cc +++ b/chromium/base/metrics/persistent_histogram_storage.cc @@ -39,8 +39,6 @@ PersistentHistogramStorage::~PersistentHistogramStorage() { PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get(); allocator->UpdateTrackingHistograms(); - // TODO(chengx): Investigate making early return depend on whethere there are - // metrics to report at this point or not. if (disabled_) return; diff --git a/chromium/base/metrics/persistent_histogram_storage_unittest.cc b/chromium/base/metrics/persistent_histogram_storage_unittest.cc index 0b9b1ce6b09..68f795a5ea9 100644 --- a/chromium/base/metrics/persistent_histogram_storage_unittest.cc +++ b/chromium/base/metrics/persistent_histogram_storage_unittest.cc @@ -50,9 +50,8 @@ class PersistentHistogramStorageTest : public testing::Test { DISALLOW_COPY_AND_ASSIGN(PersistentHistogramStorageTest); }; -// TODO(chengx): Re-enable the test on OS_IOS after issue 836789 is fixed. -// PersistentHistogramStorage is only used on OS_WIN now, so disabling this -// test on OS_IOS is fine. +// This test is disabled on OS_IOS because of crbug.com/836789. This is fine as +// PersistentHistogramStorage is only used on OS_WIN now. #if !defined(OS_NACL) && !defined(OS_IOS) TEST_F(PersistentHistogramStorageTest, HistogramWriteTest) { auto persistent_histogram_storage = diff --git a/chromium/base/metrics/persistent_memory_allocator.cc b/chromium/base/metrics/persistent_memory_allocator.cc index a8cd4a4c28e..d111ea9fde3 100644 --- a/chromium/base/metrics/persistent_memory_allocator.cc +++ b/chromium/base/metrics/persistent_memory_allocator.cc @@ -21,8 +21,9 @@ #include "base/metrics/histogram_functions.h" #include "base/metrics/sparse_histogram.h" #include "base/numerics/safe_conversions.h" -#include "base/sys_info.h" -#include "base/threading/thread_restrictions.h" +#include "base/optional.h" +#include "base/system/sys_info.h" +#include "base/threading/scoped_blocking_call.h" #include "build/build_config.h" namespace { @@ -1068,7 +1069,7 @@ bool FilePersistentMemoryAllocator::IsFileAcceptable( void FilePersistentMemoryAllocator::Cache() { // Since this method is expected to load data from permanent storage // into memory, blocking I/O may occur. - AssertBlockingAllowed(); + base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK); // Calculate begin/end addresses so that the first byte of every page // in that range can be read. Keep within the used space. The |volatile| @@ -1092,14 +1093,16 @@ void FilePersistentMemoryAllocator::Cache() { } void FilePersistentMemoryAllocator::FlushPartial(size_t length, bool sync) { - if (sync) - AssertBlockingAllowed(); if (IsReadonly()) return; + base::Optional<base::ScopedBlockingCall> scoped_blocking_call; + if (sync) + scoped_blocking_call.emplace(base::BlockingType::MAY_BLOCK); + #if defined(OS_WIN) // Windows doesn't support asynchronous flush. - AssertBlockingAllowed(); + scoped_blocking_call.emplace(base::BlockingType::MAY_BLOCK); BOOL success = ::FlushViewOfFile(data(), length); DPCHECK(success); #elif defined(OS_MACOSX) diff --git a/chromium/base/metrics/user_metrics.cc b/chromium/base/metrics/user_metrics.cc index 9fcc9e8a18a..a0c88a4b8df 100644 --- a/chromium/base/metrics/user_metrics.cc +++ b/chromium/base/metrics/user_metrics.cc @@ -13,6 +13,7 @@ #include "base/location.h" #include "base/macros.h" #include "base/threading/thread_checker.h" +#include "base/trace_event/trace_event.h" namespace base { namespace { @@ -29,6 +30,7 @@ void RecordAction(const UserMetricsAction& action) { } void RecordComputedAction(const std::string& action) { + TRACE_EVENT_INSTANT1("ui", "UserEvent", TRACE_EVENT_SCOPE_GLOBAL, "action", action); if (!g_task_runner.Get()) { DCHECK(g_callbacks.Get().empty()); return; |