[zircon_benchmarks] Bench fdio_spawn instead of zx_process_create

Remove the process creation benchmark from perf-test and replace it
with an fdio_spawn benchmark.

Why remove the process creation benchmark? It relied on a launchpad
function that's about to become private,
launchpad_create_with_process.

$ /pkgfs/packages/zircon_benchmarks/0/test/zircon_benchmarks -p --filter=Fdio/Spawn
[ RUN      ] Fdio/Spawn
[       OK ] Fdio/Spawn

      Mean    Std dev        Min        Max     Median Unit         Mean Mbytes/sec Test case
   4564408     529162    3938277   19807372    4529805 nanoseconds              N/A Fdio/Spawn

Bug: ZX-4051 #done
Test: ran perf-test and zircon_benchmarks
Change-Id: I10da5af5db1f9d96f7f41911aecf929da0592f0f
diff --git a/garnet/bin/zircon_benchmarks/BUILD.gn b/garnet/bin/zircon_benchmarks/BUILD.gn
index 58eac22..64d0773 100644
--- a/garnet/bin/zircon_benchmarks/BUILD.gn
+++ b/garnet/bin/zircon_benchmarks/BUILD.gn
@@ -13,6 +13,7 @@
     "channels.cc",
     "events.cc",
     "expose.cc",
+    "fdio_spawn.cc",
     "fifos.cc",
     "filesystem.cc",
     "handle.cc",
@@ -50,6 +51,17 @@
   libs = [ "zircon" ]
 }
 
+# "Helper" executable used to implement the fdio_spawn benchmark.
+executable("fdio_spawn_helper") {
+  output_name = "fdio_spawn_helper"
+  testonly = true
+  sources = [
+    "fdio_spawn_helper.cc",
+  ]
+  deps = []
+  public_deps = []
+}
+
 fidl("fidl_interface") {
   testonly = true
   name = "fuchsia.zircon.benchmarks"
@@ -63,6 +75,7 @@
 
   deps = [
     ":bin",
+    ":fdio_spawn_helper",
   ]
 
   tests = [
@@ -70,5 +83,9 @@
       name = "zircon_benchmarks"
       environments = basic_envs
     },
+    {
+      name = "fdio_spawn_helper"
+      environments = basic_envs
+    },
   ]
 }
diff --git a/garnet/bin/zircon_benchmarks/fdio_spawn.cc b/garnet/bin/zircon_benchmarks/fdio_spawn.cc
new file mode 100644
index 0000000..3cc1c42
--- /dev/null
+++ b/garnet/bin/zircon_benchmarks/fdio_spawn.cc
@@ -0,0 +1,40 @@
+// Copyright 2019 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 <lib/fdio/spawn.h>
+#include <lib/zx/handle.h>
+#include <lib/zx/process.h>
+#include <perftest/perftest.h>
+#include <zircon/assert.h>
+#include <zircon/process.h>
+#include <zircon/syscalls.h>
+
+namespace {
+
+// See fdio_spawn_helper.cc.
+constexpr const char* kPath =
+    "/pkgfs/packages/zircon_benchmarks/0/test/fdio_spawn_helper";
+constexpr const char* const kArgv[]{kPath, nullptr};
+
+// Benchmark |fdio_spawn| by spawning a process that simply exits.
+bool SpawnTest(perftest::RepeatState* state) {
+  while (state->KeepRunning()) {
+    zx::handle process;
+    ZX_ASSERT(fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_DEFAULT_LDSVC, kPath,
+                         kArgv, process.reset_and_get_address()) == ZX_OK);
+    ZX_ASSERT(process.wait_one(ZX_TASK_TERMINATED, zx::time::infinite(),
+                               nullptr) == ZX_OK);
+    zx_info_process_t info;
+    ZX_ASSERT(process.get_info(ZX_INFO_PROCESS, &info, sizeof(info), nullptr,
+                               nullptr) == ZX_OK);
+    ZX_ASSERT(info.exited);
+    ZX_ASSERT(info.return_code == 0);
+  }
+  return true;
+}
+
+void RegisterTests() { perftest::RegisterTest("Fdio/Spawn", SpawnTest); }
+PERFTEST_CTOR(RegisterTests)
+
+}  // namespace
diff --git a/garnet/bin/zircon_benchmarks/fdio_spawn_helper.cc b/garnet/bin/zircon_benchmarks/fdio_spawn_helper.cc
new file mode 100644
index 0000000..67b624b
--- /dev/null
+++ b/garnet/bin/zircon_benchmarks/fdio_spawn_helper.cc
@@ -0,0 +1,9 @@
+// Copyright 2019 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.
+
+// This is a no-op test executable used by the fdio_spawn.cc benchmark. It's
+// important that it does no work and simply exits.
+int main(int argc, char** argv) {
+  return 0;
+}
diff --git a/zircon/system/utest/perftest/BUILD.gn b/zircon/system/utest/perftest/BUILD.gn
index e732c95..2dbe6a7 100644
--- a/zircon/system/utest/perftest/BUILD.gn
+++ b/zircon/system/utest/perftest/BUILD.gn
@@ -12,7 +12,6 @@
     "mutex-test.cpp",
     "null-test.cpp",
     "object-wait-test.cpp",
