From 7f038f1269d7a01d52df77e550525f1d78de3aa7 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Thu, 14 Apr 2022 13:52:06 -0400 Subject: [PATCH] refactor: jsonint parsing and module validation --- .vscode/settings.json | 3 +- runtime/include/admissions_info.h | 6 +- runtime/include/json.h | 127 ++++++++++++++++++------------ runtime/include/module.h | 10 +-- runtime/include/perf_window.h | 10 +-- runtime/src/admissions_info.c | 4 +- runtime/src/main.c | 3 + runtime/src/module.c | 78 ++++++++++-------- 8 files changed, 141 insertions(+), 100 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 742e0bb..79706f3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -111,7 +111,8 @@ "wasm_memory.h": "c", "sledge_abi.h": "c", "vec.h": "c", - "module_database.h": "c" + "module_database.h": "c", + "perf_window_t.h": "c" }, "files.exclude": { "**/.git": true, diff --git a/runtime/include/admissions_info.h b/runtime/include/admissions_info.h index 0a941c5..d2e0dfb 100644 --- a/runtime/include/admissions_info.h +++ b/runtime/include/admissions_info.h @@ -4,12 +4,12 @@ struct admissions_info { struct perf_window perf_window; - int percentile; /* 50 - 99 */ + uint8_t percentile; /* 50 - 99 */ int control_index; /* Precomputed Lookup index when perf_window is full */ uint64_t estimate; /* cycles */ uint64_t relative_deadline; /* Relative deadline in cycles. This is duplicated state */ }; -void admissions_info_initialize(struct admissions_info *admissions_info, int percentile, uint64_t expected_execution, - uint64_t relative_deadline); +void admissions_info_initialize(struct admissions_info *admissions_info, uint8_t percentile, + uint64_t expected_execution, uint64_t relative_deadline); void admissions_info_update(struct admissions_info *admissions_info, uint64_t execution_duration); diff --git a/runtime/include/json.h b/runtime/include/json.h index 0d66f58..0bc0124 100644 --- a/runtime/include/json.h +++ b/runtime/include/json.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -18,12 +19,12 @@ static const int JSON_MAX_ELEMENT_SIZE = 1024; struct module_config { char *name; char *path; - int port; + uint16_t port; + uint8_t admissions_percentile; uint32_t expected_execution_us; - int admissions_percentile; uint32_t relative_deadline_us; - int32_t http_req_size; - int32_t http_resp_size; + uint32_t http_req_size; + uint32_t http_resp_size; char *http_resp_content_type; }; @@ -32,9 +33,9 @@ print_module_config(struct module_config *config) { printf("Name: %s\n", config->name); printf("Path: %s\n", config->path); - printf("Port: %d\n", config->port); + printf("Port: %u\n", config->port); printf("expected_execution_us: %u\n", config->expected_execution_us); - printf("admissions_percentile: %d\n", config->admissions_percentile); + printf("admissions_percentile: %u\n", config->admissions_percentile); printf("relative_deadline_us: %u\n", config->relative_deadline_us); printf("http_req_size: %u\n", config->http_req_size); printf("http_resp_size: %u\n", config->http_resp_size); @@ -112,6 +113,51 @@ is_valid_key(jsmntok_t tok) return true; } +static inline int +parse_uint8_t(jsmntok_t tok, const char *json_buf, const char *key, uint8_t *ret) +{ + char *end = NULL; + intmax_t temp = strtoimax(&json_buf[tok.start], &end, 10); + + if (end != &json_buf[tok.end] || temp < 0 || temp > UINT8_MAX) { + fprintf(stderr, "Unable to parse uint8_t for key %s\n", key); + return -1; + } + + *ret = (uint8_t)temp; + return 0; +} + +static inline int +parse_uint16_t(jsmntok_t tok, const char *json_buf, const char *key, uint16_t *ret) +{ + char *end = NULL; + intmax_t temp = strtoimax(&json_buf[tok.start], &end, 10); + + if (end != &json_buf[tok.end] || temp < 0 || temp > UINT16_MAX) { + fprintf(stderr, "Unable to parse uint16_t for key %s\n", key); + return -1; + } + + *ret = (uint16_t)temp; + return 0; +} + +static inline int +parse_uint32_t(jsmntok_t tok, const char *json_buf, const char *key, uint32_t *ret) +{ + char *end = NULL; + uintmax_t temp = strtoimax(&json_buf[tok.start], &end, 10); + + if (end != &json_buf[tok.end] || temp < 0 || temp > UINT32_MAX) { + fprintf(stderr, "Unable to parse uint32_t for key %s\n", key); + return -1; + } + + *ret = (uint32_t)temp; + return 0; +} + /** * Parses a JSON file into an array of module configs * @param file_name The path of the JSON file @@ -167,8 +213,7 @@ parse_json(const char *json_buf, ssize_t json_buf_size, struct module_config **m int module_fields_remaining = 0; for (int i = 1; i < total_tokens; i++) { - char key[32] = { 0 }; - char val[256] = { 0 }; + char key[32] = { 0 }; /* Assumption: Objects are never used within a module_config. This likely will not be true in the * future due to routes or multiple entrypoints */ @@ -193,72 +238,54 @@ parse_json(const char *json_buf, ssize_t json_buf_size, struct module_config **m if (strcmp(key, "name") == 0) { if (!is_nonempty_string(tokens[i], key)) goto json_parse_err; - sprintf(val, "%.*s", tokens[i].end - tokens[i].start, json_buf + tokens[i].start); - (*module_config_vec)[module_idx].name = strndup(val, tokens[i].end - tokens[i].start); + (*module_config_vec)[module_idx].name = strndup(json_buf + tokens[i].start, + tokens[i].end - tokens[i].start); } else if (strcmp(key, "path") == 0) { if (!is_nonempty_string(tokens[i], key)) goto json_parse_err; - sprintf(val, "%.*s", tokens[i].end - tokens[i].start, json_buf + tokens[i].start); - (*module_config_vec)[module_idx].path = strndup(val, tokens[i].end - tokens[i].start); + (*module_config_vec)[module_idx].path = strndup(json_buf + tokens[i].start, + tokens[i].end - tokens[i].start); } else if (strcmp(key, "port") == 0) { if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE)) goto json_parse_err; - sprintf(val, "%.*s", tokens[i].end - tokens[i].start, json_buf + tokens[i].start); - int buffer = atoi(val); - if (buffer < 0 || buffer > 65535) - panic("Expected port between 0 and 65535, saw %d\n", buffer); - (*module_config_vec)[module_idx].port = buffer; + int rc = parse_uint16_t(tokens[i], json_buf, "port", + &(*module_config_vec)[module_idx].port); + if (rc < 0) goto json_parse_err; } else if (strcmp(key, "expected-execution-us") == 0) { if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE)) goto json_parse_err; - sprintf(val, "%.*s", tokens[i].end - tokens[i].start, json_buf + tokens[i].start); - int64_t buffer = strtoll(val, NULL, 10); - if (buffer < 0 || buffer > (int64_t)RUNTIME_EXPECTED_EXECUTION_US_MAX) - panic("Relative-deadline-us must be between 0 and %ld, was %ld\n", - (int64_t)RUNTIME_EXPECTED_EXECUTION_US_MAX, buffer); - (*module_config_vec)[module_idx].expected_execution_us = (uint32_t)buffer; + int rc = parse_uint32_t(tokens[i], json_buf, "expected-execution-us", + &(*module_config_vec)[module_idx].expected_execution_us); + if (rc < 0) goto json_parse_err; } else if (strcmp(key, "admissions-percentile") == 0) { if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE)) goto json_parse_err; - sprintf(val, "%.*s", tokens[i].end - tokens[i].start, json_buf + tokens[i].start); - int32_t buffer = strtol(val, NULL, 10); - if (buffer > 99 || buffer < 50) - panic("admissions-percentile must be > 50 and <= 99 but was %d\n", buffer); - (*module_config_vec)[module_idx].admissions_percentile = buffer; + int rc = parse_uint8_t(tokens[i], json_buf, "admissions-percentile", + &(*module_config_vec)[module_idx].admissions_percentile); + if (rc < 0) goto json_parse_err; } else if (strcmp(key, "relative-deadline-us") == 0) { if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE)) goto json_parse_err; - sprintf(val, "%.*s", tokens[i].end - tokens[i].start, json_buf + tokens[i].start); - int64_t buffer = strtoll(val, NULL, 10); - if (buffer < 0 || buffer > (int64_t)RUNTIME_RELATIVE_DEADLINE_US_MAX) - panic("Relative-deadline-us must be between 0 and %ld, was %ld\n", - (int64_t)RUNTIME_RELATIVE_DEADLINE_US_MAX, buffer); - (*module_config_vec)[module_idx].relative_deadline_us = (uint32_t)buffer; + int rc = parse_uint32_t(tokens[i], json_buf, "relative-deadline-us", + &(*module_config_vec)[module_idx].relative_deadline_us); + if (rc < 0) goto json_parse_err; } else if (strcmp(key, "http-req-size") == 0) { if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE)) goto json_parse_err; - sprintf(val, "%.*s", tokens[i].end - tokens[i].start, json_buf + tokens[i].start); - int64_t buffer = strtoll(val, NULL, 10); - if (buffer < 0 || buffer > RUNTIME_HTTP_REQUEST_SIZE_MAX) - panic("http-req-size must be between 0 and %ld, was %ld\n", - (int64_t)RUNTIME_HTTP_REQUEST_SIZE_MAX, buffer); - (*module_config_vec)[module_idx].http_req_size = (int32_t)buffer; + int rc = parse_uint32_t(tokens[i], json_buf, "http-req-size", + &(*module_config_vec)[module_idx].http_req_size); + if (rc < 0) goto json_parse_err; } else if (strcmp(key, "http-resp-size") == 0) { if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE)) goto json_parse_err; - sprintf(val, "%.*s", tokens[i].end - tokens[i].start, json_buf + tokens[i].start); - int64_t buffer = strtoll(val, NULL, 10); - if (buffer < 0 || buffer > RUNTIME_HTTP_REQUEST_SIZE_MAX) - panic("http-req-size must be between 0 and %ld, was %ld\n", - (int64_t)RUNTIME_HTTP_RESPONSE_SIZE_MAX, buffer); - (*module_config_vec)[module_idx].http_resp_size = (int32_t)buffer; + int rc = parse_uint32_t(tokens[i], json_buf, "http-resp-size", + &(*module_config_vec)[module_idx].http_resp_size); + if (rc < 0) goto json_parse_err; } else if (strcmp(key, "http-resp-content-type") == 0) { if (!has_valid_type(tokens[i], key, JSMN_STRING)) goto json_parse_err; - sprintf(val, "%.*s", tokens[i].end - tokens[i].start, json_buf + tokens[i].start); - (*module_config_vec)[module_idx].http_resp_content_type = strndup(val, - tokens[i].end - - tokens[i].start); + (*module_config_vec)[module_idx].http_resp_content_type = + strndup(json_buf + tokens[i].start, tokens[i].end - tokens[i].start); } else { fprintf(stderr, "%s is not a valid key\n", key); goto json_parse_err; diff --git a/runtime/include/module.h b/runtime/include/module.h index d7e6f32..c8de37a 100644 --- a/runtime/include/module.h +++ b/runtime/include/module.h @@ -57,7 +57,7 @@ struct module { char path[MODULE_MAX_PATH_LENGTH]; uint32_t stack_size; /* a specification? */ uint32_t relative_deadline_us; - int port; + uint16_t port; struct admissions_info admissions_info; uint64_t relative_deadline; /* cycles */ @@ -241,7 +241,7 @@ module_free_linear_memory(struct module *module, struct wasm_memory *memory) * Public Methods from module.c * *******************************/ -void module_free(struct module *module); -struct module * -module_alloc(char *mod_name, char *mod_path, uint32_t stack_sz, uint32_t relative_deadline_us, int port, int req_sz, - int resp_sz, int admissions_percentile, uint32_t expected_execution_us, char *response_content_type); +void module_free(struct module *module); +struct module *module_alloc(char *name, char *path, uint32_t stack_size, uint32_t relative_deadline_us, uint16_t port, + uint32_t request_size, uint32_t response_size, uint8_t admissions_percentile, + uint32_t expected_execution_us, char *response_content_type); diff --git a/runtime/include/perf_window.h b/runtime/include/perf_window.h index b796fbe..0045a97 100644 --- a/runtime/include/perf_window.h +++ b/runtime/include/perf_window.h @@ -145,16 +145,16 @@ done: * @returns execution time */ static inline uint64_t -perf_window_get_percentile(struct perf_window *perf_window, int percentile, int precomputed_index) +perf_window_get_percentile(struct perf_window *perf_window, uint8_t percentile, int precomputed_index) { assert(perf_window != NULL); assert(percentile >= 50 && percentile <= 99); - int size = perf_window->count; - assert(size > 0); + assert(perf_window->count > 0); - if (likely(size >= PERF_WINDOW_BUFFER_SIZE)) return perf_window->by_duration[precomputed_index].execution_time; + if (likely(perf_window->count >= PERF_WINDOW_BUFFER_SIZE)) + return perf_window->by_duration[precomputed_index].execution_time; - return perf_window->by_duration[size * percentile / 100].execution_time; + return perf_window->by_duration[perf_window->count * percentile / 100].execution_time; } /** diff --git a/runtime/src/admissions_info.c b/runtime/src/admissions_info.c index ae2279a..0f59cf7 100644 --- a/runtime/src/admissions_info.c +++ b/runtime/src/admissions_info.c @@ -6,7 +6,7 @@ * @param admissions_info */ void -admissions_info_initialize(struct admissions_info *admissions_info, int percentile, uint64_t expected_execution, +admissions_info_initialize(struct admissions_info *admissions_info, uint8_t percentile, uint64_t expected_execution, uint64_t relative_deadline) { #ifdef ADMISSIONS_CONTROL @@ -24,7 +24,7 @@ admissions_info_initialize(struct admissions_info *admissions_info, int percenti admissions_info->control_index = PERF_WINDOW_BUFFER_SIZE * percentile / 100; #ifdef LOG_ADMISSIONS_CONTROL - debuglog("Percentile: %d\n", admissions_info->percentile); + debuglog("Percentile: %u\n", admissions_info->percentile); debuglog("Control Index: %d\n", admissions_info->control_index); #endif #endif diff --git a/runtime/src/main.c b/runtime/src/main.c index 6817c19..603b1f5 100644 --- a/runtime/src/main.c +++ b/runtime/src/main.c @@ -475,6 +475,9 @@ main(int argc, char **argv) module_config_vec[module_idx].expected_execution_us, module_config_vec[module_idx].http_resp_content_type); if (unlikely(module == NULL)) panic("failed to initialize module(s) defined in %s\n", json_path); + /* Start listening for requests */ + int rc = module_listen(module); + if (rc < 0) exit(-1); } for (int i = 0; i < runtime_worker_threads_count; i++) { diff --git a/runtime/src/module.c b/runtime/src/module.c index ec13d05..50ba095 100644 --- a/runtime/src/module.c +++ b/runtime/src/module.c @@ -108,12 +108,49 @@ module_free(struct module *module) } static inline int -module_init(struct module *module, char *name, char *path, uint32_t stack_size, uint32_t relative_deadline_us, int port, - int request_size, int response_size, int admissions_percentile, uint32_t expected_execution_us, - char *response_content_type) +module_init(struct module *module, char *name, char *path, uint32_t stack_size, uint32_t relative_deadline_us, + uint16_t port, uint32_t request_size, uint32_t response_size, uint8_t admissions_percentile, + uint32_t expected_execution_us, char *response_content_type) { assert(module != NULL); + /* Validate presence of required fields */ + if (strlen(name) == 0) panic("name field is required\n"); + if (strlen(path) == 0) panic("path field is required\n"); + if (port == 0) panic("port field is required\n"); + + if (relative_deadline_us > (uint32_t)RUNTIME_RELATIVE_DEADLINE_US_MAX) + panic("Relative-deadline-us must be between 0 and %u, was %u\n", + (uint32_t)RUNTIME_RELATIVE_DEADLINE_US_MAX, relative_deadline_us); + + if (request_size > RUNTIME_HTTP_REQUEST_SIZE_MAX) + panic("request_size must be between 0 and %u, was %u\n", (uint32_t)RUNTIME_HTTP_REQUEST_SIZE_MAX, + request_size); + + if (response_size > RUNTIME_HTTP_RESPONSE_SIZE_MAX) + panic("response-size must be between 0 and %u, was %u\n", (uint32_t)RUNTIME_HTTP_RESPONSE_SIZE_MAX, + response_size); + +#ifdef ADMISSIONS_CONTROL + /* expected-execution-us and relative-deadline-us are required in case of admissions control */ + if (expected_execution_us == 0) panic("expected-execution-us is required\n"); + if (relative_deadline_us == 0) panic("relative_deadline_us is required\n"); + + if (admissions_percentile > 99 || admissions_percentile < 50) + panic("admissions-percentile must be > 50 and <= 99 but was %u\n", admissions_percentile); + + /* If the ratio is too big, admissions control is too coarse */ + uint32_t ratio = relative_deadline_us / expected_execution_us; + if (ratio > ADMISSIONS_CONTROL_GRANULARITY) + panic("Ratio of Deadline to Execution time cannot exceed admissions control " + "granularity of " + "%d\n", + ADMISSIONS_CONTROL_GRANULARITY); +#else + /* relative-deadline-us is required if scheduler is EDF */ + if (scheduler == SCHEDULER_EDF && relative_deadline_us == 0) panic("relative_deadline_us is required\n"); +#endif + int rc = 0; atomic_init(&module->reference_count, 0); @@ -149,11 +186,6 @@ module_init(struct module *module, char *name, char *path, uint32_t stack_size, module_alloc_table(module); module_initialize_pools(module); - - /* Start listening for requests */ - rc = module_listen(module); - if (rc < 0) goto err; - done: return rc; err: @@ -163,8 +195,7 @@ err: /** * Module Contructor - * Creates a new module, invokes initialize_tables to initialize the indirect table, adds it to the module DB, and - *starts listening for HTTP Requests + * Creates a new module, invokes initialize_tables to initialize the indirect table, and adds it to the module DB * * @param name * @param path @@ -176,31 +207,10 @@ err: */ struct module * -module_alloc(char *name, char *path, uint32_t stack_size, uint32_t relative_deadline_us, int port, int request_size, - int response_size, int admissions_percentile, uint32_t expected_execution_us, char *response_content_type) +module_alloc(char *name, char *path, uint32_t stack_size, uint32_t relative_deadline_us, uint16_t port, + uint32_t request_size, uint32_t response_size, uint8_t admissions_percentile, + uint32_t expected_execution_us, char *response_content_type) { - /* Validate presence of required fields */ - if (strlen(name) == 0) panic("name field is required\n"); - if (strlen(path) == 0) panic("path field is required\n"); - if (port == 0) panic("port field is required\n"); - -#ifdef ADMISSIONS_CONTROL - /* expected-execution-us and relative-deadline-us are required in case of admissions control */ - if (expected_execution_us == 0) panic("expected-execution-us is required\n"); - if (relative_deadline_us == 0) panic("relative_deadline_us is required\n"); - - /* If the ratio is too big, admissions control is too coarse */ - uint32_t ratio = relative_deadline_us / expected_execution_us; - if (ratio > ADMISSIONS_CONTROL_GRANULARITY) - panic("Ratio of Deadline to Execution time cannot exceed admissions control " - "granularity of " - "%d\n", - ADMISSIONS_CONTROL_GRANULARITY); -#else - /* relative-deadline-us is required if scheduler is EDF */ - if (scheduler == SCHEDULER_EDF && relative_deadline_us == 0) panic("relative_deadline_us is required\n"); -#endif - struct module *module = (struct module *)calloc(1, sizeof(struct module)); if (!module) { fprintf(stderr, "Failed to allocate module: %s\n", strerror(errno));