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