[perftest] Create Timer benchmark

Create a benchmark to test timers with varying degrees of
slack.

ZX-3083

Test: /system/test/sys/perf-test -p --filter Timer
Change-Id: I5e6f2bc0273f15425672005c01cf9c28c2ef0e78
diff --git a/system/utest/perftest/rules.mk b/system/utest/perftest/rules.mk
index 8799e6d..47cea7d 100644
--- a/system/utest/perftest/rules.mk
+++ b/system/utest/perftest/rules.mk
@@ -20,6 +20,7 @@
     $(LOCAL_DIR)/runner-test.cpp \
     $(LOCAL_DIR)/sleep-test.cpp \
     $(LOCAL_DIR)/syscalls-test.cpp \
+    $(LOCAL_DIR)/timer-test.cpp \
 
 MODULE_NAME := perf-test
 
diff --git a/system/utest/perftest/timer-test.cpp b/system/utest/perftest/timer-test.cpp
new file mode 100644
index 0000000..44b3c99
--- /dev/null
+++ b/system/utest/perftest/timer-test.cpp
@@ -0,0 +1,73 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include <fbl/string_printf.h>
+#include <lib/zx/timer.h>
+#include <perftest/perftest.h>
+
+namespace {
+
+struct TimerState {
+    zx::duration wait_time;
+    zx::duration slack_time;
+};
+
+const char* SlackTypeToString(uint32_t slack_type) {
+    switch (slack_type) {
+    case ZX_TIMER_SLACK_LATE:
+        return "SlackLate";
+    case ZX_TIMER_SLACK_EARLY:
+        return "SlackEarly";
+    case ZX_TIMER_SLACK_CENTER:
+        return "SlackCenter";
+    default:
+        ZX_ASSERT_MSG(true, "Slack type unsupported\n");
+        return nullptr;
+    }
+}
+
+// Measures how long a timer takes to fire based on the wait time, slack time,
+// and slack type. This can be useful for measuring the overhead of sleeping.
+// It can also be used to measure the variation in actual sleep times.
+bool TimerWaitTest(perftest::RepeatState* state, TimerState timer_state, uint32_t slack_type) {
+    zx_status_t status;
+    zx::timer timer;
+
+    status = zx::timer::create(slack_type, ZX_CLOCK_MONOTONIC, &timer);
+    ZX_ASSERT(status == ZX_OK);
+
+    while (state->KeepRunning()) {
+        status = timer.set(zx::deadline_after(timer_state.wait_time),
+                           timer_state.slack_time);
+        ZX_ASSERT(status == ZX_OK);
+        zx_status_t status = timer.wait_one(
+            ZX_TIMER_SIGNALED, zx::time::infinite(), nullptr);
+        ZX_ASSERT(status == ZX_OK);
+    }
+
+    return true;
+}
+
+void RegisterTests() {
+    const TimerState timers[] = {
+        TimerState{zx::msec(1), zx::usec(0)},
+        TimerState{zx::msec(1), zx::usec(500)}};
+    const uint32_t slack_types[] = {ZX_TIMER_SLACK_LATE,
+                                    ZX_TIMER_SLACK_EARLY, ZX_TIMER_SLACK_CENTER};
+
+    for (auto timer : timers) {
+        for (auto slack_type : slack_types) {
+            auto name = fbl::StringPrintf("Timer/%lumsWait/%s%luus",
+                                          timer.wait_time.to_msecs(),
+                                          SlackTypeToString(slack_type),
+                                          timer.slack_time.to_usecs());
+            perftest::RegisterTest(name.c_str(), TimerWaitTest, timer, slack_type);
+        }
+    }
+}
+PERFTEST_CTOR(RegisterTests);
+
+} // namespace