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