Compare commits

..

1 Commits

Author SHA1 Message Date
Sean McBride a6649365a5 refactor: WIP replace vec_u8 with memstream
3 years ago

@ -1,3 +1,4 @@
---
BasedOnStyle: Mozilla
IndentWidth: 8
Language: Cpp
@ -9,10 +10,10 @@ AlignConsecutiveMacros: true
AlignEscapedNewlines: Left
AlignTrailingComments: true
AllowShortBlocksOnASingleLine: Always
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterReturnType: AllDefinitions
@ -37,7 +38,7 @@ BreakBeforeBinaryOperators: NonAssignment
ColumnLimit: 120
Cpp11BracedListStyle: true
Cpp11BracedListStyle: false
IndentCaseLabels: false
IndentWrappedFunctionNames: false
@ -46,9 +47,10 @@ KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 2
DerivePointerAlignment: false
PointerAlignment: Right
SortIncludes: true
SortIncludes: false
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true

@ -4,7 +4,7 @@ on: [push, pull_request]
env:
LLVM_VERSION: 13
WASI_SDK_VERSION: 12
WASI_SDK_URL: https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-linux.tar.gz
WASI_SDK_PATH: /opt/wasi-sdk
LANG: C.UTF-8
LANGUAGE: C.UTF-8
@ -13,18 +13,18 @@ env:
# job control
jobs:
format:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- name: Apt Update
run: sudo apt-get update
- uses: actions/checkout@v2
- name: Install Clang Format
- name: Install LLVM
run: |
sudo ./install_llvm.sh $LLVM_VERSION
- name: Run Clang Format
- name: Clang Format
run: ./format.sh -d
test:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- name: Apt Update
run: sudo apt-get update
@ -62,7 +62,6 @@ jobs:
echo "/root/.cargo/bin:$PATH" >> $GITHUB_PATH
- name: Get wasi-sdk
run: |
WASI_SDK_URL=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$WASI_SDK_VERSION/wasi-sdk-$WASI_SDK_VERSION.0-linux.tar.gz
wget $WASI_SDK_URL -O wasi-sdk.tar.gz
mkdir -p $WASI_SDK_PATH
tar xvfz wasi-sdk.tar.gz --strip-components=1 -C $WASI_SDK_PATH
@ -95,9 +94,6 @@ jobs:
- name: Compile SLEdge
run: |
make runtime
- name: Install wasm_apps link
run: |
make wasm_apps
# TODO:Cache assets before being copied to ./runtime/bin
- name: Cache gocr
uses: actions/cache@v2

1
.gitignore vendored

@ -52,7 +52,6 @@ dkms.conf
runtime/tags
runtime/bin
applications/wasm_apps
applications/tmp/
applications/**/*.csv
applications/**/*.txt

4
.gitmodules vendored

@ -12,3 +12,7 @@
[submodule "jsmn"]
path = runtime/thirdparty/jsmn
url = https://github.com/gwsystems/jsmn.git
[submodule "wasm_apps"]
path = applications/wasm_apps
url = https://github.com/gwsystems/wasm_apps.git
branch = master

@ -1,23 +1,24 @@
{
"configurations": [
{
"name": "Linux",
"intelliSenseMode": "clang-x64",
"includePath": [
"/usr/include/",
"${workspaceFolder}/runtime/include/",
"${workspaceFolder}/runtime/thirdparty/ck/include/",
"${workspaceFolder}/runtime/thirdparty/http-parser/",
"${workspaceFolder}/runtime/thirdparty/jsmn/",
"${workspaceFolder}/awsm/runtime/libc/wasi/include/",
"${workspaceFolder}/libsledge/include"
],
"defines": [
"x86_64",
"_GNU_SOURCE"
],
"cStandard": "c17"
}
],
"version": 4
}
"configurations": [
{
"name": "Linux",
"intelliSenseMode": "clang-x64",
"includePath": [
"/usr/include/",
"${workspaceFolder}/runtime/include/",
"${workspaceFolder}/runtime/thirdparty/ck/include/",
"${workspaceFolder}/runtime/thirdparty/http-parser/",
"${workspaceFolder}/runtime/thirdparty/jsmn/",
"${workspaceFolder}/awsm/runtime/libc/wasi/include/",
"${workspaceFolder}/libsledge/include"
],
"defines": [
"x86_64",
"_GNU_SOURCE"
],
"cStandard": "c17",
"compilerPath": "/usr/bin/clang"
}
],
"version": 4
}

@ -116,7 +116,7 @@
"tenant.h": "c",
"route_config.h": "c",
"http_router.h": "c",
"execution_histogram.h": "c",
"admissions_info.h": "c",
"tcp_server.h": "c",
"stdint.h": "c",
"scheduler_options.h": "c",
@ -125,39 +125,7 @@
"pool.h": "c",
"local_cleanup_queue.h": "c",
"sandbox_state_transition.h": "c",
"http_session_perf_log.h": "c",
"traffic_control.h": "c",
"memory_resource": "c",
"memory": "c",
"istream": "c",
"ostream": "c",
"sstream": "c",
"streambuf": "c",
"sandbox_perf_log.h": "c",
"global_request_scheduler_deque.h": "c",
"message.h": "c",
"dbf.h": "c",
"dbf_generic.h": "c",
"tenant_functions.h": "c",
"thread": "c",
"limits": "c",
"algorithm": "c",
"stdio.h": "c",
"get_time.h": "c",
"unistd.h": "c",
"wasi.h": "c",
"stat.h": "c",
"functional": "c",
"sandbox_state.h": "c",
"ratio": "c",
"tuple": "c",
"type_traits": "c",
"perf_window.h": "c",
"http_route_total.h": "c",
"sledge_abi_symbols.h": "c",
"mutex": "c",
"lock.h": "c",
"route_latency.h": "c"
"http_session_perf_log.h": "c"
},
"files.exclude": {
"**/.git": true,
@ -219,6 +187,5 @@
"TKILL",
"WASI"
],
"C_Cpp.errorSquiggles": "Enabled",
"C_Cpp.default.compilerPath": "/usr/bin/clang"
"C_Cpp.errorSquiggles": "Enabled"
}

@ -1,12 +1,9 @@
# using ubuntu 20 docker image
FROM ubuntu:focal
ENV LLVM_VERSION=13
ENV WASI_SDK_VERSION=12
ARG DEBIAN_FRONTEND=noninteractive
ARG HEY_URL=https://hey-release.s3.us-east-2.amazonaws.com/hey_linux_amd64
ARG WASI_SDK_URL=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$WASI_SDK_VERSION/wasi-sdk_$WASI_SDK_VERSION.0_amd64.deb
ARG WASI_SDK_URL=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk_12.0_amd64.deb
ARG SHFMT_URL=https://github.com/mvdan/sh/releases/download/v3.2.4/shfmt_v3.2.4_linux_amd64
ARG SHELLCHECK_URL=https://github.com/koalaman/shellcheck/releases/download/stable/shellcheck-stable.linux.x86_64.tar.xz
@ -77,11 +74,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
vim \
wabt
ENV LLVM_VERSION=12
ADD install_llvm.sh /sledge/install_llvm.sh
RUN ./sledge/install_llvm.sh $LLVM_VERSION
# WASI-SDK
RUN curl -sS -L -O $WASI_SDK_URL && dpkg -i wasi-sdk_$WASI_SDK_VERSION.0_amd64.deb && rm -f wasi-sdk_$WASI_SDK_VERSION.0_amd64.deb
RUN curl -sS -L -O $WASI_SDK_URL && dpkg -i wasi-sdk_12.0_amd64.deb && rm -f wasi-sdk_12.0_amd64.deb
ENV WASI_SDK_PATH=/opt/wasi-sdk
# Create non-root user and add to sudoers

@ -11,7 +11,7 @@ submodules:
git submodule update --init --recursive
.PHONY: install
install: submodules wasm_apps all
install: submodules all
# aWsm: the WebAssembly to LLVM bitcode compiler
.PHONY: awsm
@ -50,10 +50,6 @@ applications:
applications.clean:
make -C applications clean
# Instead of having two copies of wasm_apps, just link to the awsm repo's copy
wasm_apps:
ln -sr awsm/applications/wasm_apps/ applications/
# Tests
.PHONY: test
test:

