#pragma once #include #include #include #include "client_socket.h" #include "panic.h" #include "sandbox_request.h" #include "memlogging.h" /*************************** * Public API * **************************/ struct sandbox *sandbox_allocate(struct sandbox_request *sandbox_request); void sandbox_free(struct sandbox *sandbox); void sandbox_main(struct sandbox *sandbox); void sandbox_switch_to(struct sandbox *next_sandbox); static inline void sandbox_close_http(struct sandbox *sandbox) { assert(sandbox != NULL); int rc = epoll_ctl(worker_thread_epoll_file_descriptor, EPOLL_CTL_DEL, sandbox->client_socket_descriptor, NULL); if (unlikely(rc < 0)) panic_err(); client_socket_close(sandbox->client_socket_descriptor, &sandbox->client_address); } /** * Remove the client fd from epoll */ static inline void sandbox_remove_from_epoll(struct sandbox *sandbox) { assert(sandbox != NULL); int rc = epoll_ctl(worker_thread_epoll_file_descriptor, EPOLL_CTL_DEL, sandbox->client_socket_descriptor, NULL); if (unlikely(rc < 0)) panic_err(); } /** * Initializes a sandbox fd ready for use with the proper preopen magic * @param sandbox * @return index of handle we preopened or -1 on error (sandbox is null or all io_handles are exhausted) */ static inline int sandbox_initialize_file_descriptor(struct sandbox *sandbox) { if (!sandbox) return -1; int sandbox_fd; for (sandbox_fd = 0; sandbox_fd < SANDBOX_MAX_FD_COUNT; sandbox_fd++) { if (sandbox->file_descriptors[sandbox_fd] < 0) break; } if (sandbox_fd == SANDBOX_MAX_FD_COUNT) return -1; sandbox->file_descriptors[sandbox_fd] = SANDBOX_FILE_DESCRIPTOR_PREOPEN_MAGIC; return sandbox_fd; } /** * Free Linear Memory, leaving stack in place * @param sandbox */ static inline void sandbox_free_linear_memory(struct sandbox *sandbox) { int rc = munmap(sandbox->linear_memory_start, SANDBOX_MAX_MEMORY + PAGE_SIZE); if (rc == -1) panic("sandbox_free_linear_memory - munmap failed\n"); sandbox->linear_memory_start = NULL; } /** * Given a sandbox, returns the module that sandbox is executing * @param sandbox the sandbox whose module we want * @return the module of the provided sandbox */ static inline struct module * sandbox_get_module(struct sandbox *sandbox) { if (!sandbox) return NULL; return sandbox->module; } /** * Resolve a sandbox's fd to the host fd it maps to * @param sandbox * @param sandbox_fd index into the sandbox's fd table * @returns file descriptor or -1 in case of error */ static inline int sandbox_get_file_descriptor(struct sandbox *sandbox, int sandbox_fd) { if (!sandbox) return -1; if (sandbox_fd >= SANDBOX_MAX_FD_COUNT || sandbox_fd < 0) return -1; return sandbox->file_descriptors[sandbox_fd]; } static inline uint64_t sandbox_get_priority(void *element) { struct sandbox *sandbox = (struct sandbox *)element; return sandbox->absolute_deadline; }; static inline uint64_t sandbox_get_srsf_priority(void *element) { struct sandbox *sandbox = (struct sandbox *)element; uint64_t now = __getcycles(); int64_t remaining_slack = sandbox->remaining_slack - (now - sandbox->last_update_timestamp); if (remaining_slack < 0) { return 0; } return remaining_slack; }; /** * Maps a sandbox fd to an underlying host fd * Returns error condition if the file_descriptor to set does not contain sandbox preopen magic * @param sandbox * @param sandbox_fd index of the sandbox fd we want to set * @param file_descriptor the file descripter we want to set it to * @returns the index that was set or -1 in case of error */ static inline int sandbox_set_file_descriptor(struct sandbox *sandbox, int sandbox_fd, int host_fd) { if (!sandbox) return -1; if (sandbox_fd >= SANDBOX_MAX_FD_COUNT || sandbox_fd < 0) return -1; if (host_fd < 0 || sandbox->file_descriptors[sandbox_fd] != SANDBOX_FILE_DESCRIPTOR_PREOPEN_MAGIC) return -1; sandbox->file_descriptors[sandbox_fd] = host_fd; return sandbox_fd; } /** * Map the host stdin, stdout, stderr to the sandbox * @param sandbox - the sandbox on which we are initializing stdio */ static inline void sandbox_initialize_stdio(struct sandbox *sandbox) { int sandbox_fd, rc; for (int host_fd = 0; host_fd <= 2; host_fd++) { sandbox_fd = sandbox_initialize_file_descriptor(sandbox); assert(sandbox_fd == host_fd); rc = sandbox_set_file_descriptor(sandbox, sandbox_fd, host_fd); assert(rc != -1); } } static inline void sandbox_open_http(struct sandbox *sandbox) { assert(sandbox != NULL); http_parser_init(&sandbox->http_parser, HTTP_REQUEST); /* Set the sandbox as the data the http-parser has access to */ sandbox->http_parser.data = sandbox; /* Freshly allocated sandbox going runnable for first time, so register client socket with epoll */ struct epoll_event accept_evt; accept_evt.data.ptr = (void *)sandbox; accept_evt.events = EPOLLIN | EPOLLOUT | EPOLLET; int rc = epoll_ctl(worker_thread_epoll_file_descriptor, EPOLL_CTL_ADD, sandbox->client_socket_descriptor, &accept_evt); if (unlikely(rc < 0)) panic_err(); } /** * Prints key performance metrics for a sandbox to runtime_sandbox_perf_log in runtime * This is defined by an environment variable * @param sandbox */ static inline void sandbox_print_perf(struct sandbox *sandbox) { #ifndef LOG_RUNTIME_FILE_LOG return; #endif /* If the log was not defined by an environment variable, early out */ if (runtime_sandbox_perf_log == NULL) return; uint32_t total_time_us = sandbox->total_time / runtime_processor_speed_MHz; uint32_t queued_us = (sandbox->allocation_timestamp - sandbox->enqueue_timestamp) / runtime_processor_speed_MHz; uint32_t initializing_us = sandbox->initializing_duration / runtime_processor_speed_MHz; uint32_t runnable_us = sandbox->runnable_duration / runtime_processor_speed_MHz; uint32_t running_us = sandbox->running_duration / runtime_processor_speed_MHz; uint32_t blocked_us = sandbox->blocked_duration / runtime_processor_speed_MHz; uint32_t returned_us = sandbox->returned_duration / runtime_processor_speed_MHz; /* * Assumption: A sandbox is never able to free pages. If linear memory management * becomes more intelligent, then peak linear memory size needs to be tracked * seperately from current linear memory size. */ fprintf(runtime_sandbox_perf_log, "%lu,%s():%d,%s,%u,%u,%u,%u,%u,%u,%u,%u,%u\n", sandbox->id, sandbox->module->name, sandbox->module->port, sandbox_state_stringify(sandbox->state), sandbox->module->relative_deadline_us, total_time_us, queued_us, initializing_us, runnable_us, running_us, blocked_us, returned_us, sandbox->linear_memory_size); } /** * Prints key performance metrics for a sandbox to memory in runtime * @param sandbox */ static inline void sandbox_mem_print_perf(struct sandbox *sandbox) { #ifndef LOG_RUNTIME_MEM_LOG return; #endif /* If the log was not defined by an environment variable, early out */ if (runtime_sandbox_perf_log == NULL) return; uint32_t total_time_us = sandbox->total_time / runtime_processor_speed_MHz; uint32_t queued_us = (sandbox->allocation_timestamp - sandbox->enqueue_timestamp) / runtime_processor_speed_MHz; uint32_t initializing_us = sandbox->initializing_duration / runtime_processor_speed_MHz; uint32_t runnable_us = sandbox->runnable_duration / runtime_processor_speed_MHz; uint32_t running_us = sandbox->running_duration / runtime_processor_speed_MHz; uint32_t blocked_us = sandbox->blocked_duration / runtime_processor_speed_MHz; uint32_t returned_us = sandbox->returned_duration / runtime_processor_speed_MHz; if (sandbox->module->next_module == NULL) { uint32_t total_time = (sandbox->completion_timestamp - sandbox->request_arrival_timestamp) / runtime_processor_speed_MHz; bool miss_deadline = sandbox->completion_timestamp > sandbox->absolute_deadline ? true : false; uint32_t delayed_us = (sandbox->completion_timestamp - sandbox->absolute_deadline) / runtime_processor_speed_MHz; if (miss_deadline) { mem_log("%lu miss deadline, delayed %u us, actual cost %u module name %s\n", sandbox->id, delayed_us, total_time, sandbox->module->name); } else { mem_log("%lu meet deadline, actual cost %u module name %s\n", sandbox->id, total_time, sandbox->module->name); } } /* * Assumption: A sandbox is never able to free pages. If linear memory management * becomes more intelligent, then peak linear memory size needs to be tracked * seperately from current linear memory size. */ mem_log("%lu,%s():%d,%s,%u,%u,%u,%u,%u,%u,%u,%u,%u\n", sandbox->id, sandbox->module->name, sandbox->module->port, sandbox_state_stringify(sandbox->state), sandbox->module->relative_deadline_us, total_time_us, queued_us, initializing_us, runnable_us, running_us, blocked_us, returned_us, sandbox->linear_memory_size); }