summaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorblundell <blundell@chromium.org>2015-07-30 13:18:30 -0700
committerCommit bot <commit-bot@chromium.org>2015-07-30 20:19:42 +0000
commitb5c6b5a18863e2e9094b5bb3328edab690fa7111 (patch)
tree0c36eec1009b7a9ab601ebcf91c4590058f4e775 /components
parent34960e7a547930177cc05020921b7ee3eec4fd39 (diff)
downloadchromium_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.gyp1
-rw-r--r--components/metrics.gypi7
-rw-r--r--components/metrics/BUILD.gn8
-rw-r--r--components/metrics/drive_metrics_provider.cc97
-rw-r--r--components/metrics/drive_metrics_provider.h100
-rw-r--r--components/metrics/drive_metrics_provider_android.cc16
-rw-r--r--components/metrics/drive_metrics_provider_ios.mm16
-rw-r--r--components/metrics/drive_metrics_provider_linux.cc64
-rw-r--r--components/metrics/drive_metrics_provider_mac.mm76
-rw-r--r--components/metrics/drive_metrics_provider_unittest.cc20
-rw-r--r--components/metrics/drive_metrics_provider_win.cc90
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