Repository: randombit/botan
Pull request #2251 (2020-01-29 15:19:02)

Add a util to detect speed of the machines CPU counter


Patch:
diff --git a/src/cli/utils.cpp b/src/cli/utils.cpp
index d55a81e7c..c6d013029 100644
--- a/src/cli/utils.cpp
+++ b/src/cli/utils.cpp
@@ -10,6 +10,7 @@
 #include <botan/version.h>
 #include <botan/cpuid.h>
 #include <botan/internal/stl_util.h>
+#include <botan/internal/os_utils.h>
 #include <sstream>
 #include <iomanip>
 
@@ -253,6 +254,81 @@ class Print_Cpuid final : public Command
 
 BOTAN_REGISTER_COMMAND("cpuid", Print_Cpuid);
 
+class Cycle_Counter final : public Command
+   {
+   public:
+      Cycle_Counter() : Command("cpu_clock --test-duration=500") {}
+
+      std::string group() const override
+         {
+         return "info";
+         }
+
+      std::string description() const override
+         {
+         return "Estimate the speed of the CPU cycle counter";
+         }
+
+      void go() override
+         {
+         if(Botan::OS::get_cpu_cycle_counter() == 0)
+            {
+            output() << "No CPU cycle counter on this machine\n";
+            return;
+            }
+
+         const uint64_t test_duration_ns = get_arg_sz("test-duration") * 1000000;
+
+         if(test_duration_ns == 0)
+            {
+            output() << "Invalid test duration\n";
+            return;
+            }
+
+         const uint64_t cc_start = Botan::OS::get_cpu_cycle_counter();
+         const uint64_t ns_start = Botan::OS::get_system_timestamp_ns();
+
+         uint64_t cc_end = 0;
+         uint64_t ns_end = ns_start;
+
+         while((ns_end - ns_start) < test_duration_ns)
+            {
+            ns_end = Botan::OS::get_system_timestamp_ns();
+            cc_end = Botan::OS::get_cpu_cycle_counter();
+            }
+
+         if(cc_end <= cc_start)
+            {
+            output() << "Cycle counter seems to have wrapped, try again\n";
+            return;
+            }
+
+         if(ns_end <= ns_start)
+            {
+            output() << "System clock seems to have wrapped (?!?)\n";
+            return;
+            }
+
+         const uint64_t ns_duration = ns_end - ns_start;
+         const uint64_t cc_duration = cc_end - cc_start;
+
+         const double ratio = static_cast<double>(cc_duration) / ns_duration;
+
+         if(ratio >= 1.0)
+            {
+            // GHz
+            output() << "Estimated CPU clock " << std::setprecision(2) << ratio << " GHz\n";
+            }
+         else
+            {
+            // MHz
+            output() << "Estimated CPU clock " << static_cast<size_t>(ratio * 1000) << " MHz\n";
+            }
+         }
+   };
+
+BOTAN_REGISTER_COMMAND("cpu_clock", Cycle_Counter);
+
 #if defined(BOTAN_HAS_UUID)
 
 class Print_UUID final : public Command


Test patch:
diff --git a/src/scripts/test_cli.py b/src/scripts/test_cli.py
index fd9058f54..3b986dad6 100755
--- a/src/scripts/test_cli.py
+++ b/src/scripts/test_cli.py
@@ -151,6 +151,19 @@ def cli_gen_prime_tests(_tmp_dir):
     test_cli("gen_prime", "64", "15568813029901363163")
     test_cli("gen_prime", "128", "287193909494025008847286845478788766073")
 
+def cli_cycle_counter(_tmp_dir):
+    output = test_cli("cpu_clock", None, None)
+
+    if output.startswith('No CPU cycle counter on this machine'):
+        return
+
+    have_clock_re = re.compile(r'Estimated CPU clock [0-9\.]+ (M|G)Hz')
+
+    if have_clock_re.match(output):
+        return
+
+    logging.error('Unexpected output from cpu_clock: %s', output)
+
 def cli_entropy_tests(_tmp_dir):
     output = test_cli("entropy", ["all"], None)
 
@@ -1279,6 +1292,7 @@ def main(args=None):
         cli_base64_tests,
         cli_bcrypt_tests,
         cli_cc_enc_tests,
+        cli_cycle_counter,
         cli_cert_issuance_tests,
         cli_compress_tests,
         cli_config_tests,