Add WASI compatibility (#2144)

* Detect non-Emscripten WASI builds
* Guard clocks & system APIs

google#1774

Co-authored-by: Roman Lebedev <lebedev.ri@gmail.com>
diff --git a/src/benchmark.cc b/src/benchmark.cc
index acf4d3b..e866d29 100644
--- a/src/benchmark.cc
+++ b/src/benchmark.cc
@@ -22,7 +22,8 @@
 #include "internal_macros.h"
 
 #ifndef BENCHMARK_OS_WINDOWS
-#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
+#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT) && \
+    !defined(BENCHMARK_OS_WASI)
 #include <sys/resource.h>
 #endif
 #include <sys/time.h>
diff --git a/src/benchmark_register.cc b/src/benchmark_register.cc
index 730d275..560a762 100644
--- a/src/benchmark_register.cc
+++ b/src/benchmark_register.cc
@@ -15,7 +15,8 @@
 #include "benchmark_register.h"
 
 #ifndef BENCHMARK_OS_WINDOWS
-#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
+#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT) && \
+    !defined(BENCHMARK_OS_WASI)
 #include <sys/resource.h>
 #endif
 #include <sys/time.h>
diff --git a/src/benchmark_runner.cc b/src/benchmark_runner.cc
index 7efbad4..f6d37e0 100644
--- a/src/benchmark_runner.cc
+++ b/src/benchmark_runner.cc
@@ -23,7 +23,8 @@
 #include "internal_macros.h"
 
 #ifndef BENCHMARK_OS_WINDOWS
-#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
+#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT) && \
+    !defined(BENCHMARK_OS_WASI)
 #include <sys/resource.h>
 #endif
 #include <sys/time.h>
diff --git a/src/cycleclock.h b/src/cycleclock.h
index 2633f22..23d67d7 100644
--- a/src/cycleclock.h
+++ b/src/cycleclock.h
@@ -235,7 +235,7 @@
   struct timeval tv;
   gettimeofday(&tv, nullptr);
   return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
-#elif defined(__hppa__) || defined(__linux__)
+#elif defined(__hppa__) || defined(__linux__) || defined(BENCHMARK_OS_WASI)
   // Fallback for all other architectures with a recent Linux kernel, e.g.:
   // HP PA-RISC provides a user-readable clock counter (cr16), but
   // it's not syncronized across CPUs and only 32-bit wide when programs
diff --git a/src/internal_macros.h b/src/internal_macros.h
index 22e3e21..a0bd0e1 100644
--- a/src/internal_macros.h
+++ b/src/internal_macros.h
@@ -77,6 +77,8 @@
   #define BENCHMARK_OS_NACL 1
 #elif defined(__EMSCRIPTEN__)
   #define BENCHMARK_OS_EMSCRIPTEN 1
+#elif defined(__wasi__)
+  #define BENCHMARK_OS_WASI 1
 #elif defined(__rtems__)
   #define BENCHMARK_OS_RTEMS 1
 #elif defined(__Fuchsia__)
diff --git a/src/sysinfo.cc b/src/sysinfo.cc
index 39da0fe..ca32daa 100644
--- a/src/sysinfo.cc
+++ b/src/sysinfo.cc
@@ -27,7 +27,8 @@
 #include <codecvt>
 #else
 #include <fcntl.h>
-#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
+#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT) && \
+    !defined(BENCHMARK_OS_WASI)
 #include <sys/resource.h>
 #endif
 #include <sys/time.h>
@@ -444,7 +445,8 @@
   return GetCacheSizesWindows();
 #elif defined(BENCHMARK_OS_QNX)
   return GetCacheSizesQNX();
-#elif defined(BENCHMARK_OS_QURT) || defined(__EMSCRIPTEN__)
+#elif defined(BENCHMARK_OS_QURT) || defined(BENCHMARK_OS_EMSCRIPTEN) || \
+    defined(BENCHMARK_OS_WASI)
   return std::vector<CPUInfo::CacheInfo>();
 #else
   return GetCacheSizesFromKVFS();
@@ -477,6 +479,8 @@
     str += std::to_string(arch_version_struct.arch_version);
   }
   return str;
+#elif defined(BENCHMARK_OS_WASI)
+  return std::string("wasi");
 #else
 #ifndef HOST_NAME_MAX
 #ifdef BENCHMARK_HAS_SYSCTL  // BSD/Mac doesn't have HOST_NAME_MAX defined
diff --git a/src/timers.cc b/src/timers.cc
index f8d9560..53cf487 100644
--- a/src/timers.cc
+++ b/src/timers.cc
@@ -23,7 +23,8 @@
 #include <windows.h>
 #else
 #include <fcntl.h>
-#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
+#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT) && \
+    !defined(BENCHMARK_OS_WASI)
 #include <sys/resource.h>
 #endif
 #include <sys/time.h>
@@ -84,7 +85,8 @@
           static_cast<double>(user.QuadPart)) *
          1e-7;
 }
-#elif !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
+#elif !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT) && \
+    !defined(BENCHMARK_OS_WASI)
 double MakeTime(struct rusage const& ru) {
   return (static_cast<double>(ru.ru_utime.tv_sec) +
           static_cast<double>(ru.ru_utime.tv_usec) * 1e-6 +
@@ -140,6 +142,14 @@
   // same as total time, but this is ok because there aren't long-latency
   // synchronous system calls in Emscripten.
   return emscripten_get_now() * 1e-3;
+#elif defined(BENCHMARK_OS_WASI)
+  // WASI lacks CLOCK_PROCESS_CPUTIME_ID and getrusage; use monotonic clock.
+  struct timespec ts {};
+  if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+    return static_cast<double>(ts.tv_sec) +
+           (static_cast<double>(ts.tv_nsec) * 1e-9);
+  }
+  DiagnoseAndExit("clock_gettime(CLOCK_MONOTONIC, ...) failed");
 #elif defined(CLOCK_PROCESS_CPUTIME_ID) && !defined(BENCHMARK_OS_MACOSX)
   // FIXME We want to use clock_gettime, but its not available in MacOS 10.11.
   // See https://github.com/google/benchmark/pull/292
@@ -195,6 +205,9 @@
 #elif defined(BENCHMARK_OS_ZOS)
   // z/OS doesn't support CLOCK_THREAD_CPUTIME_ID.
   return ProcessCPUUsage();
+#elif defined(BENCHMARK_OS_WASI)
+  // WASI doesn't support per-thread CPU timing; fall back to process time.
+  return ProcessCPUUsage();
 #elif defined(BENCHMARK_OS_SOLARIS)
   struct rusage ru;
   if (getrusage(RUSAGE_LWP, &ru) == 0) return MakeTime(ru);