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
15namespace chrome {
16
17namespace {
18
19const int kNumIterations = 1000;
20
21}  // anonymous namespace
22
23void 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