Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
FROM registry.fedoraproject.org/fedora:43 as builder

ARG RUST_VERSION=stable

RUN dnf install -y \
# for stub \
nasm \
# for script jit \
llvm-devel \
zlib-devel \
libxml2-devel \
libstdc++-static \
# for bpf \
clang \
kernel-devel \
libbpf-devel && \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --default-toolchain $RUST_VERSION --profile minimal

# $HOME is not evaluated here, any better way of getting the toolchain
# directory?
ENV PATH="${PATH}:/root/.cargo/bin"

RUN rustup component add rustfmt clippy

RUN if [ "${RUST_VERSION}" == "nightly" ]; then \
rustup component add rust-src --toolchain nightly; \
fi

ADD ./ /berserker/

WORKDIR /berserker/

RUN nasm -f elf64 -o stub.o stub.asm && ld -o stub stub.o

RUN cargo fmt --check

RUN cargo clippy -- -D warnings

RUN cargo build -r

# Test will require stub binary to be available
ENV PATH="${PATH}:/berserker:/berserker/target/release"

RUN if [ "${RUST_VERSION}" == "nightly" ]; then \
TARGET=$(rustc --version --verbose | grep host | cut -d" " -f2) && \
RUSTFLAGS="-Z sanitizer=address" cargo +nightly test -Z build-std --target "$TARGET"; \
else \
cargo test; \
fi

FROM registry.fedoraproject.org/fedora:43

RUN mkdir /etc/berserker

COPY --from=builder /berserker/target/release/berserker /usr/local/bin/berserker
COPY --from=builder /berserker/workload.toml /etc/berserker/workload.toml
COPY --from=builder /berserker/stub /usr/local/bin/stub

ENV PATH="${PATH}:/usr/local/bin"

ENTRYPOINT berserker
14 changes: 14 additions & 0 deletions Containerfile.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM berserker:latest

RUN dnf install -y \
which \
bpftrace \
bpftool \
procps-ng

# Test will require stub binary to be available
ENV PATH="${PATH}:/berserker:/usr/local/bin"

ADD ./tests /tests/

ENTRYPOINT ["/tests/entrypoint.sh"]
13 changes: 0 additions & 13 deletions Dockerfile

This file was deleted.

35 changes: 0 additions & 35 deletions Dockerfile.build

This file was deleted.

7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ ifeq ($(BERSERKER_TAG),)
BERSERKER_TAG=$(shell git describe --tags --abbrev=10 --dirty)
endif


.PHONY: all
all:
docker build -t builder -f Dockerfile.build .
docker build -t berserker .
docker build -t berserker -f Containerfile .
docker build -t berserker -f Containerfile --build-arg=RUST_VERSION=nightly .
docker build -t berserker-test -f Containerfile.test .
docker run --privileged berserker-test

.PHONY: build-network
build-berserker-network:
Expand Down
12 changes: 8 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![feature(sanitize)]

