1 /*
2 * Copyright (c) 2013, 2020, 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/globals_extension.hpp"
28 #include "runtime/orderAccess.hpp"
29 #include "runtime/thread.inline.hpp"
30 #include "vm_version_ext_x86.hpp"
31
32 // The following header contains the implementations of rdtsc()
33 #include OS_CPU_HEADER_INLINE(os)
34
35 static jlong _epoch = 0;
36 static bool rdtsc_elapsed_counter_enabled = false;
37 static jlong tsc_frequency = 0;
38
set_epoch()39 static jlong set_epoch() {
40 assert(0 == _epoch, "invariant");
41 _epoch = os::rdtsc();
42 return _epoch;
43 }
44
45 // Base loop to estimate ticks frequency for tsc counter from user mode.
46 // Volatiles and sleep() are used to prevent compiler from applying optimizations.
do_time_measurements(volatile jlong & time_base,volatile jlong & time_fast,volatile jlong & time_base_elapsed,volatile jlong & time_fast_elapsed)47 static void do_time_measurements(volatile jlong& time_base,
48 volatile jlong& time_fast,
49 volatile jlong& time_base_elapsed,
50 volatile jlong& time_fast_elapsed) {
51 static const unsigned int FT_SLEEP_MILLISECS = 1;
52 const unsigned int loopcount = 3;
53
54 volatile jlong start = 0;
55 volatile jlong fstart = 0;
56 volatile jlong end = 0;
57 volatile jlong fend = 0;
58
59 // Figure out the difference between rdtsc and os provided timer.
60 // base algorithm adopted from JRockit.
61 for (unsigned int times = 0; times < loopcount; times++) {
62 start = os::elapsed_counter();
63 OrderAccess::fence();
64 fstart = os::rdtsc();
65
66 // use sleep to prevent compiler from optimizing
67 JavaThread::current()->sleep(FT_SLEEP_MILLISECS);
68
69 end = os::elapsed_counter();
70 OrderAccess::fence();
71 fend = os::rdtsc();
72
73 time_base += end - start;
74 time_fast += fend - fstart;
75
76 // basis for calculating the os tick start
77 // to fast time tick start offset
78 time_base_elapsed += end;
79 time_fast_elapsed += (fend - _epoch);
80 }
81
82 time_base /= loopcount;
83 time_fast /= loopcount;
84 time_base_elapsed /= loopcount;
85 time_fast_elapsed /= loopcount;
86 }
87
initialize_frequency()88 static jlong initialize_frequency() {
89 assert(0 == tsc_frequency, "invariant");
90 assert(0 == _epoch, "invariant");
91 const jlong initial_counter = set_epoch();
92 if (initial_counter == 0) {
93 return 0;
94 }
95 // os time frequency
96 static double os_freq = (double)os::elapsed_frequency();
97 assert(os_freq > 0, "os_elapsed frequency corruption!");
98
99 double tsc_freq = .0;
100 double os_to_tsc_conv_factor = 1.0;
101
102 // if platform supports invariant tsc,
103 // apply higher resolution and granularity for conversion calculations
104 if (VM_Version_Ext::supports_tscinv_ext()) {
105 // for invariant tsc platforms, take the maximum qualified cpu frequency
106 tsc_freq = (double)VM_Version_Ext::maximum_qualified_cpu_frequency();
107 os_to_tsc_conv_factor = tsc_freq / os_freq;
108 } else {
109 // use measurements to estimate
110 // a conversion factor and the tsc frequency
111
112 volatile jlong time_base = 0;
113 volatile jlong time_fast = 0;
114 volatile jlong time_base_elapsed = 0;
115 volatile jlong time_fast_elapsed = 0;
116
117 // do measurements to get base data
118 // on os timer and fast ticks tsc time relation.
119 do_time_measurements(time_base, time_fast, time_base_elapsed, time_fast_elapsed);
120
121 // if invalid measurements, cannot proceed
122 if (time_fast == 0 || time_base == 0) {
123 return 0;
124 }
125
126 os_to_tsc_conv_factor = (double)time_fast / (double)time_base;
127 if (os_to_tsc_conv_factor > 1) {
128 // estimate on tsc counter frequency
129 tsc_freq = os_to_tsc_conv_factor * os_freq;
130 }
131 }
132
133 if ((tsc_freq < 0) || (tsc_freq > 0 && tsc_freq <= os_freq) || (os_to_tsc_conv_factor <= 1)) {
134 // safer to run with normal os time
135 tsc_freq = .0;
136 }
137
138 // frequency of the tsc_counter
139 return (jlong)tsc_freq;
140 }
141
initialize_elapsed_counter()142 static bool initialize_elapsed_counter() {
143 tsc_frequency = initialize_frequency();
144 return tsc_frequency != 0 && _epoch != 0;
145 }
146
ergonomics()147 static bool ergonomics() {
148 const bool invtsc_support = Rdtsc::is_supported();
149 if (FLAG_IS_DEFAULT(UseFastUnorderedTimeStamps) && invtsc_support) {
150 FLAG_SET_ERGO(UseFastUnorderedTimeStamps, true);
151 }
152
153 bool ft_enabled = UseFastUnorderedTimeStamps && invtsc_support;
154
155 if (!ft_enabled) {
156 if (UseFastUnorderedTimeStamps && VM_Version::supports_tsc()) {
157 warning("\nThe hardware does not support invariant tsc (INVTSC) register and/or cannot guarantee tsc synchronization between sockets at startup.\n"\
158 "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");
159 ft_enabled = true;
160 }
161 }
162
163 if (!ft_enabled) {
164 // Warn if unable to support command-line flag
165 if (UseFastUnorderedTimeStamps && !VM_Version::supports_tsc()) {
166 warning("Ignoring UseFastUnorderedTimeStamps, hardware does not support normal tsc");
167 }
168 }
169
170 return ft_enabled;
171 }
172
is_supported()173 bool Rdtsc::is_supported() {
174 return VM_Version_Ext::supports_tscinv_ext();
175 }
176
is_elapsed_counter_enabled()177 bool Rdtsc::is_elapsed_counter_enabled() {
178 return rdtsc_elapsed_counter_enabled;
179 }
180
frequency()181 jlong Rdtsc::frequency() {
182 return tsc_frequency;
183 }
184
elapsed_counter()185 jlong Rdtsc::elapsed_counter() {
186 return os::rdtsc() - _epoch;
187 }
188
epoch()189 jlong Rdtsc::epoch() {
190 return _epoch;
191 }
192
raw()193 jlong Rdtsc::raw() {
194 return os::rdtsc();
195 }
196
initialize()197 bool Rdtsc::initialize() {
198 static bool initialized = false;
199 if (!initialized) {
200 assert(!rdtsc_elapsed_counter_enabled, "invariant");
201 VM_Version_Ext::initialize();
202 assert(0 == tsc_frequency, "invariant");
203 assert(0 == _epoch, "invariant");
204 bool result = initialize_elapsed_counter(); // init hw
205 if (result) {
206 result = ergonomics(); // check logical state
207 }
208 rdtsc_elapsed_counter_enabled = result;
209 initialized = true;
210 }
211 return rdtsc_elapsed_counter_enabled;
212 }
213