Compatibility Notes

libopenblas

libopenblas is a fairly low-level library, and can get pulled in transitively via dependencies. e.g., tgen uses libigraph, which links against liblapack, which links against blas.

libopenblas, when compiled with pthread support, uses busy-loops in its worker threads.

There are several known workarounds:

  • Use Shadow's --model-unblocked-syscall-latency feature. See busy-loops for details and caveats.

  • Use a different implementation of libblas. e.g. on Ubuntu, there are several alternative packages that can provide libblas. In particular, libblas3 doesn't have this issue.

  • Install libopenblas compiled without pthread support. e.g. on Ubuntu this can be obtained by installing libopenblas0-serial instead of libopenblas0-pthread.

  • Configure libopenblas to not use threads at runtime. This can be done by setting the environment variable OPENBLAS_NUM_THREADS=1, in the process's environment attribute in the Shadow config. Example: tor-minimal.yaml:109

See also:

cURL

Example

general: stop_time: 10s model_unblocked_syscall_latency: true network: graph: type: 1_gbit_switch hosts: server: network_node_id: 0 processes: - path: python3 args: -m http.server 80 start_time: 0s expected_final_state: running client1: &client_host network_node_id: 0 processes: - path: curl args: -s server start_time: 2s client2: *client_host client3: *client_host
rm -rf shadow.data; shadow shadow.yaml > shadow.log cat shadow.data/hosts/client1/curl.1000.stdout

Notes

  1. Older versions of cURL use a busy loop that is incompatible with Shadow and will cause Shadow to deadlock. model_unblocked_syscall_latency works around this (see busy-loops). Newer versions of cURL, such as the version provided in Ubuntu 20.04, don't have this issue. See issue #1794 for details.

Wget2

Example

general: stop_time: 10s network: graph: type: 1_gbit_switch hosts: server: network_node_id: 0 processes: - path: python3 args: -m http.server 80 start_time: 0s expected_final_state: running client1: &client_host network_node_id: 0 processes: - path: wget2 args: --no-tcp-fastopen server start_time: 2s client2: *client_host client3: *client_host
rm -rf shadow.data; shadow shadow.yaml > shadow.log cat shadow.data/hosts/client1/index.html

Notes

  1. Shadow doesn't support TCP_FASTOPEN so you must run Wget2 using the --no-tcp-fastopen option.

Nginx

Example

shadow.yaml

general: stop_time: 10s network: graph: type: 1_gbit_switch hosts: server: network_node_id: 0 processes: - path: nginx args: -c ../../../nginx.conf -p . start_time: 0s expected_final_state: running client1: &client_host network_node_id: 0 processes: - path: curl args: -s server start_time: 2s client2: *client_host client3: *client_host

nginx.conf

error_log stderr; # shadow wants to run nginx in the foreground daemon off; # shadow doesn't support some syscalls that nginx uses to set up and control # worker child processes. # https://github.com/shadow/shadow/issues/3174 master_process off; worker_processes 0; # don't use the system pid file pid nginx.pid; events { # we're not using any workers, so this is the maximum number # of simultaneous connections we can support worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; # shadow does not support sendfile() sendfile off; access_log off; server { listen 80; location / { root /var/www/html; index index.nginx-debian.html; } } }
rm -rf shadow.data; shadow shadow.yaml > shadow.log cat shadow.data/hosts/client1/curl.1000.stdout

Notes

  1. Shadow currently doesn't support some syscalls that nginx uses to set up and control worker child processes, so you must disable additional processes using master_process off and worker_processes 0. See https://github.com/shadow/shadow/issues/3174.

  2. Shadow doesn't support sendfile() so you must disable it using sendfile off.

iPerf 2

Example

general: stop_time: 10s network: graph: type: 1_gbit_switch hosts: server: network_node_id: 0 processes: - path: iperf args: -s start_time: 0s expected_final_state: running client: network_node_id: 0 processes: - path: iperf args: -c server -t 5 start_time: 2s
rm -rf shadow.data; shadow shadow.yaml > shadow.log

Notes

  1. You must use an iPerf 2 version >= 2.1.1. Older versions of iPerf have a no-syscall busy loop that is incompatible with Shadow.

iPerf 3

Example

general: stop_time: 10s model_unblocked_syscall_latency: true network: graph: type: 1_gbit_switch hosts: server: network_node_id: 0 processes: - path: iperf3 args: -s --bind 0.0.0.0 start_time: 0s # Tell shadow to expect this process to still be running at the end of the # simulation. expected_final_state: running client: network_node_id: 0 processes: - path: iperf3 args: -c server -t 5 start_time: 2s
rm -rf shadow.data; shadow shadow.yaml > shadow.log

Notes

  1. By default iPerf 3 servers bind to an IPv6 address, but Shadow doesn't support IPv6. Instead you need to bind the server to an IPv4 address such as 0.0.0.0.

  2. The iPerf 3 server exits with a non-zero error code and the message "unable to start listener for connections: Address already in use" after the client disconnects. This is likely due to Shadow not supporting the SO_REUSEADDR socket option.

  3. iPerf 3 uses a busy loop that is incompatible with Shadow and will cause Shadow to deadlock. A workaround is to use the model_unblocked_syscall_latency option.

Jetty

Running Jetty with the http module works, but we haven't tested anything more than this.

Example

shadow.yaml

