diff --git a/runtime/experiments/applications/imageresize/.gitignore b/runtime/experiments/applications/imageresize/.gitignore deleted file mode 100644 index 34cb46d..0000000 --- a/runtime/experiments/applications/imageresize/.gitignore +++ /dev/null @@ -1 +0,0 @@ -flower.jpg diff --git a/runtime/experiments/applications/imageresize/README.md b/runtime/experiments/applications/imageresize/README.md deleted file mode 100644 index 889eab9..0000000 --- a/runtime/experiments/applications/imageresize/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Resize - -Resizes Images - -It appears that the initial request cuts off the bottom of the image. Thereafter, it seems that the runtime crashes out due to a socket error. - -``` -write: Bad file descriptor -C: 07, T: 0x7f20eed26700, F: current_sandbox_main> - Unable to build and send client response - -C: 07, T: 0x7f20eed26700, F: client_socket_send> - Error sending to client: Bad file descriptor -C: 07, T: 0x7f20eed26700, F: sandbox_close_http> PANIC! - Bad file descriptor -find: 'result_13192.jpg': No such file or directory -``` diff --git a/runtime/experiments/applications/imageresize/by_resolution/.gitignore b/runtime/experiments/applications/imageresize/by_resolution/.gitignore new file mode 100644 index 0000000..978a56e --- /dev/null +++ b/runtime/experiments/applications/imageresize/by_resolution/.gitignore @@ -0,0 +1 @@ +result_*.png diff --git a/runtime/experiments/applications/imageresize/by_resolution/README.md b/runtime/experiments/applications/imageresize/by_resolution/README.md new file mode 100644 index 0000000..8bdd787 --- /dev/null +++ b/runtime/experiments/applications/imageresize/by_resolution/README.md @@ -0,0 +1,5 @@ +# Resize Image by Resolution + +The goal of this experiment is to run the resize operation on small, medium, and large source images and measure how the size of the input image affects execution time. + +The workload works sporadically, but the runtime errors out due to calls to `mremap`. The runtime gratuitously logs these calls for the time being. diff --git a/runtime/experiments/applications/imageresize/debug.sh b/runtime/experiments/applications/imageresize/by_resolution/debug.sh similarity index 94% rename from runtime/experiments/applications/imageresize/debug.sh rename to runtime/experiments/applications/imageresize/by_resolution/debug.sh index a561392..6ab3666 100755 --- a/runtime/experiments/applications/imageresize/debug.sh +++ b/runtime/experiments/applications/imageresize/by_resolution/debug.sh @@ -5,7 +5,7 @@ # Also disables pagination and stopping on SIGUSR1 experiment_directory=$(pwd) -project_directory=$(cd ../../.. && pwd) +project_directory=$(cd ../../../.. && pwd) binary_directory=$(cd "$project_directory"/bin && pwd) export LD_LIBRARY_PATH="$binary_directory:$LD_LIBRARY_PATH" diff --git a/runtime/experiments/applications/imageresize/by_resolution/expected_result.png b/runtime/experiments/applications/imageresize/by_resolution/expected_result.png new file mode 100644 index 0000000..f4534cd Binary files /dev/null and b/runtime/experiments/applications/imageresize/by_resolution/expected_result.png differ diff --git a/runtime/experiments/applications/imageresize/expected_result.jpg b/runtime/experiments/applications/imageresize/by_resolution/expected_result_old.png similarity index 99% rename from runtime/experiments/applications/imageresize/expected_result.jpg rename to runtime/experiments/applications/imageresize/by_resolution/expected_result_old.png index f61b344..1f95aae 100644 Binary files a/runtime/experiments/applications/imageresize/expected_result.jpg and b/runtime/experiments/applications/imageresize/by_resolution/expected_result_old.png differ diff --git a/runtime/experiments/applications/imageresize/by_resolution/flower.jpg b/runtime/experiments/applications/imageresize/by_resolution/flower.jpg new file mode 100644 index 0000000..de1fc28 Binary files /dev/null and b/runtime/experiments/applications/imageresize/by_resolution/flower.jpg differ diff --git a/runtime/experiments/applications/imageresize/by_resolution/run.sh b/runtime/experiments/applications/imageresize/by_resolution/run.sh new file mode 100755 index 0000000..99f6f39 --- /dev/null +++ b/runtime/experiments/applications/imageresize/by_resolution/run.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# Executes the runtime in GDB +# Substitutes the absolute path from the container with a path relatively derived from the location of this script +# This allows debugging outside of the Docker container +# Also disables pagination and stopping on SIGUSR1 + +experiment_directory=$(pwd) +project_directory=$(cd ../../../.. && pwd) +binary_directory=$(cd "$project_directory"/bin && pwd) + +# Copy Flower Image if not here +if [[ ! -f "./flower.jpg" ]]; then + cp ../../../../tests/sod/bin/flower.jpg ./flower.jpg +fi + +if [ "$1" != "-d" ]; then + PATH="$binary_directory:$PATH" LD_LIBRARY_PATH="$binary_directory:$LD_LIBRARY_PATH" sledgert "$experiment_directory/spec.json" & + sleep 1 +else + echo "Running under gdb" +fi + +success_count=0 +total_count=10 + +for ((i = 0; i < total_count; i++)); do + echo "$i" + ext="$RANDOM" + + curl -H 'Expect:' -H "Content-Type: image/jpg" --data-binary "@shrinking_man_small.jpg" --output "result_${ext}_small.png" localhost:10000 2>/dev/null 1>/dev/null + pixel_differences="$(compare -identify -metric AE "result_${ext}_small.png" expected_result.png null: 2>&1 >/dev/null)" + if [[ "$pixel_differences" == "0" ]]; then + success_count=$((success_count + 1)) + else + echo "Small FAIL" + echo "$pixel_differences pixel differences detected" + exit + fi + + curl -H 'Expect:' -H "Content-Type: image/jpg" --data-binary "@shrinking_man_medium.jpg" --output "result_${ext}_medium.png" localhost:10001 2>/dev/null 1>/dev/null + pixel_differences="$(compare -identify -metric AE "result_${ext}_medium.png" expected_result.png null: 2>&1 >/dev/null)" + if [[ "$pixel_differences" == "0" ]]; then + success_count=$((success_count + 1)) + else + echo "Medium FAIL" + echo "$pixel_differences pixel differences detected" + exit + fi + + curl -H 'Expect:' -H "Content-Type: image/jpg" --data-binary "@shrinking_man_large.jpg" --output "result_${ext}_large.png" localhost:10002 2>/dev/null 1>/dev/null + pixel_differences="$(compare -identify -metric AE "result_${ext}_large.png" expected_result.png null: 2>&1 >/dev/null)" + + if [[ "$pixel_differences" == "0" ]]; then + success_count=$((success_count + 1)) + else + echo "Large FAIL" + echo "$pixel_differences pixel differences detected" + exit + fi +done + +echo "$success_count / $total_count" +rm result_*.png + +if [ "$1" != "-d" ]; then + sleep 5 + echo -n "Running Cleanup: " + pkill sledgert >/dev/null 2>/dev/null + echo "[DONE]" +fi diff --git a/runtime/experiments/applications/imageresize/by_resolution/shrinking_man_large.jpg b/runtime/experiments/applications/imageresize/by_resolution/shrinking_man_large.jpg new file mode 100644 index 0000000..4b80121 Binary files /dev/null and b/runtime/experiments/applications/imageresize/by_resolution/shrinking_man_large.jpg differ diff --git a/runtime/experiments/applications/imageresize/by_resolution/shrinking_man_medium.jpg b/runtime/experiments/applications/imageresize/by_resolution/shrinking_man_medium.jpg new file mode 100644 index 0000000..820e877 Binary files /dev/null and b/runtime/experiments/applications/imageresize/by_resolution/shrinking_man_medium.jpg differ diff --git a/runtime/experiments/applications/imageresize/by_resolution/shrinking_man_small.jpg b/runtime/experiments/applications/imageresize/by_resolution/shrinking_man_small.jpg new file mode 100644 index 0000000..59081a7 Binary files /dev/null and b/runtime/experiments/applications/imageresize/by_resolution/shrinking_man_small.jpg differ diff --git a/runtime/experiments/applications/imageresize/by_resolution/spec.json b/runtime/experiments/applications/imageresize/by_resolution/spec.json new file mode 100644 index 0000000..06f8459 --- /dev/null +++ b/runtime/experiments/applications/imageresize/by_resolution/spec.json @@ -0,0 +1,42 @@ +{ + "active": "yes", + "name": "resize_small", + "path": "resize_wasm.so", + "port": 10000, + "relative-deadline-us": 50000, + "argsize": 1, + "http-req-headers": [], + "http-req-content-type": "image/jpeg", + "http-req-size": 1024000, + "http-resp-headers": [], + "http-resp-size": 1024000, + "http-resp-content-type": "image/png" +} +{ + "active": "yes", + "name": "resize_medium", + "path": "resize_wasm.so", + "port": 10001, + "relative-deadline-us": 50000, + "argsize": 1, + "http-req-headers": [], + "http-req-content-type": "image/jpeg", + "http-req-size": 1024000, + "http-resp-headers": [], + "http-resp-size": 1024000, + "http-resp-content-type": "image/png" +} +{ + "active": "yes", + "name": "resize_large", + "path": "resize_wasm.so", + "port": 10002, + "relative-deadline-us": 50000, + "argsize": 1, + "http-req-headers": [], + "http-req-content-type": "image/jpeg", + "http-req-size": 1024000, + "http-resp-headers": [], + "http-resp-size": 1024000, + "http-resp-content-type": "image/png" +} diff --git a/runtime/experiments/applications/imageresize/test/.gitignore b/runtime/experiments/applications/imageresize/test/.gitignore new file mode 100644 index 0000000..978a56e --- /dev/null +++ b/runtime/experiments/applications/imageresize/test/.gitignore @@ -0,0 +1 @@ +result_*.png diff --git a/runtime/experiments/applications/imageresize/test/README.md b/runtime/experiments/applications/imageresize/test/README.md new file mode 100644 index 0000000..94508fb --- /dev/null +++ b/runtime/experiments/applications/imageresize/test/README.md @@ -0,0 +1,5 @@ +# Resize Images + +This script runs several test executions of the resize application, testing that the output is pixel-for-pixel identical to a known good output. + +The workload works sporadically, but the runtime errors out due to calls to `mremap`. The runtime gratuitously logs these calls for the time being. diff --git a/runtime/experiments/applications/imageresize/test/debug.sh b/runtime/experiments/applications/imageresize/test/debug.sh new file mode 100755 index 0000000..6ab3666 --- /dev/null +++ b/runtime/experiments/applications/imageresize/test/debug.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# Executes the runtime in GDB +# Substitutes the absolute path from the container with a path relatively derived from the location of this script +# This allows debugging outside of the Docker container +# Also disables pagination and stopping on SIGUSR1 + +experiment_directory=$(pwd) +project_directory=$(cd ../../../.. && pwd) +binary_directory=$(cd "$project_directory"/bin && pwd) + +export LD_LIBRARY_PATH="$binary_directory:$LD_LIBRARY_PATH" +export PATH="$binary_directory:$PATH" + +gdb --eval-command="handle SIGUSR1 nostop" \ + --eval-command="handle SIGPIPE nostop" \ + --eval-command="set pagination off" \ + --eval-command="set substitute-path /sledge/runtime $project_directory" \ + --eval-command="run $experiment_directory/spec.json" \ + sledgert diff --git a/runtime/experiments/applications/imageresize/test/expected_result.png b/runtime/experiments/applications/imageresize/test/expected_result.png new file mode 100644 index 0000000..1f95aae Binary files /dev/null and b/runtime/experiments/applications/imageresize/test/expected_result.png differ diff --git a/runtime/experiments/applications/imageresize/test/flower.jpg b/runtime/experiments/applications/imageresize/test/flower.jpg new file mode 100644 index 0000000..de1fc28 Binary files /dev/null and b/runtime/experiments/applications/imageresize/test/flower.jpg differ diff --git a/runtime/experiments/applications/imageresize/run.sh b/runtime/experiments/applications/imageresize/test/run.sh similarity index 58% rename from runtime/experiments/applications/imageresize/run.sh rename to runtime/experiments/applications/imageresize/test/run.sh index 3f70483..ebdee98 100755 --- a/runtime/experiments/applications/imageresize/run.sh +++ b/runtime/experiments/applications/imageresize/test/run.sh @@ -5,12 +5,12 @@ # Also disables pagination and stopping on SIGUSR1 experiment_directory=$(pwd) -project_directory=$(cd ../../.. && pwd) +project_directory=$(cd ../../../.. && pwd) binary_directory=$(cd "$project_directory"/bin && pwd) # Copy Flower Image if not here if [[ ! -f "./flower.jpg" ]]; then - cp ../../../tests/sod/bin/flower.jpg ./flower.jpg + cp ../../../../tests/sod/bin/flower.jpg ./flower.jpg fi if [ "$1" != "-d" ]; then @@ -20,42 +20,31 @@ else echo "Running under gdb" fi -# expected_result="$(cat ./expected_result.jpg)" -expected_size="$(find expected_result.jpg -printf "%s")" success_count=0 -total_count=50 - -# curl -H 'Expect:' -H "Content-Type: image/jpg" --data-binary "@flower.jpg" --output result.jpg localhost:10000 - -# WIP -# exit +total_count=10 for ((i = 0; i < total_count; i++)); do echo "$i" ext="$RANDOM" - curl -H 'Expect:' -H "Content-Type: image/jpg" --data-binary "@flower.jpg" --output "result_$ext.jpg" localhost:10000 2>/dev/null - actual_size="$(find result_"$ext".jpg -printf "%s")" + curl -H 'Expect:' -H "Content-Type: image/jpg" --data-binary "@flower.jpg" --output "result_$ext.png" localhost:10000 2>/dev/null 1>/dev/null + + pixel_differences="$(compare -identify -metric AE "result_$ext.png" expected_result.png null: 2>&1 >/dev/null)" - # echo "$result" - if [[ "$expected_size" == "$actual_size" ]]; then + if [[ "$pixel_differences" == "0" ]]; then success_count=$((success_count + 1)) - rm result.jpg else echo "FAIL" - echo "Expected Size:" - echo "$expected_size" - echo "===============================================" - echo "Actual Size:" - echo "$actual_size" + echo "$pixel_differences pixel differences detected" + exit fi done echo "$success_count / $total_count" +rm result_*.png if [ "$1" != "-d" ]; then sleep 5 echo -n "Running Cleanup: " - rm result_*.jpg pkill sledgert >/dev/null 2>/dev/null echo "[DONE]" fi diff --git a/runtime/experiments/applications/imageresize/spec.json b/runtime/experiments/applications/imageresize/test/spec.json similarity index 82% rename from runtime/experiments/applications/imageresize/spec.json rename to runtime/experiments/applications/imageresize/test/spec.json index 2088364..29d0904 100644 --- a/runtime/experiments/applications/imageresize/spec.json +++ b/runtime/experiments/applications/imageresize/test/spec.json @@ -7,8 +7,8 @@ "argsize": 1, "http-req-headers": [], "http-req-content-type": "image/jpeg", - "http-req-size": 102400, + "http-req-size": 1024000, "http-resp-headers": [], - "http-resp-size": 102400, + "http-resp-size": 1024000, "http-resp-content-type": "image/png" } diff --git a/runtime/include/client_socket.h b/runtime/include/client_socket.h index 5ca0868..98f01d5 100644 --- a/runtime/include/client_socket.h +++ b/runtime/include/client_socket.h @@ -15,7 +15,12 @@ static inline void client_socket_close(int client_socket) { - if (close(client_socket) < 0) debuglog("Error closing client socket - %s", strerror(errno)); + /* Should never close 0, 1, or 2 */ + assert(client_socket != STDIN_FILENO); + assert(client_socket != STDOUT_FILENO); + assert(client_socket != STDERR_FILENO); + + if (unlikely(close(client_socket) < 0)) debuglog("Error closing client socket - %s", strerror(errno)); } @@ -50,6 +55,8 @@ client_socket_send(int client_socket, int status_code) if (rc < 0) { if (errno == EAGAIN) { debuglog("Unexpectedly blocking on write of %s\n", response); } + debuglog("Error with %s\n", strerror(errno)); + goto send_err; } sent += rc; diff --git a/runtime/src/libc/syscall.c b/runtime/src/libc/syscall.c index 9540bd1..d54c5a1 100644 --- a/runtime/src/libc/syscall.c +++ b/runtime/src/libc/syscall.c @@ -237,7 +237,11 @@ wasm_open(int32_t path_off, int32_t flags, int32_t mode) int32_t wasm_close(int32_t io_handle_index) { - int fd = current_sandbox_get_file_descriptor(io_handle_index); + int fd = current_sandbox_get_file_descriptor(io_handle_index); + + // Silently disregard client requests to close STDIN, STDOUT, or STDERR + if (fd <= STDERR_FILENO) return 0; + int32_t res = (int32_t)close(fd); if (res == -1) return -errno; @@ -504,6 +508,26 @@ wasm_writev(int32_t fd, int32_t iov_offset, int32_t iovcnt) return res; } +#define SYS_MREMAP 25 +#define MREMAP_MAYMOVE 1 +#define MREMAP_FIXED 2 +int32_t +wasm_mremap(int32_t offset, int32_t old_size, int32_t new_size, int32_t flags) +{ + /* Should fit within the 32-bit linear address space */ + /* TODO: Improve with errno */ + assert(offset + old_size < INT32_MAX); + assert(new_size < INT32_MAX); + + /* Not really implemented, so dump out usage to understand requirements */ + debuglog("Offset: %d, Old Size: %d, New Size: %d, May Move: %s, Fixed: %s\n", offset, old_size, new_size, + (flags & MREMAP_MAYMOVE) == MREMAP_MAYMOVE ? "true" : "false", + (flags & MREMAP_FIXED) == MREMAP_FIXED ? "true" : "false"); + + /* Return the current offset, hoping for the best */ + return offset; +} + #define SYS_MADVISE 28 #define SYS_GETPID 39 @@ -730,6 +754,8 @@ inner_syscall_handler(int32_t n, int32_t a, int32_t b, int32_t c, int32_t d, int case SYS_SET_THREAD_AREA: case SYS_SET_TID_ADDRESS: case SYS_BRK: + case SYS_MREMAP: + return wasm_mremap(a, b, c, b); case SYS_MADVISE: /* Note: These are called, but are unimplemented and fail silently */ return 0; diff --git a/runtime/src/module.c b/runtime/src/module.c index 710d073..03439ab 100644 --- a/runtime/src/module.c +++ b/runtime/src/module.c @@ -34,16 +34,20 @@ module_listen(struct module *module) /* Allocate a new TCP/IP socket, setting it to be non-blocking */ int socket_descriptor = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); - if (socket_descriptor < 0) goto err_create_socket; + if (unlikely(socket_descriptor < 0)) goto err_create_socket; + /* Socket should never have returned on fd 0, 1, or 2 */ + assert(socket_descriptor != STDIN_FILENO); + assert(socket_descriptor != STDOUT_FILENO); + assert(socket_descriptor != STDERR_FILENO); /* Configure the socket to allow multiple sockets to bind to the same host and port */ int optval = 1; rc = setsockopt(socket_descriptor, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); - if (rc < 0) goto err_set_socket_option; + if (unlikely(rc < 0)) goto err_set_socket_option; optval = 1; rc = setsockopt(socket_descriptor, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); - if (rc < 0) goto err_set_socket_option; + if (unlikely(rc < 0)) goto err_set_socket_option; /* Bind name [all addresses]:[module->port] to socket */ module->socket_descriptor = socket_descriptor; @@ -51,11 +55,11 @@ module_listen(struct module *module) module->socket_address.sin_addr.s_addr = htonl(INADDR_ANY); module->socket_address.sin_port = htons((unsigned short)module->port); rc = bind(socket_descriptor, (struct sockaddr *)&module->socket_address, sizeof(module->socket_address)); - if (rc < 0) goto err_bind_socket; + if (unlikely(rc < 0)) goto err_bind_socket; /* Listen to the interface */ rc = listen(socket_descriptor, MODULE_MAX_PENDING_CLIENT_REQUESTS); - if (rc < 0) goto err_listen; + if (unlikely(rc < 0)) goto err_listen; /* Set the socket descriptor and register with our global epoll instance to monitor for incoming HTTP @@ -64,7 +68,7 @@ module_listen(struct module *module) accept_evt.data.ptr = (void *)module; accept_evt.events = EPOLLIN; rc = epoll_ctl(runtime_epoll_file_descriptor, EPOLL_CTL_ADD, module->socket_descriptor, &accept_evt); - if (rc < 0) goto err_add_to_epoll; + if (unlikely(rc < 0)) goto err_add_to_epoll; rc = 0; done: diff --git a/runtime/src/runtime.c b/runtime/src/runtime.c index 8b4fe87..b840d32 100644 --- a/runtime/src/runtime.c +++ b/runtime/src/runtime.c @@ -152,12 +152,17 @@ listener_thread_main(void *dummy) int client_socket = accept4(module->socket_descriptor, (struct sockaddr *)&client_address, &address_length, SOCK_NONBLOCK); - if (client_socket < 0) { + if (unlikely(client_socket < 0)) { if (errno == EWOULDBLOCK || errno == EAGAIN) break; panic("accept4: %s", strerror(errno)); } + /* We should never have accepted on fd 0, 1, or 2 */ + assert(client_socket != STDIN_FILENO); + assert(client_socket != STDOUT_FILENO); + assert(client_socket != STDERR_FILENO); + /* * According to accept(2), it is possible that the the sockaddr structure client_address * may be too small, resulting in data being truncated to fit. The appect call mutates diff --git a/runtime/tools/httpclient/client.c b/runtime/tools/httpclient/client.c index a4b7729..0113bd6 100644 --- a/runtime/tools/httpclient/client.c +++ b/runtime/tools/httpclient/client.c @@ -169,11 +169,16 @@ connect_n_send(void) { int socket_descriptor; - if ((socket_descriptor = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + if (unlikely((socket_descriptor = socket(AF_INET, SOCK_STREAM, 0)) < 0)) { perror("socket"); return -1; } + /* Should never receive sockets 0, 1, or 2 */ + assert(socket_descriptor != STDIN_FILENO); + assert(socket_descriptor != STDOUT_FILENO); + assert(socket_descriptor != STDERR_FILENO); + struct sockaddr_in socket_address; socket_address.sin_family = AF_INET; socket_address.sin_port = htons(SERVER_PORT);