@ -80,34 +80,26 @@ And then simply delete this repository.
An SLEdge serverless function consists of a shared library (\*.so) and a JSON configuration file that determines how the runtime should execute the serverless function. As an example, here is the configuration file for our sample fibonacci function:
```json
[
{
"name": "GWU",
"port": 10010,
"routes": [
{
"route": "/fib",
"path": "fibonacci.wasm.so",
"expected-execution-us": 6000,
"relative-deadline-us": 20000,
"http-resp-content-type": "text/plain"
}
]
}
]
{
"name": "fibonacci",
"path": "fibonacci.wasm.so",
"port": 10000,
"expected-execution-us": 600,
"relative-deadline-us": 2000,
"http-req-size": 1024,
"http-resp-size": 1024,
"http-resp-content-type": "text/plain"
}
```
The `port` and `route` fields are used to determine the path where our serverless function will be served served.
The `port` and `name` fields are used to determine the path where our serverless function will be served served.
In our case, we are running the SLEdge runtime on localhost, so our function is available at `localhost:10010/fib`.
In our case, we are running the SLEdge runtime on localhost, so our function is available at `http://localhost:10000/fibonacci`
Our fibonacci function will parse a single argument from the HTTP POST body that we send. The expected Content-Type is "text/plain".
Our fibonacci function will parse a single argument from the HTTP POST body that we send. The expected Content-Type is "text/plain" and the buffer is sized to 1024 bytes for both the request and response. This is sufficient for our simple Fibonacci function, but this must be changed and sized for other functions, such as image processing.
Now that we understand roughly how the SLEdge runtime interacts with serverless function, let's run Fibonacci!
The fastest way to check it out is just to click on the following URL on your Web browser: [http://localhost:10010/fib?10](http://localhost:10010/fib?10)
From the root project directory of the host environment (not the Docker container!), navigate to the binary directory
```bash
@ -117,33 +109,25 @@ cd runtime/bin/
Now run the sledgert binary, passing the JSON file of the serverless function we want to serve. Because serverless functions are loaded by SLEdge as shared libraries, we want to add the `applications/` directory to LD_LIBRARY_PATH.
```bash
LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../../tests/fibonacci/bimodal/spec.json
LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_fibonacci.json
```
While you don't see any output to the console, the runtime is running in the foreground.
Let's now invoke our serverless function to compute the 10th fibonacci number. We'll use `cURL` and [HTTPie](https://httpie.org/) to send a HTTP GET and POST requests with the parameter we want to pass to my serverless function. Feel free to use whatever other network client you prefer!
Let's now invoke our serverless function to compute the 10th fibonacci number. I'll use [HTTPie](https://httpie.org/) to send a POST request with a body containing the parameter I want to pass to my serverless function. Feel free to use cURL or whatever network client you prefer!
Open a **new** terminal session and execute the following
Open a new terminal session and execute the following
```bash
# HTTP GET method:
http localhost:10010/fib?10
curl localhost:10010/fib?10
# HTTP POST method:
echo "10" | http POST localhost:10010/fib
curl -i -d 10 localhost:10010/fib
echo "10" | http :10000
```
You should receive the following in response. The serverless function says that the 10th fibonacci number is 55, which seems to be correct!
```bash
HTTP/1.1 200 OK
Server: SLEdge
Connection: close
Content-Type: text/plain
Content-Length: 3
Content-length: 3
Content-type: text/plain
55
```

@ -11,9 +11,6 @@ CFLAGS=-O3 -flto
LDFLAGS=-shared -fPIC -Wl,--export-dynamic,--whole-archive -L../libsledge/dist/ -lsledge -Wl,--no-whole-archive
# LDFLAGS=-flto -fvisibility=hidden
# Strips out calls to assert() and disables debuglog
CFLAGS+=-DNDEBUG
dist:
mkdir -p dist
@ -26,12 +23,15 @@ all: \
gps_ekf.install \
license_plate_detection.install \
resize_image.install \
cnn_face_detection.install \
get_jpeg_resolution.install \
scratch_storage_get.install \
scratch_storage_set.install \
scratch_storage_delete.install \
scratch_storage_upsert.install \
.PHONY: clean
clean:
@make -C wasm_apps clean
@make -C scratch_storage clean
@rm -rf dist
@rm -rf ../runtime/bin/*.so
@ -65,7 +65,7 @@ dist/%.bc: ./wasm_apps/dist/%.wasm dist
${AWSMCC} ${AWSMFLAGS} $< -o $@
dist/%.ll: dist/%.bc
llvm-dis $< -o $@
llvm-dis-12 $< -o $@
dist/%.wasm.so: dist/%.bc
${CC} ${CFLAGS} ${LDFLAGS} $^ -o $@
@ -102,12 +102,6 @@ gps_ekf.install: ../runtime/bin/gps_ekf.wasm.so
.PHONY: license_plate_detection.install
license_plate_detection.install: ../runtime/bin/license_plate_detection.wasm.so
.PHONY: cnn_face_detection.install
cnn_face_detection.install: ../runtime/bin/cnn_face_detection.wasm.so
.PHONY: get_jpeg_resolution.install
get_jpeg_resolution.install: ../runtime/bin/get_jpeg_resolution.wasm.so
.PHONY: trap_divzero.install
trap_divzero.install: ../runtime/bin/trap_divzero.wasm.so
@ -128,6 +122,3 @@ scratch_storage_delete.install: ../runtime/bin/scratch_storage_delete.wasm.so
.PHONY: scratch_storage_upsert.install
scratch_storage_upsert.install: ../runtime/bin/scratch_storage_upsert.wasm.so
.PHONY: depth_to_xyz.install
depth_to_xyz.install: ../runtime/bin/depth_to_xyz.wasm.so

@ -0,0 +1 @@
Subproject commit 0b9f67d75fd9dab652e1995e7adf91806080523b

@ -1 +1 @@
Subproject commit 272fcf42b6559ccb5c5213eb78edfc0f703520ab
Subproject commit 28f3cbfb45db367bab63fa0131865ea63ae98d4a

@ -1,9 +1,7 @@
#!/bin/bash
LLVM_VERSION=13
validate() {
utility="clang-format"
utility="clang-format-13"
utility_version="$("$utility" --version 2> /dev/null)" || {
echo "$utility not found in path!"
exit 1
@ -13,7 +11,7 @@ validate() {
declare -i major=0
declare -i minor=0
declare -i patch=0
declare -i required_major=$LLVM_VERSION
declare -i required_major=13
declare -i required_minor=0
declare -i required_patch=0
@ -45,14 +43,14 @@ help() {
dry_run() {
find runtime \
\( -path "runtime/thirdparty" \) -prune -false -o \
-type f \( -iname \*.h -o -iname \*.c -o -iname \*.s \) -print0 \
| xargs --null clang-format -Werror -n -ferror-limit=1
\( -path "runtime/thirdparty" -o -path "applications/gocr" -o -path "applications/TinyEKF" -o -path "applications/CMSIS_5_NN" -o -path "applications/sod" -o -path "applications/**/thirdparty" \) -prune -false -o \
-type f \( -iname \*.h -o -iname \*.c -o -iname \*.s \) -print \
| xargs clang-format -Werror -n -ferror-limit=0
}
format() {
find runtime \
\( -path "runtime/thirdparty" \) -prune -false -o \
\( -path "runtime/thirdparty" -o -path "applications/gocr" -o -path "applications/TinyEKF" -o -path "applications/CMSIS_5_NN" -o -path "applications/sod" -o -path "applications/**/thirdparty" \) -prune -false -o \
-type f \( -iname \*.h -o -iname \*.c -o -iname \*.s \) -print0 \
| xargs --null clang-format -i
}

@ -1,14 +1,12 @@
#!/bin/bash
# Note, wasi-sdk versions do NOT match llvm versions, e.g. wasi-sdk-12 actually uses llvm-11
LLVM_VERSION=13
WASI_SDK_VERSION=12
LLVM_VERSION=12
ARCH=$(uname -m)
ARCH=$(uname -p)
if [[ $ARCH = "x86_64" ]]; then
SHFMT_URL=https://github.com/mvdan/sh/releases/download/v3.4.3/shfmt_v3.4.3_linux_amd64
WASI_SDK_URL=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$WASI_SDK_VERSION/wasi-sdk_$WASI_SDK_VERSION.0_amd64.deb
WASI_SDK_URL=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk_12.0_amd64.deb
elif [[ $ARCH = "aarch64" ]]; then
SHFMT_URL=https://github.com/patrickvane/shfmt/releases/download/master/shfmt_linux_arm
echo "ARM64 support is still a work in progress!"
@ -66,7 +64,7 @@ wget $SHFMT_URL -O shfmt && chmod +x shfmt && sudo mv shfmt /usr/local/bin/shfmt
sudo ./install_llvm.sh $LLVM_VERSION
curl -sS -L -O $WASI_SDK_URL && sudo dpkg -i wasi-sdk_$WASI_SDK_VERSION.0_amd64.deb && rm -f wasi-sdk_$WASI_SDK_VERSION.0_amd64.deb
curl -sS -L -O $WASI_SDK_URL && sudo dpkg -i wasi-sdk_12.0_amd64.deb && rm -f wasi-sdk_12.0_amd64.deb
if [ -z "${WASI_SDK_PATH}" ]; then
export WASI_SDK_PATH=/opt/wasi-sdk

@ -8,28 +8,28 @@ echo "Installing LLVM $LLVM_VERSION"
# Script Installs clang, lldb, lld, and clangd
curl --proto '=https' --tlsv1.2 -sSf https://apt.llvm.org/llvm.sh | bash -s -- "$LLVM_VERSION"
# Installing "libc++-xx-dev" automagically installs "libc++1-xx", "libunwind-xx" and "libunwind-xx-dev"
apt-get install -y --no-install-recommends \
"libc++-$LLVM_VERSION-dev" \
"libc++abi-$LLVM_VERSION-dev" \
"libc++1-$LLVM_VERSION" \
"libunwind-$LLVM_VERSION" \
"libunwind-$LLVM_VERSION-dev" \
"clang-tools-$LLVM_VERSION" \
"clang-tidy-$LLVM_VERSION" \
"clang-format-$LLVM_VERSION"
update-alternatives --remove-all wasm-ld
update-alternatives --remove-all llvm-config
update-alternatives --remove-all llvm-objdump
update-alternatives --remove-all llvm-dis
update-alternatives --remove-all clang-format
update-alternatives --remove-all clang
update-alternatives --remove-all clang++
update-alternatives --remove-all clang-tidy
sudo update-alternatives --remove-all clang-format
sudo update-alternatives --remove-all clang
sudo update-alternatives --remove-all clang++
sudo update-alternatives --remove-all llvm-config
sudo update-alternatives --remove-all llvm-objdump
sudo update-alternatives --remove-all llvm-objdump
sudo update-alternatives --remove-all clang-tidy
update-alternatives --install /usr/bin/wasm-ld wasm-ld "/usr/bin/wasm-ld-$LLVM_VERSION" 100
update-alternatives --install /usr/bin/llvm-config llvm-config "/usr/bin/llvm-config-$LLVM_VERSION" 100
update-alternatives --install /usr/bin/llvm-objdump llvm-objdump "/usr/bin/llvm-objdump-$LLVM_VERSION" 100
update-alternatives --install /usr/bin/llvm-dis llvm-dis /usr/bin/llvm-dis-$LLVM_VERSION 100
update-alternatives --install /usr/bin/clang-format clang-format "/usr/bin/clang-format-$LLVM_VERSION" 100
update-alternatives --install /usr/bin/clang clang "/usr/bin/clang-$LLVM_VERSION" 100
update-alternatives --install /usr/bin/clang++ clang++ "/usr/bin/clang++-$LLVM_VERSION" 100
update-alternatives --install /usr/bin/llvm-config llvm-config "/usr/bin/llvm-config-$LLVM_VERSION" 100
update-alternatives --install /usr/bin/llvm-objdump llvm-objdump "/usr/bin/llvm-objdump-$LLVM_VERSION" 100
update-alternatives --install /usr/bin/clang-tidy clang-tidy "/usr/bin/clang-tidy-$LLVM_VERSION" 100
update-alternatives --install /usr/bin/wasm-ld wasm-ld "/usr/bin/wasm-ld-$LLVM_VERSION" 100

@ -1,11 +1,7 @@
CFILES := src/*.c
INCLUDES := -Iinclude/
# fPIC = Position Independent Code, necessary for linking to relative addresses.
CFLAGS := -fPIC -O3 -flto -ftls-model=initial-exec
# Strips out calls to assert() and disables debuglog
CFLAGS+=-DNDEBUG
CFLAGS := -fPIC -O3
# CFI Sanitizer
# CFLAGS+=-fvisibility=default -fsanitize=cfi

@ -15,7 +15,7 @@ libsledge defines a ABI between the sledgert runtime and a \*.so shared library
A SLEdge \*.so serverless module is generated by the latter portion of the aWsm/SLEdge toolchain.
The first portion of the toolchain is responsible for compiling a source program into a WebAssembly module. This is handled by standard compilers capable of emitting WebAssembly.
The second portion of the toolchain is the aWsm compiler, which generates a \*.bc file with a well defined ABI.
The second portion of the toolchain is the aWsm compiler, which generates a \*.bc file with a well defined ABI
The third portion of the toolchain is the LLVM compiler, which ingests a \*.bc file emitted by aWsm and the libsledge static library, and emits a SLEdge \*.so serverless module.
## Architecture
@ -24,12 +24,16 @@ In order to reduce the overhead of calling sledgert functions, libsledge operate
The `sledge_abi__wasm_module_instance` structure includes the WebAssembly function table and the WebAssembly linear memory. This subset was selected because the author believes that use of function pointers and linear memory is frequent enough that LTO when compiling the \*.so file is beneficial.
All WebAssembly state
## WebAssembly Instruction Implementation
Here is a list of WebAssembly instructions that depend on symbols from libsledge, libc, or sledgert (via the SLEdge ABI).
### [Control Instructions](https://webassembly.github.io/spec/core/syntax/instructions.html#control-instructions)
The ABI includes the
| Instruction | aWsm ABI | libc Dependencies | SLEdge ABI |
| ------------- | ------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| call_indirect | `get_function_from_table` | `stderr`, `fprintf` | `sledge_abi__current_wasm_module_instance.table`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_INVALID_INDEX`, `WASM_TRAP_MISMATCHED_TYPE` |
@ -111,6 +115,13 @@ Here is a list of WebAssembly instructions that depend on symbols from libsledge
| memory.size | `instruction_memory_size` | | `sledge_abi__current_wasm_module_instance.memory` |
| None | `initialize_region` | | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_memory_initialize_region` |
Discussion:
- Should `instruction_memory_grow` be moved into sledgert? This would simplify the handling of the "cache" and generating a memory profile?
- Rename `sledge_abi__wasm_globals_*` to `sledge_abi__wasm_global_*`
- Implement Unsupported Numeric Instructions
- Should the wasm global table be accessed directly instead of via a runtime function?
- Should the Function Table be handled by the \*.so file or sledgert? Are function pointers really called that frequently?
# SLEdge \*.so Module Loading / Initialization
@ -121,36 +132,33 @@ The `sledgert` runtime is invoked with an argument containing the path to a JSON
"relative-deadline-us"
"expected-execution-us"
"admissions-percentile"
"http-req-size"
"http-resp-size"
"http-resp-content-type"
The path to the JSON file is passed to `module_alloc_from_json`, which uses the Jasmine library to parse the JSON, performs validation, and passes the resulting specification to `module_alloc` for each module definition found. `module_alloc` allocated heap memory for a `struct module` and then calls `module_init`. `module_init` calls `sledge_abi_symbols_init`, which calls `dlopen` on the _.so file at the path specified in the JSON and then calls `dlsym` to resolve symbols within the _.so module.
- `module.abi.initialize_globals` -> `SLEDGE_ABI__INITIALIZE_GLOBALS` -> `populate_globals`
- `module.abi.initialize_memory`-> `SLEDGE_ABI__INITIALIZE_MEMORY` -> `populate_memory`
- `module.abi.initialize_table` -> `SLEDGE_ABI__INITIALIZE_TABLE` -> `populate_table`
- `module.abi.entrypoint` -> `SLEDGE_ABI__ENTRYPOINT` -> `wasmf__start`
- `module.abi.starting_pages` -> `SLEDGE_ABI__STARTING_PAGES` -> `starting_pages`
- `module.abi.max_pages` -> `SLEDGE_ABI__MAX_PAGES` -> `max_pages`
- `module.abi.globals_len` -> `SLEDGE_ABI__GLOBALS_LEN` -> `globals_len`
`module.abi.initialize_globals` -> `SLEDGE_ABI__INITIALIZE_GLOBALS` -> `populate_globals`
`module.abi.initialize_memory`-> `SLEDGE_ABI__INITIALIZE_MEMORY` -> `populate_memory`
`module.abi.initialize_table` -> `SLEDGE_ABI__INITIALIZE_TABLE` -> `populate_table`
`module.abi.entrypoint` -> `SLEDGE_ABI__ENTRYPOINT` -> `wasmf__start`
`module.abi.starting_pages` -> `SLEDGE_ABI__STARTING_PAGES` -> `starting_pages`
`module.abi.max_pages` -> `SLEDGE_ABI__MAX_PAGES` -> `max_pages`
`module.abi.globals_len` -> `SLEDGE_ABI__GLOBALS_LEN` -> `globals_len`
`module_init` then calls `module.abi.initialize_table`, which populates the indirect function table with the actual functions. This is performed once during module initialization because this table does not actually vary between instances of a module.
`module init` then calls `module.abi.initialize_table`, which populates the indirect function table with the actual functions. This is performed once during module initialization because this table does not actually vary between instances of a module.
# SLEdge \*.so Module Instantiation
When `sledgert` receives a request at the registered port specified in the JSON, it performs allocation and initialization steps. The scheduler sets the expected ABI symbols and yields to `current_sandbox_start`, which immediately calls `current_sandbox_init`. This function initializes the associated runtime state and
When `sledgert` receives a request at the registered port specified in the JSON, it performs assorted allocation and initialization steps. The scheduler sets the expected ABI symbols and yields to `current_sandbox_start`, which immediately calls `current_sandbox_init`. This function initializes the associated runtime state and
1. calls `module.abi.initialize_globals` for the current sandbox if not NULL. This is optional because the module might not have been built with the `--runtime-globals`, in which case runtime globals are not used at all. If not NULL, the globals are set in the table.
2. calls `module.abi.initialize_memory`, which copies segments into the linear memory
2. calls `module.abi.initialize_memory`, which copies regions into the linear memory
`current_sandbox_init` calls `wasi_context_init` to initialize the WASI context within the runtime.
`current_sandbox_init` returns to `current_sandbox_start`, which sets up wasm traps using `setjmp` and then calls `module.abi.entrypoint`
# Discussion (follow-up with Github issues):
# Questions:
- Should `sledge_abi__current_wasm_module_instance` be turned into a macro defined int the ABI header? That way it'll be easier to change the ABI symbols (change once, applied everywhere).
- Should `instruction_memory_grow` be moved into sledgert? This would simplify the handling of the "cache" and generating a memory profile?
- Rename `sledge_abi__wasm_globals_*` to `sledge_abi__wasm_global_*`
- Implement Unsupported Numeric Instructions
- Should the wasm global table be accessed directly instead of via a runtime function? If we expose the wasm global table to libsledge, then we have worse ABI stability, but better performance.
- Should the Function Table be handled by the \*.so file or sledgert? Are function pointers really called that frequently?
- Should `sledge_abi__current_wasm_module_instance` be turned into a macro defined int the ABI header?

@ -1,6 +1,8 @@
#include <stdint.h>
#include "sledge_abi.h"
// TODO: Validate uint32_t as return value;
uint32_t
wasi_snapshot_preview1_args_get(__wasi_size_t argv_retoffset, __wasi_size_t argv_buf_retoffset)
{

@ -38,10 +38,6 @@ BINARY_NAME=sledgert
# Feature Toggles
CFLAGS += -DEXECUTION_HISTOGRAM
# CFLAGS += -DEXECUTION_REGRESSION
# It is recommended (not mandatory) to enable this flag along with the EXECUTION_HISTOGRAM flag:
# CFLAGS += -DADMISSIONS_CONTROL
# Debugging Flags
@ -60,10 +56,10 @@ CFLAGS += -DEXECUTION_HISTOGRAM
# CFLAGS += -DLOG_TO_FILE
# Various Informational Logs for Debugging
# CFLAGS += -DLOG_EXECUTION_HISTOGRAM
# CFLAGS += -DLOG_ADMISSIONS_CONTROL
# CFLAGS += -DLOG_CONTEXT_SWITCHES
# CFLAGS += -DLOG_HTTP_PARSER
# CFLAGS += -DLOG_LOCK_OVERHEAD
# CFLAGS += -DLOG_TENANT_LOADING
# CFLAGS += -DLOG_PREEMPTION
# CFLAGS += -DLOG_SANDBOX_ALLOCATION
@ -80,22 +76,10 @@ CFLAGS += -DEXECUTION_HISTOGRAM
# page is allocated. This helps understand the relationship to memory allocation and execution time.
# CFLAGS += -DLOG_SANDBOX_MEMORY_PROFILE
# This flag enables runtime-level metrics from procfs
# CFLAGS += -DPROC_STAT_METRICS
# This flag enables HTTP-level counters of incoming requests and outgoing responses, broken out by status code
# family, such as 2XX, 4XX, 5XX.
# This flag dumps totals of incoming requests and outgoing responses, broken out by status code
# family, such as 2XX, 4XX, 5XX. It is useful to debug clients hanging waiting for a response.
# To log, run `call http_total_log()` while in GDB
# CFLAGS += -DHTTP_TOTAL_COUNTERS
# This flag enables per-route counters of incoming requests and outgoing responses, broken out by status code
# family, such as 2XX, 4XX, 5XX.
# CFLAGS += -DHTTP_ROUTE_TOTAL_COUNTERS
# This flag enables per-route latency perf-logs.
# This has a perf impact due to the use of locks.
# This flag has a dependency on the flag HTTP_ROUTE_TOTAL_COUNTERS
# CFLAGS += -DROUTE_LATENCY
# CFLAGS += -DLOG_TOTAL_REQS_RESPS
# This flag tracks the total number of sandboxes in the various states
# It is useful to debug if sandboxes are "getting caught" in a particular state

@ -1,19 +1,14 @@
#pragma once
#ifdef ADMISSIONS_CONTROL
#include <stdbool.h>
#include <stdint.h>
#define ADMISSIONS_CONTROL_GRANULARITY 1000000
extern _Atomic uint64_t admissions_control_admitted;
extern uint64_t admissions_control_capacity;
void admissions_control_initialize(void);
void admissions_control_add(uint64_t admissions_estimate);
void admissions_control_subtract(uint64_t admissions_estimate);
uint64_t admissions_control_calculate_estimate(uint64_t estimated_execution, uint64_t relative_deadline);
uint64_t admissions_control_calculate_estimate_us(uint32_t estimated_execution_us, uint32_t relative_deadline_us);
void admissions_control_log_decision(uint64_t admissions_estimate, bool admitted);
uint64_t admissions_control_decide(uint64_t admissions_estimate);
#endif

@ -0,0 +1,15 @@
#pragma once
#include "perf_window_t.h"
struct admissions_info {
struct perf_window perf_window;
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, uint8_t percentile,
uint64_t expected_execution, uint64_t relative_deadline);
void admissions_info_update(struct admissions_info *admissions_info, uint64_t execution_duration);

@ -3,9 +3,9 @@
#include <setjmp.h>
#include <ucontext.h>
#include "arch/arch_context_variant_t.h"
#include "arch/reg_t.h"
#include "arch/ureg_t.h"
#include "arch/arch_context_variant_t.h"
struct arch_context {
arch_context_variant_t variant;

@ -1,44 +0,0 @@
#pragma once
#include "likely.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
struct auto_buf {
FILE *handle;
char *data;
size_t size;
};
static inline int
auto_buf_init(struct auto_buf *buf)
{
FILE *res = open_memstream(&buf->data, &buf->size);
if (res == NULL) return errno;
buf->handle = res;
return 0;
}
static inline int
auto_buf_flush(struct auto_buf *buf)
{
return fflush(buf->handle);
}
static inline void
auto_buf_deinit(struct auto_buf *buf)
{
if (likely(buf->handle != NULL)) {
fclose(buf->handle);
buf->handle = NULL;
}
if (likely(buf->data != NULL)) {
free(buf->data);
buf->data = NULL;
}
buf->size = 0;
}

@ -3,7 +3,6 @@
#include <threads.h>
#include "current_wasm_module_instance.h"
#include "listener_thread.h"
#include "sandbox_types.h"
/* current sandbox that is active.. */
@ -31,25 +30,24 @@ current_sandbox_set(struct sandbox *sandbox)
/* Unpack hierarchy to avoid pointer chasing */
if (sandbox == NULL) {
sledge_abi__current_wasm_module_instance = (struct wasm_module_instance){
/* Public */
.abi =
(struct sledge_abi__wasm_module_instance){
.memory =
(struct sledge_abi__wasm_memory){
.size = 0,
.capacity = 0,
.max = 0,
.buffer = NULL,
},
.table = NULL,
.wasmg_0 = 0,
},
/* Private */
.wasi_context = NULL,
/* Public */
.abi =
(struct sledge_abi__wasm_module_instance){
.memory =
(struct sledge_abi__wasm_memory){
.size = 0,
.capacity = 0,
.max = 0,
.buffer = NULL,
},
.table = NULL,
.wasmg_0 = 0,
},
/* Private */
.wasi_context = NULL,
};
worker_thread_current_sandbox = NULL;
/* This is because the event core does not maintain core-assigned deadline */
if (!listener_thread_is_running()) runtime_worker_threads_deadline[worker_thread_idx] = UINT64_MAX;
worker_thread_current_sandbox = NULL;
runtime_worker_threads_deadline[worker_thread_idx] = UINT64_MAX;
} else {
sledge_abi__current_wasm_module_instance.wasi_context = sandbox->wasi_context;
memcpy(&sledge_abi__current_wasm_module_instance.abi.memory, &sandbox->memory->abi,
@ -57,9 +55,8 @@ current_sandbox_set(struct sandbox *sandbox)
sledge_abi__current_wasm_module_instance.abi.table = sandbox->module->indirect_table;
wasm_globals_update_if_used(&sandbox->globals, 0,
&sledge_abi__current_wasm_module_instance.abi.wasmg_0);
worker_thread_current_sandbox = sandbox;
if (!listener_thread_is_running())
runtime_worker_threads_deadline[worker_thread_idx] = sandbox->absolute_deadline;
worker_thread_current_sandbox = sandbox;
runtime_worker_threads_deadline[worker_thread_idx] = sandbox->absolute_deadline;
}
}

@ -1,9 +0,0 @@
#pragma once
enum epoll_tag
{
EPOLL_TAG_INVALID = 0,
EPOLL_TAG_TENANT_SERVER_SOCKET = 1,
EPOLL_TAG_METRICS_SERVER_SOCKET,
EPOLL_TAG_HTTP_SESSION_CLIENT_SOCKET,
};

@ -1,14 +0,0 @@
#pragma once
#include "perf_window_t.h"
struct execution_histogram {
struct perf_window perf_window;
uint8_t percentile; /* 50 - 99 */
int control_index; /* Precomputed Lookup index when perf_window is full */
uint64_t estimated_execution; /* cycles */
};
void execution_histogram_initialize(struct execution_histogram *execution_histogram, uint8_t percentile,
uint64_t expected_execution);
void execution_histogram_update(struct execution_histogram *execution_histogram, uint64_t execution_duration);

@ -1,26 +0,0 @@
#pragma once
#ifdef EXECUTION_REGRESSION
#include "http_session.h"
#include <stdint.h>
static inline uint64_t
get_regression_prediction(struct http_session *session)
{
/* Default Pre-processing - Extract payload size */
const int payload_size = session->http_request.body_length;
const double regression_params[2] = {payload_size, session->regression_param};
/* Perform Linear Regression using the factors provided by the regressor performed AoT on Matlab using training
* tenant-given dataset */
const struct regression_model model = session->route->regr_model;
const uint64_t prediction = (regression_params[0] / model.scale * model.beta1
+ regression_params[1] / model.scale * model.beta2)
+ model.bias;
return prediction;
}
#endif

@ -0,0 +1,11 @@
#pragma once
#include <stdint.h>
#include <threads.h>
extern thread_local uint64_t generic_thread_lock_duration;
extern thread_local uint64_t generic_thread_lock_longest;
extern thread_local uint64_t generic_thread_start_timestamp;
void generic_thread_dump_lock_overhead(void);
void generic_thread_initialize(void);

@ -5,82 +5,96 @@
#include "http_total.h"
#include "panic.h"
#define HTTP_MAX_HEADER_COUNT 32
#define HTTP_MAX_HEADER_LENGTH 64
#define HTTP_MAX_HEADER_COUNT 16
#define HTTP_MAX_HEADER_LENGTH 32
#define HTTP_MAX_HEADER_VALUE_LENGTH 256
#define HTTP_MAX_FULL_URL_LENGTH 256
#define HTTP_MAX_QUERY_PARAM_COUNT 16
#define HTTP_MAX_QUERY_PARAM_LENGTH 32
#define HTTP_RESPONSE_CONTENT_TYPE "Content-Type: %s\r\n"
#define HTTP_RESPONSE_CONTENT_LENGTH "Content-Length: %lu\r\n"
#define HTTP_RESPONSE_TERMINATOR "\r\n"
#define HTTP_RESPONSE_TERMINATOR_LENGTH 2
#define HTTP_RESPONSE_200_TEMPLATE \
"HTTP/1.1 200 OK\r\n" \
"Server: SLEdge\r\n" \
"Connection: close\r\n" \
"Content-Type: %s\r\n" \
"Content-Length: %lu\r\n" \
"\r\n"
#define HTTP_RESPONSE_200_OK \
"HTTP/1.1 200 OK\r\n" \
"Server: SLEdge\r\n" \
"Connection: close\r\n"
#define HTTP_RESPONSE_200_OK_LENGTH 52
/* The sum of format specifier characters in the template above */
#define HTTP_RESPONSE_200_TEMPLATE_FORMAT_SPECIFIER_LENGTH 5
#define HTTP_RESPONSE_400_BAD_REQUEST \
"HTTP/1.1 400 Bad Request\r\n" \
"Server: SLEdge\r\n" \
"Connection: close\r\n"
#define HTTP_RESPONSE_400_BAD_REQUEST_LENGTH 61
"Connection: close\r\n" \
"\r\n"
#define HTTP_RESPONSE_404_NOT_FOUND \
"HTTP/1.1 404 Not Found\r\n" \
"Server: SLEdge\r\n" \
"Connection: close\r\n"
#define HTTP_RESPONSE_404_NOT_FOUND_LENGTH 59
"Connection: close\r\n" \
"\r\n"
#define HTTP_RESPONSE_413_PAYLOAD_TOO_LARGE \
"HTTP/1.1 413 Payload Too Large\r\n" \
"Server: SLEdge\r\n" \
"Connection: close\r\n"
#define HTTP_RESPONSE_413_PAYLOAD_TOO_LARGE_LENGTH 67
"Connection: close\r\n" \
"\r\n"
#define HTTP_RESPONSE_429_TOO_MANY_REQUESTS \
"HTTP/1.1 429 Too Many Requests\r\n" \
"Server: SLEdge\r\n" \
"Connection: close\r\n"
#define HTTP_RESPONSE_429_TOO_MANY_REQUESTS_LENGTH 67
"Connection: close\r\n" \
"\r\n"
#define HTTP_RESPONSE_500_INTERNAL_SERVER_ERROR \
"HTTP/1.1 500 Internal Server Error\r\n" \
"Server: SLEdge\r\n" \
"Connection: close\r\n"
#define HTTP_RESPONSE_500_INTERNAL_SERVER_ERROR_LENGTH 71
"Connection: close\r\n" \
"\r\n"
#define HTTP_RESPONSE_503_SERVICE_UNAVAILABLE \
"HTTP/1.1 503 Service Unavailable\r\n" \
"Server: SLEdge\r\n" \
"Connection: close\r\n"
#define HTTP_RESPONSE_503_SERVICE_UNAVAILABLE_LENGTH 69
"Connection: close\r\n" \
"\r\n"
static inline const char *
http_header_build(int status_code)
{
const char *response;
int rc;
switch (status_code) {
case 200:
return HTTP_RESPONSE_200_OK;
case 400:
return HTTP_RESPONSE_400_BAD_REQUEST;
response = HTTP_RESPONSE_400_BAD_REQUEST;
http_total_increment_4XX();
break;
case 404:
return HTTP_RESPONSE_404_NOT_FOUND;
response = HTTP_RESPONSE_404_NOT_FOUND;
http_total_increment_4XX();
break;
case 413:
return HTTP_RESPONSE_413_PAYLOAD_TOO_LARGE;
response = HTTP_RESPONSE_413_PAYLOAD_TOO_LARGE;
http_total_increment_4XX();
break;
case 429:
return HTTP_RESPONSE_429_TOO_MANY_REQUESTS;
response = HTTP_RESPONSE_429_TOO_MANY_REQUESTS;
http_total_increment_4XX();
break;
case 500:
return HTTP_RESPONSE_500_INTERNAL_SERVER_ERROR;
response = HTTP_RESPONSE_500_INTERNAL_SERVER_ERROR;
http_total_increment_5XX();
break;
case 503:
return HTTP_RESPONSE_503_SERVICE_UNAVAILABLE;
response = HTTP_RESPONSE_503_SERVICE_UNAVAILABLE;
http_total_increment_5XX();
break;
default:
panic("%d is not a valid status code\n", status_code);
}
return response;
}
static inline size_t
@ -88,17 +102,17 @@ http_header_len(int status_code)
{
switch (status_code) {
case 400:
return HTTP_RESPONSE_400_BAD_REQUEST_LENGTH;
return strlen(HTTP_RESPONSE_400_BAD_REQUEST);
case 404:
return HTTP_RESPONSE_404_NOT_FOUND_LENGTH;
return strlen(HTTP_RESPONSE_404_NOT_FOUND);
case 413:
return HTTP_RESPONSE_413_PAYLOAD_TOO_LARGE_LENGTH;
return strlen(HTTP_RESPONSE_413_PAYLOAD_TOO_LARGE);
case 429:
return HTTP_RESPONSE_429_TOO_MANY_REQUESTS_LENGTH;
return strlen(HTTP_RESPONSE_429_TOO_MANY_REQUESTS);
case 500:
return HTTP_RESPONSE_500_INTERNAL_SERVER_ERROR_LENGTH;
return strlen(HTTP_RESPONSE_500_INTERNAL_SERVER_ERROR);
case 503:
return HTTP_RESPONSE_503_SERVICE_UNAVAILABLE_LENGTH;
return strlen(HTTP_RESPONSE_503_SERVICE_UNAVAILABLE);
default:
panic("%d is not a valid status code\n", status_code);
}

@ -1,48 +0,0 @@
#pragma once
#include <stdatomic.h>
#ifdef HTTP_ROUTE_TOTAL_COUNTERS
struct http_route_total {
atomic_ulong total_requests;
atomic_ulong total_2XX;
atomic_ulong total_4XX;
atomic_ulong total_5XX;
};
#else
struct http_route_total {
};
#endif
static inline void
http_route_total_init(struct http_route_total *rm)
{
#ifdef HTTP_ROUTE_TOTAL_COUNTERS
atomic_init(&rm->total_requests, 0);
atomic_init(&rm->total_2XX, 0);
atomic_init(&rm->total_4XX, 0);
atomic_init(&rm->total_5XX, 0);
#endif
}
static inline void
http_route_total_increment_request(struct http_route_total *rm)
{
#ifdef HTTP_ROUTE_TOTAL_COUNTERS
atomic_fetch_add(&rm->total_requests, 1);
#endif
}
static inline void
http_route_total_increment(struct http_route_total *rm, int status_code)
{
#ifdef HTTP_ROUTE_TOTAL_COUNTERS
if (status_code >= 200 && status_code <= 299) {
atomic_fetch_add(&rm->total_2XX, 1);
} else if (status_code >= 400 && status_code <= 499) {
atomic_fetch_add(&rm->total_4XX, 1);
} else if (status_code >= 500 && status_code <= 599) {
atomic_fetch_add(&rm->total_5XX, 1);
}
#endif
}

@ -1,12 +1,13 @@
#pragma once
#include <stdlib.h>
#include <string.h>
#include "http.h"
#include "module.h"
#include "route.h"
#include "route_config.h"
#include "route_latency.h"
#include "vec.h"
#include <stdlib.h>
typedef struct route route_t;
VEC(route_t)
@ -20,8 +21,7 @@ http_router_init(http_router_t *router, size_t capacity)
}
static inline int
http_router_add_route(http_router_t *router, struct route_config *config, struct module *module,
struct module *module_proprocess)
http_router_add_route(http_router_t *router, struct route_config *config, struct module *module)
{
assert(router != NULL);
assert(config != NULL);
@ -29,35 +29,18 @@ http_router_add_route(http_router_t *router, struct route_config *config, struct
assert(config->route != NULL);
assert(config->http_resp_content_type != NULL);
struct route route = {.route = config->route,
.module = module,
.relative_deadline_us = config->relative_deadline_us,
.relative_deadline = (uint64_t)config->relative_deadline_us * runtime_processor_speed_MHz,
.response_content_type = config->http_resp_content_type};
route_latency_init(&route.latency);
http_route_total_init(&route.metrics);
#ifdef EXECUTION_REGRESSION
/* Execution Regression setup */
route.module_proprocess = module_proprocess;
route.regr_model.bias = config->model_bias / 1000.0;
route.regr_model.scale = config->model_scale / 1000.0;
route.regr_model.num_of_param = config->model_num_of_param;
route.regr_model.beta1 = config->model_beta1 / 1000.0;
route.regr_model.beta2 = config->model_beta2 / 1000.0;
#endif
struct route route = { .route = config->route,
.module = module,
.relative_deadline_us = config->relative_deadline_us,
.relative_deadline = (uint64_t)config->relative_deadline_us
* runtime_processor_speed_MHz,
.response_size = config->http_resp_size,
.response_content_type = config->http_resp_content_type };
const uint64_t expected_execution = route.relative_deadline / 2;
#ifdef ADMISSIONS_CONTROL
/* Addmissions Control setup */
route.execution_histogram.estimated_execution = expected_execution;
#endif
#ifdef EXECUTION_HISTOGRAM
/* Execution Histogram setup */
execution_histogram_initialize(&route.execution_histogram, config->admissions_percentile, expected_execution);
#endif
/* Admissions Control */
uint64_t expected_execution = (uint64_t)config->expected_execution_us * runtime_processor_speed_MHz;
admissions_info_initialize(&route.admissions_info, config->admissions_percentile, expected_execution,
route.relative_deadline);
int rc = vec_route_t_push(router, route);
if (unlikely(rc == -1)) { return -1; }
@ -76,9 +59,3 @@ http_router_match_route(http_router_t *router, char *route)
return NULL;
}
static inline void
http_router_foreach(http_router_t *router, void (*cb)(route_t *, void *, void *), void *arg_one, void *arg_two)
{
for (int i = 0; i < router->length; i++) { cb(&router->buffer[i], arg_one, arg_two); }
}

@ -9,18 +9,21 @@
#include <sys/socket.h>
#include <unistd.h>
#include "auto_buf.h"
#include "tcp_session.h"
#include "debuglog.h"
#include "epoll_tag.h"
#include "http_request.h"
#include "http_parser.h"
#include "http_parser_settings.h"
#include "http_request.h"
#include "http_route_total.h"
#include "http_session_perf_log.h"
#include "http_total.h"
#include "route.h"
#include "tcp_session.h"
#include "tenant.h"
#include "unistd.h"
#include "vec.h"
#include "http_session_perf_log.h"
#define HTTP_SESSION_DEFAULT_REQUEST_RESPONSE_SIZE (PAGE_SIZE)
#define HTTP_SESSION_RESPONSE_HEADER_CAPACITY 256
#define u8 uint8_t
VEC(u8)
enum http_session_state
{
@ -33,33 +36,30 @@ enum http_session_state
HTTP_SESSION_EXECUTION_COMPLETE,
HTTP_SESSION_SENDING_RESPONSE_HEADER,
HTTP_SESSION_SEND_RESPONSE_HEADER_BLOCKED,
HTTP_SESSION_SENT_RESPONSE_HEADER,
HTTP_SESSION_SENDING_RESPONSE_BODY,
HTTP_SESSION_SEND_RESPONSE_BODY_BLOCKED,
HTTP_SESSION_SENT_RESPONSE_BODY
HTTP_SESSION_SENDING_RESPONSE,
HTTP_SESSION_SEND_RESPONSE_BLOCKED,
HTTP_SESSION_SENT_RESPONSE
};
struct http_session {
enum epoll_tag tag;
enum http_session_state state;
struct sockaddr client_address; /* client requesting connection! */
int socket;
struct http_parser http_parser;
struct http_request http_request;
struct auto_buf request_buffer;
struct auto_buf response_header;
FILE *request_handle;
char *request_buffer;
size_t request_size;
char response_header[HTTP_SESSION_RESPONSE_HEADER_CAPACITY];
size_t response_header_length;
size_t response_header_written;
struct auto_buf response_body;
size_t response_body_written;
struct vec_u8 response_buffer;
size_t response_buffer_written;
struct tenant *tenant; /* Backlink required when read blocks on listener core */
struct route *route; /* Backlink required to handle http metrics */
uint64_t request_arrival_timestamp;
uint64_t request_downloaded_timestamp;
uint64_t response_takeoff_timestamp;
uint64_t response_sent_timestamp;
bool did_preprocessing;
uint64_t preprocessing_duration;
double regression_param; /* Calculated in tenant preprocessing logic if provided */
};
extern void http_session_perf_log_print_entry(struct http_session *http_session);
@ -91,20 +91,18 @@ http_session_init(struct http_session *session, int socket_descriptor, const str
assert(socket_descriptor >= 0);
assert(socket_address != NULL);
session->tag = EPOLL_TAG_HTTP_SESSION_CLIENT_SOCKET;
session->tenant = tenant;
session->route = NULL;
session->socket = socket_descriptor;
session->request_arrival_timestamp = request_arrival_timestamp;
memcpy(&session->client_address, socket_address, sizeof(struct sockaddr));
http_session_parser_init(session);
int rc = auto_buf_init(&session->request_buffer);
if (rc < 0) return -1;
session->request_handle = open_memstream(&session->request_buffer, &session->request_size);
if (session->request_handle == NULL) return -1;
/* Defer initializing response_body until we've matched a route */
auto_buf_init(&session->response_header);
/* Defer allocating response until we've matched a route */
session->response_buffer.buffer = NULL;
session->state = HTTP_SESSION_INITIALIZED;
@ -112,16 +110,20 @@ http_session_init(struct http_session *session, int socket_descriptor, const str
}
static inline int
http_session_init_response_body(struct http_session *session)
http_session_init_response_buffer(struct http_session *session, size_t capacity)
{
assert(session != NULL);
assert(session->response_body.data == NULL);
assert(session->response_body.size == 0);
assert(session->response_body_written == 0);
assert(session->response_buffer.buffer == NULL);
int rc = auto_buf_init(&session->response_body);
int rc = vec_u8_init(&session->response_buffer, capacity);
if (rc < 0) {
auto_buf_deinit(&session->request_buffer);
fclose(session->request_handle);
session->request_handle = NULL;
if (session->request_buffer != NULL) {
free(session->request_buffer);
session->request_buffer = NULL;
}
session->request_size = 0;
return -1;
}
@ -156,9 +158,15 @@ http_session_deinit(struct http_session *session)
{
assert(session);
auto_buf_deinit(&session->request_buffer);
auto_buf_deinit(&session->response_header);
auto_buf_deinit(&session->response_body);
fclose(session->request_handle);
session->request_handle = NULL;
if (session->request_buffer != NULL) {
free(session->request_buffer);
session->request_buffer = NULL;
}
session->request_size = 0;
vec_u8_deinit(&session->response_buffer);
}
static inline void
@ -176,38 +184,26 @@ http_session_free(struct http_session *session)
* @param status_code
*/
static inline void
http_session_set_response_header(struct http_session *session, int status_code)
http_session_set_response_header(struct http_session *session, int status_code, const char *content_type,
size_t content_length)
{
assert(session != NULL);
assert(status_code >= 200 && status_code <= 599);
http_total_increment_response(status_code);
/* We might not have actually matched a route */
if (likely(session->route != NULL)) { http_route_total_increment(&session->route->metrics, status_code); }
int rc = fputs(http_header_build(status_code), session->response_header.handle);
assert(rc != EOF);
if (status_code == 200) {
/* Make sure the response_body is flushed */
int rc = auto_buf_flush(&session->response_body);
if (unlikely(rc != 0)) { panic("response_body auto_buf failed to flush: %s\n", strerror(errno)); };
/* Technically fprintf can truncate, but I assume this won't happen with a memstream */
rc = fprintf(session->response_header.handle, HTTP_RESPONSE_CONTENT_TYPE,
session->route->response_content_type);
assert(rc > 0);
rc = fprintf(session->response_header.handle, HTTP_RESPONSE_CONTENT_LENGTH,
session->response_body.size);
assert(rc > 0);
session->response_header_length = snprintf(session->response_header,
HTTP_SESSION_RESPONSE_HEADER_CAPACITY,
HTTP_RESPONSE_200_TEMPLATE, content_type, content_length);
} else {
size_t header_len = http_header_len(status_code);
size_t to_copy = HTTP_SESSION_RESPONSE_HEADER_CAPACITY < header_len
? HTTP_SESSION_RESPONSE_HEADER_CAPACITY
: header_len;
strncpy(session->response_header, http_header_build(status_code), to_copy - 1);
session->response_header_length = to_copy;
}
rc = fputs(HTTP_RESPONSE_TERMINATOR, session->response_header.handle);
assert(rc != EOF);
rc = auto_buf_flush(&session->response_header);
if (unlikely(rc != 0)) { panic("response_header auto_buf failed to flush: %s\n", strerror(errno)); };
session->response_takeoff_timestamp = __getcycles();
}
@ -229,15 +225,12 @@ static inline int
http_session_send_response_header(struct http_session *session, void_star_cb on_eagain)
{
assert(session != NULL);
assert(session->state == HTTP_SESSION_EXECUTION_COMPLETE
|| session->state == HTTP_SESSION_SEND_RESPONSE_HEADER_BLOCKED);
session->state = HTTP_SESSION_SENDING_RESPONSE_HEADER;
while (session->response_header.size > session->response_header_written) {
while (session->response_header_length > session->response_header_written) {
ssize_t sent =
tcp_session_send(session->socket,
(const char *)&session->response_header.data[session->response_header_written],
session->response_header.size - session->response_header_written, on_eagain,
(const char *)&session->response_header[session->response_header_written],
session->response_header_length - session->response_header_written, on_eagain,
session);
if (sent < 0) {
return (int)sent;
@ -246,8 +239,6 @@ http_session_send_response_header(struct http_session *session, void_star_cb on_
}
}
session->state = HTTP_SESSION_SENT_RESPONSE_HEADER;
return 0;
}
@ -262,26 +253,19 @@ http_session_send_response_body(struct http_session *session, void_star_cb on_ea
{
assert(session != NULL);
assert(session->state == HTTP_SESSION_SENT_RESPONSE_HEADER
|| session->state == HTTP_SESSION_SEND_RESPONSE_BODY_BLOCKED);
session->state = HTTP_SESSION_SENDING_RESPONSE_BODY;
/* Assumption: Already flushed in order to write content-length to header */
// TODO: Test if body is empty
while (session->response_body_written < session->response_body.size) {
while (session->response_buffer_written < session->response_buffer.length) {
ssize_t sent =
tcp_session_send(session->socket,
(const char *)&session->response_body.data[session->response_body_written],
session->response_body.size - session->response_body_written, on_eagain, session);
(const char *)&session->response_buffer.buffer[session->response_buffer_written],
session->response_buffer.length - session->response_buffer_written, on_eagain,
session);
if (sent < 0) {
return (int)sent;
} else {
session->response_body_written += (size_t)sent;
session->response_buffer_written += (size_t)sent;
}
}
session->state = HTTP_SESSION_SENT_RESPONSE_BODY;
return 0;
}
@ -301,8 +285,8 @@ http_session_parse(struct http_session *session, ssize_t bytes_received)
#endif
size_t bytes_parsed =
http_parser_execute(&session->http_parser, settings,
(const char *)&session->request_buffer.data[session->http_request.length_parsed],
(size_t)session->request_buffer.size - session->http_request.length_parsed);
(const char *)&session->request_buffer.buffer[session->http_request.length_parsed],
(size_t)session->request_buffer.length - session->http_request.length_parsed);
if (session->http_parser.http_errno != HPE_OK) {
debuglog("Error: %s, Description: %s\n",
@ -346,52 +330,36 @@ http_session_log_malformed_request(struct http_session *session)
/**
* Receive and Parse the Request for the current sandbox
* @return 0 if message parsing complete, -1 on error, -EAGAIN if would block
* @return 0 if message parsing complete, -1 on error, -ENOMEM if buffers run out of space, -3 EAGAIN if would block
*/
static inline int
http_session_receive_request(struct http_session *session, void_star_cb on_eagain)
{
assert(session != NULL);
assert(session->request_buffer.handle != NULL);
assert(session->request_handle != NULL);
assert(session->state == HTTP_SESSION_INITIALIZED || session->state == HTTP_SESSION_RECEIVE_REQUEST_BLOCKED);
session->state = HTTP_SESSION_RECEIVING_REQUEST;
struct http_request *http_request = &session->http_request;
int rc = 0;
char temp[BUFSIZ];
int rc = 0;
while (!http_request->message_end) {
ssize_t bytes_received = tcp_session_recv(session->socket, temp, BUFSIZ, on_eagain, session);
while (!session->http_request.message_end) {
ssize_t bytes_received = tcp_session_recv(session->socket, session->request_handle, on_eagain, session);
if (unlikely(bytes_received == -EAGAIN))
goto err_eagain;
else if (unlikely(bytes_received < 0))
goto err;
/* If we received an EOF before we were able to parse a complete HTTP message, request is malformed */
else if (unlikely(bytes_received == 0 && !http_request->message_end))
else if (unlikely(bytes_received == 0 && !session->http_request.message_end))
goto err;
assert(bytes_received > 0);
const char *old_buffer = session->request_buffer.data;
const ssize_t header_length = session->request_buffer.size - http_request->body_length_read;
assert(!http_request->header_end || header_length > 0);
/* Write temp buffer to memstream */
fwrite(temp, 1, bytes_received, session->request_buffer.handle);
/* fflush memstream managed buffer */
fflush(session->request_buffer.handle);
/* Update parser structure if buffer moved */
if (old_buffer != session->request_buffer.data) {
http_request->body = header_length ? session->request_buffer.data + header_length : NULL;
}
if (http_session_parse(session, bytes_received) == -1) goto err;
ssize_t bytes_parsed = http_session_parse(session, bytes_received);
if (bytes_parsed == -1) goto err;
}
assert(http_request->message_end == true);
assert(session->http_request.message_end == true);
session->state = HTTP_SESSION_RECEIVED_REQUEST;
http_session_log_query_params(session);
@ -402,6 +370,10 @@ done:
err_eagain:
rc = -EAGAIN;
goto done;
err_nobufs:
http_session_log_malformed_request(session);
rc = -ENOMEM;
goto done;
err:
http_session_log_malformed_request(session);
rc = -1;
@ -416,17 +388,31 @@ static inline int
http_session_write_response(struct http_session *session, const uint8_t *source, size_t n)
{
assert(session);
assert(session->response_body.handle != NULL);
assert(session->response_buffer.buffer != NULL);
assert(source);
return fwrite(source, 1, n, session->response_body.handle);
int rc = 0;
size_t buffer_remaining = session->response_buffer.capacity - session->response_buffer.length;
if (buffer_remaining < n) {
rc = vec_u8_resize(&session->response_buffer, session->response_buffer.capacity + n - buffer_remaining);
if (rc != 0) goto DONE;
}
assert(session->response_buffer.capacity - session->response_buffer.length >= n);
memcpy(&session->response_buffer.buffer[session->response_buffer.length], source, n);
session->response_buffer.length += n;
rc = n;
DONE:
return rc;
}
static inline void
http_session_send_response(struct http_session *session, void_star_cb on_eagain)
{
assert(session->state == HTTP_SESSION_EXECUTION_COMPLETE);
int rc = http_session_send_response_header(session, on_eagain);
/* session blocked and registered to epoll so continue to next handle */
if (unlikely(rc == -EAGAIN)) {
@ -435,8 +421,6 @@ http_session_send_response(struct http_session *session, void_star_cb on_eagain)
goto CLOSE;
}
assert(session->state == HTTP_SESSION_SENT_RESPONSE_HEADER);
rc = http_session_send_response_body(session, on_eagain);
/* session blocked and registered to epoll so continue to next handle */
if (unlikely(rc == -EAGAIN)) {
@ -445,8 +429,6 @@ http_session_send_response(struct http_session *session, void_star_cb on_eagain)
goto CLOSE;
}
assert(session->state == HTTP_SESSION_SENT_RESPONSE_BODY);
/* Terminal State Logging for Http Session */
session->response_sent_timestamp = __getcycles();
http_session_perf_log_print_entry(session);

@ -1,8 +1,8 @@
#pragma once
#include "http_session.h"
#include "pretty_print.h"
#include "runtime.h"
#include "http_session.h"
extern FILE *http_session_perf_log;
typedef struct http_session http_session;
@ -15,8 +15,8 @@ static inline void
http_session_perf_log_print_header()
{
if (http_session_perf_log == NULL) { perror("http_session perf log"); }
fprintf(http_session_perf_log, "tenant,route,state,header_len,resp_body_len,receive_duration,sent_duration,"
"total_lifetime,preprocessing,proc_MHz\n");
fprintf(http_session_perf_log,
"tenant,route,state,header_len,resp_body_len,receive_duration,sent_duration,total_lifetime,proc_MHz\n");
}
static inline void

@ -9,9 +9,10 @@
* behind a compiler flag. 2XX and 4XX can be incremented by worker cores, so they are behind a flag because
* of concerns about contention
*/
#ifdef HTTP_TOTAL_COUNTERS
extern _Atomic uint32_t http_total_requests;
extern _Atomic uint32_t http_total_5XX;
#ifdef LOG_TOTAL_REQS_RESPS
extern _Atomic uint32_t http_total_2XX;
extern _Atomic uint32_t http_total_4XX;
#endif
@ -19,32 +20,38 @@ extern _Atomic uint32_t http_total_4XX;
static inline void
http_total_init()
{
#ifdef HTTP_TOTAL_COUNTERS
atomic_init(&http_total_requests, 0);
atomic_init(&http_total_5XX, 0);
#ifdef LOG_TOTAL_REQS_RESPS
atomic_init(&http_total_2XX, 0);
atomic_init(&http_total_4XX, 0);
atomic_init(&http_total_5XX, 0);
#endif
}
static inline void
http_total_increment_request()
{
#ifdef HTTP_TOTAL_COUNTERS
atomic_fetch_add(&http_total_requests, 1);
}
static inline void
http_total_increment_2XX()
{
#ifdef LOG_TOTAL_REQS_RESPS
atomic_fetch_add(&http_total_2XX, 1);
#endif
}
static inline void
http_total_increment_response(int status_code)
http_total_increment_4XX()
{
#ifdef HTTP_TOTAL_COUNTERS
if (status_code >= 200 && status_code <= 299) {
atomic_fetch_add(&http_total_2XX, 1);
} else if (status_code >= 400 && status_code <= 499) {
atomic_fetch_add(&http_total_4XX, 1);
} else if (status_code >= 500 && status_code <= 599) {
atomic_fetch_add(&http_total_5XX, 1);
}
#ifdef LOG_TOTAL_REQS_RESPS
atomic_fetch_add(&http_total_4XX, 1);
#endif
}
static inline void
http_total_increment_5XX()
{
atomic_fetch_add(&http_total_5XX, 1);
}

@ -2,12 +2,12 @@
#include <errno.h>
#include <inttypes.h>
#include <jsmn.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jsmn.h>
static inline char *
jsmn_type(jsmntype_t type)

@ -3,6 +3,7 @@
#include <stdbool.h>
#include <stdnoreturn.h>
#include "generic_thread.h"
#include "http_session.h"
#include "module.h"

@ -1,81 +1,68 @@
#pragma once
#include <assert.h>
#include <spinlock/mcs.h>
#include <stdint.h>
#include "arch/getcycles.h"
#include "runtime.h"
#include "generic_thread.h"
/* A linked list of nodes */
struct lock_wrapper {
uint64_t longest_held;
uint64_t total_held;
ck_spinlock_mcs_t lock;
};
/* A node on the linked list */
struct lock_node {
struct ck_spinlock_mcs node;
uint64_t time_locked;
};
typedef struct lock_wrapper lock_t;
typedef struct lock_node lock_node_t;
typedef ck_spinlock_mcs_t lock_t;
/**
* Initializes a lock
* Initializes a lock of type lock_t
* @param lock - the address of the lock
*/
static inline void
lock_init(lock_t *self)
{
self->total_held = 0;
self->longest_held = 0;
ck_spinlock_mcs_init(&self->lock);
}
#define LOCK_INIT(lock) ck_spinlock_mcs_init((lock))
/**
* Checks if a lock is locked
* @param lock - the address of the lock
* @returns bool if lock is locked
*/
static inline bool
lock_is_locked(lock_t *self)
{
return ck_spinlock_mcs_locked(&self->lock);
}
#define LOCK_IS_LOCKED(lock) ck_spinlock_mcs_locked((lock))
/**
* Locks a lock, keeping track of overhead
* @param lock - the address of the lock
* @param node - node to add to lock
* @param unique_variable_name - a unique prefix to hygienically namespace an associated lock/unlock pair
*/
static inline void
lock_lock(lock_t *self, lock_node_t *node)
{
assert(node->time_locked == 0);
node->time_locked = __getcycles();
ck_spinlock_mcs_lock(&self->lock, &node->node);
}
#define LOCK_LOCK_WITH_BOOKKEEPING(lock, unique_variable_name) \
struct ck_spinlock_mcs _hygiene_##unique_variable_name##_node; \
uint64_t _hygiene_##unique_variable_name##_pre = __getcycles(); \
ck_spinlock_mcs_lock((lock), &(_hygiene_##unique_variable_name##_node)); \
uint64_t _hygiene_##unique_variable_name##_duration = (__getcycles() - _hygiene_##unique_variable_name##_pre); \
if (_hygiene_##unique_variable_name##_duration > generic_thread_lock_longest) { \
generic_thread_lock_longest = _hygiene_##unique_variable_name##_duration; \
} \
generic_thread_lock_duration += _hygiene_##unique_variable_name##_duration;
/**
* Unlocks a lock
* @param lock - the address of the lock
* @param node - node used when calling lock_lock
* @param unique_variable_name - a unique prefix to hygienically namespace an associated lock/unlock pair
*/
static inline void
lock_unlock(lock_t *self, lock_node_t *node)
{
assert(node->time_locked > 0);
#define LOCK_UNLOCK_WITH_BOOKKEEPING(lock, unique_variable_name) \
ck_spinlock_mcs_unlock(lock, &(_hygiene_##unique_variable_name##_node));
ck_spinlock_mcs_unlock(&self->lock, &node->node);
uint64_t now = __getcycles();
assert(node->time_locked < now);
uint64_t duration = now - node->time_locked;
node->time_locked = 0;
if (unlikely(duration > self->longest_held)) { self->longest_held = duration; }
self->total_held += duration;
}
/**
* Locks a lock, keeping track of overhead
* Assumes the availability of DEFAULT as a hygienic prefix for DEFAULT_node and DEFAULT_pre
*
* As such, this API can only be used once in a lexical scope.
*
* Use LOCK_LOCK_WITH_BOOKKEEPING and LOCK_UNLOCK_WITH_BOOKKEEPING if multiple locks are required
* @param lock - the address of the lock
*/
#define LOCK_LOCK(lock) LOCK_LOCK_WITH_BOOKKEEPING(lock, DEFAULT)
/**
* Unlocks a lock
* Uses lock node NODE_DEFAULT and timestamp PRE_DEFAULT, so this assumes use of LOCK_LOCK
* This API can only be used once in a lexical scope. If this isn't true, use LOCK_LOCK_WITH_BOOKKEEPING and
* LOCK_UNLOCK_WITH_BOOKKEEPING
* @param lock - the address of the lock
*/
#define LOCK_UNLOCK(lock) LOCK_UNLOCK_WITH_BOOKKEEPING(lock, DEFAULT)

@ -37,7 +37,7 @@ map_init(struct map *restrict map)
{
for (int i = 0; i < MAP_BUCKET_COUNT; i++) {
map->buckets[i].head = NULL;
lock_init(&map->buckets[i].lock);
LOCK_INIT(&map->buckets[i].lock);
}
};
@ -67,8 +67,7 @@ map_get(struct map *map, uint8_t *key, uint32_t key_len, uint32_t *ret_value_len
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
lock_node_t node = {};
lock_lock(&bucket->lock, &node);
LOCK_LOCK(&bucket->lock);
for (struct map_node *node = bucket->head; node != NULL; node = node->next) {
if (node->hash == hash) {
value = node->value;
@ -80,7 +79,7 @@ map_get(struct map *map, uint8_t *key, uint32_t key_len, uint32_t *ret_value_len
if (value == NULL) *ret_value_len = 0;
DONE:
lock_unlock(&bucket->lock, &node);
LOCK_UNLOCK(&bucket->lock);
return value;
}
@ -91,19 +90,18 @@ map_set(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint32_
uint32_t hash = MAP_HASH(key, key_len);
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
lock_node_t node;
lock_lock(&bucket->lock, &node);
LOCK_LOCK(&bucket->lock);
for (struct map_node *node = bucket->head; node != NULL; node = node->next) {
if (node->hash == hash) goto DONE;
}
struct map_node *new_node = (struct map_node *)xmalloc(sizeof(struct map_node));
*(new_node) = (struct map_node){.hash = hash,
.key = xmalloc(key_len),
.key_len = key_len,
.value = xmalloc(value_len),
.value_len = value_len,
.next = bucket->head};
*(new_node) = (struct map_node){ .hash = hash,
.key = xmalloc(key_len),
.key_len = key_len,
.value = xmalloc(value_len),
.value_len = value_len,
.next = bucket->head };
// Copy Key and Value
memcpy(new_node->key, key, key_len);
@ -113,7 +111,7 @@ map_set(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint32_
did_set = true;
DONE:
lock_unlock(&bucket->lock, &node);
LOCK_UNLOCK(&bucket->lock);
return did_set;
}
@ -127,8 +125,7 @@ map_delete(struct map *map, uint8_t *key, uint32_t key_len)
uint32_t hash = MAP_HASH(key, key_len);
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
lock_node_t node;
lock_lock(&bucket->lock, &node);
LOCK_LOCK(&bucket->lock);
struct map_node *prev = bucket->head;
if (prev != NULL && prev->hash == hash) {
@ -150,7 +147,7 @@ map_delete(struct map *map, uint8_t *key, uint32_t key_len)
}
DONE:
lock_unlock(&bucket->lock, &node);
LOCK_UNLOCK(&bucket->lock);
return did_delete;
}
@ -159,8 +156,7 @@ map_upsert(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint
{
uint32_t hash = MAP_HASH(key, key_len);
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
lock_node_t node;
lock_lock(&bucket->lock, &node);
LOCK_LOCK(&bucket->lock);
for (struct map_node *node = bucket->head; node != NULL; node = node->next) {
if (node->hash == hash) {
@ -174,12 +170,12 @@ map_upsert(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint
struct map_node *new_node = (struct map_node *)xmalloc(sizeof(struct map_node));
*(new_node) = (struct map_node){.hash = hash,
.key = xmalloc(key_len),
.key_len = key_len,
.value = xmalloc(value_len),
.value_len = value_len,
.next = bucket->head};
*(new_node) = (struct map_node){ .hash = hash,
.key = xmalloc(key_len),
.key_len = key_len,
.value = xmalloc(value_len),
.value_len = value_len,
.next = bucket->head };
assert(new_node->key);
assert(new_node->value);
@ -191,5 +187,5 @@ map_upsert(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint
bucket->head = new_node;
DONE:
lock_unlock(&bucket->lock, &node);
LOCK_UNLOCK(&bucket->lock);
}

@ -1,17 +1,10 @@
#pragma once
#include "epoll_tag.h"
#include "tcp_server.h"
struct metrics_server {
enum epoll_tag tag;
struct tcp_server tcp;
pthread_attr_t thread_settings;
};
extern struct metrics_server metrics_server;
extern struct tcp_server metrics_server;
void metrics_server_init();
void metrics_server_thread_spawn(int client_socket);
int metrics_server_listen();
int metrics_server_close();
void metrics_server_handler(int client_socket);

@ -1,11 +1,22 @@
#pragma once
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include "admissions_control.h"
#include "admissions_info.h"
#include "current_wasm_module_instance.h"
#include "panic.h"
#include "pool.h"
#include "sledge_abi_symbols.h"
#include "tcp_server.h"
#include "types.h"
#include "wasm_memory.h"
#include "sledge_abi_symbols.h"
#include "wasm_stack.h"
#include "wasm_memory.h"
#include "wasm_table.h"
extern thread_local int worker_thread_idx;
@ -17,15 +28,9 @@ struct module_pool {
struct wasm_stack_pool stack;
} CACHE_PAD_ALIGNED;
enum module_type
{
APP_MODULE,
PREPROCESS_MODULE
};
struct module {
char *path;
uint32_t stack_size; /* a specification? */
enum module_type type;
char *path;
uint32_t stack_size; /* a specification? */
/* Handle and ABI Symbols for *.so file */
struct sledge_abi_symbols abi;
@ -36,13 +41,12 @@ struct module {
struct module_pool *pools;
} CACHE_PAD_ALIGNED;
/********************************
* Public Methods from module.c *
*******************************/
void module_free(struct module *module);
struct module *module_alloc(char *path, enum module_type type);
struct module *module_alloc(char *path);
/*************************
* Public Static Inlines *
@ -111,9 +115,7 @@ module_alloc_table(struct module *module)
static inline void
module_initialize_pools(struct module *module)
{
/* Create only a single pool for the preprocessing module, since it is executed only by the event core. */
const int n = module->type == APP_MODULE ? runtime_worker_threads_count : 1;
for (int i = 0; i < n; i++) {
for (int i = 0; i < runtime_worker_threads_count; i++) {
wasm_memory_pool_init(&module->pools[i].memory, false);
wasm_stack_pool_init(&module->pools[i].stack, false);
}
@ -122,8 +124,7 @@ module_initialize_pools(struct module *module)
static inline void
module_deinitialize_pools(struct module *module)
{
const int n = module->type == APP_MODULE ? runtime_worker_threads_count : 1;
for (int i = 0; i < n; i++) {
for (int i = 0; i < runtime_worker_threads_count; i++) {
wasm_memory_pool_deinit(&module->pools[i].memory);
wasm_stack_pool_deinit(&module->pools[i].stack);
}

@ -19,10 +19,10 @@ perf_window_initialize(struct perf_window *perf_window)
{
assert(perf_window != NULL);
lock_init(&perf_window->lock);
LOCK_INIT(&perf_window->lock);
perf_window->count = 0;
memset(perf_window->by_duration, 0, sizeof(struct execution_node) * PERF_WINDOW_CAPACITY);
memset(perf_window->by_termination, 0, sizeof(uint16_t) * PERF_WINDOW_CAPACITY);
memset(perf_window->by_duration, 0, sizeof(struct execution_node) * PERF_WINDOW_BUFFER_SIZE);
memset(perf_window->by_termination, 0, sizeof(uint16_t) * PERF_WINDOW_BUFFER_SIZE);
}
@ -36,10 +36,10 @@ perf_window_initialize(struct perf_window *perf_window)
static inline void
perf_window_swap(struct perf_window *perf_window, uint16_t first_by_duration_idx, uint16_t second_by_duration_idx)
{
assert(lock_is_locked(&perf_window->lock));
assert(LOCK_IS_LOCKED(&perf_window->lock));
assert(perf_window != NULL);
assert(first_by_duration_idx < PERF_WINDOW_CAPACITY);
assert(second_by_duration_idx < PERF_WINDOW_CAPACITY);
assert(first_by_duration_idx >= 0 && first_by_duration_idx < PERF_WINDOW_BUFFER_SIZE);
assert(second_by_duration_idx >= 0 && second_by_duration_idx < PERF_WINDOW_BUFFER_SIZE);
uint16_t first_by_termination_idx = perf_window->by_duration[first_by_duration_idx].by_termination_idx;
uint16_t second_by_termination_idx = perf_window->by_duration[second_by_duration_idx].by_termination_idx;
@ -51,11 +51,11 @@ perf_window_swap(struct perf_window *perf_window, uint16_t first_by_duration_idx
uint64_t first_execution_time = perf_window->by_duration[first_by_duration_idx].execution_time;
uint64_t second_execution_time = perf_window->by_duration[second_by_duration_idx].execution_time;
/* Swap indices */
/* Swap Indices in Buffer*/
perf_window->by_termination[first_by_termination_idx] = second_by_duration_idx;
perf_window->by_termination[second_by_termination_idx] = first_by_duration_idx;
/* Swap nodes */
/* Swap by_termination_idx */
struct execution_node tmp_node = perf_window->by_duration[first_by_duration_idx];
perf_window->by_duration[first_by_duration_idx] = perf_window->by_duration[second_by_duration_idx];
perf_window->by_duration[second_by_duration_idx] = tmp_node;
@ -67,73 +67,66 @@ perf_window_swap(struct perf_window *perf_window, uint16_t first_by_duration_idx
== second_execution_time);
}
static inline void
perf_window_fill(struct perf_window *perf_window, uint64_t newest_execution_time)
{
for (uint16_t i = 0; i < PERF_WINDOW_CAPACITY; i++) {
perf_window->by_termination[i] = i;
perf_window->by_duration[i] = (struct execution_node){.execution_time = newest_execution_time,
.by_termination_idx = i};
}
perf_window->count = PERF_WINDOW_CAPACITY;
}
/**
* Adds newest_execution_time to the perf window
* Adds a new value to the perf window
* Not intended to be called directly!
* @param perf_window
* @param newest_execution_time
* @param value
*/
static inline void
perf_window_add(struct perf_window *perf_window, uint64_t newest_execution_time)
perf_window_add(struct perf_window *perf_window, uint64_t value)
{
assert(perf_window != NULL);
/* Assumption: A successful invocation should run for a non-zero amount of time */
assert(newest_execution_time > 0);
uint16_t idx_to_replace;
uint64_t previous_execution_time;
uint16_t idx_of_oldest;
bool check_up;
if (unlikely(!lock_is_locked(&perf_window->lock))) panic("lock not held when calling perf_window_add\n");
if (unlikely(!LOCK_IS_LOCKED(&perf_window->lock))) panic("lock not held when calling perf_window_add\n");
/* A successful invocation should run for a non-zero amount of time */
assert(value > 0);
/* If perf window is empty, fill all elements with newest_execution_time */
/* If count is 0, then fill entire array with initial execution times */
if (perf_window->count == 0) {
perf_window_fill(perf_window, newest_execution_time);
for (int i = 0; i < PERF_WINDOW_BUFFER_SIZE; i++) {
perf_window->by_termination[i] = i;
perf_window->by_duration[i] = (struct execution_node){ .execution_time = value,
.by_termination_idx = i };
}
perf_window->count = PERF_WINDOW_BUFFER_SIZE;
goto done;
}
/* If full, replace the oldest execution_time. Save the old execution time to know which direction to swap */
idx_to_replace = perf_window->by_termination[perf_window->count % PERF_WINDOW_CAPACITY];
previous_execution_time = perf_window->by_duration[idx_to_replace].execution_time;
perf_window->by_duration[idx_to_replace].execution_time = newest_execution_time;
/* Otherwise, replace the oldest value, and then sort */
idx_of_oldest = perf_window->by_termination[perf_window->count % PERF_WINDOW_BUFFER_SIZE];
check_up = value > perf_window->by_duration[idx_of_oldest].execution_time;
perf_window->by_duration[idx_of_oldest].execution_time = value;
/* At this point, the by_duration array is partially sorted. The node we overwrote needs to be shifted left or
* right. We can determine which direction to shift by comparing with the previous execution time. */
if (newest_execution_time > previous_execution_time) {
for (uint16_t i = idx_to_replace;
i + 1 < PERF_WINDOW_CAPACITY
if (check_up) {
for (uint16_t i = idx_of_oldest;
i + 1 < PERF_WINDOW_BUFFER_SIZE
&& perf_window->by_duration[i + 1].execution_time < perf_window->by_duration[i].execution_time;
i++) {
perf_window_swap(perf_window, i, i + 1);
}
} else {
for (uint16_t i = idx_to_replace;
i >= 1
for (int i = idx_of_oldest;
i - 1 >= 0
&& perf_window->by_duration[i - 1].execution_time > perf_window->by_duration[i].execution_time;
i--) {
perf_window_swap(perf_window, i, i - 1);
}
}
/* The idx that we replaces should still point to the same newest_execution_time */
assert(perf_window->by_duration[perf_window->by_termination[perf_window->count % PERF_WINDOW_CAPACITY]]
/* The idx that we replaces should still point to the same value */
assert(perf_window->by_duration[perf_window->by_termination[perf_window->count % PERF_WINDOW_BUFFER_SIZE]]
.execution_time
== newest_execution_time);
== value);
/* The by_duration array should be ordered by execution time */
#ifndef NDEBUG
for (int i = 1; i < PERF_WINDOW_CAPACITY; i++) {
for (int i = 1; i < PERF_WINDOW_BUFFER_SIZE; i++) {
assert(perf_window->by_duration[i - 1].execution_time <= perf_window->by_duration[i].execution_time);
}
#endif
@ -156,18 +149,12 @@ perf_window_get_percentile(struct perf_window *perf_window, uint8_t percentile,
{
assert(perf_window != NULL);
assert(percentile >= 50 && percentile <= 99);
assert(perf_window->count > 0);
if (unlikely(perf_window->count == 0)) return 0;
uint64_t perf_window_size = PERF_WINDOW_CAPACITY;
if (unlikely(perf_window->count < PERF_WINDOW_CAPACITY)) perf_window_size = perf_window->count;
int idx = precomputed_index;
if (precomputed_index == 0 || perf_window->count < PERF_WINDOW_CAPACITY) {
idx = perf_window_size * percentile / 100;
}
if (likely(perf_window->count >= PERF_WINDOW_BUFFER_SIZE))
return perf_window->by_duration[precomputed_index].execution_time;
return perf_window->by_duration[idx].execution_time;
return perf_window->by_duration[perf_window->count * percentile / 100].execution_time;
}
/**

@ -1,19 +1,15 @@
#pragma once
#include <assert.h>
#include <stdint.h>
#include "lock.h"
enum
{
PERF_WINDOW_CAPACITY = 256
};
static_assert(PERF_WINDOW_CAPACITY && !(PERF_WINDOW_CAPACITY & (PERF_WINDOW_CAPACITY - 1)),
"PERF_WINDOW_CAPACITY must be power of 2!");
/* Should be Power of 2! */
#define PERF_WINDOW_BUFFER_SIZE 16
static_assert(PERF_WINDOW_CAPACITY <= UINT16_MAX, "PERF_WINDOW_CAPACITY must be indexable by a 16-bit unsigned int");
#if ((PERF_WINDOW_BUFFER_SIZE == 0) || (PERF_WINDOW_BUFFER_SIZE & (PERF_WINDOW_BUFFER_SIZE - 1)) != 0)
#error "PERF_WINDOW_BUFFER_SIZE must be power of 2!"
#endif
/*
* The by_duration array sorts the last N executions by execution time
@ -28,8 +24,8 @@ struct execution_node {
};
struct perf_window {
struct execution_node by_duration[PERF_WINDOW_CAPACITY];
uint16_t by_termination[PERF_WINDOW_CAPACITY];
struct execution_node by_duration[PERF_WINDOW_BUFFER_SIZE];
uint16_t by_termination[PERF_WINDOW_BUFFER_SIZE];
uint64_t count;
lock_t lock;
};

@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdlib.h>
#include "generic_thread.h"
#include "lock.h"
#include "ps_list.h"
@ -25,7 +26,7 @@
{ \
ps_list_head_init(&self->list); \
self->use_lock = use_lock; \
if (use_lock) lock_init(&self->lock); \
if (use_lock) LOCK_INIT(&self->lock); \
} \
\
static inline void STRUCT_NAME##_pool_deinit(struct STRUCT_NAME##_pool *self) \
@ -43,7 +44,7 @@
static inline struct STRUCT_NAME *STRUCT_NAME##_pool_remove_nolock(struct STRUCT_NAME##_pool *self) \
{ \
assert(self != NULL); \
assert(!self->use_lock || lock_is_locked(&self->lock)); \
assert(!self->use_lock || LOCK_IS_LOCKED(&self->lock)); \
\
struct STRUCT_NAME *obj = NULL; \
\
@ -65,10 +66,9 @@
bool is_empty = STRUCT_NAME##_pool_is_empty(self); \
if (is_empty) return obj; \
\
lock_node_t node = {}; \
lock_lock(&self->lock, &node); \
LOCK_LOCK(&self->lock); \
obj = STRUCT_NAME##_pool_remove_nolock(self); \
lock_unlock(&self->lock, &node); \
LOCK_UNLOCK(&self->lock); \
return obj; \
} \
\
@ -76,7 +76,7 @@
{ \
assert(self != NULL); \
assert(obj != NULL); \
assert(!self->use_lock || lock_is_locked(&self->lock)); \
assert(!self->use_lock || LOCK_IS_LOCKED(&self->lock)); \
\
ps_list_head_add_d(&self->list, obj); \
} \
@ -87,8 +87,7 @@
assert(obj != NULL); \
assert(self->use_lock); \
\
lock_node_t node = {}; \
lock_lock(&self->lock, &node); \
LOCK_LOCK(&self->lock); \
STRUCT_NAME##_pool_add_nolock(self, obj); \
lock_unlock(&self->lock, &node); \
LOCK_UNLOCK(&self->lock); \
}

@ -1,7 +1,7 @@
#pragma once
#include <stdarg.h>
#include <stdio.h>
#include <stdarg.h>
#define PRETTY_PRINT_COLOR_CODE_RED "\033[1;31m"
#define PRETTY_COLOR_CODE_GREEN "\033[0;32m"

@ -3,8 +3,8 @@
#include <errno.h>
#include "listener_thread.h"
#include "lock.h"
#include "listener_thread.h"
#include "panic.h"
#include "runtime.h"
#include "worker_thread.h"
@ -60,7 +60,7 @@ priority_queue_append(struct priority_queue *priority_queue, void *new_item)
{
assert(priority_queue != NULL);
assert(new_item != NULL);
assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock));
int rc;
@ -85,7 +85,7 @@ static inline bool
priority_queue_is_empty(struct priority_queue *priority_queue)
{
assert(priority_queue != NULL);
assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock));
return priority_queue->size == 0;
}
@ -99,7 +99,7 @@ priority_queue_percolate_up(struct priority_queue *priority_queue)
{
assert(priority_queue != NULL);
assert(priority_queue->get_priority_fn != NULL);
assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock));
/* If there's only one element, set memoized lookup and early out */
if (priority_queue->size == 1) {
@ -135,7 +135,7 @@ priority_queue_find_smallest_child(struct priority_queue *priority_queue, const
assert(priority_queue != NULL);
assert(parent_index >= 1 && parent_index <= priority_queue->size);
assert(priority_queue->get_priority_fn != NULL);
assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock));
int left_child_index = 2 * parent_index;
int right_child_index = 2 * parent_index + 1;
@ -167,7 +167,7 @@ priority_queue_percolate_down(struct priority_queue *priority_queue, int parent_
{
assert(priority_queue != NULL);
assert(priority_queue->get_priority_fn != NULL);
assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock));
assert(!listener_thread_is_running());
bool update_highest_value = parent_index == 1;
@ -217,7 +217,7 @@ priority_queue_dequeue_if_earlier_nolock(struct priority_queue *priority_queue,
assert(dequeued_element != NULL);
assert(priority_queue->get_priority_fn != NULL);
assert(!listener_thread_is_running());
assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock));
int return_code;
@ -251,10 +251,9 @@ priority_queue_dequeue_if_earlier(struct priority_queue *priority_queue, void **
{
int return_code;
lock_node_t node = {};
lock_lock(&priority_queue->lock, &node);
LOCK_LOCK(&priority_queue->lock);
return_code = priority_queue_dequeue_if_earlier_nolock(priority_queue, dequeued_element, target_deadline);
lock_unlock(&priority_queue->lock, &node);
LOCK_UNLOCK(&priority_queue->lock);
return return_code;
}
@ -282,7 +281,7 @@ priority_queue_initialize(size_t capacity, bool use_lock, priority_queue_get_pri
priority_queue->get_priority_fn = get_priority_fn;
priority_queue->use_lock = use_lock;
if (use_lock) lock_init(&priority_queue->lock);
if (use_lock) LOCK_INIT(&priority_queue->lock);
return priority_queue;
}
@ -333,7 +332,7 @@ static inline int
priority_queue_length_nolock(struct priority_queue *priority_queue)
{
assert(priority_queue != NULL);
assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock));
return priority_queue->size;
}
@ -345,10 +344,9 @@ priority_queue_length_nolock(struct priority_queue *priority_queue)
static inline int
priority_queue_length(struct priority_queue *priority_queue)
{
lock_node_t node = {};
lock_lock(&priority_queue->lock, &node);
LOCK_LOCK(&priority_queue->lock);
int size = priority_queue_length_nolock(priority_queue);
lock_unlock(&priority_queue->lock, &node);
LOCK_UNLOCK(&priority_queue->lock);
return size;
}
@ -362,7 +360,7 @@ priority_queue_enqueue_nolock(struct priority_queue *priority_queue, void *value
{
assert(priority_queue != NULL);
assert(value != NULL);
assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock));
int rc;
@ -388,10 +386,9 @@ priority_queue_enqueue(struct priority_queue *priority_queue, void *value)
{
int rc;
lock_node_t node = {};
lock_lock(&priority_queue->lock, &node);
LOCK_LOCK(&priority_queue->lock);
rc = priority_queue_enqueue_nolock(priority_queue, value);
lock_unlock(&priority_queue->lock, &node);
LOCK_UNLOCK(&priority_queue->lock);
return rc;
}
@ -406,7 +403,7 @@ priority_queue_delete_nolock(struct priority_queue *priority_queue, void *value)
{
assert(priority_queue != NULL);
assert(value != NULL);
assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock));
for (int i = 1; i <= priority_queue->size; i++) {
if (priority_queue->items[i] == value) {
@ -430,10 +427,9 @@ priority_queue_delete(struct priority_queue *priority_queue, void *value)
{
int rc;
lock_node_t node = {};
lock_lock(&priority_queue->lock, &node);
LOCK_LOCK(&priority_queue->lock);
rc = priority_queue_delete_nolock(priority_queue, value);
lock_unlock(&priority_queue->lock, &node);
LOCK_UNLOCK(&priority_queue->lock);
return rc;
}
@ -472,7 +468,7 @@ priority_queue_top_nolock(struct priority_queue *priority_queue, void **dequeued
assert(priority_queue != NULL);
assert(dequeued_element != NULL);
assert(priority_queue->get_priority_fn != NULL);
assert(!priority_queue->use_lock || lock_is_locked(&priority_queue->lock));
assert(!priority_queue->use_lock || LOCK_IS_LOCKED(&priority_queue->lock));
int return_code;
@ -499,10 +495,9 @@ priority_queue_top(struct priority_queue *priority_queue, void **dequeued_elemen
{
int return_code;
lock_node_t node = {};
lock_lock(&priority_queue->lock, &node);
LOCK_LOCK(&priority_queue->lock);
return_code = priority_queue_top_nolock(priority_queue, dequeued_element);
lock_unlock(&priority_queue->lock, &node);
LOCK_UNLOCK(&priority_queue->lock);
return return_code;
}

@ -1,103 +0,0 @@
#pragma once
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "runtime.h" /* For runtime_pid */
/* Used to read process-level metrics associated with sledgert from procfs
* The parsing behavior is based on prtstat -r
*/
enum PROC_STAT
{
PROC_STAT_PID = 0, /* Process ID */
PROC_STAT_COMM = 1, /* Process Name */
PROC_STAT_STATE = 2, /* State */
PROC_STAT_PPID, /* Parent Process ID */
PROC_STAT_PGRP, /* Group ID */
PROC_STAT_SESSION, /* Session ID */
PROC_STAT_TTY_NR, /* ??? */
PROC_STAT_TPGID, /* ??? */
PROC_STAT_FLAGS, /* ??? */
PROC_STAT_MINFLT, /* Minor Page Faults */
PROC_STAT_CMINFLT, /* Minor Page Faults of children */
PROC_STAT_MAJFLT, /* Major Page Faults */
PROC_STAT_CMAJFLT, /* Major Page Faults of children */
PROC_STAT_UTIME, /* User Time */
PROC_STAT_STIME, /* System Time */
PROC_STAT_CUTIME, /* Child User Time */
PROC_STAT_CSTIME, /* Child System Time */
PROC_STAT_PRIORITY,
PROC_STAT_NICE,
PROC_STAT_NUM_THREADS,
PROC_STAT_ITREALVALUE,
PROC_STAT_STARTTIME, /* Start Time */
PROC_STAT_VSIZE, /* Virtual Memory */
PROC_STAT_RSS,
PROC_STAT_RSSLIM,
PROC_STAT_STARTCODE,
PROC_STAT_ENDCODE,
PROC_STAT_STARTSTACK,
PROC_STAT_KSTKESP,
PROC_STAT_KSTKEIP,
PROC_STAT_WCHAN,
PROC_STAT_NSWAP,
PROC_STAT_CNSWAP,
PROC_STAT_EXIT_SIGNAL,
PROC_STAT_PROCESSOR,
PROC_STAT_RT_PRIORITY,
PROC_STAT_POLICY,
PROC_STAT_DELAYACCR_BLKIO_TICKS,
PROC_STAT_GUEST_TIME,
PROC_STAT_CGUEST_TIME,
PROC_STAT_COUNT
};
struct proc_stat_metrics {
uint64_t minor_page_faults;
uint64_t major_page_faults;
uint64_t child_minor_page_faults;
uint64_t child_major_page_faults;
uint64_t user_time;
uint64_t system_time;
uint64_t guest_time;
};
static inline void
proc_stat_metrics_init(struct proc_stat_metrics *stat)
{
assert(runtime_pid > 0);
// Open sledgert's stat file in procfs
char path[256];
snprintf(path, 256, "/proc/%d/stat", runtime_pid);
FILE *proc_stat = fopen(path, "r");
/* Read stat file into in-memory buffer */
char buf[BUFSIZ];
fgets(buf, BUFSIZ, proc_stat);
fclose(proc_stat);
/* Parse into an array of tokens with indices aligning to the PROC_STAT enum */
char *pos = NULL;
char *proc_stat_values[PROC_STAT_COUNT];
for (int i = 0; i < PROC_STAT_COUNT; i++) {
char *tok = i == 0 ? strtok_r(buf, " ", &pos) : strtok_r(NULL, " ", &pos);
proc_stat_values[i] = tok;
}
/* Fill the proc_state_metrics struct with metrics of interest */
/* Minor Page Faults, Major Page Faults, Vsize, User, System, Guest, Uptime */
stat->minor_page_faults = strtoul(proc_stat_values[PROC_STAT_MINFLT], NULL, 10);
stat->major_page_faults = strtoul(proc_stat_values[PROC_STAT_MAJFLT], NULL, 10);
stat->child_minor_page_faults = strtoul(proc_stat_values[PROC_STAT_CMINFLT], NULL, 10);
stat->child_major_page_faults = strtoul(proc_stat_values[PROC_STAT_CMAJFLT], NULL, 10);
stat->user_time = strtoul(proc_stat_values[PROC_STAT_UTIME], NULL, 10);
stat->system_time = strtoul(proc_stat_values[PROC_STAT_STIME], NULL, 10);
stat->guest_time = strtoul(proc_stat_values[PROC_STAT_GUEST_TIME], NULL, 10);
}

@ -1,32 +1,19 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stddef.h>
#include "execution_histogram.h"
#include "http_route_total.h"
#include "admissions_info.h"
#include "module.h"
#include "perf_window.h"
struct regression_model {
double bias;
double scale;
uint32_t num_of_param;
double beta1;
double beta2;
};
/* Assumption: entrypoint is always _start. This should be enhanced later */
struct route {
char *route;
struct http_route_total metrics;
struct module *module;
char *route;
struct module *module;
/* HTTP State */
uint32_t relative_deadline_us;
uint64_t relative_deadline; /* cycles */
char *response_content_type;
struct execution_histogram execution_histogram;
struct perf_window latency;
struct module *module_proprocess;
struct regression_model regr_model;
uint32_t relative_deadline_us;
uint64_t relative_deadline; /* cycles */
size_t response_size;
char *response_content_type;
struct admissions_info admissions_info;
};

@ -4,7 +4,6 @@
#include <stdio.h>
#include <stdlib.h>
#include "admissions_control.h"
#include "runtime.h"
#include "scheduler_options.h"
@ -13,13 +12,9 @@ enum route_config_member
route_config_member_route,
route_config_member_path,
route_config_member_admissions_percentile,
route_config_member_expected_execution_us,
route_config_member_relative_deadline_us,
route_config_member_path_preprocess,
route_config_member_model_bias,
route_config_member_model_scale,
route_config_member_model_num_of_param,
route_config_member_model_beta1,
route_config_member_model_beta2,
route_config_member_http_resp_size,
route_config_member_http_resp_content_type,
route_config_member_len
};
@ -28,13 +23,9 @@ struct route_config {
char *route;
char *path;
uint8_t admissions_percentile;
uint32_t expected_execution_us;
uint32_t relative_deadline_us;
char *path_preprocess;
uint32_t model_bias;
uint32_t model_scale;
uint32_t model_num_of_param;
uint32_t model_beta1;
uint32_t model_beta2;
uint32_t http_resp_size;
char *http_resp_content_type;
};
@ -55,15 +46,10 @@ route_config_print(struct route_config *config)
printf("[Route] Route: %s\n", config->route);
printf("[Route] Path: %s\n", config->path);
printf("[Route] Admissions Percentile: %hhu\n", config->admissions_percentile);
printf("[Route] Expected Execution (us): %u\n", config->expected_execution_us);
printf("[Route] Relative Deadline (us): %u\n", config->relative_deadline_us);
printf("[Route] HTTP Response Size: %u\n", config->http_resp_size);
printf("[Route] HTTP Response Content Type: %s\n", config->http_resp_content_type);
#ifdef EXECUTION_HISTOGRAM
printf("[Route] Path of Preprocessing Module: %s\n", config->path_preprocess);
printf("[Route] Model Bias: %u\n", config->model_bias);
printf("[Route] Model Scale: %u\n", config->model_scale);
printf("[Route] Model Num of Parameters: %u\n", config->model_num_of_param);
printf("[Route] Model Betas: [%u, %u]\n", config->model_beta1, config->model_beta2);
#endif
}
/**
@ -75,7 +61,7 @@ static inline int
route_config_validate(struct route_config *config, bool *did_set)
{
if (did_set[route_config_member_route] == false) {
fprintf(stderr, "route field is required\n");
fprintf(stderr, "path field is required\n");
return -1;
}
@ -89,9 +75,9 @@ route_config_validate(struct route_config *config, bool *did_set)
config->http_resp_content_type = "text/plain";
}
if (scheduler != SCHEDULER_FIFO && scheduler != SCHEDULER_SJF) {
if (scheduler != SCHEDULER_FIFO) {
if (did_set[route_config_member_relative_deadline_us] == false) {
fprintf(stderr, "relative_deadline_us is required for the selected scheduler\n");
fprintf(stderr, "relative_deadline_us is required\n");
return -1;
}
@ -100,80 +86,36 @@ route_config_validate(struct route_config *config, bool *did_set)
(uint32_t)RUNTIME_RELATIVE_DEADLINE_US_MAX, config->relative_deadline_us);
return -1;
}
}
#ifdef EXECUTION_HISTOGRAM
if (config->admissions_percentile > 99 || config->admissions_percentile < 50) {
fprintf(stderr, "admissions-percentile must be > 50 and <= 99 but was %u, defaulting to 70\n",
config->admissions_percentile);
config->admissions_percentile = 70;
}
#endif
#ifdef EXECUTION_REGRESSION
if (did_set[route_config_member_path_preprocess] == false) {
fprintf(stderr, "model path_preprocess field is required. Put zero if just default preprocessing\n");
return -1;
} else if (strcmp(config->path_preprocess, "0") == 0) {
config->path_preprocess = NULL;
}
if (did_set[route_config_member_model_bias] == false) {
fprintf(stderr, "model bias field is required\n");
return -1;
}
if (did_set[route_config_member_model_scale] == false) {
fprintf(stderr, "model scale field is required\n");
return -1;
}
if (config->model_scale == 0) {
fprintf(stderr, "model scale cannot be zero (to avoid divide by zero)\n");
return -1;
}
if (did_set[route_config_member_model_num_of_param] == false) {
fprintf(stderr, "model num_of_param field is required\n");
return -1;
}
if (did_set[route_config_member_model_beta1] == false) {
fprintf(stderr, "model beta1 field is required\n");
return -1;
}
if (config->model_beta1 == 0) {
fprintf(stderr, "model beta1 cannot be zero (to avoid divide by zero)\n");
return -1;
}
if (did_set[route_config_member_model_beta2] == false) {
fprintf(stderr, "model beta2 field is required. Put zero for just default preprocessing\n");
return -1;
}
#ifdef ADMISSIONS_CONTROL
if (did_set[route_config_member_expected_execution_us] == false) {
fprintf(stderr, "expected-execution-us is required\n");
return -1;
}
if (config->model_num_of_param < 1) {
fprintf(stderr, "model num_of_param must be at least 1 (just default preprocessing)\n");
return -1;
} else if (config->model_num_of_param == 1) {
if (config->path_preprocess) {
fprintf(stderr, "model_num_of_param cannot be 1 when using tenant preprocessing\n");
if (did_set[route_config_member_admissions_percentile] == false) {
fprintf(stderr, "admissions_percentile is required\n");
return -1;
}
config->model_beta2 = 1; /* This is to avoid divide-by-zero */
} else {
/* For now we just support up to two params */
assert(config->model_num_of_param == 2);
if (config->path_preprocess == NULL) {
fprintf(stderr, "model_num_of_param cannot be more than 1 when just default preprocessing\n");
if (config->admissions_percentile > 99 || config->admissions_percentile < 50) {
fprintf(stderr, "admissions-percentile must be > 50 and <= 99 but was %u\n",
route_config->admissions_percentile);
return -1;
}
if (config->model_beta2 == 0) {
fprintf(stderr, "model beta2 cannot be zero (to avoid divide by zero)\n");
/* If the ratio is too big, admissions control is too coarse */
uint32_t ratio = route_config->relative_deadline_us / route_config->expected_execution_us;
if (ratio > ADMISSIONS_CONTROL_GRANULARITY) {
fprintf(stderr,
"Ratio of Deadline to Execution time cannot exceed admissions control "
"granularity of "
"%d\n",
ADMISSIONS_CONTROL_GRANULARITY);
return -1;
}
}
#endif
}
return 0;
}

@ -6,10 +6,13 @@
#include "json.h"
#include "route_config.h"
static const char *route_config_json_keys[route_config_member_len] =
{"route", "path", "admissions-percentile", "relative-deadline-us",
"path_preprocess", "model-bias", "model-scale", "model-num-of-param",
"model-beta1", "model-beta2", "http-resp-content-type"};
static const char *route_config_json_keys[route_config_member_len] = { "route",
"path",
"admissions-percentile",
"expected-execution-us",
"relative-deadline-us",
"http-resp-size",
"http-resp-content-type" };
static inline int
route_config_set_key_once(bool *did_set, enum route_config_member member)
@ -28,8 +31,8 @@ route_config_parse(struct route_config *config, const char *json_buf, jsmntok_t
int tokens_size)
{
int i = tokens_base;
char key[32] = {0};
bool did_set[route_config_member_len] = {false};
char key[32] = { 0 };
bool did_set[route_config_member_len] = { false };
if (!has_valid_type(tokens[i], "Anonymous Route Config Object", JSMN_OBJECT, json_buf)) return -1;
if (!is_nonempty_object(tokens[i], "Anonymous Route Config Object")) return -1;
@ -59,11 +62,6 @@ route_config_parse(struct route_config *config, const char *json_buf, jsmntok_t
if (route_config_set_key_once(did_set, route_config_member_path) == -1) return -1;
config->path = strndup(json_buf + tokens[i].start, tokens[i].end - tokens[i].start);
} else if (strcmp(key, route_config_json_keys[route_config_member_path_preprocess]) == 0) {
if (!is_nonempty_string(tokens[i], key)) return -1;
if (route_config_set_key_once(did_set, route_config_member_path_preprocess) == -1) return -1;
config->path_preprocess = strndup(json_buf + tokens[i].start, tokens[i].end - tokens[i].start);
} else if (strcmp(key, route_config_json_keys[route_config_member_admissions_percentile]) == 0) {
if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1;
if (route_config_set_key_once(did_set, route_config_member_admissions_percentile) == -1)
@ -73,57 +71,31 @@ route_config_parse(struct route_config *config, const char *json_buf, jsmntok_t
route_config_json_keys[route_config_member_admissions_percentile],
&config->admissions_percentile);
if (rc < 0) return -1;
} else if (strcmp(key, route_config_json_keys[route_config_member_relative_deadline_us]) == 0) {
} else if (strcmp(key, route_config_json_keys[route_config_member_expected_execution_us]) == 0) {
if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1;
if (route_config_set_key_once(did_set, route_config_member_relative_deadline_us) == -1)
if (route_config_set_key_once(did_set, route_config_member_expected_execution_us) == -1)
return -1;
int rc = parse_uint32_t(tokens[i], json_buf,
route_config_json_keys[route_config_member_relative_deadline_us],
&config->relative_deadline_us);
if (rc < 0) return -1;
} else if (strcmp(key, "expected-execution-us") == 0) {
if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1;
printf("The \"expected-execution-us\" field has been deprecated, so no need.\n");
} else if (strcmp(key, route_config_json_keys[route_config_member_model_bias]) == 0) {
if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1;
if (route_config_set_key_once(did_set, route_config_member_model_bias) == -1) return -1;
int rc = parse_uint32_t(tokens[i], json_buf,
route_config_json_keys[route_config_member_model_bias],
&config->model_bias);
if (rc < 0) return -1;
} else if (strcmp(key, route_config_json_keys[route_config_member_model_scale]) == 0) {
if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1;
if (route_config_set_key_once(did_set, route_config_member_model_scale) == -1) return -1;
int rc = parse_uint32_t(tokens[i], json_buf,
route_config_json_keys[route_config_member_model_scale],
&config->model_scale);
if (rc < 0) return -1;
} else if (strcmp(key, route_config_json_keys[route_config_member_model_num_of_param]) == 0) {
if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1;
if (route_config_set_key_once(did_set, route_config_member_model_num_of_param) == -1) return -1;
int rc = parse_uint32_t(tokens[i], json_buf,
route_config_json_keys[route_config_member_model_num_of_param],
&config->model_num_of_param);
route_config_json_keys[route_config_member_expected_execution_us],
&config->expected_execution_us);
if (rc < 0) return -1;
} else if (strcmp(key, route_config_json_keys[route_config_member_model_beta1]) == 0) {
} else if (strcmp(key, route_config_json_keys[route_config_member_relative_deadline_us]) == 0) {
if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1;
if (route_config_set_key_once(did_set, route_config_member_model_beta1) == -1) return -1;
if (route_config_set_key_once(did_set, route_config_member_relative_deadline_us) == -1)
return -1;
int rc = parse_uint32_t(tokens[i], json_buf,
route_config_json_keys[route_config_member_model_beta1],
&config->model_beta1);
route_config_json_keys[route_config_member_relative_deadline_us],
&config->relative_deadline_us);
if (rc < 0) return -1;
} else if (strcmp(key, route_config_json_keys[route_config_member_model_beta2]) == 0) {
} else if (strcmp(key, route_config_json_keys[route_config_member_http_resp_size]) == 0) {
if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1;
if (route_config_set_key_once(did_set, route_config_member_model_beta2) == -1) return -1;
if (route_config_set_key_once(did_set, route_config_member_http_resp_size) == -1) return -1;
int rc = parse_uint32_t(tokens[i], json_buf,
route_config_json_keys[route_config_member_model_beta2],
&config->model_beta2);
route_config_json_keys[route_config_member_http_resp_size],
&config->http_resp_size);
if (rc < 0) return -1;
} else if (strcmp(key, route_config_json_keys[route_config_member_http_resp_content_type]) == 0) {
if (!is_nonempty_string(tokens[i], key)) return -1;

@ -1,38 +0,0 @@
#pragma once
#include <stdint.h>
#include "perf_window.h"
static inline void
route_latency_init(struct perf_window *route_latency)
{
#ifdef ROUTE_LATENCY
perf_window_initialize(route_latency);
#endif
}
static inline uint64_t
route_latency_get(struct perf_window *route_latency, uint8_t percentile, int precomputed_index)
{
#ifdef ROUTE_LATENCY
lock_node_t node = {};
lock_lock(&route_latency->lock, &node);
uint64_t res = perf_window_get_percentile(route_latency, percentile, precomputed_index);
lock_unlock(&route_latency->lock, &node);
return res;
#else
return 0;
#endif
}
static inline void
route_latency_add(struct perf_window *route_latency, uint64_t value)
{
#ifdef ROUTE_LATENCY
lock_node_t node = {};
lock_lock(&route_latency->lock, &node);
perf_window_add(route_latency, value);
lock_unlock(&route_latency->lock, &node);
#endif
}

@ -1,10 +1,9 @@
#pragma once
#include <pthread.h>
#include <sys/epoll.h> /* for epoll_create1(), epoll_ctl(), struct epoll_event */
#include <stdatomic.h>
#include <stdbool.h>
#include <sys/epoll.h> /* for epoll_create1(), epoll_ctl(), struct epoll_event */
#include <sys/types.h> /* for pid_t */
#include "likely.h"
#include "types.h"
@ -35,9 +34,7 @@ enum RUNTIME_SIGALRM_HANDLER
RUNTIME_SIGALRM_HANDLER_TRIAGED = 1
};
extern pid_t runtime_pid;
extern bool runtime_preemption_enabled;
extern bool runtime_worker_spinloop_pause_enabled;
extern uint32_t runtime_processor_speed_MHz;
extern uint32_t runtime_quantum_us;
extern enum RUNTIME_SIGALRM_HANDLER runtime_sigalrm_handler;

@ -1,12 +1,12 @@
#pragma once
#include <sys/mman.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/mman.h>
#include "panic.h"
#include "sandbox_types.h"
#include "tenant.h"
#include "sandbox_types.h"
/***************************
* Public API *

@ -13,10 +13,9 @@ static inline void
sandbox_perf_log_print_header()
{
if (sandbox_perf_log == NULL) { perror("sandbox perf log"); }
fprintf(sandbox_perf_log,
"id,tenant,route,state,deadline,actual,queued,uninitialized,allocated,initialized,"
"runnable,interrupted,preempted,"
"running_sys,running_user,asleep,returned,complete,error,proc_MHz,payload_size,regression_param\n");
fprintf(sandbox_perf_log, "id,tenant,route,state,deadline,actual,queued,uninitialized,allocated,initialized,"
"runnable,interrupted,preempted,"
"running_sys,running_user,asleep,returned,complete,error,proc_MHz,memory\n");
}
/**
@ -37,8 +36,7 @@ sandbox_perf_log_print_entry(struct sandbox *sandbox)
* becomes more intelligent, then peak linear memory size needs to be tracked
* seperately from current linear memory size.
*/
fprintf(sandbox_perf_log,
"%lu,%s,%s,%s,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%u,%u,%d,%d,%lf\n",
fprintf(sandbox_perf_log, "%lu,%s,%s,%s,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%u\n",
sandbox->id, sandbox->tenant->name, sandbox->route->route, sandbox_state_stringify(sandbox->state),
sandbox->route->relative_deadline, sandbox->total_time, queued_duration,
sandbox->duration_of_state[SANDBOX_UNINITIALIZED], sandbox->duration_of_state[SANDBOX_ALLOCATED],
@ -47,8 +45,7 @@ sandbox_perf_log_print_entry(struct sandbox *sandbox)
sandbox->duration_of_state[SANDBOX_RUNNING_SYS], sandbox->duration_of_state[SANDBOX_RUNNING_USER],
sandbox->duration_of_state[SANDBOX_ASLEEP], sandbox->duration_of_state[SANDBOX_RETURNED],
sandbox->duration_of_state[SANDBOX_COMPLETE], sandbox->duration_of_state[SANDBOX_ERROR],
runtime_processor_speed_MHz, sandbox->response_code, 0, sandbox->payload_size,
sandbox->regression_param);
runtime_processor_speed_MHz);
}
static inline void

@ -5,10 +5,10 @@
#include "arch/getcycles.h"
#include "local_runqueue.h"
#include "sandbox_types.h"
#include "sandbox_state.h"
#include "sandbox_state_history.h"
#include "sandbox_state_transition.h"
#include "sandbox_types.h"
/**
* Transitions a sandbox to the SANDBOX_ASLEEP state.

@ -3,9 +3,7 @@
#include <assert.h>
#include <stdint.h>
#include "admissions_control.h"
#include "arch/getcycles.h"
#include "execution_histogram.h"
#include "panic.h"
#include "sandbox_functions.h"
#include "sandbox_perf_log.h"
@ -48,24 +46,14 @@ sandbox_set_as_complete(struct sandbox *sandbox, sandbox_state_t last_state)
sandbox_state_totals_increment(SANDBOX_COMPLETE);
sandbox_state_totals_decrement(last_state);
struct route *route = sandbox->route;
#ifdef EXECUTION_HISTOGRAM
/* Execution Histogram Post Processing */
const uint64_t execution_duration = sandbox->duration_of_state[SANDBOX_RUNNING_USER]
+ sandbox->duration_of_state[SANDBOX_RUNNING_SYS];
execution_histogram_update(&route->execution_histogram, execution_duration);
#endif
#ifdef ADMISSIONS_CONTROL
/* Admissions Control Post Processing */
admissions_info_update(&sandbox->route->admissions_info, sandbox->duration_of_state[SANDBOX_RUNNING_USER]
+ sandbox->duration_of_state[SANDBOX_RUNNING_SYS]);
admissions_control_subtract(sandbox->admissions_estimate);
#endif
/* Terminal State Logging for Sandbox */
sandbox_perf_log_print_entry(sandbox);
sandbox_summarize_page_allocations(sandbox);
route_latency_add(&route->latency, sandbox->total_time);
/* State Change Hooks */
sandbox_state_transition_from_hook(sandbox, last_state);

@ -3,17 +3,16 @@
#include <assert.h>
#include <stdint.h>
#include "admissions_control.h"
#include "arch/getcycles.h"
#include "listener_thread.h"
#include "local_runqueue.h"
#include "panic.h"
#include "sandbox_state.h"
#include "sandbox_functions.h"
#include "sandbox_perf_log.h"
#include "sandbox_state.h"
#include "sandbox_state_history.h"
#include "sandbox_state_transition.h"
#include "sandbox_summarize_page_allocations.h"
#include "panic.h"
/**
* Transitions a sandbox to the SANDBOX_ERROR state.
@ -49,24 +48,17 @@ sandbox_set_as_error(struct sandbox *sandbox, sandbox_state_t last_state)
/* State Change Bookkeeping */
assert(now > sandbox->timestamp_of.last_state_change);
sandbox->last_state_duration = now - sandbox->timestamp_of.last_state_change;
if (last_state == SANDBOX_RUNNING_SYS) {
sandbox->remaining_exec = (sandbox->remaining_exec > sandbox->last_state_duration)
? sandbox->remaining_exec - sandbox->last_state_duration
: 0;
}
sandbox->duration_of_state[last_state] += sandbox->last_state_duration;
sandbox->timestamp_of.last_state_change = now;
sandbox_state_history_append(&sandbox->state_history, SANDBOX_ERROR);
sandbox_state_totals_increment(SANDBOX_ERROR);
sandbox_state_totals_decrement(last_state);
#ifdef ADMISSIONS_CONTROL
/* Admissions Control Post Processing */
admissions_control_subtract(sandbox->admissions_estimate);
#endif
/* Return HTTP session to listener core to be written back to client */
http_session_set_response_header(sandbox->http, 500);
http_session_set_response_header(sandbox->http, 500, NULL, 0);
sandbox->http->state = HTTP_SESSION_EXECUTION_COMPLETE;
http_session_send_response(sandbox->http, (void_star_cb)listener_thread_register_http_session);
sandbox->http = NULL;

@ -25,10 +25,6 @@ sandbox_set_as_interrupted(struct sandbox *sandbox, sandbox_state_t last_state)
/* State Change Bookkeeping */
assert(now > sandbox->timestamp_of.last_state_change);
sandbox->last_state_duration = now - sandbox->timestamp_of.last_state_change;
assert(last_state == SANDBOX_RUNNING_USER);
sandbox->remaining_exec = (sandbox->remaining_exec > sandbox->last_state_duration)
? sandbox->remaining_exec - sandbox->last_state_duration
: 0;
sandbox->duration_of_state[last_state] += sandbox->last_state_duration;
sandbox->timestamp_of.last_state_change = now;
/* We do not append SANDBOX_INTERRUPTED to the sandbox_state_history because it would quickly fill the buffer */

@ -4,7 +4,6 @@
#include <stdint.h>
#include "arch/getcycles.h"
#include "auto_buf.h"
#include "listener_thread.h"
#include "local_runqueue.h"
#include "panic.h"
@ -45,16 +44,14 @@ sandbox_set_as_returned(struct sandbox *sandbox, sandbox_state_t last_state)
/* State Change Bookkeeping */
assert(now > sandbox->timestamp_of.last_state_change);
sandbox->last_state_duration = now - sandbox->timestamp_of.last_state_change;
sandbox->remaining_exec = (sandbox->remaining_exec > sandbox->last_state_duration)
? sandbox->remaining_exec - sandbox->last_state_duration
: 0;
sandbox->duration_of_state[last_state] += sandbox->last_state_duration;
sandbox->timestamp_of.last_state_change = now;
sandbox_state_history_append(&sandbox->state_history, SANDBOX_RETURNED);
sandbox_state_totals_increment(SANDBOX_RETURNED);
sandbox_state_totals_decrement(last_state);
http_session_set_response_header(sandbox->http, 200);
http_session_set_response_header(sandbox->http, 200, sandbox->route->response_content_type,
sandbox->http->response_buffer.length);
sandbox->http->state = HTTP_SESSION_EXECUTION_COMPLETE;
http_session_send_response(sandbox->http, (void_star_cb)listener_thread_register_http_session);
sandbox->http = NULL;
@ -63,7 +60,5 @@ sandbox_set_as_returned(struct sandbox *sandbox, sandbox_state_t last_state)
sandbox_state_transition_from_hook(sandbox, last_state);
sandbox_state_transition_to_hook(sandbox, SANDBOX_RETURNED);
assert(sandbox->response_code == 0);
sandbox->response_code = 200;
sandbox_process_scheduler_updates(sandbox);
}

@ -41,11 +41,6 @@ sandbox_set_as_running_sys(struct sandbox *sandbox, sandbox_state_t last_state)
/* State Change Bookkeeping */
assert(now > sandbox->timestamp_of.last_state_change);
sandbox->last_state_duration = now - sandbox->timestamp_of.last_state_change;
if (last_state == SANDBOX_RUNNING_USER) {
sandbox->remaining_exec = (sandbox->remaining_exec > sandbox->last_state_duration)
? sandbox->remaining_exec - sandbox->last_state_duration
: 0;
}
sandbox->duration_of_state[last_state] += sandbox->last_state_duration;
sandbox->timestamp_of.last_state_change = now;
sandbox_state_history_append(&sandbox->state_history, SANDBOX_RUNNING_SYS);
@ -60,8 +55,6 @@ sandbox_set_as_running_sys(struct sandbox *sandbox, sandbox_state_t last_state)
static inline void
sandbox_syscall(struct sandbox *sandbox)
{
if (sandbox->module->type == PREPROCESS_MODULE) return;
assert(sandbox->state == SANDBOX_RUNNING_USER);
sandbox_set_as_running_sys(sandbox, SANDBOX_RUNNING_USER);

@ -6,10 +6,10 @@
#include "arch/getcycles.h"
#include "current_sandbox.h"
#include "panic.h"
#include "sandbox_functions.h"
#include "sandbox_state_history.h"
#include "sandbox_state_transition.h"
#include "sandbox_types.h"
#include "sandbox_functions.h"
static inline void
sandbox_set_as_running_user(struct sandbox *sandbox, sandbox_state_t last_state)
@ -37,11 +37,6 @@ sandbox_set_as_running_user(struct sandbox *sandbox, sandbox_state_t last_state)
/* State Change Bookkeeping */
assert(now > sandbox->timestamp_of.last_state_change);
sandbox->last_state_duration = now - sandbox->timestamp_of.last_state_change;
if (last_state == SANDBOX_RUNNING_SYS) {
sandbox->remaining_exec = (sandbox->remaining_exec > sandbox->last_state_duration)
? sandbox->remaining_exec - sandbox->last_state_duration
: 0;
}
sandbox->duration_of_state[last_state] += sandbox->last_state_duration;
sandbox->timestamp_of.last_state_change = now;
sandbox_state_history_append(&sandbox->state_history, SANDBOX_RUNNING_USER);
@ -66,8 +61,6 @@ sandbox_set_as_running_user(struct sandbox *sandbox, sandbox_state_t last_state)
static inline void
sandbox_return(struct sandbox *sandbox)
{
if (sandbox->module->type == PREPROCESS_MODULE) return;
assert(sandbox->state == SANDBOX_RUNNING_SYS);
sandbox_set_as_running_user(sandbox, SANDBOX_RUNNING_SYS);
}

@ -4,16 +4,16 @@
#include <stdint.h>
/* Count of the total number of requests we've ever allocated. Never decrements as it is used to generate IDs */
extern _Atomic uint64_t sandbox_total;
extern _Atomic uint32_t sandbox_total;
static inline void
sandbox_total_initialize()
{
atomic_init(&sandbox_total, 0);
atomic_init(&sandbox_total, 1); /* keep zero for error-handling purposes */
}
static inline uint64_t
static inline uint32_t
sandbox_total_postfix_increment()
{
return atomic_fetch_add(&sandbox_total, 1) + 1;
return atomic_fetch_add(&sandbox_total, 1);
}

@ -9,11 +9,11 @@
#include "sandbox_state.h"
#include "sandbox_state_history.h"
#include "tenant.h"
#include "wasi.h"
#include "wasm_globals.h"
#include "wasm_memory.h"
#include "wasm_stack.h"
#include "wasm_types.h"
#include "wasm_stack.h"
#include "wasm_globals.h"
#include "wasi.h"
/*********************
* Structs and Types *
@ -38,7 +38,6 @@ struct sandbox {
uint64_t id;
sandbox_state_t state;
struct sandbox_state_history state_history;
uint16_t response_code;
/* Accounting Info */
@ -62,13 +61,10 @@ struct sandbox {
uint64_t duration_of_state[SANDBOX_STATE_COUNT];
uint64_t last_state_duration;
uint64_t remaining_exec;
uint64_t absolute_deadline;
uint64_t admissions_estimate; /* estimated execution time (cycles) * runtime_admissions_granularity / relative
deadline (cycles) */
uint64_t total_time; /* Total time from Request to Response */
int payload_size;
double regression_param; /* Calculated in tenant preprocessing logic if provided */
/* System Interface State */
int32_t return_value;

@ -9,19 +9,20 @@
#include "global_request_scheduler_deque.h"
#include "global_request_scheduler_minheap.h"
#include "global_request_scheduler_mtds.h"
#include "local_cleanup_queue.h"
#include "local_runqueue.h"
#include "local_runqueue_list.h"
#include "local_runqueue_minheap.h"
#include "local_runqueue_list.h"
#include "local_cleanup_queue.h"
#include "local_runqueue_mtds.h"
#include "panic.h"
#include "sandbox_functions.h"
#include "sandbox_set_as_interrupted.h"
#include "sandbox_types.h"
#include "sandbox_set_as_preempted.h"
#include "sandbox_set_as_runnable.h"
#include "sandbox_set_as_running_sys.h"
#include "sandbox_set_as_interrupted.h"
#include "sandbox_set_as_running_user.h"
#include "sandbox_types.h"
#include "scheduler_execute_epoll_loop.h"
#include "scheduler_options.h"
@ -106,31 +107,6 @@ done:
return local_runqueue_get_next();
}
static inline struct sandbox *
scheduler_sjf_get_next()
{
struct sandbox *local = local_runqueue_get_next();
uint64_t local_rem_exec = local == NULL ? UINT64_MAX : local->remaining_exec;
struct sandbox *global = NULL;
uint64_t global_remaining_exec = global_request_scheduler_peek();
/* Try to pull and allocate from the global queue if earlier
* This will be placed at the head of the local runqueue */
if (global_remaining_exec < local_rem_exec) {
if (global_request_scheduler_remove_if_earlier(&global, local_rem_exec) == 0) {
assert(global != NULL);
assert(global->remaining_exec < local_rem_exec);
sandbox_prepare_execution_environment(global);
assert(global->state == SANDBOX_INITIALIZED);
sandbox_set_as_runnable(global, SANDBOX_INITIALIZED);
}
}
/* Return what is at the head of the local runqueue or NULL if empty */
return local_runqueue_get_next();
}
static inline struct sandbox *
scheduler_edf_get_next()
{
@ -188,8 +164,6 @@ scheduler_get_next()
return scheduler_mtdbf_get_next();
case SCHEDULER_MTDS:
return scheduler_mtds_get_next();
case SCHEDULER_SJF:
return scheduler_sjf_get_next();
case SCHEDULER_EDF:
return scheduler_edf_get_next();
case SCHEDULER_FIFO:
@ -204,13 +178,12 @@ scheduler_initialize()
{
switch (scheduler) {
case SCHEDULER_MTDBF:
/* TODO: loading */
// global_request_scheduler_mtdbf_initialize();
break;
case SCHEDULER_MTDS:
global_request_scheduler_mtds_initialize();
break;
case SCHEDULER_EDF:
case SCHEDULER_SJF:
global_request_scheduler_minheap_initialize();
break;
case SCHEDULER_FIFO:
@ -232,7 +205,6 @@ scheduler_runqueue_initialize()
local_runqueue_mtds_initialize();
break;
case SCHEDULER_EDF:
case SCHEDULER_SJF:
local_runqueue_minheap_initialize();
break;
case SCHEDULER_FIFO:
@ -251,8 +223,6 @@ scheduler_print(enum SCHEDULER variant)
return "FIFO";
case SCHEDULER_EDF:
return "EDF";
case SCHEDULER_SJF:
return "SJF";
case SCHEDULER_MTDS:
return "MTDS";
case SCHEDULER_MTDBF:
@ -318,7 +288,6 @@ scheduler_process_policy_specific_updates_on_interrupts(struct sandbox *interrup
case SCHEDULER_FIFO:
return;
case SCHEDULER_EDF:
case SCHEDULER_SJF:
return;
case SCHEDULER_MTDS:
local_timeout_queue_process_promotions();
@ -344,6 +313,7 @@ scheduler_preemptive_sched(ucontext_t *interrupted_context)
assert(interrupted_context != NULL);
/* Process epoll to make sure that all runnable jobs are considered for execution */
scheduler_execute_epoll_loop();
struct sandbox *interrupted_sandbox = current_sandbox_get();
assert(interrupted_sandbox != NULL);
@ -431,6 +401,9 @@ scheduler_idle_loop()
/* Deferred signals should have been cleared by this point */
assert(deferred_sigalrm == 0);
/* Try to wakeup sleeping sandboxes */
scheduler_execute_epoll_loop();
/* Switch to a sandbox if one is ready to run */
struct sandbox *next_sandbox = scheduler_get_next();
if (next_sandbox != NULL) {
@ -439,9 +412,6 @@ scheduler_idle_loop()
/* Clear the cleanup queue */
local_cleanup_queue_free();
/* Improve the performance of spin-wait loops (works only if preemptions enabled) */
if (runtime_worker_spinloop_pause_enabled) pause();
}
}
@ -471,6 +441,9 @@ scheduler_cooperative_sched(bool add_to_cleanup_queue)
/* Deferred signals should have been cleared by this point */
assert(deferred_sigalrm == 0);
/* Try to wakeup sleeping sandboxes */
scheduler_execute_epoll_loop();
/* We have not added ourself to the cleanup queue, so we can free */
local_cleanup_queue_free();

@ -0,0 +1,70 @@
#pragma once
#include <assert.h>
#include <errno.h>
#include "http_session.h"
#include "panic.h"
#include "runtime.h"
#include "sandbox_functions.h"
#include "sandbox_set_as_error.h"
#include "sandbox_state.h"
#include "sandbox_types.h"
#include "worker_thread.h"
/**
* Run all outstanding events in the local thread's epoll loop
*/
static inline void
scheduler_execute_epoll_loop(void)
{
while (true) {
struct epoll_event epoll_events[RUNTIME_MAX_EPOLL_EVENTS];
int descriptor_count = epoll_wait(worker_thread_epoll_file_descriptor, epoll_events,
RUNTIME_MAX_EPOLL_EVENTS, 0);
if (descriptor_count < 0) {
if (errno == EINTR) continue;
panic_err();
}
if (descriptor_count == 0) break;
for (int i = 0; i < descriptor_count; i++) {
if (epoll_events[i].events & (EPOLLIN | EPOLLOUT)) {
/* Re-add to runqueue if asleep */
struct sandbox *sandbox = (struct sandbox *)epoll_events[i].data.ptr;
assert(sandbox);
if (sandbox->state == SANDBOX_ASLEEP) { sandbox_wakeup(sandbox); }
} else if (epoll_events[i].events & (EPOLLERR | EPOLLHUP)) {
/* Close socket and set as error on socket error or unexpected client hangup */
struct sandbox *sandbox = (struct sandbox *)epoll_events[i].data.ptr;
int error = 0;
socklen_t errlen = sizeof(error);
getsockopt(epoll_events[i].data.fd, SOL_SOCKET, SO_ERROR, (void *)&error, &errlen);
if (error > 0) {
debuglog("Socket error: %s", strerror(error));
} else if (epoll_events[i].events & EPOLLHUP) {
debuglog("Client Hungup");
} else {
debuglog("Unknown Socket error");
}
switch (sandbox->state) {
case SANDBOX_RETURNED:
case SANDBOX_COMPLETE:
case SANDBOX_ERROR:
panic("Expected to have closed socket");
default:
sandbox_set_as_error(sandbox, sandbox->state);
}
} else {
panic("Mystery epoll event!\n");
};
}
}
}

@ -2,11 +2,10 @@
enum SCHEDULER
{
SCHEDULER_FIFO,
SCHEDULER_EDF,
SCHEDULER_SJF,
SCHEDULER_MTDS,
SCHEDULER_MTDBF
SCHEDULER_FIFO = 0,
SCHEDULER_EDF = 1,
SCHEDULER_MTDS = 2,
SCHEDULER_MTDBF = 3
};
extern enum SCHEDULER scheduler;

@ -5,8 +5,8 @@
#include <stdint.h>
#include "debuglog.h"
#include "sledge_abi.h"
#include "wasm_types.h"
#include "sledge_abi.h"
struct sledge_abi_symbols {
void *handle;

@ -1,7 +1,7 @@
#pragma once
#include <signal.h>
#include <stdatomic.h>
#include <signal.h>
#include <stdlib.h>
#include "worker_thread.h"

@ -3,10 +3,10 @@
#include <assert.h>
#include <errno.h>
#include <netdb.h>
#include <stdint.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include "debuglog.h"

@ -1,7 +1,7 @@
#pragma once
#include <arpa/inet.h>
#include <assert.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
@ -9,8 +9,8 @@
#include <unistd.h>
#include "debuglog.h"
#include "likely.h"
#include "panic.h"
#include "likely.h"
static inline void
tcp_session_close(int client_socket, struct sockaddr *client_address)
@ -21,7 +21,7 @@ tcp_session_close(int client_socket, struct sockaddr *client_address)
assert(client_socket != STDERR_FILENO);
if (unlikely(close(client_socket) < 0)) {
char client_address_text[INET6_ADDRSTRLEN] = {'\0'};
char client_address_text[INET6_ADDRSTRLEN] = { '\0' };
if (unlikely(inet_ntop(AF_INET, &client_address, client_address_text, INET6_ADDRSTRLEN) == NULL)) {
debuglog("Failed to log client_address: %s", strerror(errno));
}
@ -59,7 +59,7 @@ tcp_session_send(int client_socket, const char *buffer, size_t buffer_len, void_
}
/**
* Writes buffer to the client socket
* Reads client socket into memstream
* @param client_socket - the client
* @param buffer - buffer to reach the socket into
* @param buffer_len - buffer to reach the socket into
@ -67,20 +67,31 @@ tcp_session_send(int client_socket, const char *buffer, size_t buffer_len, void_
* @returns nwritten on success, -errno on error, -eagain on block
*/
static inline ssize_t
tcp_session_recv(int client_socket, char *buffer, size_t buffer_len, void_star_cb on_eagain, void *dataptr)
tcp_session_recv(int client_socket, FILE *handle, void_star_cb on_eagain, void *dataptr)
{
assert(buffer != NULL);
assert(buffer_len > 0);
assert(handle != NULL);
ssize_t received = read(client_socket, buffer, buffer_len);
if (received < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
if (on_eagain != NULL) on_eagain(dataptr);
return -EAGAIN;
} else {
return -errno;
char buf[BUFSIZ];
ssize_t received = 0;
do {
received = read(client_socket, buf, BUFSIZ);
if (received < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
if (on_eagain != NULL) on_eagain(dataptr);
return -EAGAIN;
} else {
return -errno;
}
}
}
/* TODO: Validate memory stream error handling */
size_t written = fwrite(buf, 1, received, handle);
assert(written == received);
} while (received != 0);
int rc = fflush(handle);
if (rc < 0) { return -errno; };
return received;
}

@ -1,6 +1,5 @@
#pragma once
#include "epoll_tag.h"
#include "http_router.h"
#include "map.h"
#include "module_database.h"
@ -33,7 +32,6 @@ struct tenant_global_request_queue {
};
struct tenant {
enum epoll_tag tag; /* Tag must be first member */
char *name;
struct tcp_server tcp_server;
http_router_t router;

@ -45,10 +45,8 @@ tenant_config_print(struct tenant_config *config)
{
printf("[Tenant] Name: %s\n", config->name);
printf("[Tenant] Path: %d\n", config->port);
if (scheduler == SCHEDULER_MTDS) {
printf("[Tenant] Replenishment Period (us): %u\n", config->replenishment_period_us);
printf("[Tenant] Max Budget (us): %u\n", config->max_budget_us);
}
printf("[Tenant] Replenishment Period (us): %u\n", config->replenishment_period_us);
printf("[Tenant] Max Budget (us): %u\n", config->max_budget_us);
printf("[Tenant] Routes Size: %zu\n", config->routes_len);
for (int i = 0; i < config->routes_len; i++) { route_config_print(&config->routes[i]); }
}
@ -73,19 +71,19 @@ tenant_config_validate(struct tenant_config *config, bool *did_set)
if (scheduler == SCHEDULER_MTDS) {
if (did_set[tenant_config_member_replenishment_period_us] == false) {
fprintf(stderr, "replenishment-period-us field is missing, so defaulting to 0\n");
config->replenishment_period_us = 0;
fprintf(stderr, "replenishment-period-us field is required\n");
return -1;
}
if (config->replenishment_period_us > (uint32_t)RUNTIME_RELATIVE_DEADLINE_US_MAX) {
fprintf(stderr, "relative-deadline-us must be between 0 and %u, was %u\n",
fprintf(stderr, "Relative-deadline-us must be between 0 and %u, was %u\n",
(uint32_t)RUNTIME_RELATIVE_DEADLINE_US_MAX, config->replenishment_period_us);
return -1;
}
if (did_set[tenant_config_member_max_budget_us] == false) {
fprintf(stderr, "max-budget-us field is missing, so defaulting to 0\n");
config->max_budget_us = 0;
fprintf(stderr, "max-budget-us field is required\n");
return -1;
}
if (config->max_budget_us > (uint32_t)RUNTIME_RELATIVE_DEADLINE_US_MAX) {

@ -9,8 +9,8 @@
#include "route_config_parse.h"
#include "tenant_config.h"
static const char *tenant_config_json_keys[tenant_config_member_len] = {"name", "port", "replenishment-period-us",
"max-budget-us", "routes"};
static const char *tenant_config_json_keys[tenant_config_member_len] = { "name", "port", "replenishment-period-us",
"max-budget-us", "routes" };
static inline int
tenant_config_set_key_once(bool *did_set, enum tenant_config_member member)
@ -29,8 +29,8 @@ tenant_config_parse(struct tenant_config *config, const char *json_buf, jsmntok_
int tokens_size)
{
int i = tokens_base;
char key[32] = {0};
bool did_set[tenant_config_member_len] = {false};
char key[32] = { 0 };
bool did_set[tenant_config_member_len] = { false };
if (!has_valid_type(tokens[i], "Anonymous Tenant Config Object", JSMN_OBJECT, json_buf)) return -1;
if (!is_nonempty_object(tokens[i], "Anonymous Tenant Config Object")) return -1;

@ -3,15 +3,16 @@
#include <stdint.h>
#include <string.h>
#include "admissions_info.h"
#include "http.h"
#include "listener_thread.h"
#include "module_database.h"
#include "panic.h"
#include "priority_queue.h"
#include "sandbox_functions.h"
#include "scheduler_options.h"
#include "tenant.h"
#include "tenant_config.h"
#include "priority_queue.h"
#include "sandbox_functions.h"
int tenant_database_add(struct tenant *tenant);
struct tenant *tenant_database_find_by_name(char *name);
@ -19,9 +20,6 @@ struct tenant *tenant_database_find_by_socket_descriptor(int socket_descriptor);
struct tenant *tenant_database_find_by_port(uint16_t port);
struct tenant *tenant_database_find_by_ptr(void *ptr);
typedef void (*tenant_database_foreach_cb_t)(struct tenant *, void *, void *);
void tenant_database_foreach(tenant_database_foreach_cb_t, void *, void *);
static inline int
tenant_policy_specific_init(struct tenant *tenant, struct tenant_config *config)
{
@ -29,7 +27,6 @@ tenant_policy_specific_init(struct tenant *tenant, struct tenant_config *config)
case SCHEDULER_FIFO:
break;
case SCHEDULER_EDF:
case SCHEDULER_SJF:
break;
case SCHEDULER_MTDS:
/* Deferable Server Initialization */
@ -87,7 +84,6 @@ tenant_alloc(struct tenant_config *config)
struct tenant *tenant = (struct tenant *)calloc(1, sizeof(struct tenant));
/* Move name */
tenant->tag = EPOLL_TAG_TENANT_SERVER_SOCKET;
tenant->name = config->name;
config->name = NULL;
@ -103,7 +99,7 @@ tenant_alloc(struct tenant_config *config)
struct module *module = module_database_find_by_path(&tenant->module_db, config->routes[i].path);
if (module == NULL) {
/* Ownership of path moves here */
module = module_alloc(config->routes[i].path, APP_MODULE);
module = module_alloc(config->routes[i].path);
if (module != NULL) {
module_database_add(&tenant->module_db, module);
config->routes[i].path = NULL;
@ -115,30 +111,8 @@ tenant_alloc(struct tenant_config *config)
assert(module != NULL);
struct module *module_proprocess = NULL;
#ifdef EXECUTION_REGRESSION
if (config->routes[i].path_preprocess) {
module_proprocess = module_database_find_by_path(&tenant->module_db,
config->routes[i].path_preprocess);
if (module_proprocess == NULL) {
/* Ownership of path moves here */
module_proprocess = module_alloc(config->routes[i].path_preprocess, PREPROCESS_MODULE);
if (module_proprocess != NULL) {
module_database_add(&tenant->module_db, module_proprocess);
config->routes[i].path_preprocess = NULL;
}
} else {
free(config->routes[i].path_preprocess);
config->routes[i].path_preprocess = NULL;
}
assert(module_proprocess != NULL);
}
#endif
/* Ownership of config's route and http_resp_content_type move here */
int rc = http_router_add_route(&tenant->router, &config->routes[i], module, module_proprocess);
int rc = http_router_add_route(&tenant->router, &config->routes[i], module);
if (unlikely(rc != 0)) {
panic("Tenant %s defined %lu routes, but router failed to grow beyond %lu\n", tenant->name,
config->routes_len, tenant->router.capacity);
@ -182,6 +156,5 @@ get_next_timeout_of_tenant(uint64_t replenishment_period)
* @param tenant
* @returns 0 on success, -1 on error
*/
int tenant_listen(struct tenant *tenant);
int listener_thread_register_tenant(struct tenant *tenant);
void tenant_preprocess(struct http_session *session);
int tenant_listen(struct tenant *tenant);
int listener_thread_register_tenant(struct tenant *tenant);

@ -85,9 +85,9 @@ wasm_globals_set_i32(struct vec_wasm_global_t *globals, uint32_t idx, int32_t va
wasm_global_t *current = vec_wasm_global_t_get(globals, idx);
if (unlikely(current->type != WASM_GLOBAL_TYPE_UNUSED && current->mut == false)) return -2;
int rc =
vec_wasm_global_t_insert(globals, idx,
(wasm_global_t){.mut = is_mutable, .type = WASM_GLOBAL_TYPE_I32, .value = value});
int rc = vec_wasm_global_t_insert(globals, idx,
(wasm_global_t){
.mut = is_mutable, .type = WASM_GLOBAL_TYPE_I32, .value = value });
return rc;
}
@ -99,8 +99,8 @@ wasm_globals_set_i64(struct vec_wasm_global_t *globals, uint32_t idx, int64_t va
if (unlikely(current->type != WASM_GLOBAL_TYPE_UNUSED && current->mut == false)) return -2;
// Returns -1 if idx > capacity
int rc =
vec_wasm_global_t_insert(globals, idx,
(wasm_global_t){.mut = is_mutable, .type = WASM_GLOBAL_TYPE_I64, .value = value});
int rc = vec_wasm_global_t_insert(globals, idx,
(wasm_global_t){
.mut = is_mutable, .type = WASM_GLOBAL_TYPE_I64, .value = value });
return rc;
}

@ -8,8 +8,8 @@
#include <sys/mman.h>
#include "ps_list.h"
#include "sledge_abi.h"
#include "types.h" /* PAGE_SIZE */
#include "sledge_abi.h"
#include "wasm_types.h"
#define WASM_MEMORY_MAX (uint64_t) UINT32_MAX + 1

@ -1,7 +1,7 @@
#pragma once
#include "sledge_abi.h"
#include "wasm_memory.h"
#include "wasm_table.h"
#include "sledge_abi.h"
/* This structure is the runtime representation of the unique state of a module instance
* Currently this is not spec-compliant, as it only supports a single table and a single memory and it excludes many

@ -2,9 +2,9 @@
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <strings.h>
#include <sys/mman.h>
#include "ps_list.h"
#include "types.h"

@ -109,5 +109,5 @@ wasm_table_set(struct sledge_abi__wasm_table *wasm_table, uint32_t idx, uint32_t
/* TODO: atomic for multiple concurrent invocations? Issue #97 */
if (wasm_table->buffer[idx].type_id == type_id && wasm_table->buffer[idx].func_pointer == pointer) return;
wasm_table->buffer[idx] = (struct sledge_abi__wasm_table_entry){.type_id = type_id, .func_pointer = pointer};
wasm_table->buffer[idx] = (struct sledge_abi__wasm_table_entry){ .type_id = type_id, .func_pointer = pointer };
}

@ -2,9 +2,11 @@
#include <threads.h>
#include "generic_thread.h"
#include "runtime.h"
extern thread_local struct arch_context worker_thread_base_context;
extern thread_local int worker_thread_epoll_file_descriptor;
extern thread_local int worker_thread_idx;
void *worker_thread_main(void *return_code);

@ -0,0 +1,23 @@
#pragma once
#include "worker_thread.h"
static inline void
worker_thread_epoll_add_sandbox(struct sandbox *sandbox)
{
assert(sandbox != NULL);
/* 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->http->socket, &accept_evt);
if (unlikely(rc < 0)) panic_err();
}
static inline void
worker_thread_epoll_remove_sandbox(struct sandbox *sandbox)
{
assert(sandbox != NULL);
int rc = epoll_ctl(worker_thread_epoll_file_descriptor, EPOLL_CTL_DEL, sandbox->http->socket, NULL);
if (unlikely(rc < 0)) panic_err();
}

@ -1,14 +1,8 @@
#include <assert.h>
#include <stdatomic.h>
#include <unistd.h>
#include "admissions_control.h"
#include "debuglog.h"
#include "likely.h"
#include "panic.h"
#include "runtime.h"
#ifdef ADMISSIONS_CONTROL
/*
* Unitless estimate of the instantaneous fraction of system capacity required to complete all previously
@ -20,33 +14,39 @@
* These estimates are incremented on request acceptance and decremented on request completion (either
* success or failure)
*/
_Atomic uint64_t admissions_control_admitted;
uint64_t admissions_control_capacity;
const double admissions_control_overhead = 0.2;
const double admissions_control_overhead = 0.2;
void
admissions_control_initialize()
{
#ifdef ADMISSIONS_CONTROL
atomic_init(&admissions_control_admitted, 0);
admissions_control_capacity = runtime_worker_threads_count * ADMISSIONS_CONTROL_GRANULARITY
* ((double)1.0 - admissions_control_overhead);
#endif
}
void
admissions_control_add(uint64_t admissions_estimate)
{
#ifdef ADMISSIONS_CONTROL
assert(admissions_estimate > 0);
atomic_fetch_add(&admissions_control_admitted, admissions_estimate);
#ifdef LOG_ADMISSIONS_CONTROL
debuglog("Runtime Admitted: %lu / %lu\n", admissions_control_admitted, admissions_control_capacity);
#endif
#endif /* ADMISSIONS_CONTROL */
}
void
admissions_control_subtract(uint64_t admissions_estimate)
{
#ifdef ADMISSIONS_CONTROL
/* Assumption: Should never underflow */
if (unlikely(admissions_estimate > admissions_control_admitted)) panic("Admissions Estimate underflow\n");
@ -55,11 +55,14 @@ admissions_control_subtract(uint64_t admissions_estimate)
#ifdef LOG_ADMISSIONS_CONTROL
debuglog("Runtime Admitted: %lu / %lu\n", admissions_control_admitted, admissions_control_capacity);
#endif
#endif /* ADMISSIONS_CONTROL */
}
uint64_t
admissions_control_calculate_estimate(uint64_t estimated_execution, uint64_t relative_deadline)
{
#ifdef ADMISSIONS_CONTROL
assert(relative_deadline != 0);
uint64_t admissions_estimate = (estimated_execution * (uint64_t)ADMISSIONS_CONTROL_GRANULARITY)
/ relative_deadline;
@ -67,6 +70,20 @@ admissions_control_calculate_estimate(uint64_t estimated_execution, uint64_t rel
panic("Ratio of Deadline to Execution time cannot exceed %d\n", ADMISSIONS_CONTROL_GRANULARITY);
return admissions_estimate;
#else
return 0;
#endif
}
uint64_t
admissions_control_calculate_estimate_us(uint32_t estimated_execution_us, uint32_t relative_deadline_us)
{
#ifdef ADMISSIONS_CONTROL
assert(relative_deadline_us != 0);
return (uint64_t)((uint64_t)(estimated_execution_us * ADMISSIONS_CONTROL_GRANULARITY)) / relative_deadline_us;
#else
return 0;
#endif
}
void
@ -83,6 +100,7 @@ admissions_control_decide(uint64_t admissions_estimate)
{
uint64_t work_admitted = 1; /* Nominal non-zero value in case admissions control is disabled */
#ifdef ADMISSIONS_CONTROL
if (unlikely(admissions_estimate == 0)) panic("Admissions estimate should never be zero");
uint64_t total_admitted = atomic_load(&admissions_control_admitted);
@ -95,8 +113,7 @@ admissions_control_decide(uint64_t admissions_estimate)
admissions_control_add(admissions_estimate);
work_admitted = admissions_estimate;
}
#endif /* ADMISSIONS_CONTROL */
return work_admitted;
}
#endif /* ADMISSIONS_CONTROL */

@ -0,0 +1,53 @@
#include "admissions_info.h"
#include "debuglog.h"
/**
* Initializes perf window
* @param admissions_info
*/
void
admissions_info_initialize(struct admissions_info *admissions_info, uint8_t percentile, uint64_t expected_execution,
uint64_t relative_deadline)
{
#ifdef ADMISSIONS_CONTROL
assert(relative_deadline > 0);
assert(expected_execution > 0);
admissions_info->relative_deadline = relative_deadline;
admissions_info->estimate = admissions_control_calculate_estimate(expected_execution, relative_deadline);
debuglog("Initial Estimate: %lu\n", admissions_info->estimate);
assert(admissions_info != NULL);
perf_window_initialize(&admissions_info->perf_window);
if (unlikely(percentile < 50 || percentile > 99)) panic("Invalid admissions percentile");
admissions_info->percentile = percentile;
admissions_info->control_index = PERF_WINDOW_BUFFER_SIZE * percentile / 100;
#ifdef LOG_ADMISSIONS_CONTROL
debuglog("Percentile: %u\n", admissions_info->percentile);
debuglog("Control Index: %d\n", admissions_info->control_index);
#endif
#endif
}
/*
* Adds an execution value to the perf window and calculates and caches and updated estimate
* @param admissions_info
* @param execution_duration
*/
void
admissions_info_update(struct admissions_info *admissions_info, uint64_t execution_duration)
{
#ifdef ADMISSIONS_CONTROL
struct perf_window *perf_window = &admissions_info->perf_window;
LOCK_LOCK(&admissions_info->perf_window.lock);
perf_window_add(perf_window, execution_duration);
uint64_t estimated_execution = perf_window_get_percentile(perf_window, admissions_info->percentile,
admissions_info->control_index);
admissions_info->estimate = admissions_control_calculate_estimate(estimated_execution,
admissions_info->relative_deadline);
LOCK_UNLOCK(&admissions_info->perf_window.lock);
#endif
}

@ -1,17 +1,19 @@
#include <threads.h>
#include <setjmp.h>
#include <threads.h>
#include "current_sandbox.h"
#include "sandbox_functions.h"
#include "sandbox_set_as_asleep.h"
#include "sandbox_set_as_complete.h"
#include "sandbox_set_as_error.h"
#include "sandbox_set_as_returned.h"
#include "sandbox_set_as_running_sys.h"
#include "sandbox_set_as_complete.h"
#include "sandbox_set_as_running_user.h"
#include "sandbox_set_as_running_sys.h"
#include "scheduler.h"
#include "software_interrupt.h"
#include "wasi.h"
#include "worker_thread_epoll.h"
thread_local struct sandbox *worker_thread_current_sandbox = NULL;
@ -26,6 +28,8 @@ current_sandbox_sleep()
struct sandbox *sleeping_sandbox = current_sandbox_get();
assert(sleeping_sandbox != NULL);
generic_thread_dump_lock_overhead();
switch (sleeping_sandbox->state) {
case SANDBOX_RUNNING_SYS: {
sandbox_sleep(sleeping_sandbox);
@ -50,6 +54,8 @@ current_sandbox_exit()
struct sandbox *exiting_sandbox = current_sandbox_get();
assert(exiting_sandbox != NULL);
generic_thread_dump_lock_overhead();
switch (exiting_sandbox->state) {
case SANDBOX_RETURNED:
sandbox_exit_success(exiting_sandbox);
@ -100,6 +106,8 @@ current_sandbox_wasm_trap_handler(int trapno)
}
debuglog("%s", error_message);
worker_thread_epoll_remove_sandbox(sandbox);
generic_thread_dump_lock_overhead();
current_sandbox_exit();
assert(0);
}
@ -115,6 +123,8 @@ current_sandbox_init()
int rc = 0;
char *error_message = NULL;
worker_thread_epoll_add_sandbox(sandbox);
/* Initialize sandbox memory */
struct module *current_module = sandbox_get_module(sandbox);
module_initialize_memory(current_module);
@ -144,6 +154,8 @@ current_sandbox_init()
err:
debuglog("%s", error_message);
worker_thread_epoll_remove_sandbox(sandbox);
generic_thread_dump_lock_overhead();
current_sandbox_exit();
return NULL;
}
@ -161,11 +173,13 @@ current_sandbox_fini()
sandbox->total_time = sandbox->timestamp_of.completion - sandbox->timestamp_of.allocation;
assert(sandbox->state == SANDBOX_RUNNING_SYS);
worker_thread_epoll_remove_sandbox(sandbox);
done:
sandbox_set_as_returned(sandbox, SANDBOX_RUNNING_SYS);
/* Cleanup connection and exit sandbox */
generic_thread_dump_lock_overhead();
current_sandbox_exit();
assert(0);
err:
@ -193,5 +207,5 @@ current_sandbox_start(void)
current_sandbox_wasm_trap_handler(rc);
}
if (sandbox->module->type == APP_MODULE) current_sandbox_fini();
current_sandbox_fini();
}

@ -1,20 +1,20 @@
#include <stdlib.h>
#include "current_sandbox.h"
#include "wasm_memory.h"
#include "wasm_module_instance.h"
#include "wasm_memory.h"
thread_local struct wasm_module_instance sledge_abi__current_wasm_module_instance = {
.abi.memory =
(struct sledge_abi__wasm_memory){
.size = 0,
.max = 0,
.capacity = 0,
.buffer = NULL,
},
.abi.table = NULL,
.abi.wasmg_0 = 0xDEADBEEF,
.wasi_context = NULL,
.abi.memory =
(struct sledge_abi__wasm_memory){
.size = 0,
.max = 0,
.capacity = 0,
.buffer = NULL,
},
.abi.table = NULL,
.abi.wasmg_0 = 0xDEADBEEF,
.wasi_context = NULL,
};
void

@ -1,50 +0,0 @@
#ifdef EXECUTION_HISTOGRAM
#include "execution_histogram.h"
#include "debuglog.h"
#include "perf_window.h"
/**
* Initializes execution_histogram and its perf window
* @param execution_histogram
*/
void
execution_histogram_initialize(struct execution_histogram *execution_histogram, uint8_t percentile,
uint64_t expected_execution)
{
assert(expected_execution > 0);
execution_histogram->estimated_execution = expected_execution;
assert(execution_histogram != NULL);
perf_window_initialize(&execution_histogram->perf_window);
if (unlikely(percentile < 50 || percentile > 99)) panic("Invalid percentile");
execution_histogram->percentile = percentile;
execution_histogram->control_index = PERF_WINDOW_CAPACITY * percentile / 100;
#ifdef LOG_EXECUTION_HISTOGRAM
debuglog("Percentile: %u\n", execution_histogram->percentile);
debuglog("Control Index: %d\n", execution_histogram->control_index);
#endif
}
/*
* Adds an execution value to the perf window
* @param execution_histogram
* @param execution_duration
*/
void
execution_histogram_update(struct execution_histogram *execution_histogram, uint64_t execution_duration)
{
struct perf_window *perf_window = &execution_histogram->perf_window;
lock_node_t node = {};
lock_lock(&perf_window->lock, &node);
perf_window_add(perf_window, execution_duration);
uint64_t estimated_execution = perf_window_get_percentile(perf_window, execution_histogram->percentile,
execution_histogram->control_index);
execution_histogram->estimated_execution = estimated_execution;
lock_unlock(&perf_window->lock, &node);
}
#endif

@ -0,0 +1,39 @@
#include <stdint.h>
#include <threads.h>
#include "arch/getcycles.h"
#include "debuglog.h"
extern uint32_t runtime_processor_speed_MHz;
extern uint32_t runtime_quantum_us;
/* Implemented by listener and workers */
thread_local uint64_t generic_thread_lock_duration = 0;
thread_local uint64_t generic_thread_lock_longest = 0;
thread_local uint64_t generic_thread_start_timestamp = 0;
void
generic_thread_initialize()
{
generic_thread_start_timestamp = __getcycles();
generic_thread_lock_longest = 0;
generic_thread_lock_duration = 0;
}
/**
* Reports lock contention
*/
void
generic_thread_dump_lock_overhead()
{
#ifndef NDEBUG
#ifdef LOG_LOCK_OVERHEAD
uint64_t duration = __getcycles() - generic_thread_start_timestamp;
debuglog("Locks consumed %lu / %lu cycles, or %f%%\n", generic_thread_lock_duration, duration,
(double)generic_thread_lock_duration / duration * 100);
debuglog("Longest Held Lock was %lu cycles, or %f quantums\n", generic_thread_lock_longest,
(double)generic_thread_lock_longest / ((uint64_t)runtime_processor_speed_MHz * runtime_quantum_us));
#endif
#endif
}

@ -24,9 +24,9 @@ uninitialized_peek()
/* The global of our polymorphic interface */
static struct global_request_scheduler_config global_request_scheduler = {.add_fn = uninitialized_add,
.remove_fn = uninitialized_remove,
.peek_fn = uninitialized_peek};
static struct global_request_scheduler_config global_request_scheduler = { .add_fn = uninitialized_add,
.remove_fn = uninitialized_remove,
.peek_fn = uninitialized_peek };
/**
* Initializes the polymorphic interface with a concrete implementation

@ -1,8 +1,8 @@
#include "global_request_scheduler_deque.h"
#include "global_request_scheduler.h"
#include "global_request_scheduler_deque.h"
#include "runtime.h"
#define GLOBAL_REQUEST_SCHEDULER_DEQUE_CAPACITY (1 << 12)
#define GLOBAL_REQUEST_SCHEDULER_DEQUE_CAPACITY (1 << 19)
static struct deque_sandbox *global_request_scheduler_deque;
@ -57,10 +57,11 @@ global_request_scheduler_deque_initialize()
deque_init_sandbox(global_request_scheduler_deque, GLOBAL_REQUEST_SCHEDULER_DEQUE_CAPACITY);
/* Register Function Pointers for Abstract Scheduling API */
struct global_request_scheduler_config config = {.add_fn = global_request_scheduler_deque_add,
.remove_fn = global_request_scheduler_deque_remove,
.remove_if_earlier_fn =
global_request_scheduler_deque_remove_if_earlier};
struct global_request_scheduler_config config = {
.add_fn = global_request_scheduler_deque_add,
.remove_fn = global_request_scheduler_deque_remove,
.remove_if_earlier_fn = global_request_scheduler_deque_remove_if_earlier
};
global_request_scheduler_initialize(&config);
}

@ -43,10 +43,10 @@ global_request_scheduler_minheap_remove(struct sandbox **removed_sandbox)
* @returns 0 if successful, -ENOENT if empty or if request isn't earlier than target_deadline
*/
int
global_request_scheduler_minheap_remove_if_earlier(struct sandbox **removed_sandbox, uint64_t target_latest_start)
global_request_scheduler_minheap_remove_if_earlier(struct sandbox **removed_sandbox, uint64_t target_deadline)
{
return priority_queue_dequeue_if_earlier(global_request_scheduler_minheap, (void **)removed_sandbox,
target_latest_start);
target_deadline);
}
/**
@ -65,8 +65,6 @@ uint64_t
sandbox_get_priority_fn(void *element)
{
struct sandbox *sandbox = (struct sandbox *)element;
if (scheduler == SCHEDULER_SJF) return sandbox->remaining_exec;
assert(scheduler == SCHEDULER_EDF);
return sandbox->absolute_deadline;
};
@ -79,11 +77,12 @@ global_request_scheduler_minheap_initialize()
{
global_request_scheduler_minheap = priority_queue_initialize(4096, true, sandbox_get_priority_fn);
struct global_request_scheduler_config config = {.add_fn = global_request_scheduler_minheap_add,
.remove_fn = global_request_scheduler_minheap_remove,
.remove_if_earlier_fn =
global_request_scheduler_minheap_remove_if_earlier,
.peek_fn = global_request_scheduler_minheap_peek};
struct global_request_scheduler_config config = {
.add_fn = global_request_scheduler_minheap_add,
.remove_fn = global_request_scheduler_minheap_remove,
.remove_if_earlier_fn = global_request_scheduler_minheap_remove_if_earlier,
.peek_fn = global_request_scheduler_minheap_peek
};
global_request_scheduler_initialize(&config);
}

@ -60,8 +60,7 @@ global_request_scheduler_mtds_add(struct sandbox *sandbox)
struct tenant_global_request_queue *tgrq = sandbox->tenant->tgrq_requests;
lock_node_t node = {};
lock_lock(&global_lock, &node);
LOCK_LOCK(&global_lock);
struct priority_queue *destination_queue = global_request_scheduler_mtds_default;
if (sandbox->tenant->tgrq_requests->mt_class == MT_GUARANTEED) {
@ -85,7 +84,7 @@ global_request_scheduler_mtds_add(struct sandbox *sandbox)
// debuglog("Added the TGRQ back to the Global runqueue - %s to Heapify", QUEUE_NAME);
}
lock_unlock(&global_lock, &node);
LOCK_UNLOCK(&global_lock);
return sandbox;
}
@ -125,9 +124,9 @@ global_request_scheduler_mtds_remove_with_mt_class(struct sandbox **removed_sand
enum MULTI_TENANCY_CLASS target_mt_class)
{
int rc = -ENOENT;
;
lock_node_t node = {};
lock_lock(&global_lock, &node);
LOCK_LOCK(&global_lock);
/* Avoid unnessary locks when the target_deadline is tighter than the head of the Global runqueue */
uint64_t global_guaranteed_deadline = priority_queue_peek(global_request_scheduler_mtds_guaranteed);
@ -181,7 +180,7 @@ global_request_scheduler_mtds_remove_with_mt_class(struct sandbox **removed_sand
}
done:
lock_unlock(&global_lock, &node);
LOCK_UNLOCK(&global_lock);
return rc;
}
@ -228,13 +227,14 @@ global_request_scheduler_mtds_initialize()
global_tenant_timeout_queue = priority_queue_initialize(RUNTIME_MAX_TENANT_COUNT, false,
tenant_timeout_get_priority);
lock_init(&global_lock);
LOCK_INIT(&global_lock);
struct global_request_scheduler_config config = {.add_fn = global_request_scheduler_mtds_add,
.remove_fn = global_request_scheduler_mtds_remove,
.remove_if_earlier_fn =
global_request_scheduler_mtds_remove_if_earlier,
.peek_fn = global_request_scheduler_mtds_peek};
struct global_request_scheduler_config config = {
.add_fn = global_request_scheduler_mtds_add,
.remove_fn = global_request_scheduler_mtds_remove,
.remove_if_earlier_fn = global_request_scheduler_mtds_remove_if_earlier,
.peek_fn = global_request_scheduler_mtds_peek
};
global_request_scheduler_initialize(&config);
}
@ -265,8 +265,7 @@ global_request_scheduler_mtds_promote_lock(struct tenant_global_request_queue *t
assert(tgrq != NULL);
// assert(priority_queue_length_nolock(tgrq->sandbox_requests) == 0);
lock_node_t node = {};
lock_lock(&global_lock, &node);
LOCK_LOCK(&global_lock);
if (tgrq->mt_class == MT_GUARANTEED) goto done;
if (priority_queue_length_nolock(tgrq->sandbox_requests) == 0) goto done;
@ -284,7 +283,7 @@ global_request_scheduler_mtds_promote_lock(struct tenant_global_request_queue *t
if (rc == -ENOSPC) panic("Global Guaranteed queue is full!\n");
done:
lock_unlock(&global_lock, &node);
LOCK_UNLOCK(&global_lock);
}
/*

@ -3,8 +3,8 @@
#include "debuglog.h"
#include "http.h"
#include "http_parser_settings.h"
#include "http_request.h"
#include "http_parser_settings.h"
#include "likely.h"
http_parser_settings runtime_http_parser_settings;
@ -211,7 +211,7 @@ http_parser_settings_on_header_end(http_parser *parser)
}
}
const char *http_methods[] = {"OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"};
const char *http_methods[] = { "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT" };
/**
* http-parser callback called for HTTP Bodies

@ -18,8 +18,8 @@ http_session_perf_log_print_entry(struct http_session *http_session)
const uint64_t sent_duration = http_session->response_sent_timestamp - http_session->response_takeoff_timestamp;
const uint64_t total_lifetime = http_session->response_sent_timestamp - http_session->request_arrival_timestamp;
fprintf(http_session_perf_log, "%s,%s,%u,%lu,%lu,%lu,%lu,%lu,%lu,%u\n", http_session->tenant->name,
fprintf(http_session_perf_log, "%s,%s,%u,%lu,%lu,%lu,%lu,%lu,%u\n", http_session->tenant->name,
http_session->http_request.full_url, http_session->state, http_session->response_header_written,
http_session->response_body_written, receive_duration, sent_duration, total_lifetime,
http_session->preprocessing_duration, runtime_processor_speed_MHz);
http_session->response_buffer_written, receive_duration, sent_duration, total_lifetime,
runtime_processor_speed_MHz);
}

@ -5,26 +5,31 @@
/* 2XX + 4XX should equal sandboxes */
/* Listener Core Bookkeeping */
#ifdef HTTP_TOTAL_COUNTERS
_Atomic uint32_t http_total_requests = 0;
_Atomic uint32_t http_total_5XX = 0;
_Atomic uint32_t http_total_2XX = 0;
_Atomic uint32_t http_total_4XX = 0;
#ifdef LOG_TOTAL_REQS_RESPS
_Atomic uint32_t http_total_2XX = 0;
_Atomic uint32_t http_total_4XX = 0;
#endif
/* Primarily intended to be called via GDB */
void
http_total_log()
{
#ifdef HTTP_TOTAL_COUNTERS
uint32_t total_reqs = atomic_load(&http_total_requests);
uint32_t total_2XX = atomic_load(&http_total_2XX);
uint32_t total_4XX = atomic_load(&http_total_4XX);
uint32_t total_5XX = atomic_load(&http_total_5XX);
int64_t total_responses = total_2XX + total_4XX + total_5XX;
int64_t outstanding_requests = (int64_t)total_reqs - total_responses;
uint32_t total_reqs = atomic_load(&http_total_requests);
uint32_t total_5XX = atomic_load(&http_total_5XX);
#ifdef LOG_TOTAL_REQS_RESPS
uint32_t total_2XX = atomic_load(&http_total_2XX);
uint32_t total_4XX = atomic_load(&http_total_4XX);
int64_t total_responses = total_2XX + total_4XX + total_5XX;
int64_t outstanding_requests = (int64_t)total_reqs - total_responses;
debuglog("Requests: %u (%ld outstanding)\n\tResponses: %ld\n\t\t2XX: %u\n\t\t4XX: %u\n\t\t5XX: %u\n",
total_reqs, outstanding_requests, total_responses, total_2XX, total_4XX, total_5XX);
#else
debuglog("Requests: %u\n\tResponses:\n\t\t\t5XX: %u\n", total_reqs, total_5XX);
#endif
};

@ -112,10 +112,8 @@ wasi_context_init(wasi_options_t *options)
}
}
/* Seed Random
* Commented out as a temporary fix for the mutex blocking delay srandom causes in libc.
*/
// srandom(time(NULL));
/* Seed Random */
srandom(time(NULL));
/* TODO: Preopens */
@ -788,9 +786,23 @@ wasi_snapshot_preview1_backing_fd_write(wasi_context_t *context, __wasi_fd_t fd,
size_t iovs_len, __wasi_size_t *nwritten_retptr)
{
if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
struct sandbox *s = current_sandbox_get();
__wasi_size_t nwritten = 0;
int rc = 0;
struct sandbox *s = current_sandbox_get();
size_t buffer_remaining = 0;
__wasi_size_t nwritten = 0;
int rc = 0;
/* Precompute required buffer size for entire iovec to call realloc at most once */
size_t total_size_to_copy = 0;
for (size_t i = 0; i < iovs_len; i++) { total_size_to_copy += iovs[i].buf_len; }
buffer_remaining = s->http->response_buffer.capacity - s->http->response_buffer.length;
if (buffer_remaining < total_size_to_copy) {
rc = vec_u8_resize(&s->http->response_buffer,
s->http->response_buffer.capacity + total_size_to_copy - buffer_remaining);
assert(rc == 0);
}
assert(s->http->response_buffer.capacity - s->http->response_buffer.length >= total_size_to_copy);
for (size_t i = 0; i < iovs_len; i++) {
#ifdef LOG_SANDBOX_STDERR
@ -798,8 +810,8 @@ wasi_snapshot_preview1_backing_fd_write(wasi_context_t *context, __wasi_fd_t fd,
debuglog("STDERR from Sandbox: %.*s", iovs[i].buf_len, iovs[i].buf);
}
#endif
rc = fwrite(iovs[i].buf, 1, iovs[i].buf_len, s->http->response_body.handle);
if (rc != iovs[i].buf_len) return __WASI_ERRNO_FBIG;
rc = http_session_write_response(s->http, iovs[i].buf, iovs[i].buf_len);
if (rc < 0) return __WASI_ERRNO_FBIG;
nwritten += rc;
}

@ -2,18 +2,17 @@
#include <unistd.h>
#include "arch/getcycles.h"
#include "execution_regression.h"
#include "global_request_scheduler.h"
#include "http_session_perf_log.h"
#include "generic_thread.h"
#include "listener_thread.h"
#include "metrics_server.h"
#include "module.h"
#include "runtime.h"
#include "sandbox_functions.h"
#include "sandbox_perf_log.h"
#include "tcp_session.h"
#include "tenant.h"
#include "tenant_functions.h"
#include "http_session_perf_log.h"
static void listener_thread_unregister_http_session(struct http_session *http);
static void panic_on_epoll_error(struct epoll_event *evt);
@ -86,9 +85,9 @@ listener_thread_register_http_session(struct http_session *http)
accept_evt.events = EPOLLOUT;
http->state = HTTP_SESSION_SEND_RESPONSE_HEADER_BLOCKED;
break;
case HTTP_SESSION_SENDING_RESPONSE_BODY:
case HTTP_SESSION_SENDING_RESPONSE:
accept_evt.events = EPOLLOUT;
http->state = HTTP_SESSION_SEND_RESPONSE_BODY_BLOCKED;
http->state = HTTP_SESSION_SEND_RESPONSE_BLOCKED;
break;
default:
panic("Invalid HTTP Session State: %d\n", http->state);
@ -147,7 +146,7 @@ listener_thread_register_metrics_server()
struct epoll_event accept_evt;
accept_evt.data.ptr = (void *)&metrics_server;
accept_evt.events = EPOLLIN;
rc = epoll_ctl(listener_thread_epoll_file_descriptor, EPOLL_CTL_ADD, metrics_server.tcp.socket_descriptor,
rc = epoll_ctl(listener_thread_epoll_file_descriptor, EPOLL_CTL_ADD, metrics_server.socket_descriptor,
&accept_evt);
return rc;
@ -163,8 +162,8 @@ panic_on_epoll_error(struct epoll_event *evt)
if (getsockopt(evt->data.fd, SOL_SOCKET, SO_ERROR, (void *)&error, &errlen) == 0) {
panic("epoll_wait: %s\n", strerror(error));
}
debuglog("epoll_error: Most likely client disconnected. Closing session.");
}
panic("epoll_wait");
};
}
static void
@ -184,7 +183,7 @@ on_client_request_arrival(int client_socket, const struct sockaddr *client_addre
/* Failed to allocate memory */
debuglog("Failed to allocate http session\n");
session->state = HTTP_SESSION_EXECUTION_COMPLETE;
http_session_set_response_header(session, 500);
http_session_set_response_header(session, 500, NULL, 0);
on_client_response_header_sending(session);
return;
}
@ -195,39 +194,23 @@ on_client_request_receiving(struct http_session *session)
{
/* Read HTTP request */
int rc = http_session_receive_request(session, (void_star_cb)listener_thread_register_http_session);
/* Check if the route is accurate only when the URL is downloaded. Stop downloading if inaccurate. */
if (session->route == NULL && strlen(session->http_request.full_url) > 0) {
struct route *route = http_router_match_route(&session->tenant->router, session->http_request.full_url);
if (route == NULL) {
debuglog("Did not match any routes\n");
session->state = HTTP_SESSION_EXECUTION_COMPLETE;
http_session_set_response_header(session, 404);
on_client_response_header_sending(session);
return;
}
session->route = route;
}
if (rc == 0) {
#ifdef EXECUTION_REGRESSION
if (!session->did_preprocessing) tenant_preprocess(session);
#endif
if (likely(rc == 0)) {
on_client_request_received(session);
return;
} else if (rc == -EAGAIN) {
/* session blocked and registered to epoll, so continue to next handle */
#ifdef EXECUTION_REGRESSION
/* try tenant preprocessing if min 4k Bytes received */
if (!session->did_preprocessing && session->http_request.body_length_read > 4096)
tenant_preprocess(session);
#endif
} else if (unlikely(rc == -EAGAIN)) {
/* session blocked and registered to epoll so continue to next handle */
return;
} else if (unlikely(rc == -ENOMEM)) {
/* Failed to grow request buffer */
debuglog("Failed to grow http request buffer\n");
session->state = HTTP_SESSION_EXECUTION_COMPLETE;
http_session_set_response_header(session, 500, NULL, 0);
on_client_response_header_sending(session);
return;
} else if (rc < 0) {
debuglog("Failed to receive or parse request\n");
session->state = HTTP_SESSION_EXECUTION_COMPLETE;
http_session_set_response_header(session, 400);
http_session_set_response_header(session, 400, NULL, 0);
on_client_response_header_sending(session);
return;
}
@ -240,29 +223,28 @@ on_client_request_received(struct http_session *session)
{
assert(session->state == HTTP_SESSION_RECEIVED_REQUEST);
session->request_downloaded_timestamp = __getcycles();
struct route *route = session->route;
uint64_t estimated_execution = route->execution_histogram.estimated_execution;
uint64_t work_admitted = 1;
#ifdef EXECUTION_REGRESSION
estimated_execution = get_regression_prediction(session);
#endif
struct route *route = http_router_match_route(&session->tenant->router, session->http_request.full_url);
if (route == NULL) {
debuglog("Did not match any routes\n");
session->state = HTTP_SESSION_EXECUTION_COMPLETE;
http_session_set_response_header(session, 404, NULL, 0);
on_client_response_header_sending(session);
return;
}
#ifdef ADMISSIONS_CONTROL
/*
* Perform admissions control.
* If 0, workload was rejected, so close with 429 "Too Many Requests" and continue
* TODO: Consider providing a Retry-After header
*/
uint64_t admissions_estimate = admissions_control_calculate_estimate(estimated_execution,
route->relative_deadline);
work_admitted = admissions_control_decide(admissions_estimate);
uint64_t work_admitted = admissions_control_decide(route->admissions_info.estimate);
if (work_admitted == 0) {
session->state = HTTP_SESSION_EXECUTION_COMPLETE;
http_session_set_response_header(session, 429);
http_session_set_response_header(session, 429, NULL, 0);
on_client_response_header_sending(session);
return;
}
#endif
/* Allocate a Sandbox */
session->state = HTTP_SESSION_EXECUTING;
@ -270,23 +252,17 @@ on_client_request_received(struct http_session *session)
if (unlikely(sandbox == NULL)) {
debuglog("Failed to allocate sandbox\n");
session->state = HTTP_SESSION_EXECUTION_COMPLETE;
http_session_set_response_header(session, 500);
http_session_set_response_header(session, 500, NULL, 0);
on_client_response_header_sending(session);
return;
}
sandbox->remaining_exec = estimated_execution;
/* If the global request scheduler is full, return a 429 to the client */
if (unlikely(global_request_scheduler_add(sandbox) == NULL)) {
// debuglog("Failed to add sandbox to global queue\n");
sandbox->response_code = 4290;
sandbox->state = SANDBOX_ERROR;
sandbox_perf_log_print_entry(sandbox);
sandbox->http = NULL;
debuglog("Failed to add sandbox to global queue\n");
sandbox_free(sandbox);
session->state = HTTP_SESSION_EXECUTION_COMPLETE;
http_session_set_response_header(session, 429);
http_session_set_response_header(session, 429, NULL, 0);
on_client_response_header_sending(session);
}
}
@ -294,6 +270,9 @@ on_client_request_received(struct http_session *session)
static void
on_client_response_header_sending(struct http_session *session)
{
assert(session->state = HTTP_SESSION_EXECUTION_COMPLETE);
session->state = HTTP_SESSION_SENDING_RESPONSE_HEADER;
int rc = http_session_send_response_header(session, (void_star_cb)listener_thread_register_http_session);
if (likely(rc == 0)) {
on_client_response_body_sending(session);
@ -329,8 +308,6 @@ on_client_response_body_sending(struct http_session *session)
static void
on_client_response_sent(struct http_session *session)
{
assert(session->state = HTTP_SESSION_SENT_RESPONSE_BODY);
/* Terminal State Logging for Http Session */
session->response_sent_timestamp = __getcycles();
http_session_perf_log_print_entry(session);
@ -377,16 +354,15 @@ on_metrics_server_epoll_event(struct epoll_event *evt)
/* Accept as many clients requests as possible, returning when we would have blocked */
while (true) {
/* We accept the client connection with blocking semantics because we spawn ephemeral worker threads */
int client_socket = accept4(metrics_server.tcp.socket_descriptor, (struct sockaddr *)&client_address,
&address_length, 0);
int client_socket = accept4(metrics_server.socket_descriptor, (struct sockaddr *)&client_address,
&address_length, SOCK_NONBLOCK);
if (unlikely(client_socket < 0)) {
if (errno == EWOULDBLOCK || errno == EAGAIN) return;
panic("accept4: %s", strerror(errno));
}
metrics_server_thread_spawn(client_socket);
metrics_server_handler(client_socket);
}
}
@ -409,7 +385,7 @@ on_client_socket_epoll_event(struct epoll_event *evt)
assert((evt->events & EPOLLOUT) == EPOLLOUT);
on_client_response_header_sending(session);
break;
case HTTP_SESSION_SEND_RESPONSE_BODY_BLOCKED:
case HTTP_SESSION_SEND_RESPONSE_BLOCKED:
assert((evt->events & EPOLLOUT) == EPOLLOUT);
on_client_response_body_sending(session);
break;
@ -433,7 +409,10 @@ listener_thread_main(void *dummy)
{
struct epoll_event epoll_events[RUNTIME_MAX_EPOLL_EVENTS];
generic_thread_initialize();
metrics_server_init();
metrics_server_listen();
listener_thread_register_metrics_server();
/* Set my priority */
@ -456,22 +435,15 @@ listener_thread_main(void *dummy)
for (int i = 0; i < descriptor_count; i++) {
panic_on_epoll_error(&epoll_events[i]);
enum epoll_tag tag = *(enum epoll_tag *)epoll_events[i].data.ptr;
switch (tag) {
case EPOLL_TAG_TENANT_SERVER_SOCKET:
if (epoll_events[i].data.ptr == &metrics_server) {
on_metrics_server_epoll_event(&epoll_events[i]);
} else if (tenant_database_find_by_ptr(epoll_events[i].data.ptr) != NULL) {
on_tenant_socket_epoll_event(&epoll_events[i]);
break;
case EPOLL_TAG_HTTP_SESSION_CLIENT_SOCKET:
} else {
on_client_socket_epoll_event(&epoll_events[i]);
break;
case EPOLL_TAG_METRICS_SERVER_SOCKET:
on_metrics_server_epoll_event(&epoll_events[i]);
break;
default:
panic("Unknown epoll type!");
}
}
generic_thread_dump_lock_overhead();
}
panic("Listener thread unexpectedly broke loop\n");

@ -2,8 +2,8 @@
#include "current_sandbox.h"
#include "global_request_scheduler.h"
#include "local_runqueue.h"
#include "local_runqueue_list.h"
#include "local_runqueue.h"
#include "sandbox_functions.h"
thread_local static struct ps_list_head local_runqueue_list;
@ -81,9 +81,9 @@ local_runqueue_list_initialize()
ps_list_head_init(&local_runqueue_list);
/* Register Function Pointers for Abstract Scheduling API */
struct local_runqueue_config config = {.add_fn = local_runqueue_list_append,
.is_empty_fn = local_runqueue_list_is_empty,
.delete_fn = local_runqueue_list_remove,
.get_next_fn = local_runqueue_list_get_next};
struct local_runqueue_config config = { .add_fn = local_runqueue_list_append,
.is_empty_fn = local_runqueue_list_is_empty,
.delete_fn = local_runqueue_list_remove,
.get_next_fn = local_runqueue_list_get_next };
local_runqueue_initialize(&config);
};

@ -9,8 +9,8 @@
#include "local_runqueue_minheap.h"
#include "panic.h"
#include "priority_queue.h"
#include "runtime.h"
#include "sandbox_functions.h"
#include "runtime.h"
thread_local static struct priority_queue *local_runqueue_minheap;
@ -84,10 +84,10 @@ local_runqueue_minheap_initialize()
local_runqueue_minheap = priority_queue_initialize(RUNTIME_RUNQUEUE_SIZE, false, sandbox_get_priority);
/* Register Function Pointers for Abstract Scheduling API */
struct local_runqueue_config config = {.add_fn = local_runqueue_minheap_add,
.is_empty_fn = local_runqueue_minheap_is_empty,
.delete_fn = local_runqueue_minheap_delete,
.get_next_fn = local_runqueue_minheap_get_next};
struct local_runqueue_config config = { .add_fn = local_runqueue_minheap_add,
.is_empty_fn = local_runqueue_minheap_is_empty,
.delete_fn = local_runqueue_minheap_delete,
.get_next_fn = local_runqueue_minheap_get_next };
local_runqueue_initialize(&config);
}

@ -9,9 +9,9 @@
#include "local_runqueue_mtds.h"
#include "panic.h"
#include "priority_queue.h"
#include "runtime.h"
#include "sandbox_functions.h"
#include "tenant_functions.h"
#include "runtime.h"
thread_local static struct priority_queue *local_runqueue_mtds_guaranteed;
thread_local static struct priority_queue *local_runqueue_mtds_default;
@ -172,10 +172,10 @@ local_runqueue_mtds_initialize()
perworker_tenant_get_priority);
/* Register Function Pointers for Abstract Scheduling API */
struct local_runqueue_config config = {.add_fn = local_runqueue_mtds_add,
.is_empty_fn = local_runqueue_mtds_is_empty,
.delete_fn = local_runqueue_mtds_delete,
.get_next_fn = local_runqueue_mtds_get_next};
struct local_runqueue_config config = { .add_fn = local_runqueue_mtds_add,
.is_empty_fn = local_runqueue_mtds_is_empty,
.delete_fn = local_runqueue_mtds_delete,
.get_next_fn = local_runqueue_mtds_get_next };
local_runqueue_initialize(&config);
}

@ -2,27 +2,25 @@
#include <dlfcn.h>
#include <math.h>
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <sched.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef LOG_TO_FILE
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#endif
#include "debuglog.h"
#include "json_parse.h"
#include "pretty_print.h"
#include "debuglog.h"
#include "listener_thread.h"
#include "panic.h"
#include "pretty_print.h"
#include "runtime.h"
#include "sandbox_perf_log.h"
#include "sandbox_types.h"
#include "scheduler.h"
#include "software_interrupt.h"
@ -38,11 +36,9 @@ uint32_t runtime_worker_threads_count = 0;
enum RUNTIME_SIGALRM_HANDLER runtime_sigalrm_handler = RUNTIME_SIGALRM_HANDLER_BROADCAST;
bool runtime_preemption_enabled = true;
bool runtime_worker_spinloop_pause_enabled = false;
uint32_t runtime_quantum_us = 1000; /* 1ms */
bool runtime_preemption_enabled = true;
uint32_t runtime_quantum_us = 5000; /* 5ms */
uint64_t runtime_boot_timestamp;
pid_t runtime_pid = 0;
/**
* Returns instructions on use of CLI if used incorrectly
@ -102,48 +98,32 @@ runtime_allocate_available_cores()
* We are also assuming this value is static
* @return proceccor speed in MHz
*/
static inline void
static inline uint32_t
runtime_get_processor_speed_MHz(void)
{
char *proc_mhz_raw = getenv("SLEDGE_PROC_MHZ");
FILE *cmd = NULL;
uint32_t return_value;
if (proc_mhz_raw != NULL) {
/* The case with manual override for the CPU freq */
runtime_processor_speed_MHz = atoi(proc_mhz_raw);
} else {
/* The case when we have to get CPU freq from */
usleep(200000); /* wait a bit for the workers to launch for more accuracy */
/* Get the average of the cpufreqs only for worker cores (no event core and reserved) */
char command[128] = {0};
sprintf(command, "grep '^cpu MHz' /proc/cpuinfo | sed -n '%u,%up' | \
awk '{ total += $4; count++ } END { print total/count }'",
runtime_first_worker_processor + 1,
runtime_first_worker_processor + runtime_worker_threads_count);
cmd = popen(command, "r");
if (unlikely(cmd == NULL)) goto err;
char buff[16];
size_t n = fread(buff, 1, sizeof(buff) - 1, cmd);
buff[n] = '\0';
pclose(cmd);
float processor_speed_MHz;
n = sscanf(buff, "%f", &processor_speed_MHz);
if (unlikely(n != 1)) goto err;
if (unlikely(processor_speed_MHz < 0)) goto err;
runtime_processor_speed_MHz = (uint32_t)nearbyintf(processor_speed_MHz);
}
FILE *cmd = popen("grep '^cpu MHz' /proc/cpuinfo | head -n 1 | awk '{print $4}'", "r");
if (unlikely(cmd == NULL)) goto err;
pretty_print_key_value("Worker CPU Freq", "%u MHz\n", runtime_processor_speed_MHz);
char buff[16];
size_t n = fread(buff, 1, sizeof(buff) - 1, cmd);
if (unlikely(n <= 0)) goto err;
buff[n] = '\0';
float processor_speed_MHz;
n = sscanf(buff, "%f", &processor_speed_MHz);
if (unlikely(n != 1)) goto err;
if (unlikely(processor_speed_MHz < 0)) goto err;
return_value = (uint32_t)nearbyintf(processor_speed_MHz);
done:
return;
pclose(cmd);
return return_value;
err:
return_value = 0;
goto done;
panic("Failed to detect processor frequency");
}
/**
@ -208,12 +188,10 @@ runtime_configure()
scheduler = SCHEDULER_MTDS;
} else if (strcmp(scheduler_policy, "EDF") == 0) {
scheduler = SCHEDULER_EDF;
} else if (strcmp(scheduler_policy, "SJF") == 0) {
scheduler = SCHEDULER_SJF;
} else if (strcmp(scheduler_policy, "FIFO") == 0) {
scheduler = SCHEDULER_FIFO;
} else {
panic("Invalid scheduler policy: %s. Must be {MTDBF|MTDS|EDF|SJF|FIFO}\n", scheduler_policy);
panic("Invalid scheduler policy: %s. Must be {MTDBF|MTDS|EDF|FIFO}\n", scheduler_policy);
}
pretty_print_key_value("Scheduler Policy", "%s\n", scheduler_print(scheduler));
@ -251,17 +229,6 @@ runtime_configure()
http_session_perf_log_init();
}
void
runtime_configure_worker_spinloop_pause()
{
/* Runtime Worker-Spinloop-Pause Toggle */
char *pause_enable = getenv("SLEDGE_SPINLOOP_PAUSE_ENABLED");
if (pause_enable != NULL && strcmp(pause_enable, "true") == 0) runtime_worker_spinloop_pause_enabled = true;
pretty_print_key_value("Worker-Spinloop-Pause", "%s\n",
runtime_worker_spinloop_pause_enabled ? PRETTY_PRINT_GREEN_ENABLED
: PRETTY_PRINT_RED_DISABLED);
}
void
log_compiletime_config()
{
@ -286,18 +253,6 @@ log_compiletime_config()
pretty_print_key_disabled("Admissions Control");
#endif
#ifdef EXECUTION_HISTOGRAM
pretty_print_key_enabled("Execution Histogram");
#else
pretty_print_key_disabled("Execution Histogram");
#endif
#ifdef EXECUTION_REGRESSION
pretty_print_key_enabled("Execution Regression");
#else
pretty_print_key_disabled("Execution Regression");
#endif
/* Debugging Flags */
printf("Static Compiler Flags (Debugging):\n");
@ -332,6 +287,12 @@ log_compiletime_config()
pretty_print_key_disabled("Log HTTP Parser");
#endif
#ifdef LOG_LOCK_OVERHEAD
pretty_print_key_enabled("Log Lock Overhead");
#else
pretty_print_key_disabled("Log Lock Overhead");
#endif
#ifdef LOG_TENANT_LOADING
pretty_print_key_enabled("Log Tenant Loading");
#else
@ -362,22 +323,10 @@ log_compiletime_config()
pretty_print_key_disabled("Log State Changes");
#endif
#ifdef HTTP_TOTAL_COUNTERS
pretty_print_key_enabled("HTTP Total Counters");
#ifdef LOG_TOTAL_REQS_RESPS
pretty_print_key_enabled("Log Total Reqs/Resps");
#else
pretty_print_key_disabled("HTTP Total Counters");
#endif
#ifdef HTTP_ROUTE_TOTAL_COUNTERS
pretty_print_key_enabled("HTTP Route Total Counters");
#else
pretty_print_key_disabled("HTTP Route Total Counters");
#endif
#ifdef PROC_STAT_METRICS
pretty_print_key_enabled("procfs Metrics");
#else
pretty_print_key_disabled("procfs Metrics");
pretty_print_key_disabled("Log Total Reqs/Resps");
#endif
#ifdef SANDBOX_STATE_TOTALS
@ -481,8 +430,6 @@ main(int argc, char **argv)
exit(-1);
}
runtime_pid = getpid();
printf("Starting the Sledge runtime\n");
log_compiletime_config();
@ -490,6 +437,13 @@ main(int argc, char **argv)
printf("Runtime Environment:\n");
runtime_processor_speed_MHz = runtime_get_processor_speed_MHz();
if (unlikely(runtime_processor_speed_MHz == 0)) panic("Failed to detect processor speed\n");
int heading_length = 30;
pretty_print_key_value("Processor Speed", "%u MHz\n", runtime_processor_speed_MHz);
runtime_set_resource_limits_to_max();
runtime_allocate_available_cores();
runtime_configure();
@ -498,8 +452,6 @@ main(int argc, char **argv)
listener_thread_initialize();
runtime_start_runtime_worker_threads();
runtime_get_processor_speed_MHz();
runtime_configure_worker_spinloop_pause();
software_interrupt_arm_timer();
#ifdef LOG_TENANT_LOADING

@ -1,86 +1,32 @@
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "admissions_control.h"
#include "debuglog.h"
#include "http.h"
#include "http_total.h"
#include "metrics_server.h"
#include "proc_stat.h"
#include "runtime.h"
#include "sandbox_state.h"
#include "sandbox_total.h"
#include "tcp_server.h"
#include "http_total.h"
/* We run threads on the "reserved OS core" using blocking semantics */
#define METRICS_SERVER_CORE_ID 0
#define METRICS_SERVER_PORT 1776
struct metrics_server metrics_server;
static void *metrics_server_handler(void *arg);
extern void metrics_server_route_level_metrics_render(FILE *ostream);
struct tcp_server metrics_server;
void
metrics_server_init()
{
metrics_server.tag = EPOLL_TAG_METRICS_SERVER_SOCKET;
tcp_server_init(&metrics_server.tcp, METRICS_SERVER_PORT);
int rc = tcp_server_listen(&metrics_server.tcp);
assert(rc == 0);
/* Configure pthread attributes to pin metrics server threads to CPU 0 */
pthread_attr_init(&metrics_server.thread_settings);
cpu_set_t cs;
CPU_ZERO(&cs);
CPU_SET(METRICS_SERVER_CORE_ID, &cs);
pthread_attr_setaffinity_np(&metrics_server.thread_settings, sizeof(cpu_set_t), &cs);
tcp_server_init(&metrics_server, 1776);
}
int
metrics_server_close()
metrics_server_listen()
{
return tcp_server_close(&metrics_server.tcp);
return tcp_server_listen(&metrics_server);
}
void
metrics_server_thread_spawn(int client_socket)
int
metrics_server_close()
{
/* Fire and forget, so we don't save the thread handles */
pthread_t metrics_server_thread;
int rc = pthread_create(&metrics_server_thread, &metrics_server.thread_settings, metrics_server_handler,
(void *)(long)client_socket);
if (rc != 0) {
debuglog("Metrics Server failed to spawn pthread with %s\n", strerror(rc));
close(client_socket);
}
return tcp_server_close(&metrics_server);
}
static void *
metrics_server_handler(void *arg)
void
metrics_server_handler(int client_socket)
{
/* Intermediate cast to integral value of 64-bit width to silence compiler nits */
int client_socket = (int)(long)arg;
/* Duplicate fd so fclose doesn't close the actual client_socket */
int temp_fd = dup(client_socket);
FILE *req_body = fdopen(temp_fd, "r");
/* Basic L7 routing to filter out favicon requests */
char http_status_code_buf[256];
fgets(http_status_code_buf, 256, req_body);
fclose(req_body);
if (strncmp(http_status_code_buf, "GET /metrics HTTP", 10) != 0) {
write(client_socket, http_header_build(404), http_header_len(404));
close(client_socket);
pthread_exit(NULL);
}
int rc = 0;
char *ostream_base = NULL;
@ -88,140 +34,34 @@ metrics_server_handler(void *arg)
FILE *ostream = open_memstream(&ostream_base, &ostream_size);
assert(ostream != NULL);
#ifdef HTTP_TOTAL_COUNTERS
uint32_t total_reqs = atomic_load(&http_total_requests);
uint32_t total_5XX = atomic_load(&http_total_5XX);
uint32_t total_2XX = atomic_load(&http_total_2XX);
uint32_t total_4XX = atomic_load(&http_total_4XX);
#endif
uint64_t total_sandboxes = atomic_load(&sandbox_total);
#ifdef SANDBOX_STATE_TOTALS
uint32_t total_sandboxes_uninitialized = atomic_load(&sandbox_state_totals[SANDBOX_UNINITIALIZED]);
uint32_t total_sandboxes_allocated = atomic_load(&sandbox_state_totals[SANDBOX_ALLOCATED]);
uint32_t total_sandboxes_initialized = atomic_load(&sandbox_state_totals[SANDBOX_INITIALIZED]);
uint32_t total_sandboxes_runnable = atomic_load(&sandbox_state_totals[SANDBOX_RUNNABLE]);
uint32_t total_sandboxes_preempted = atomic_load(&sandbox_state_totals[SANDBOX_PREEMPTED]);
uint32_t total_sandboxes_running_sys = atomic_load(&sandbox_state_totals[SANDBOX_RUNNING_SYS]);
uint32_t total_sandboxes_running_user = atomic_load(&sandbox_state_totals[SANDBOX_RUNNING_USER]);
uint32_t total_sandboxes_interrupted = atomic_load(&sandbox_state_totals[SANDBOX_INTERRUPTED]);
uint32_t total_sandboxes_asleep = atomic_load(&sandbox_state_totals[SANDBOX_ASLEEP]);
uint32_t total_sandboxes_returned = atomic_load(&sandbox_state_totals[SANDBOX_RETURNED]);
uint32_t total_sandboxes_complete = atomic_load(&sandbox_state_totals[SANDBOX_COMPLETE]);
uint32_t total_sandboxes_error = atomic_load(&sandbox_state_totals[SANDBOX_ERROR]);
#endif
#ifdef ADMISSIONS_CONTROL
uint32_t work_admitted = atomic_load(&admissions_control_admitted);
double work_admitted_percentile = (double)work_admitted / admissions_control_capacity * 100;
#endif
#ifdef PROC_STAT_METRICS
struct proc_stat_metrics stat;
proc_stat_metrics_init(&stat);
#endif
fprintf(ostream, HTTP_RESPONSE_200_OK HTTP_RESPONSE_TERMINATOR);
#ifdef PROC_STAT_METRICS
fprintf(ostream, "# TYPE os_proc_major_page_faults counter\n");
fprintf(ostream, "os_proc_major_page_faults: %lu\n", stat.major_page_faults);
fprintf(ostream, "# TYPE os_proc_minor_page_faults counter\n");
fprintf(ostream, "os_proc_minor_page_faults: %lu\n", stat.minor_page_faults);
fprintf(ostream, "# TYPE os_proc_child_major_page_faults counter\n");
fprintf(ostream, "os_proc_child_major_page_faults: %lu\n", stat.child_major_page_faults);
fprintf(ostream, "# TYPE os_proc_child_minor_page_faults counter\n");
fprintf(ostream, "os_proc_child_minor_page_faults: %lu\n", stat.child_minor_page_faults);
fprintf(ostream, "# TYPE os_proc_user_time counter\n");
fprintf(ostream, "os_proc_user_time: %lu\n", stat.user_time);
fprintf(ostream, "# TYPE os_proc_sys_time counter\n");
fprintf(ostream, "os_proc_sys_time: %lu\n", stat.system_time);
fprintf(ostream, "# TYPE os_proc_guest_time counter\n");
fprintf(ostream, "os_proc_guest_time: %lu\n", stat.guest_time);
#endif /* PROC_STAT_METRICS */
#ifdef ADMISSIONS_CONTROL
fprintf(ostream, "# TYPE work_admitted_percentile gauge\n");
fprintf(ostream, "work_admitted_percentile: %f\n", work_admitted_percentile);
#endif
#ifdef HTTP_TOTAL_COUNTERS
fprintf(ostream, "# TYPE total_requests counter\n");
fprintf(ostream, "total_requests: %d\n", total_reqs);
fprintf(ostream, "# TYPE total_2XX counter\n");
fprintf(ostream, "total_2XX: %d\n", total_2XX);
fprintf(ostream, "# TYPE total_4XX counter\n");
fprintf(ostream, "total_4XX: %d\n", total_4XX);
fprintf(ostream, "HTTP/1.1 200 OK\r\n\r\n");
fprintf(ostream, "# TYPE total_5XX counter\n");
fprintf(ostream, "total_5XX: %d\n", total_5XX);
#endif
metrics_server_route_level_metrics_render(ostream);
fprintf(ostream, "# TYPE total_sandboxes counter\n");
fprintf(ostream, "total_sandboxes: %lu\n", total_sandboxes);
#ifdef SANDBOX_STATE_TOTALS
fprintf(ostream, "# TYPE total_sandboxes_uninitialized gauge\n");
fprintf(ostream, "total_sandboxes_uninitialized: %d\n", total_sandboxes_uninitialized);
fprintf(ostream, "# TYPE total_sandboxes_allocated gauge\n");
fprintf(ostream, "total_sandboxes_allocated: %d\n", total_sandboxes_allocated);
fprintf(ostream, "# TYPE total_sandboxes_initialized gauge\n");
fprintf(ostream, "total_sandboxes_initialized: %d\n", total_sandboxes_initialized);
fprintf(ostream, "# TYPE total_sandboxes_runnable gauge\n");
fprintf(ostream, "total_sandboxes_runnable: %d\n", total_sandboxes_runnable);
fprintf(ostream, "# TYPE total_sandboxes_preempted gauge\n");
fprintf(ostream, "total_sandboxes_preempted: %d\n", total_sandboxes_preempted);
fprintf(ostream, "# TYPE total_sandboxes_running_sys gauge\n");
fprintf(ostream, "total_sandboxes_running_sys: %d\n", total_sandboxes_running_sys);
fprintf(ostream, "# TYPE total_sandboxes_running_user gauge\n");
fprintf(ostream, "total_sandboxes_running_user: %d\n", total_sandboxes_running_user);
fprintf(ostream, "# TYPE total_sandboxes_interrupted gauge\n");
fprintf(ostream, "total_sandboxes_interrupted: %d\n", total_sandboxes_interrupted);
fprintf(ostream, "# TYPE total_sandboxes_asleep gauge\n");
fprintf(ostream, "total_sandboxes_asleep: %d\n", total_sandboxes_asleep);
fprintf(ostream, "# TYPE total_requests counter\n");
fprintf(ostream, "total_requests: %d\n", total_reqs);
fprintf(ostream, "# TYPE total_sandboxes_returned gauge\n");
fprintf(ostream, "total_sandboxes_returned: %d\n", total_sandboxes_returned);
fprintf(ostream, "# TYPE total_rejections counter\n");
fprintf(ostream, "total_rejections: %d\n", total_5XX);
fflush(ostream);
fprintf(ostream, "# TYPE total_sandboxes_complete gauge\n");
fprintf(ostream, "total_sandboxes_complete: %d\n", total_sandboxes_complete);
rewind(ostream);
fprintf(ostream, "# TYPE total_sandboxes_error gauge\n");
fprintf(ostream, "total_sandboxes_error: %d\n", total_sandboxes_error);
#endif
char buf[256] = { 0 };
size_t nread = 0;
do {
nread = fread(buf, 1, 255, ostream);
buf[nread] = '\0';
/* TODO: Deal with blocking here! */
write(client_socket, buf, nread);
} while (nread > 0);
fflush(ostream);
assert(ostream_size > 0);
rc = fclose(ostream);
assert(rc == 0);
/* Closing the memstream does not close the generated buffer */
ssize_t nwritten = write(client_socket, ostream_base, ostream_size);
assert(nwritten == ostream_size);
free(ostream_base);
ostream_size = 0;
close(client_socket);
pthread_exit(NULL);
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save