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 }