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