-    "process-test.cpp",
     "results-test.cpp",
     "runner-test.cpp",
     "sleep-test.cpp",
@@ -25,10 +24,8 @@
     "$zx/system/ulib/async:async-default",
     "$zx/system/ulib/async-loop",
     "$zx/system/ulib/async-loop:async-loop-cpp",
-    "$zx/system/ulib/elf-psabi",
     "$zx/system/ulib/fbl",
     "$zx/system/ulib/fdio",
-    "$zx/system/ulib/launchpad",
     "$zx/system/ulib/perftest",
     "$zx/system/ulib/trace",
     "$zx/system/ulib/trace-engine",
diff --git a/zircon/system/utest/perftest/process-test.cpp b/zircon/system/utest/perftest/process-test.cpp
deleted file mode 100644
index 8612c18..0000000
--- a/zircon/system/utest/perftest/process-test.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-// 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 <dlfcn.h>
-#include <launchpad/launchpad.h>
-#include <lib/elf-psabi/sp.h>
-#include <limits.h>
-#include <perftest/perftest.h>
-#include <zircon/assert.h>
-#include <zircon/process.h>
-#include <zircon/syscalls.h>
-
-namespace {
-
-constexpr char pname[] = "benchmark-process";
-constexpr char tname[] = "benchmark-thread";
-
-// ProcessFixture is a reusable test fixture for creating a minimal child process.
-//
-// When started, the child process simply calls zx_thread_exit.
-//
-// For each iteration, call the following methods in this order:
-//     Create();
-//     Init();
-//     Start();
-//     Wait();
-//     Close();
-class ProcessFixture {
-public:
-    ProcessFixture();
-
-    // Creates an "empty" child process.
-    void Create();
-
-    // Initializes minimal process.
-    void Init();
-
-    // Starts the process.
-    void Start();
-
-    // Waits for the process to terminate.
-    void Wait();
-
-    // Closes handles and frees resources.
-    void Close();
-
-private:
-    // Offset of the zx_thread_exit() syscall from the start of the vDSO.
-    uintptr_t thread_exit_offset_ = 0;
-
-    uintptr_t sp_ = 0;
-
-    // Address in child process of zx_thread_exit() syscall.
-    uintptr_t thread_exit_addr_ = 0;
-    zx_handle_t proc_handle_ = ZX_HANDLE_INVALID;
-    zx_handle_t vmar_handle_ = ZX_HANDLE_INVALID;
-    zx_handle_t thread_handle_ = ZX_HANDLE_INVALID;
-    zx_handle_t stack_vmo_ = ZX_HANDLE_INVALID;
-    zx_handle_t vdso_vmo_ = ZX_HANDLE_INVALID;
-    zx_handle_t channel_ = ZX_HANDLE_INVALID;
-    zx_handle_t channel_to_transfer_ = ZX_HANDLE_INVALID;
-};
-
-ProcessFixture::ProcessFixture() {
-    // The child process will simply call zx_thread_exit() so we need to know the address of the
-    // syscall in the child's addres space. We'll compute that by finding its offset in the vDSO and
-    // later adding the offset to the vDSO's base address.
-    Dl_info dl_info;
-    ZX_ASSERT(dladdr(reinterpret_cast<void*>(&zx_thread_exit), &dl_info) != 0);
-    thread_exit_offset_ = (uintptr_t)dl_info.dli_saddr - (uintptr_t)dl_info.dli_fbase;
-}
-
-void ProcessFixture::Create() {
-    ZX_ASSERT(zx_process_create(zx_job_default(), pname, sizeof(pname), 0, &proc_handle_,
-                                &vmar_handle_) == ZX_OK);
-}
-
-void ProcessFixture::Init() {
-    // Initialization of the child process is modeled after mini-process.
-
-    // In order to make a syscall, the child needs to have the vDSO mapped.  Launchpad makes this
-    // easy. Use launchpad to map the vDSO into the child process and compute the address of
-    // zx_thread_exit(). Since launchpad takes ownership of the handles passed to
-    // launchpad_create_with_process, duplicate them first so we can destroy the launchpad once the
-    // vDSO is mapped.
-    zx_handle_t lp_proc_handle = ZX_HANDLE_INVALID;
-    ZX_ASSERT(zx_handle_duplicate(proc_handle_, ZX_RIGHT_SAME_RIGHTS, &lp_proc_handle) == ZX_OK);
-
-    zx_handle_t lp_vmar_handle = ZX_HANDLE_INVALID;
-    ZX_ASSERT(zx_handle_duplicate(vmar_handle_, ZX_RIGHT_SAME_RIGHTS, &lp_vmar_handle) == ZX_OK);
-
-    launchpad_t* lp = NULL;
-    ZX_ASSERT(launchpad_create_with_process(lp_proc_handle, lp_vmar_handle, &lp) == ZX_OK);
-
-    ZX_ASSERT(launchpad_get_vdso_vmo(&vdso_vmo_) == ZX_OK);
-
-    zx_vaddr_t vdso_base;
-    ZX_ASSERT(launchpad_elf_load_extra(lp, vdso_vmo_, &vdso_base, NULL) == ZX_OK);
-
-    launchpad_destroy(lp);
-    thread_exit_addr_ = vdso_base + thread_exit_offset_;
-
-    // The child process needs a stack for the vDSO code to use.
-    constexpr uint32_t stack_perm =
-        ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
-    constexpr uint64_t stack_size = PAGE_SIZE;
-    ZX_ASSERT(zx_vmo_create(stack_size, 0, &stack_vmo_) == ZX_OK);
-    uintptr_t stack_base;
-    ZX_ASSERT(zx_vmar_map(vmar_handle_, stack_perm, 0, stack_vmo_, 0, stack_size,
-                          &stack_base) == ZX_OK);
-    sp_ = compute_initial_stack_pointer(stack_base, stack_size);
-
-    // The child process needs a thread.
-    ZX_ASSERT(zx_thread_create(proc_handle_, tname, sizeof(tname), 0, &thread_handle_) == ZX_OK);
-
-    // It will also need a channel to its parent even though it won't use it.
-    ZX_ASSERT(zx_channel_create(0, &channel_, &channel_to_transfer_) == ZX_OK);
-}
-
-void ProcessFixture::Start() {
-    ZX_ASSERT(zx_process_start(proc_handle_, thread_handle_,
-                               thread_exit_addr_, sp_, channel_to_transfer_,
-                               0) == ZX_OK);
-    channel_to_transfer_ = ZX_HANDLE_INVALID;
-}
-
-void ProcessFixture::Wait() {
-    ZX_ASSERT(zx_object_wait_one(thread_handle_, ZX_TASK_TERMINATED, ZX_TIME_INFINITE, NULL) ==
-              ZX_OK);
-}
-
-void ProcessFixture::Close() {
-    if (proc_handle_ != ZX_HANDLE_INVALID) {
-        ZX_ASSERT(zx_handle_close(proc_handle_) == ZX_OK);
-        proc_handle_ = ZX_HANDLE_INVALID;
-    }
-    if (vmar_handle_ != ZX_HANDLE_INVALID) {
-        ZX_ASSERT(zx_handle_close(vmar_handle_) == ZX_OK);
-        vmar_handle_ = ZX_HANDLE_INVALID;
-    }
-    if (thread_handle_ != ZX_HANDLE_INVALID) {
-        ZX_ASSERT(zx_handle_close(thread_handle_) == ZX_OK);
-        thread_handle_ = ZX_HANDLE_INVALID;
-    }
-    if (stack_vmo_ != ZX_HANDLE_INVALID) {
-        ZX_ASSERT(zx_handle_close(stack_vmo_) == ZX_OK);
-        stack_vmo_ = ZX_HANDLE_INVALID;
-    }
-    if (vdso_vmo_ != ZX_HANDLE_INVALID) {
-        ZX_ASSERT(zx_handle_close(vdso_vmo_) == ZX_OK);
-        vdso_vmo_ = ZX_HANDLE_INVALID;
-    }
-    if (channel_ != ZX_HANDLE_INVALID) {
-        ZX_ASSERT(zx_handle_close(channel_) == ZX_OK);
-        channel_ = ZX_HANDLE_INVALID;
-    }
-    if (channel_to_transfer_ != ZX_HANDLE_INVALID) {
-        ZX_ASSERT(zx_handle_close(channel_to_transfer_) == ZX_OK);
-        channel_to_transfer_ = ZX_HANDLE_INVALID;
-    }
-}
-
-// This benchmark measures creating, starting, and waiting for completion of a
-// minimal process.
-bool StartTest(perftest::RepeatState* state) {
-    state->DeclareStep("create");
-    state->DeclareStep("init");
-    state->DeclareStep("start");
-    state->DeclareStep("wait");
-    state->DeclareStep("close");
-
-    ProcessFixture proc;
-    while (state->KeepRunning()) {
-        proc.Create();
-        state->NextStep();
-        proc.Init();
-        state->NextStep();
-        proc.Start();
-        state->NextStep();
-        proc.Wait();
-        state->NextStep();
-        proc.Close();
-    }
-    return true;
-}
-
-void RegisterTests() {
-    perftest::RegisterTest("Process/Start", StartTest);
-}
-PERFTEST_CTOR(RegisterTests)
-
-} // namespace