// Copyright 2016 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 #include #include #include #include #include #include #include #include #include #include #include #define TU_FAIL_ERRCODE 10 void* tu_malloc(size_t size) { void* result = malloc(size); if (result == NULL) { // TODO(dje): printf may try to malloc too ... unittest_printf_critical("out of memory trying to malloc(%zu)\n", size); exit(TU_FAIL_ERRCODE); } return result; } void* tu_calloc(size_t nmemb, size_t size) { void* result = calloc(nmemb, size); if (result == NULL) { // TODO(dje): printf may try to malloc too ... unittest_printf_critical("out of memory trying to calloc(%zu, %zu)\n", nmemb, size); exit(TU_FAIL_ERRCODE); } return result; } char* tu_strdup(const char* s) { size_t len = strlen(s) + 1; char* r = tu_malloc(len); strcpy(r, s); return r; } char* tu_asprintf(const char* fmt, ...) { va_list args; char* result; va_start(args, fmt); if (vasprintf(&result, fmt, args) < 0) { unittest_printf_critical("out of memory trying to asprintf(%s)\n", fmt); exit(TU_FAIL_ERRCODE); } va_end(args); return result; } void tu_fatal(const char *what, zx_status_t status) { const char* reason = zx_status_get_string(status); unittest_printf_critical("\nFATAL: %s failed, rc %d (%s)\n", what, status, reason); // Request a backtrace to assist debugging. unittest_printf_critical("FATAL: backtrace follows:\n"); unittest_printf_critical(" (using sw breakpoint request to crashlogger)\n"); backtrace_request(); unittest_printf_critical("FATAL: exiting process\n"); exit(TU_FAIL_ERRCODE); } void tu_handle_close(zx_handle_t handle) { zx_status_t status = zx_handle_close(handle); // TODO(dje): It's still an open question as to whether errors other than ZX_ERR_BAD_HANDLE are "advisory". if (status < 0) { tu_fatal(__func__, status); } } zx_handle_t tu_handle_duplicate(zx_handle_t handle) { zx_handle_t copy = ZX_HANDLE_INVALID; zx_status_t status = zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS, ©); if (status < 0) tu_fatal(__func__, status); return copy; } // N.B. This creates a C11 thread. // See, e.g., musl/include/threads.h. void tu_thread_create_c11(thrd_t* t, thrd_start_t entry, void* arg, const char* name) { int ret = thrd_create_with_name(t, entry, arg, name); if (ret != thrd_success) { // tu_fatal takes zx_status_t values. // The translation doesn't have to be perfect. switch (ret) { case thrd_nomem: tu_fatal(__func__, ZX_ERR_NO_MEMORY); default: tu_fatal(__func__, ZX_ERR_BAD_STATE); } __UNREACHABLE; } } zx_status_t tu_wait(uint32_t num_objects, const zx_handle_t* handles, const zx_signals_t* signals, zx_signals_t* pending) { zx_wait_item_t items[num_objects]; for (uint32_t n = 0; n < num_objects; n++) { items[n].handle = handles[n]; items[n].waitfor = signals[n]; } zx_status_t status = zx_object_wait_many(items, num_objects, ZX_TIME_INFINITE); for (uint32_t n = 0; n < num_objects; n++) { pending[n] = items[n].pending; } return status; } void tu_channel_create(zx_handle_t* handle0, zx_handle_t* handle1) { zx_handle_t handles[2]; zx_status_t status = zx_channel_create(0, &handles[0], &handles[1]); if (status < 0) tu_fatal(__func__, status); *handle0 = handles[0]; *handle1 = handles[1]; } void tu_channel_write(zx_handle_t handle, uint32_t flags, const void* bytes, uint32_t num_bytes, const zx_handle_t* handles, uint32_t num_handles) { zx_status_t status = zx_channel_write(handle, flags, bytes, num_bytes, handles, num_handles); if (status < 0) tu_fatal(__func__, status); } void tu_channel_read(zx_handle_t handle, uint32_t flags, void* bytes, uint32_t* num_bytes, zx_handle_t* handles, uint32_t* num_handles) { zx_status_t status = zx_channel_read(handle, flags, bytes, handles, num_bytes ? *num_bytes : 0, num_handles ? *num_handles : 0, num_bytes, num_handles); if (status < 0) tu_fatal(__func__, status); } bool tu_channel_wait_readable(zx_handle_t channel) { zx_signals_t signals = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED; zx_signals_t pending; zx_status_t result = tu_wait(1, &channel, &signals, &pending); if (result != ZX_OK) tu_fatal(__func__, result); if ((pending & ZX_CHANNEL_READABLE) == 0) { unittest_printf("%s: peer closed\n", __func__); return false; } return true; } zx_handle_t tu_launch(zx_handle_t job, const char* name, int argc, const char* const* argv, const char* const* envp, size_t num_handles, zx_handle_t* handles, uint32_t* handle_ids) { launchpad_t* lp; launchpad_create(job, name, &lp); launchpad_load_from_file(lp, argv[0]); launchpad_set_args(lp, argc, argv); launchpad_set_environ(lp, envp); launchpad_add_handles(lp, num_handles, handles, handle_ids); zx_status_t status; zx_handle_t child; status = launchpad_go(lp, &child, NULL); if (status < 0) tu_fatal("tu_launch", status); return child; } launchpad_t* tu_launch_fdio_init(zx_handle_t job, const char* name, int argc, const char* const* argv, const char* const* envp, size_t hnds_count, zx_handle_t* handles, uint32_t* ids) { // This is the first part of launchpad_launch_fdio_etc. // It does everything except start the process running. launchpad_t* lp; const char* filename = argv[0]; if (name == NULL) name = filename; launchpad_create(job, name, &lp); launchpad_load_from_file(lp, filename); launchpad_set_args(lp, argc, argv); launchpad_set_environ(lp, envp); launchpad_clone(lp, LP_CLONE_FDIO_ALL); launchpad_add_handles(lp, hnds_count, handles, ids); return lp; } zx_handle_t tu_launch_fdio_fini(launchpad_t* lp) { zx_handle_t proc; zx_status_t status; if ((status = launchpad_go(lp, &proc, NULL)) < 0) tu_fatal("tu_launch_fdio_fini", status); return proc; } void tu_process_wait_signaled(zx_handle_t process) { zx_signals_t signals = ZX_PROCESS_TERMINATED; zx_signals_t pending; zx_status_t result = tu_wait(1, &process, &signals, &pending); if (result != ZX_OK) tu_fatal(__func__, result); if ((pending & ZX_PROCESS_TERMINATED) == 0) { unittest_printf_critical("%s: unexpected return from tu_wait\n", __func__); exit(TU_FAIL_ERRCODE); } } bool tu_process_has_exited(zx_handle_t process) { zx_info_process_t info; zx_status_t status; if ((status = zx_object_get_info(process, ZX_INFO_PROCESS, &info, sizeof(info), NULL, NULL)) < 0) tu_fatal("get process info", status); return info.exited; } int tu_process_get_return_code(zx_handle_t process) { zx_info_process_t info; zx_status_t status; if ((status = zx_object_get_info(process, ZX_INFO_PROCESS, &info, sizeof(info), NULL, NULL)) < 0) tu_fatal("get process info", status); if (!info.exited) { unittest_printf_critical( "attempt to read return code of non-exited process"); exit(TU_FAIL_ERRCODE); } return info.return_code; } int tu_process_wait_exit(zx_handle_t process) { tu_process_wait_signaled(process); return tu_process_get_return_code(process); } zx_handle_t tu_process_get_thread(zx_handle_t process, zx_koid_t tid) { zx_handle_t thread; zx_status_t status = zx_object_get_child(process, tid, ZX_RIGHT_SAME_RIGHTS, &thread); if (status == ZX_ERR_NOT_FOUND) return ZX_HANDLE_INVALID; if (status < 0) tu_fatal(__func__, status); return thread; } size_t tu_process_get_threads(zx_handle_t process, zx_koid_t* threads, size_t max_threads) { size_t num_threads; size_t buf_size = max_threads * sizeof(threads[0]); zx_status_t status = zx_object_get_info(process, ZX_INFO_PROCESS_THREADS, threads, buf_size, &num_threads, NULL); if (status < 0) tu_fatal(__func__, status); return num_threads; } zx_handle_t tu_job_create(zx_handle_t job) { zx_handle_t child_job; zx_status_t status = zx_job_create(job, 0, &child_job); if (status < 0) tu_fatal(__func__, status); return child_job; } zx_handle_t tu_io_port_create(void) { zx_handle_t handle; zx_status_t status = zx_port_create(0, &handle); if (status < 0) tu_fatal(__func__, status); return handle; } void tu_set_exception_port(zx_handle_t handle, zx_handle_t eport, uint64_t key, uint32_t options) { if (handle == 0) handle = zx_process_self(); zx_status_t status = zx_task_bind_exception_port(handle, eport, key, options); if (status < 0) tu_fatal(__func__, status); } void tu_object_wait_async(zx_handle_t handle, zx_handle_t port, zx_signals_t signals) { uint64_t key = tu_get_koid(handle); uint32_t options = ZX_WAIT_ASYNC_REPEATING; zx_status_t status = zx_object_wait_async(handle, port, key, signals, options); if (status < 0) tu_fatal(__func__, status); } void tu_handle_get_basic_info(zx_handle_t handle, zx_info_handle_basic_t* info) { zx_status_t status = zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, info, sizeof(*info), NULL, NULL); if (status < 0) tu_fatal(__func__, status); } zx_koid_t tu_get_koid(zx_handle_t handle) { zx_info_handle_basic_t info; tu_handle_get_basic_info(handle, &info); return info.koid; } zx_koid_t tu_get_related_koid(zx_handle_t handle) { zx_info_handle_basic_t info; tu_handle_get_basic_info(handle, &info); return info.related_koid; } zx_handle_t tu_get_thread(zx_handle_t proc, zx_koid_t tid) { zx_handle_t thread; zx_status_t status = zx_object_get_child(proc, tid, ZX_RIGHT_SAME_RIGHTS, &thread); if (status != ZX_OK) tu_fatal(__func__, status); return thread; } zx_info_thread_t tu_thread_get_info(zx_handle_t thread) { zx_info_thread_t info; zx_status_t status = zx_object_get_info(thread, ZX_INFO_THREAD, &info, sizeof(info), NULL, NULL); if (status < 0) tu_fatal("zx_object_get_info(ZX_INFO_THREAD)", status); return info; } uint32_t tu_thread_get_state(zx_handle_t thread) { zx_info_thread_t info = tu_thread_get_info(thread); return info.state; } bool tu_thread_is_dying_or_dead(zx_handle_t thread) { zx_info_thread_t info = tu_thread_get_info(thread); return (info.state == ZX_THREAD_STATE_DYING || info.state == ZX_THREAD_STATE_DEAD); } void tu_task_kill(zx_handle_t task) { zx_status_t status = zx_task_kill(task); if (status < 0) tu_fatal("zx_task_kill", status); } int tu_run_program(const char *progname, int argc, const char** argv) { launchpad_t* lp; unittest_printf("%s: running %s\n", __func__, progname); launchpad_create(ZX_HANDLE_INVALID, progname, &lp); launchpad_clone(lp, LP_CLONE_ALL); launchpad_load_from_file(lp, argv[0]); launchpad_set_args(lp, argc, argv); zx_status_t status; zx_handle_t child; if ((status = launchpad_go(lp, &child, NULL)) < 0) { tu_fatal(__func__, status); } int rc = tu_process_wait_exit(child); tu_handle_close(child); unittest_printf("%s: child returned %d\n", __func__, rc); return rc; } int tu_run_command(const char* progname, const char* cmd) { const char* argv[] = { "/boot/bin/sh", "-c", cmd }; return tu_run_program(progname, countof(argv), argv); }