use core_affinity::CoreId;
use serde::{Deserialize, Deserializer};
use std::{collections::HashMap, fmt::Display, net::Ipv4Addr, str::FromStr};
Expand Down Expand Up @@ -28,7 +30,8 @@ pub struct WorkloadConfig {
/// Custom workload configuration.
pub workload: Workload,

/// For how long to run the worker. Default value is zero, meaning no limit.
/// For how long to run the worker. Default value is zero, meaning no
/// limit.
#[serde(default = "default_duration")]
pub duration: u64,
}
Expand Down Expand Up @@ -194,8 +197,8 @@ pub enum Workload {
/// Maximum number of dynamic connections
connections_dyn_max: u32,

// How many connections to make to the same server address and port with
// different client ports
/// How many connections to make to the same server address and port
/// with different client ports
#[serde(default = "default_conns_per_addr")]
conns_per_addr: u16,

Expand All @@ -211,7 +214,8 @@ pub enum Workload {
/// Whether or not to wait for a connection to be removed before adding
/// a new one, when the dynamic connection limit is reached.
/// if true: an old connection will be forcibly removed
/// if false: wait for a connection to naturally age-off before adding a new one
/// if false: wait for a connection to naturally age-off before adding
/// a new one
preempt: bool,
},

Expand Down
6 changes: 4 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ fn main() {
.required(false),
)
// Add in settings from the environment (with a prefix of APP)
// Eg.. `BERSERKER__WORKLOAD__ARRIVAL_RATE=1` would set the `arrival_rate` key
// Eg.. `BERSERKER__WORKLOAD__ARRIVAL_RATE=1` would set the
// `arrival_rate` key
.add_source(
config::Environment::with_prefix("BERSERKER")
.try_parsing(true)
Expand All @@ -230,7 +231,8 @@ fn main() {
let elapsed = duration_timer.elapsed().unwrap().as_secs();

// Ignore processes without specified duration -- we don't want
// neither terminate them, nor count against processes to compare.
// neither terminate them, nor count against processes to
// compare.
let watched_processes = processes
.iter()
.filter(|(_, duration)| *duration > 0)
Expand Down
8 changes: 5 additions & 3 deletions src/worker/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ pub static RUNTIME: LazyLock<HashMap<String, RuntimeFunc>> =
});

impl ScriptWorker {
#[sanitize(address = "off")]
fn jit_instruction(name: String, arg: Arg, ctx: &BuildContext) {
let mut arg_ptr = Self::get_arg_value(arg, ctx);

Expand Down Expand Up @@ -320,7 +321,8 @@ impl ScriptWorker {
&mut err,
) != 0
{
// In case of error, we must avoid using the uninitialized ExecutionEngineRef.
// In case of error, we must avoid using the uninitialized
// ExecutionEngineRef.
assert!(!err.is_null());
panic!(
"Failed to create execution engine: {:?}",
Expand Down Expand Up @@ -385,8 +387,8 @@ impl ScriptWorker {
function_type,
);

// Create a basic block in the function and set our builder to generate
// code in it.
// Create a basic block in the function and set our builder to
// generate code in it.
let bb = LLVMAppendBasicBlockInContext(
context,
function,
Expand Down
4 changes: 2 additions & 2 deletions src/worker/syscalls/ioctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ pub struct IoctlCall {

impl IoctlCall {
pub fn new(_: &ArgsMap) -> Self {
// Zero initialize all fields, fd will be initialized in `Syscaller::init`.
// All other fields can be overridden as needed
// Zero initialize all fields, fd will be initialized in
// `Syscaller::init`. All other fields can be overridden as needed
Default::default()
}
}
Expand Down
4 changes: 4 additions & 0 deletions tests/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
mount -t debugfs none /sys/kernel/debug

./tests/workers/script/unit/smoke.sh
96 changes: 96 additions & 0 deletions tests/workers/script/dist/smoke.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env bash
set -eou pipefail

stop() { echo "$*" 1>&2 ; exit 1; }

which bpftrace &>/dev/null || stop "Don't have bpftrace"
which bpftool &>/dev/null || stop "Don't have bpftool"
which berserker &>/dev/null || stop "Don't have berserker"
which stub &>/dev/null || stop "Don't have random process tool"
which pkill &>/dev/null || stop "Don't have pkill"

if [ ! -d "tests/workers/script/unit" ]; then
echo "Can't find test directory. Smoke tests have to be run from the project root directory"
fi

echo "Cleanup..."
rm -f /tmp/berserker.log
rm -f /tmp/events.log

# in case if it's still running from a previous run
pkill berserker || true

# make berserkers verbose
export RUST_LOG=trace

echo "Starting bpftrace..."
bpftrace tests/workers/script/unit/syscalls.bt &> /tmp/events.log &

# let bpftrace attach probes
attempts=0

while ! bpftool prog | grep -q bpftrace ;
do
if [[ "$attempts" -gt 40 ]]; then
echo "Can't find bpftool after ${attempts} attempts."
cat /tmp/events.log
exit 1
fi;

attempts=$((attempts+1))
echo "Wait for bpftrace";
sleep 0.5;
done

echo "Starting berserker..."
berserker -f tests/workers/script/dist/workload.ber &> /tmp/berserker.log &

# let berserker do some work
sleep 5;

echo "Stopping..."
pkill berserker || true
pkill bpftrace || true

echo "Verifying the results..."
if ! grep -q -E 'exec .*/stub' /tmp/events.log; then
echo "FAIL: no task instruction"
cat /tmp/berserker.log
cat /tmp/events.log
exit 1;
fi

if ! grep -q -E 'openat /tmp/tests/test' /tmp/events.log; then
echo "FAIL: no open instruction"
cat /tmp/berserker.log
cat /tmp/events.log
exit 1;
fi

if ! grep -E 'openat /tmp/tests/.*' /tmp/events.log | grep -q -v '/tmp/tests/test'; then
echo "FAIL: no open random path instruction"
cat /tmp/berserker.log
cat /tmp/events.log
exit 1;
fi

if ! grep -q -E 'connect .*' /tmp/events.log; then
echo "FAIL: no ping instruction"
cat /tmp/berserker.log
cat /tmp/events.log
exit 1;
fi

if ! grep -q -E 'sendto .*' /tmp/events.log; then
echo "FAIL: ping instruction did not work"
cat /tmp/berserker.log
cat /tmp/events.log
exit 1;
fi

echo "PASS"

rm -f /tmp/berserker.log
rm -f /tmp/events.log

exit 0;
20 changes: 20 additions & 0 deletions tests/workers/script/dist/syscalls.bt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
t:syscalls:sys_enter_execve /comm == "berserker"/
{
printf("exec %s\n", str(args->filename));
}

t:syscalls:sys_enter_openat /comm == "berserker"/ {
printf("openat %s\n", str(args->filename));
}

t:syscalls:sys_enter_connect /comm == "berserker"/ {
$addr_in = (struct sockaddr_in *)args->uservaddr;
$addr = ntop($addr_in->sin_addr.s_addr);
printf("connect %s\n", $addr);
}

t:syscalls:sys_enter_sendto /comm == "berserker"/ {
$addr_in = (struct sockaddr_in *)args->addr;
$addr = ntop($addr_in->sin_addr.s_addr);
printf("sendto %s\n", $addr);
}
20 changes: 20 additions & 0 deletions tests/workers/script/dist/workload.ber
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
machine {
server(8080);
path("/tmp/tests");
}

main (workers = 1) {
debug("run task stub");
task(stub);

debug("open file /tmp/tests/test");
open("/tmp/tests/test");

debug("open random file under /tmp/tests/");
open(random_path("/tmp/tests"));

debug("ping server");
ping("127.0.0.1:8080");
} : exp {
rate = 10.0;
}
Loading
Loading