general: stop_time: 10s network: graph: type: 1_gbit_switch hosts: server: network_node_id: 0 processes: - path: java args: -jar ../../../jetty-home-12.0.12/start.jar jetty.http.port=80 --modules=http expected_final_state: running client1: &client_host network_node_id: 0 processes: - path: curl args: -s server start_time: 2s client2: *client_host client3: *client_host
if [ ! -d jetty-home-12.0.12/ ]; then wget https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-home/12.0.12/jetty-home-12.0.12.zip echo "2dc2c60a8a3cb84df64134bed4df1c45598118e9a228604eaeb8b9b42d80bc07 jetty-home-12.0.12.zip" | sha256sum -c unzip -q jetty-home-12.0.12.zip && rm jetty-home-12.0.12.zip fi rm -rf shadow.data; shadow shadow.yaml > shadow.log cat shadow.data/hosts/client1/curl.1000.stdout

etcd (distributed key-value store)

Example

Example for etcd version 3.3.x.

general: stop_time: 30s network: graph: type: gml inline: | graph [ node [ id 0 host_bandwidth_down "20 Mbit" host_bandwidth_up "20 Mbit" ] edge [ source 0 target 0 latency "150 ms" packet_loss 0.01 ] ] hosts: server1: network_node_id: 0 processes: - path: etcd args: --name server1 --log-output=stdout --initial-cluster-token etcd-cluster-1 --initial-cluster 'server1=http://server1:2380,server2=http://server2:2380,server3=http://server3:2380' --listen-client-urls http://0.0.0.0:2379 --advertise-client-urls http://server1:2379 --listen-peer-urls http://0.0.0.0:2380 --initial-advertise-peer-urls http://server1:2380 expected_final_state: running - path: etcdctl args: set my-key my-value start_time: 10s server2: network_node_id: 0 processes: - path: etcd # each etcd peer must have a different start time # https://github.com/shadow/shadow/issues/2858 start_time: 1ms args: --name server2 --log-output=stdout --initial-cluster-token etcd-cluster-1 --initial-cluster 'server1=http://server1:2380,server2=http://server2:2380,server3=http://server3:2380' --listen-client-urls http://0.0.0.0:2379 --advertise-client-urls http://server2:2379 --listen-peer-urls http://0.0.0.0:2380 --initial-advertise-peer-urls http://server2:2380 expected_final_state: running - path: etcdctl args: get my-key start_time: 12s server3: network_node_id: 0 processes: - path: etcd start_time: 2ms args: --name server3 --log-output=stdout --initial-cluster-token etcd-cluster-1 --initial-cluster 'server1=http://server1:2380,server2=http://server2:2380,server3=http://server3:2380' --listen-client-urls http://0.0.0.0:2379 --advertise-client-urls http://server3:2379 --listen-peer-urls http://0.0.0.0:2380 --initial-advertise-peer-urls http://server3:2380 expected_final_state: running - path: etcdctl args: get my-key start_time: 12s
rm -rf shadow.data; shadow shadow.yaml > shadow.log cat shadow.data/hosts/*/etcdctl.*.stdout

Notes

  1. The etcd binary must not be statically linked. You can build a dynamically linked version by replacing CGO_ENABLED=0 with CGO_ENABLED=1 in etcd's scripts/build.sh and scripts/build_lib.sh scripts. The etcd packages included in the Debian and Ubuntu APT repositories are dynamically linked, so they can be used directly.

  2. Each etcd peer must be started at a different time since etcd uses the current time as an RNG seed. See issue #2858 for details.

  3. If using etcd version greater than 3.5.4, you must build etcd from source and comment out the keepalive period assignment as Shadow does not support this.

CTorrent and opentracker

Example

general: stop_time: 60s network: graph: type: 1_gbit_switch hosts: tracker: network_node_id: 0 processes: - path: opentracker # Tell shadow to expect this process to still be running at the end of the # simulation. expected_final_state: running uploader: network_node_id: 0 processes: - path: cp args: ../../../foo . start_time: 10s # Create the torrent file - path: ctorrent args: -t foo -s example.torrent -u http://tracker:6969/announce start_time: 11s # Serve the torrent - path: ctorrent args: example.torrent start_time: 12s expected_final_state: running downloader1: &downloader_host network_node_id: 0 processes: # Download and share the torrent - path: ctorrent args: ../uploader/example.torrent start_time: 30s expected_final_state: running downloader2: *downloader_host downloader3: *downloader_host downloader4: *downloader_host downloader5: *downloader_host
echo "bar" > foo rm -rf shadow.data; shadow shadow.yaml > shadow.log cat shadow.data/hosts/downloader1/foo

Notes

  1. Shadow must be run as a non-root user since opentracker will attempt to drop privileges if it detects that the effective user is root.

http-server

Example

general: stop_time: 10s model_unblocked_syscall_latency: true network: graph: type: 1_gbit_switch hosts: server: network_node_id: 0 processes: - path: node args: /usr/local/bin/http-server -p 80 -d start_time: 3s expected_final_state: running client: network_node_id: 0 processes: - path: curl args: -s server start_time: 5s
rm -rf shadow.data; shadow shadow.yaml > shadow.log cat shadow.data/hosts/client/curl.1000.stdout

Notes

  1. Either the Node.js runtime or http-server uses a busy loop that is incompatible with Shadow and will cause Shadow to deadlock. model_unblocked_syscall_latency works around this (see busy-loops).