diff options
author | blundell <blundell@chromium.org> | 2015-07-30 13:18:30 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-30 20:19:42 +0000 |
commit | b5c6b5a18863e2e9094b5bb3328edab690fa7111 (patch) | |
tree | 0c36eec1009b7a9ab601ebcf91c4590058f4e775 /components | |
parent | 34960e7a547930177cc05020921b7ee3eec4fd39 (diff) | |
download | chromium_src-b5c6b5a18863e2e9094b5bb3328edab690fa7111.zip chromium_src-b5c6b5a18863e2e9094b5bb3328edab690fa7111.tar.gz chromium_src-b5c6b5a18863e2e9094b5bb3328edab690fa7111.tar.bz2 |
Componentize DriveMetricsProvider*
This metrics provider is used on iOS, so to enable clean integration on iOS
this CL componentizes it and injects its //chrome-level dependencies from the
embedder.
BUG=512426
Review URL: https://codereview.chromium.org/1254983003
Cr-Commit-Position: refs/heads/master@{#341183}
Diffstat (limited to 'components')
-rw-r--r-- | components/components_tests.gyp | 1 | ||||
-rw-r--r-- | components/metrics.gypi | 7 | ||||
-rw-r--r-- | components/metrics/BUILD.gn | 8 | ||||
-rw-r--r-- | components/metrics/drive_metrics_provider.cc | 97 | ||||
-rw-r--r-- | components/metrics/drive_metrics_provider.h | 100 | ||||
-rw-r--r-- | components/metrics/drive_metrics_provider_android.cc | 16 | ||||
-rw-r--r-- | components/metrics/drive_metrics_provider_ios.mm | 16 | ||||
-rw-r--r-- | components/metrics/drive_metrics_provider_linux.cc | 64 | ||||
-rw-r--r-- | components/metrics/drive_metrics_provider_mac.mm | 76 | ||||
-rw-r--r-- | components/metrics/drive_metrics_provider_unittest.cc | 20 | ||||
-rw-r--r-- | components/metrics/drive_metrics_provider_win.cc | 90 |
11 files changed, 495 insertions, 0 deletions
diff --git a/components/components_tests.gyp b/components/components_tests.gyp index 4b4b2dd..75943fb 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -306,6 +306,7 @@ 'metrics/call_stack_profile_metrics_provider_unittest.cc', 'metrics/compression_utils_unittest.cc', 'metrics/daily_event_unittest.cc', + 'metrics/drive_metrics_provider_unittest.cc', 'metrics/gpu/gpu_metrics_provider_unittest.cc', 'metrics/histogram_encoder_unittest.cc', 'metrics/machine_id_provider_win_unittest.cc', diff --git a/components/metrics.gypi b/components/metrics.gypi index e2e1c49..d6bdbdc 100644 --- a/components/metrics.gypi +++ b/components/metrics.gypi @@ -35,6 +35,13 @@ 'metrics/compression_utils.h', 'metrics/daily_event.cc', 'metrics/daily_event.h', + 'metrics/drive_metrics_provider.cc', + 'metrics/drive_metrics_provider.h', + 'metrics/drive_metrics_provider_android.cc', + 'metrics/drive_metrics_provider_ios.mm', + 'metrics/drive_metrics_provider_linux.cc', + 'metrics/drive_metrics_provider_mac.mm', + 'metrics/drive_metrics_provider_win.cc', 'metrics/histogram_encoder.cc', 'metrics/histogram_encoder.h', 'metrics/machine_id_provider.h', diff --git a/components/metrics/BUILD.gn b/components/metrics/BUILD.gn index 90e1c6a..58e88bf 100644 --- a/components/metrics/BUILD.gn +++ b/components/metrics/BUILD.gn @@ -17,6 +17,13 @@ source_set("metrics") { "compression_utils.h", "daily_event.cc", "daily_event.h", + "drive_metrics_provider.cc", + "drive_metrics_provider.h", + "drive_metrics_provider_android.cc", + "drive_metrics_provider_ios.mm", + "drive_metrics_provider_linux.cc", + "drive_metrics_provider_mac.mm", + "drive_metrics_provider_win.cc", "histogram_encoder.cc", "histogram_encoder.h", "machine_id_provider.h", @@ -172,6 +179,7 @@ source_set("unit_tests") { sources = [ "compression_utils_unittest.cc", "daily_event_unittest.cc", + "drive_metrics_provider_unittest.cc", "histogram_encoder_unittest.cc", "machine_id_provider_win_unittest.cc", "metrics_hashes_unittest.cc", diff --git a/components/metrics/drive_metrics_provider.cc b/components/metrics/drive_metrics_provider.cc new file mode 100644 index 0000000..2df320d --- /dev/null +++ b/components/metrics/drive_metrics_provider.cc @@ -0,0 +1,97 @@ +// 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 "components/metrics/drive_metrics_provider.h" + +#include "base/base_paths.h" +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/metrics/histogram_macros.h" +#include "base/path_service.h" +#include "base/task_runner_util.h" +#include "base/time/time.h" + +namespace metrics { + +DriveMetricsProvider::DriveMetricsProvider( + scoped_refptr<base::SequencedTaskRunner> file_thread, + int local_state_path_key) + : file_thread_(file_thread), + local_state_path_key_(local_state_path_key), + weak_ptr_factory_(this) {} + +DriveMetricsProvider::~DriveMetricsProvider() {} + +void DriveMetricsProvider::ProvideSystemProfileMetrics( + metrics::SystemProfileProto* system_profile_proto) { + auto* hardware = system_profile_proto->mutable_hardware(); + FillDriveMetrics(metrics_.app_drive, hardware->mutable_app_drive()); + FillDriveMetrics(metrics_.user_data_drive, + hardware->mutable_user_data_drive()); +} + +void DriveMetricsProvider::GetDriveMetrics(const base::Closure& done) { + got_metrics_callback_ = done; + + base::PostTaskAndReplyWithResult( + file_thread_.get(), FROM_HERE, + base::Bind(&DriveMetricsProvider::GetDriveMetricsOnFileThread, + local_state_path_key_), + base::Bind(&DriveMetricsProvider::GotDriveMetrics, + weak_ptr_factory_.GetWeakPtr())); +} + +DriveMetricsProvider::SeekPenaltyResponse::SeekPenaltyResponse() + : success(false) {} + +// static +DriveMetricsProvider::DriveMetrics +DriveMetricsProvider::GetDriveMetricsOnFileThread(int local_state_path_key) { + DriveMetricsProvider::DriveMetrics metrics; + QuerySeekPenalty(base::FILE_EXE, &metrics.app_drive); + QuerySeekPenalty(local_state_path_key, &metrics.user_data_drive); + return metrics; +} + +// static +void DriveMetricsProvider::QuerySeekPenalty( + int path_service_key, + DriveMetricsProvider::SeekPenaltyResponse* response) { + DCHECK(response); + + base::FilePath path; + if (!PathService::Get(path_service_key, &path)) + return; + + base::TimeTicks start = base::TimeTicks::Now(); + + response->success = HasSeekPenalty(path, &response->has_seek_penalty); + + UMA_HISTOGRAM_TIMES("Hardware.Drive.HasSeekPenalty_Time", + base::TimeTicks::Now() - start); + UMA_HISTOGRAM_BOOLEAN("Hardware.Drive.HasSeekPenalty_Success", + response->success); + if (response->success) { + UMA_HISTOGRAM_BOOLEAN("Hardware.Drive.HasSeekPenalty", + response->has_seek_penalty); + } +} + +void DriveMetricsProvider::GotDriveMetrics( + const DriveMetricsProvider::DriveMetrics& metrics) { + DCHECK(thread_checker_.CalledOnValidThread()); + metrics_ = metrics; + got_metrics_callback_.Run(); +} + +void DriveMetricsProvider::FillDriveMetrics( + const DriveMetricsProvider::SeekPenaltyResponse& response, + metrics::SystemProfileProto::Hardware::Drive* drive) { + if (response.success) + drive->set_has_seek_penalty(response.has_seek_penalty); +} + +} // namespace metrics diff --git a/components/metrics/drive_metrics_provider.h b/components/metrics/drive_metrics_provider.h new file mode 100644 index 0000000..f936d98 --- /dev/null +++ b/components/metrics/drive_metrics_provider.h @@ -0,0 +1,100 @@ +// 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 COMPONENTS_METRICS_DRIVE_METRICS_PROVIDER_H_ +#define COMPONENTS_METRICS_DRIVE_METRICS_PROVIDER_H_ + +#include "base/callback.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/sequenced_task_runner.h" +#include "base/threading/thread_checker.h" +#include "components/metrics/metrics_provider.h" +#include "components/metrics/proto/system_profile.pb.h" + +namespace base { +class FilePath; +} + +namespace metrics { + +// Provides metrics about the local drives on a user's computer. Currently only +// checks to see if they incur a seek-time penalty (e.g. if they're SSDs). +// +// Defers gathering metrics until after "rush hour" (startup) so as to not bog +// down the file thread. +class DriveMetricsProvider : public metrics::MetricsProvider { + public: + DriveMetricsProvider(scoped_refptr<base::SequencedTaskRunner> file_thread, + int local_state_path_key); + ~DriveMetricsProvider() override; + + // metrics::MetricsDataProvider: + void ProvideSystemProfileMetrics( + metrics::SystemProfileProto* system_profile_proto) override; + + // Called to start gathering metrics. + void GetDriveMetrics(const base::Closure& done); + + private: + FRIEND_TEST_ALL_PREFIXES(DriveMetricsProviderTest, HasSeekPenalty); + + // A response to querying a drive as to whether it incurs a seek penalty. + // |has_seek_penalty| is set if |success| is true. + struct SeekPenaltyResponse { + SeekPenaltyResponse(); + bool success; + bool has_seek_penalty; + }; + + struct DriveMetrics { + SeekPenaltyResponse app_drive; + SeekPenaltyResponse user_data_drive; + }; + + // Determine whether the device that services |path| has a seek penalty. + // Returns false if it couldn't be determined (e.g., |path| doesn't exist). + static bool HasSeekPenalty(const base::FilePath& path, + bool* has_seek_penalty); + + // Gather metrics about various drives on |file_thread_|. + static DriveMetrics GetDriveMetricsOnFileThread(int local_state_path_key); + + // Tries to determine whether there is a penalty for seeking on the drive that + // hosts |path_service_key| (for example: the drive that holds "Local State"). + static void QuerySeekPenalty(int path_service_key, + SeekPenaltyResponse* response); + + // Called when metrics are done being gathered from the FILE thread. + void GotDriveMetrics(const DriveMetrics& metrics); + + // Fills |drive| with information from successful |response|s. + void FillDriveMetrics(const SeekPenaltyResponse& response, + metrics::SystemProfileProto::Hardware::Drive* drive); + + // The thread on which file operations are performed (supplied by the + // embedder). + scoped_refptr<base::SequencedTaskRunner> file_thread_; + + // The key to give to base::PathService to obtain the path to local state + // (supplied by the embedder). + int local_state_path_key_; + + // Information gathered about various important drives. + DriveMetrics metrics_; + + // Called when metrics are done being collected. + base::Closure got_metrics_callback_; + + base::ThreadChecker thread_checker_; + base::WeakPtrFactory<DriveMetricsProvider> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(DriveMetricsProvider); +}; + +} // namespace metrics + +#endif // COMPONENTS_METRICS_DRIVE_METRICS_PROVIDER_H_ diff --git a/components/metrics/drive_metrics_provider_android.cc b/components/metrics/drive_metrics_provider_android.cc new file mode 100644 index 0000000..a653dd6 --- /dev/null +++ b/components/metrics/drive_metrics_provider_android.cc @@ -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 "components/metrics/drive_metrics_provider.h" + +namespace metrics { + +// static +bool DriveMetricsProvider::HasSeekPenalty(const base::FilePath& path, + bool* has_seek_penalty) { + *has_seek_penalty = false; + return true; +} + +} // namespace metrics diff --git a/components/metrics/drive_metrics_provider_ios.mm b/components/metrics/drive_metrics_provider_ios.mm new file mode 100644 index 0000000..a653dd6 --- /dev/null +++ b/components/metrics/drive_metrics_provider_ios.mm @@ -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 "components/metrics/drive_metrics_provider.h" + +namespace metrics { + +// static +bool DriveMetricsProvider::HasSeekPenalty(const base::FilePath& path, + bool* has_seek_penalty) { + *has_seek_penalty = false; + return true; +} + +} // namespace metrics diff --git a/components/metrics/drive_metrics_provider_linux.cc b/components/metrics/drive_metrics_provider_linux.cc new file mode 100644 index 0000000..4655bf9 --- /dev/null +++ b/components/metrics/drive_metrics_provider_linux.cc @@ -0,0 +1,64 @@ +// 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 "components/metrics/drive_metrics_provider.h" + +#include <linux/kdev_t.h> // For MAJOR()/MINOR(). +#include <sys/stat.h> +#include <string> + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/strings/stringprintf.h" + +#if defined(OS_CHROMEOS) +#include "base/sys_info.h" +#endif + +namespace metrics { + +namespace { + +// See http://www.kernel.org/doc/Documentation/devices.txt for more info. +const int kFirstScsiMajorNumber = 8; +const int kPartitionsPerScsiDevice = 16; +const char kRotationalFormat[] = "/sys/block/sd%c/queue/rotational"; + +} // namespace + +// static +bool DriveMetricsProvider::HasSeekPenalty(const base::FilePath& path, + bool* has_seek_penalty) { +#if defined(OS_CHROMEOS) + std::string board = base::SysInfo::GetLsbReleaseBoard(); + if (board != "unknown" && board != "parrot") { + // All ChromeOS devices have SSDs. Except some parrots. + *has_seek_penalty = false; + return true; + } +#endif + + base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ); + if (!file.IsValid()) + return false; + + struct stat path_stat; + int error = fstat(file.GetPlatformFile(), &path_stat); + if (error < 0 || MAJOR(path_stat.st_dev) != kFirstScsiMajorNumber) { + // TODO(dbeam): support more SCSI major numbers (e.g. /dev/sdq+) and LVM? + return false; + } + + char sdX = 'a' + MINOR(path_stat.st_dev) / kPartitionsPerScsiDevice; + std::string rotational_path = base::StringPrintf(kRotationalFormat, sdX); + std::string rotates; + if (!base::ReadFileToString(base::FilePath(rotational_path), &rotates)) + return false; + + *has_seek_penalty = rotates.substr(0, 1) == "1"; + return true; +} + +} // namespace metrics diff --git a/components/metrics/drive_metrics_provider_mac.mm b/components/metrics/drive_metrics_provider_mac.mm new file mode 100644 index 0000000..a6a3761 --- /dev/null +++ b/components/metrics/drive_metrics_provider_mac.mm @@ -0,0 +1,76 @@ +// 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 "components/metrics/drive_metrics_provider.h" + +#include <CoreFoundation/CoreFoundation.h> +#include <DiskArbitration/DiskArbitration.h> +#include <Foundation/Foundation.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/storage/IOStorageDeviceCharacteristics.h> +#include <stdlib.h> +#include <sys/stat.h> + +#include "base/files/file_path.h" +#include "base/mac/foundation_util.h" +#include "base/mac/mac_util.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/mac/scoped_ioobject.h" + +namespace metrics { + +// static +bool DriveMetricsProvider::HasSeekPenalty(const base::FilePath& path, + bool* has_seek_penalty) { + struct stat path_stat; + if (stat(path.value().c_str(), &path_stat) < 0) + return false; + + const char* dev_name = devname(path_stat.st_dev, S_IFBLK); + if (!dev_name) + return false; + + std::string bsd_name("/dev/"); + bsd_name.append(dev_name); + + base::ScopedCFTypeRef<DASessionRef> session( + DASessionCreate(kCFAllocatorDefault)); + if (!session) + return false; + + base::ScopedCFTypeRef<DADiskRef> disk( + DADiskCreateFromBSDName(kCFAllocatorDefault, session, bsd_name.c_str())); + if (!disk) + return false; + + base::mac::ScopedIOObject<io_object_t> io_media(DADiskCopyIOMedia(disk)); + base::ScopedCFTypeRef<CFDictionaryRef> characteristics( + static_cast<CFDictionaryRef>(IORegistryEntrySearchCFProperty( + io_media, kIOServicePlane, CFSTR(kIOPropertyDeviceCharacteristicsKey), + kCFAllocatorDefault, + kIORegistryIterateRecursively | kIORegistryIterateParents))); + if (!characteristics) + return false; + + CFStringRef type_ref = base::mac::GetValueFromDictionary<CFStringRef>( + characteristics, CFSTR(kIOPropertyMediumTypeKey)); + if (!type_ref) + return false; + + NSString* type = base::mac::CFToNSCast(type_ref); + if ([type isEqualToString:@kIOPropertyMediumTypeRotationalKey]) { + *has_seek_penalty = true; + return true; + } else if ([type isEqualToString:@kIOPropertyMediumTypeSolidStateKey]) { + *has_seek_penalty = false; + return true; + } + + // TODO(dbeam): should I look for these Rotational/Solid State keys in + // |characteristics|? What if I find device characteristic but there's no + // type? Assume rotational? + return false; +} + +} // namespace metrics diff --git a/components/metrics/drive_metrics_provider_unittest.cc b/components/metrics/drive_metrics_provider_unittest.cc new file mode 100644 index 0000000..142faf7 --- /dev/null +++ b/components/metrics/drive_metrics_provider_unittest.cc @@ -0,0 +1,20 @@ +// 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 "components/metrics/drive_metrics_provider.h" + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace metrics { + +TEST(DriveMetricsProviderTest, HasSeekPenalty) { + base::FilePath tmp_path; + ASSERT_TRUE(base::GetTempDir(&tmp_path)); + bool unused; + DriveMetricsProvider::HasSeekPenalty(tmp_path, &unused); +} + +} // namespace metrics diff --git a/components/metrics/drive_metrics_provider_win.cc b/components/metrics/drive_metrics_provider_win.cc new file mode 100644 index 0000000..3e190d5 --- /dev/null +++ b/components/metrics/drive_metrics_provider_win.cc @@ -0,0 +1,90 @@ +// 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 "components/metrics/drive_metrics_provider.h" + +#include <windows.h> +#include <ntddscsi.h> +#include <winioctl.h> +#include <vector> + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/strings/stringprintf.h" +#include "base/win/windows_version.h" + +namespace metrics { + +namespace { + +// Semi-copy of similarly named struct from ata.h in WinDDK. +struct IDENTIFY_DEVICE_DATA { + USHORT UnusedWords[217]; + USHORT NominalMediaRotationRate; + USHORT MoreUnusedWords[38]; +}; +COMPILE_ASSERT(sizeof(IDENTIFY_DEVICE_DATA) == 512, IdentifyDeviceDataSize); + +struct AtaRequest { + ATA_PASS_THROUGH_EX query; + IDENTIFY_DEVICE_DATA result; +}; + +} // namespace + +// static +bool DriveMetricsProvider::HasSeekPenalty(const base::FilePath& path, + bool* has_seek_penalty) { + std::vector<base::FilePath::StringType> components; + path.GetComponents(&components); + + int flags = base::File::FLAG_OPEN; + bool win7_or_higher = base::win::GetVersion() >= base::win::VERSION_WIN7; + if (!win7_or_higher) + flags |= base::File::FLAG_READ | base::File::FLAG_WRITE; + + base::File volume(base::FilePath(L"\\\\.\\" + components[0]), flags); + if (!volume.IsValid()) + return false; + + if (win7_or_higher) { + STORAGE_PROPERTY_QUERY query = {}; + query.QueryType = PropertyStandardQuery; + query.PropertyId = StorageDeviceSeekPenaltyProperty; + + DEVICE_SEEK_PENALTY_DESCRIPTOR result; + DWORD bytes_returned; + BOOL success = DeviceIoControl( + volume.GetPlatformFile(), IOCTL_STORAGE_QUERY_PROPERTY, &query, + sizeof(query), &result, sizeof(result), &bytes_returned, NULL); + if (success == FALSE || bytes_returned < sizeof(result)) + return false; + + *has_seek_penalty = result.IncursSeekPenalty != FALSE; + } else { + AtaRequest request = {}; + request.query.AtaFlags = ATA_FLAGS_DATA_IN; + request.query.CurrentTaskFile[6] = ID_CMD; + request.query.DataBufferOffset = sizeof(request.query); + request.query.DataTransferLength = sizeof(request.result); + request.query.Length = sizeof(request.query); + request.query.TimeOutValue = 10; + + DWORD bytes_returned; + BOOL success = DeviceIoControl( + volume.GetPlatformFile(), IOCTL_ATA_PASS_THROUGH, &request, + sizeof(request), &request, sizeof(request), &bytes_returned, NULL); + if (success == FALSE || bytes_returned < sizeof(request) || + request.query.CurrentTaskFile[0]) { + return false; + } + + *has_seek_penalty = request.result.NominalMediaRotationRate != 1; + } + + return true; +} + +} // namespace metrics |