refactor: jsonint parsing and module validation

master
Sean McBride 3 years ago
parent a07533bd2c
commit 7f038f1269

@ -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,

@ -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);

@ -1,6 +1,7 @@
#pragma once
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
@ -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;

@ -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);

@ -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;
}
/**

@ -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

@ -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++) {

@ -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));

Loading…
Cancel
Save