DO NO COMMIT - randhammer test app

Change-Id: Ia3b456f394225feb0ac5f0b71a5e10feae47fa9a
diff --git a/system/uapp/randhammer/randhammer b/system/uapp/randhammer/randhammer
new file mode 100755
index 0000000..fe2befa
--- /dev/null
+++ b/system/uapp/randhammer/randhammer
Binary files differ
diff --git a/system/uapp/randhammer/randhammer-host.cpp b/system/uapp/randhammer/randhammer-host.cpp
new file mode 100644
index 0000000..c7c043b
--- /dev/null
+++ b/system/uapp/randhammer/randhammer-host.cpp
@@ -0,0 +1,123 @@
+// 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 <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#define ZX_CPRNG_DRAW_MAX_LEN 256
+
+const int kDefaultMaxWorkers = 64;
+const size_t kTotalDrawLen = 1UL << 24;
+
+typedef struct {
+    std::mutex mtx;
+    std::condition_variable cnd;
+    bool waved;
+} green_flag_t;
+
+void print_usage(const char* argv0) {
+    printf("usage: %s [-n <num_workers>]\n\n", argv0);
+    printf("Spawn threads to zx_crpng_draw %zu bytes.\n\n", kTotalDrawLen);
+    printf("-n <num_workers>   Specifies the max number of threads.\n");
+    printf("                   Defaults to %d.\n", kDefaultMaxWorkers);
+}
+
+void worker(green_flag_t* green_flag) {
+    ssize_t rc;
+
+    uint8_t buf[ZX_CPRNG_DRAW_MAX_LEN];
+    size_t iters = kTotalDrawLen / ZX_CPRNG_DRAW_MAX_LEN;
+    size_t actual;
+
+    int fd = open("/dev/urandom", O_RDONLY);
+    if (fd < 0) {
+        perror("open('udev/random', O_RDONLY)");
+        return;
+    }
+
+    {
+        std::unique_lock<std::mutex> guard(green_flag->mtx);
+        if (!green_flag->waved && green_flag->cnd.wait_for(guard, std::chrono::seconds(10)) == std::cv_status::timeout) {
+            printf("timeout\n");
+            return;
+        }
+    }
+
+    for (size_t i = 0; i < iters; ++i) {
+        if ((rc = read(fd, buf, sizeof(buf))) < 0) {
+            perror("read");
+            return;
+        }
+    }
+}
+
+int main(int argc, char** argv) {
+    int max_workers;
+    switch (argc) {
+    case 1:
+        max_workers = kDefaultMaxWorkers;
+        break;
+    case 3:
+        max_workers = atoi(argv[2]);
+        if (strcmp(argv[1], "-n") == 0 && max_workers > 0) {
+            break;
+        }
+    // fall through
+    default:
+        print_usage(argv[0]);
+        return EINVAL;
+    }
+
+    green_flag_t green_flag;
+    std::vector<std::thread> threads;
+    struct timespec tp;
+    uint64_t start, finish, per_call;
+    for (int num_workers = 1; num_workers <= max_workers; ++num_workers) {
+        {
+            std::unique_lock<std::mutex> guard(green_flag.mtx);
+            threads.clear();
+            green_flag.waved = false;
+
+            for (int i = 0; i < num_workers; ++i) {
+                threads.push_back(std::thread(worker, &green_flag));
+            }
+
+            if (clock_gettime(CLOCK_MONOTONIC, &tp) < 0) {
+                perror("clock_gettime");
+                return errno;
+            }
+            start = (tp.tv_sec * 1000000000) + tp.tv_nsec;
+            green_flag.waved = true;
+            green_flag.cnd.notify_all();
+        }
+
+        for (auto& thrd : threads) {
+            thrd.join();
+        }
+
+        if (clock_gettime(CLOCK_MONOTONIC, &tp) < 0) {
+            perror("clock_gettime");
+            return errno;
+        }
+        finish = (tp.tv_sec * 1000000000) + tp.tv_nsec;
+        per_call = (finish - start) /  ((kTotalDrawLen / 1024) * num_workers);
+        printf("%d workers, %zu bytes, %d bytes/call => %" PRIu64 " ns/kb.\n",
+               num_workers, kTotalDrawLen, ZX_CPRNG_DRAW_MAX_LEN, per_call);
+    }
+
+    return 0;
+}
diff --git a/system/uapp/randhammer/randhammer.cpp b/system/uapp/randhammer/randhammer.cpp
new file mode 100644
index 0000000..94ce797
--- /dev/null
+++ b/system/uapp/randhammer/randhammer.cpp
@@ -0,0 +1,115 @@
+// 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 <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <threads.h>
+
+#include <zircon/status.h>
+#include <zircon/syscalls.h>
+#include <zircon/types.h>
+#include <zx/event.h>
+#include <zx/time.h>
+
+const int kDefaultMaxWorkers = 64;
+const size_t kTotalDrawLen = 1UL << 24;
+
+void print_usage(const char* argv0) {
+    printf("usage: %s [-n <num_workers>]\n\n", argv0);
+    printf("Spawn threads to zx_crpng_draw %zu bytes.\n\n", kTotalDrawLen);
+    printf("-n <num_workers>   Specifies the max number of threads.\n");
+    printf("                   Defaults to %d.\n", kDefaultMaxWorkers);
+}
+
+int worker(void* arg) {
+    zx_status_t rc;
+
+    uint8_t buf[ZX_CPRNG_DRAW_MAX_LEN];
+    size_t iters = kTotalDrawLen / ZX_CPRNG_DRAW_MAX_LEN;
+    size_t actual;
+
+    zx::event* green_flag = static_cast<zx::event*>(arg);
+    zx_signals_t observed;
+    if ((rc = green_flag->wait_one(ZX_USER_SIGNAL_0,
+                                   zx::deadline_after(zx::sec(10)),
+                                   &observed)) != ZX_OK) {
+        printf("event::wait_one failed: %s\n", zx_status_get_string(rc));
+        return rc;
+    }
+
+    for (size_t i = 0; i < iters; ++i) {
+        if ((rc = zx_cprng_draw(buf, sizeof(buf), &actual)) != ZX_OK) {
+            return rc;
+        }
+    }
+
+    return ZX_OK;
+}
+
+int main(int argc, char** argv) {
+    zx_status_t rc;
+
+    int max_workers;
+    switch (argc) {
+    case 1:
+        max_workers = kDefaultMaxWorkers;
+        break;
+    case 3:
+        max_workers = atoi(argv[2]);
+        if (strcmp(argv[1], "-n") == 0 && max_workers > 0) {
+            break;
+        }
+    // fall through
+    default:
+        print_usage(argv[0]);
+        return ZX_ERR_INVALID_ARGS;
+    }
+
+    zx::event green_flag;
+    if ((rc = zx::event::create(0, &green_flag)) != ZX_OK) {
+        printf("zx::event::create failed: %s\n", zx_status_get_string(rc));
+        return rc;
+    }
+
+    for (int num_workers = 1; num_workers <= max_workers; ++num_workers) {
+        if ((rc = green_flag.signal(ZX_USER_SIGNAL_0, 0)) != ZX_OK) {
+            printf("zx::event::signal failed: %s\n", zx_status_get_string(rc));
+            return rc;
+        }
+
+        thrd_t tids[num_workers];
+        zx_status_t rcs[num_workers];
+        for (int i = 0; i < num_workers; ++i) {
+            if (thrd_create(&tids[i], worker, &green_flag) != thrd_success) {
+                printf("failed to start worker %d\n", i);
+                return ZX_ERR_NO_RESOURCES;
+            }
+        }
+
+        zx::time start = zx::clock::get(ZX_CLOCK_MONOTONIC);
+        if ((rc = green_flag.signal(0, ZX_USER_SIGNAL_0)) != ZX_OK) {
+            printf("zx::event::signal failed: %s\n", zx_status_get_string(rc));
+            return rc;
+        }
+        for (int i = 0; i < num_workers; ++i) {
+            thrd_join(tids[i], &rcs[i]);
+        }
+        zx::time finish = zx::clock::get(ZX_CLOCK_MONOTONIC);
+
+        for (int i = 0; i < num_workers; ++i) {
+            if (rcs[i] != ZX_OK) {
+                printf("worker %d returned %s\n", i, zx_status_get_string(rcs[i]));
+            }
+        }
+
+        zx::duration per_call = (finish - start) / ((kTotalDrawLen / 1024) * num_workers);
+        printf("%d workers, %zu bytes, %d bytes/call => %" PRIu64 " ns/kb.\n",
+            num_workers, kTotalDrawLen, ZX_CPRNG_DRAW_MAX_LEN, per_call.get());
+    }
+
+    return 0;
+}
diff --git a/system/uapp/randhammer/rules.mk b/system/uapp/randhammer/rules.mk
new file mode 100644
index 0000000..346abee
--- /dev/null
+++ b/system/uapp/randhammer/rules.mk
@@ -0,0 +1,22 @@
+# 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.
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_TYPE := userapp
+MODULE_GROUP := core
+
+MODULE_SRCS += $(LOCAL_DIR)/randhammer.cpp
+
+MODULE_STATIC_LIBS := \
+	system/ulib/zx \
+
+MODULE_LIBS := \
+	system/ulib/fdio \
+	system/ulib/zircon \
+	system/ulib/c \
+
+include make/module.mk