1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/metrics/time_ticks_experiment_win.h"
6
7 #if defined(OS_WIN)
8
9 #include "base/cpu.h"
10 #include "base/metrics/histogram.h"
11 #include "base/win/windows_version.h"
12
13 #include <windows.h>
14
15 namespace chrome {
16
17 namespace {
18
19 const int kNumIterations = 1000;
20
21 } // anonymous namespace
22
CollectTimeTicksStats()23 void CollectTimeTicksStats() {
24 // This bit is supposed to indicate that rdtsc is safe across cores. If so, we
25 // can use QPC as long as it uses rdtsc.
26 // TODO(simonjam): We should look for other signals that QPC might be safe and
27 // test them out here.
28 base::CPU cpu;
29 UMA_HISTOGRAM_BOOLEAN("WinTimeTicks.NonStopTsc",
30 cpu.has_non_stop_time_stamp_counter());
31 if (!cpu.has_non_stop_time_stamp_counter()) {
32 return;
33 }
34
35 DWORD_PTR default_mask;
36 DWORD_PTR system_mask;
37 if (!GetProcessAffinityMask(GetCurrentProcess(),
38 &default_mask, &system_mask)) {
39 return;
40 }
41 if (!default_mask) {
42 return;
43 }
44
45 DWORD_PTR current_mask = 1;
46 bool failed_to_change_cores = false;
47
48 base::win::OSInfo* info = base::win::OSInfo::GetInstance();
49 UMA_HISTOGRAM_ENUMERATION("WinTimeTicks.VersionTotal", info->version(),
50 base::win::VERSION_WIN_LAST);
51
52 LARGE_INTEGER qpc_frequency;
53 QueryPerformanceFrequency(&qpc_frequency);
54
55 int min_delta = 1e9;
56 LARGE_INTEGER qpc_last;
57 QueryPerformanceCounter(&qpc_last);
58 for (int i = 0; i < kNumIterations; ++i) {
59 LARGE_INTEGER qpc_now;
60 QueryPerformanceCounter(&qpc_now);
61 int delta = static_cast<int>(qpc_now.QuadPart - qpc_last.QuadPart);
62 if (delta != 0) {
63 min_delta = std::min(min_delta, delta);
64 }
65 qpc_last = qpc_now;
66
67 // Change cores every 10 iterations.
68 if (i % 10 == 0) {
69 DWORD_PTR old_mask = current_mask;
70 current_mask <<= 1;
71 while ((current_mask & default_mask) == 0) {
72 current_mask <<= 1;
73 if (!current_mask) {
74 current_mask = 1;
75 }
76 if (current_mask == old_mask) {
77 break;
78 }
79 }
80 if (!SetThreadAffinityMask(GetCurrentThread(), current_mask)) {
81 failed_to_change_cores = true;
82 break;
83 }
84 }
85 }
86
87 SetThreadAffinityMask(GetCurrentThread(), default_mask);
88 if (failed_to_change_cores) {
89 UMA_HISTOGRAM_ENUMERATION("WinTimeTicks.FailedToChangeCores",
90 info->version(), base::win::VERSION_WIN_LAST);
91 return;
92 }
93
94 if (min_delta < 0) {
95 UMA_HISTOGRAM_ENUMERATION("WinTimeTicks.TickedBackwards", info->version(),
96 base::win::VERSION_WIN_LAST);
97 return;
98 }
99
100 int min_delta_ns = static_cast<int>(
101 min_delta * (1e9 / qpc_frequency.QuadPart));
102 UMA_HISTOGRAM_CUSTOM_COUNTS("WinTimeTicks.MinResolutionNanoseconds",
103 min_delta_ns, 1, 1000000, 50);
104
105 bool success = min_delta_ns <= 10000;
106 if (success) {
107 UMA_HISTOGRAM_ENUMERATION("WinTimeTicks.VersionSuccessful",
108 info->version(), base::win::VERSION_WIN_LAST);
109 }
110 }
111
112 } // namespace chrome
113
114 #endif // defined(OS_WIN)
115