1/*
2 * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25#include "precompiled.hpp"
26#include "rdtsc_x86.hpp"
27#include "runtime/thread.inline.hpp"
28#include "vm_version_ext_x86.hpp"
29
30// The following header contains the implementations of rdtsc()
31#include OS_CPU_HEADER_INLINE(os)
32
33static jlong _epoch = 0;
34static bool rdtsc_elapsed_counter_enabled = false;
35static jlong tsc_frequency = 0;
36
37static jlong set_epoch() {
38 assert(0 == _epoch, "invariant");
39 _epoch = os::rdtsc();
40 return _epoch;
41}
42
43// Base loop to estimate ticks frequency for tsc counter from user mode.
44// Volatiles and sleep() are used to prevent compiler from applying optimizations.
45static void do_time_measurements(volatile jlong& time_base,
46 volatile jlong& time_fast,
47 volatile jlong& time_base_elapsed,
48 volatile jlong& time_fast_elapsed) {
49 static const unsigned int FT_SLEEP_MILLISECS = 1;
50 const unsigned int loopcount = 3;
51
52 volatile jlong start = 0;
53 volatile jlong fstart = 0;
54 volatile jlong end = 0;
55 volatile jlong fend = 0;
56
57 // Figure out the difference between rdtsc and os provided timer.
58 // base algorithm adopted from JRockit.
59 for (unsigned int times = 0; times < loopcount; times++) {
60 start = os::elapsed_counter();
61 OrderAccess::fence();
62 fstart = os::rdtsc();
63
64 // use sleep to prevent compiler from optimizing
65 os::sleep(Thread::current(), FT_SLEEP_MILLISECS, true);
66
67 end = os::elapsed_counter();
68 OrderAccess::fence();
69 fend = os::rdtsc();
70
71 time_base += end - start;
72 time_fast += fend - fstart;
73
74 // basis for calculating the os tick start
75 // to fast time tick start offset
76 time_base_elapsed += end;
77 time_fast_elapsed += (fend - _epoch);
78 }
79
80 time_base /= loopcount;
81 time_fast /= loopcount;
82 time_base_elapsed /= loopcount;
83 time_fast_elapsed /= loopcount;
84}
85
86static jlong initialize_frequency() {
87 assert(0 == tsc_frequency, "invariant");
88 assert(0 == _epoch, "invariant");
89 const jlong initial_counter = set_epoch();
90 if (initial_counter == 0) {
91 return 0;
92 }
93 // os time frequency
94 static double os_freq = (double)os::elapsed_frequency();
95 assert(os_freq > 0, "os_elapsed frequency corruption!");
96
97 double tsc_freq = .0;
98 double os_to_tsc_conv_factor = 1.0;
99
100 // if platform supports invariant tsc,
101 // apply higher resolution and granularity for conversion calculations
102 if (VM_Version_Ext::supports_tscinv_ext()) {
103 // for invariant tsc platforms, take the maximum qualified cpu frequency
104 tsc_freq = (double)VM_Version_Ext::maximum_qualified_cpu_frequency();
105 os_to_tsc_conv_factor = tsc_freq / os_freq;
106 } else {
107 // use measurements to estimate
108 // a conversion factor and the tsc frequency
109
110 volatile jlong time_base = 0;
111 volatile jlong time_fast = 0;
112 volatile jlong time_base_elapsed = 0;
113 volatile jlong time_fast_elapsed = 0;
114
115 // do measurements to get base data
116 // on os timer and fast ticks tsc time relation.
117 do_time_measurements(time_base, time_fast, time_base_elapsed, time_fast_elapsed);
118
119 // if invalid measurements, cannot proceed
120 if (time_fast == 0 || time_base == 0) {
121 return 0;
122 }
123
124 os_to_tsc_conv_factor = (double)time_fast / (double)time_base;
125 if (os_to_tsc_conv_factor > 1) {
126 // estimate on tsc counter frequency
127 tsc_freq = os_to_tsc_conv_factor * os_freq;
128 }
129 }
130
131 if ((tsc_freq < 0) || (tsc_freq > 0 && tsc_freq <= os_freq) || (os_to_tsc_conv_factor <= 1)) {
132 // safer to run with normal os time
133 tsc_freq = .0;
134 }
135
136 // frequency of the tsc_counter
137 return (jlong)tsc_freq;
138}
139
140static bool initialize_elapsed_counter() {
141 tsc_frequency = initialize_frequency();
142 return tsc_frequency != 0 && _epoch != 0;
143}
144
145static bool ergonomics() {
146 const bool invtsc_support = Rdtsc::is_supported();
147 if (FLAG_IS_DEFAULT(UseFastUnorderedTimeStamps) && invtsc_support) {
148 FLAG_SET_ERGO(UseFastUnorderedTimeStamps, true);
149 }
150
151 bool ft_enabled = UseFastUnorderedTimeStamps && invtsc_support;
152
153 if (!ft_enabled) {
154 if (UseFastUnorderedTimeStamps && VM_Version::supports_tsc()) {
155 warning("\nThe hardware does not support invariant tsc (INVTSC) register and/or cannot guarantee tsc synchronization between sockets at startup.\n"\
156 "Values returned via rdtsc() are not guaranteed to be accurate, esp. when comparing values from cross sockets reads. Enabling UseFastUnorderedTimeStamps on non-invariant tsc hardware should be considered experimental.\n");
157 ft_enabled = true;
158 }
159 }
160
161 if (!ft_enabled) {
162 // Warn if unable to support command-line flag
163 if (UseFastUnorderedTimeStamps && !VM_Version::supports_tsc()) {
164 warning("Ignoring UseFastUnorderedTimeStamps, hardware does not support normal tsc");
165 }
166 }
167
168 return ft_enabled;
169}
170
171bool Rdtsc::is_supported() {
172 return VM_Version_Ext::supports_tscinv_ext();
173}
174
175bool Rdtsc::is_elapsed_counter_enabled() {
176 return rdtsc_elapsed_counter_enabled;
177}
178
179jlong Rdtsc::frequency() {
180 return tsc_frequency;
181}
182
183jlong Rdtsc::elapsed_counter() {
184 return os::rdtsc() - _epoch;
185}
186
187jlong Rdtsc::epoch() {
188 return _epoch;
189}
190
191jlong Rdtsc::raw() {
192 return os::rdtsc();
193}
194
195bool Rdtsc::initialize() {
196 static bool initialized = false;
197 if (!initialized) {
198 assert(!rdtsc_elapsed_counter_enabled, "invariant");
199 VM_Version_Ext::initialize();
200 assert(0 == tsc_frequency, "invariant");
201 assert(0 == _epoch, "invariant");
202 bool result = initialize_elapsed_counter(); // init hw
203 if (result) {
204 result = ergonomics(); // check logical state
205 }
206 rdtsc_elapsed_counter_enabled = result;
207 initialized = true;
208 }
209 return rdtsc_elapsed_counter_enabled;
210}
211