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/thread.inline.hpp" 28 #include "vm_version_ext_x86.hpp" 29 30 // The following header contains the implementations of rdtsc() 31 #ifdef TARGET_OS_ARCH_linux_x86 32 #include "os_linux_x86.inline.hpp" 33 #endif 34 35 static jlong baseline_counter = 0; 36 static bool rdtsc_elapsed_counter_enabled = false; 37 static jlong tsc_frequency = 0; 38 39 static jlong set_baseline_counter() { 40 assert(0 == baseline_counter, "invariant"); 41 baseline_counter = os::rdtsc(); 42 return baseline_counter; 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. 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 os::sleep(Thread::current(), FT_SLEEP_MILLISECS, true); 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 - baseline_counter); 80 } 81 82 time_base /= loopcount; 83 time_fast /= loopcount; 84 time_base_elapsed /= loopcount; 85 time_fast_elapsed /= loopcount; 86 } 87 88 static jlong initialize_frequency() { 89 assert(0 == tsc_frequency, "invariant"); 90 assert(0 == baseline_counter, "invariant"); 91 jlong initial_counter = set_baseline_counter(); 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 // for non-trusted platforms, 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 142 static bool initialize_elapsed_counter() { 143 tsc_frequency = initialize_frequency(); 144 return tsc_frequency != 0 && baseline_counter != 0; 145 } 146 147 static bool ergonomics() { 148 const bool invtsc_support = Rdtsc::is_supported(); 149 if (FLAG_IS_DEFAULT(UseFastUnorderedTimeStamps) && invtsc_support) { 150 FLAG_SET_ERGO(bool, 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 173 bool Rdtsc::is_supported() { 174 return VM_Version_Ext::supports_tscinv_ext(); 175 } 176 177 bool Rdtsc::is_elapsed_counter_enabled() { 178 return rdtsc_elapsed_counter_enabled; 179 } 180 181 jlong Rdtsc::frequency() { 182 return tsc_frequency; 183 } 184 185 jlong Rdtsc::elapsed_counter() { 186 return os::rdtsc() - baseline_counter; 187 } 188 189 jlong Rdtsc::raw() { 190 return os::rdtsc(); 191 } 192 193 bool Rdtsc::initialize() { 194 static bool initialized = false; 195 if (!initialized) { 196 assert(!rdtsc_elapsed_counter_enabled, "invariant"); 197 VM_Version_Ext::initialize(); 198 assert(0 == tsc_frequency, "invariant"); 199 assert(0 == baseline_counter, "invariant"); 200 bool result = initialize_elapsed_counter(); // init hw 201 if (result) { 202 result = ergonomics(); // check logical state 203 } 204 rdtsc_elapsed_counter_enabled = result; 205 initialized = true; 206 } 207 return rdtsc_elapsed_counter_enabled; 208 }