├── agents ├── .gitignore ├── key_gen.c ├── cpp_rand.cc ├── loader.c ├── picohttpparser.h ├── CMakeLists.txt ├── stats.c ├── args.c ├── agent.c ├── http_app.cc ├── app_proto.c ├── memcache.c ├── redis.c ├── timestamping.c └── rand_gen.c ├── agent-manager ├── manager │ ├── .gitignore │ ├── __init__.py │ ├── __main__.py │ ├── lancet.py │ ├── agentcontroller.py │ ├── stats.py │ └── proto.py ├── .gitignore ├── requirements.txt ├── Makefile ├── README.md ├── lancet.py └── setup.py ├── coordinator ├── Makefile ├── deploy.go ├── config.go ├── stats.go ├── main.go └── proto.go ├── .gitignore ├── LICENSE ├── scripts ├── prepare_clients.sh ├── clean_up.sh └── deploy_lancet.sh ├── inc └── lancet │ ├── manager.h │ ├── key_gen.h │ ├── cpp_rand.h │ ├── memcache_bin.h │ ├── misc.h │ ├── error.h │ ├── tp_proto.h │ ├── timestamping.h │ ├── agent.h │ ├── stats.h │ ├── coord_proto.h │ ├── rand_gen.h │ └── app_proto.h ├── CMakeLists.txt ├── Makefile ├── external └── R2P2 │ └── CMakeLists.txt ├── .clang-format └── README.md /agents/.gitignore: -------------------------------------------------------------------------------- 1 | assets/ 2 | -------------------------------------------------------------------------------- /agent-manager/manager/.gitignore: -------------------------------------------------------------------------------- 1 | assets/ 2 | -------------------------------------------------------------------------------- /agent-manager/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | *.egg-info/ 3 | build/ 4 | -------------------------------------------------------------------------------- /agent-manager/requirements.txt: -------------------------------------------------------------------------------- 1 | posix_ipc 2 | scipy 3 | statsmodels -------------------------------------------------------------------------------- /coordinator/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: coordinator all 2 | .DEFAULT_GOAL := all 3 | 4 | all: coordinator 5 | 6 | coordinator: 7 | go build 8 | -------------------------------------------------------------------------------- /agent-manager/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: wheel 2 | 3 | .DEFAULT_GOAL := wheel 4 | 5 | current_dir= $(shell pwd) 6 | 7 | wheel: 8 | python3 setup.py bdist_wheel 9 | -------------------------------------------------------------------------------- /agent-manager/README.md: -------------------------------------------------------------------------------- 1 | # Building the binary assets 2 | 3 | The binary assets need to build at a specific path. 4 | Relative to this folder do the following 5 | 6 | ```shell 7 | mkdir manager/assets 8 | cd manager/assets 9 | cmake ../../../ # using additional flags as you'd like 10 | make # or ninja 11 | ``` 12 | 13 | Without this, the `setup.py` script fails to avoid silently missing these files. 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # editor cruft 2 | 3 | # vim can generate more than just swp and swo... 4 | [._]*.s[a-v][a-z] 5 | [._]*.sw[a-p] 6 | [._]s[a-rt-v][a-z] 7 | [._]ss[a-gi-z] 8 | [._]sw[a-p] 9 | tags 10 | 11 | # just this for emacs by default 12 | *~ 13 | 14 | # build cruft 15 | *.o 16 | *.so 17 | *.d 18 | *.cmd 19 | *.pyc 20 | 21 | # latex / misc cruft 22 | 23 | *.aux 24 | *.bbl 25 | *.blg 26 | *.log 27 | *-eps-converted-to.pdf 28 | latex-all.tex 29 | paper.out 30 | paper.pdf 31 | *.xml 32 | 33 | # specific cruft 34 | 35 | t_agent 36 | ll_read_agent 37 | coordinator/coordinator 38 | agents/l_rpclib_agent 39 | agents/l_tcp_agent 40 | agents/redis_loader 41 | agents/t_rpclib_agent 42 | agents/t_tcp_agent 43 | build/ 44 | .eggs/ 45 | 46 | # CLion default cmake build directories 47 | cmake-build-*/ 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /agent-manager/manager/__init__.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | -------------------------------------------------------------------------------- /scripts/prepare_clients.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # MIT License 4 | # 5 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | for hostname in `IFS=',' inarr=($1) && echo ${inarr[@]}`; do 26 | ssh $hostname "sudo apt-get install -y virtualenv" 27 | done 28 | -------------------------------------------------------------------------------- /scripts/clean_up.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # MIT License 4 | # 5 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | dirname=${2:-/tmp/`whoami`}/lancet 26 | for hostname in `IFS=',' inarr=($1) && echo ${inarr[@]}`; do 27 | ssh $hostname rm -rf $dirname 28 | done 29 | -------------------------------------------------------------------------------- /agent-manager/manager/__main__.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | # just an entry point for the setuptools script 23 | from .lancet import main as lancet_main 24 | 25 | # Need this entrypoint for lancet 26 | def main(): 27 | lancet_main() 28 | 29 | if __name__ == "__main__": 30 | main() -------------------------------------------------------------------------------- /inc/lancet/manager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #pragma once 26 | 27 | #include 28 | 29 | #define MANAGER_PORT 5001 30 | #define AGG_SAMPLE_SIZE 0x100000 31 | 32 | int should_load(void); 33 | int should_measure(void); 34 | int manager_run(void); 35 | int manager_init(int thread_count); 36 | -------------------------------------------------------------------------------- /agent-manager/lancet.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | #!/usr/bin/env python3 23 | 24 | from manager.lancet import main as lancet_main 25 | import logging 26 | logging.basicConfig() 27 | log = logging.getLogger(__file__) 28 | log.setLevel(logging.DEBUG) 29 | 30 | if __name__ == "__main__": 31 | lancet_main() 32 | else: 33 | log.error("This script should only be invoked directly, not imported") -------------------------------------------------------------------------------- /inc/lancet/key_gen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #pragma once 26 | 27 | #include 28 | 29 | #include 30 | 31 | struct key_gen { 32 | struct iovec *(*get_key)(struct key_gen *kg); 33 | struct rand_gen *key_size_gen; 34 | int key_count; 35 | struct iovec *keys; 36 | }; 37 | 38 | struct key_gen *init_key_gen(char *type, int key_count); 39 | -------------------------------------------------------------------------------- /inc/lancet/cpp_rand.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #pragma once 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | struct cpp_gen { 31 | void *d; 32 | void *gen; 33 | }; 34 | 35 | struct cpp_gen *new_normal_gen(); 36 | double get_normal_rand(struct cpp_gen *ng); 37 | struct cpp_gen *new_gamma_gen(double alpha, double beta); 38 | double get_gamma_rand(struct cpp_gen *gg); 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | -------------------------------------------------------------------------------- /scripts/deploy_lancet.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # MIT License 4 | # 5 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | for hostname in `IFS=',' inarr=($1) && echo ${inarr[@]}`; do 26 | dirname=${2:-/tmp/`whoami`}/lancet 27 | ssh $hostname mkdir -p $dirname 28 | scp agent-manager/dist/lancet_manager-0.1.0-py3-none-any.whl $hostname:$dirname 29 | ssh $hostname << EOF 30 | cd $dirname 31 | virtualenv -p python3 venv 32 | source venv/bin/activate && pip3 install $dirname/lancet_manager-0.1.0-py3-none-any.whl 33 | EOF 34 | done 35 | -------------------------------------------------------------------------------- /inc/lancet/memcache_bin.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #pragma once 26 | 27 | #define CMD_GET 0x00 28 | #define CMD_GETK 0x0c 29 | #define CMD_SET 0x01 30 | 31 | struct __attribute__((__packed__)) bmc_header { 32 | uint8_t magic; 33 | uint8_t opcode; 34 | uint16_t key_len; 35 | 36 | uint8_t extra_len; 37 | uint8_t data_type; 38 | union { 39 | uint16_t vbucket; // request use 40 | uint16_t status; // response use 41 | }; 42 | 43 | uint32_t body_len; 44 | uint32_t opaque; 45 | uint64_t version; 46 | }; 47 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | cmake_minimum_required(VERSION 3.12...3.13) 23 | 24 | project(lancet VERSION 1.0.0 25 | DESCRIPTION "Latency measurement tool" 26 | LANGUAGES C CXX) 27 | 28 | option(BUILD_R2P2 "Build with R2P2" OFF) 29 | option(R2P2_NIC_TS "Enaleble NIC timestamping for R2P2" OFF) 30 | 31 | if(BUILD_R2P2) 32 | add_subdirectory("${PROJECT_SOURCE_DIR}/external/R2P2") 33 | endif() 34 | 35 | add_library( lancet INTERFACE ) 36 | target_include_directories( lancet INTERFACE "${PROJECT_SOURCE_DIR}/inc" ) 37 | 38 | add_subdirectory(agents) 39 | -------------------------------------------------------------------------------- /inc/lancet/misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #pragma once 26 | 27 | #include 28 | 29 | static inline long time_us(void) 30 | { 31 | struct timeval tv; 32 | gettimeofday(&tv, NULL); 33 | return (long)tv.tv_sec * 1000000 + (long)tv.tv_usec; 34 | } 35 | 36 | static inline int64_t time_ns() 37 | { 38 | struct timespec ts; 39 | int r = clock_gettime(CLOCK_MONOTONIC, &ts); 40 | assert(r == 0); 41 | return (ts.tv_nsec + ts.tv_sec * 1e9); 42 | } 43 | 44 | static inline void time_ns_to_ts(struct timespec *ts) 45 | { 46 | int r = clock_gettime(CLOCK_MONOTONIC, ts); 47 | assert(r == 0); 48 | } 49 | 50 | static inline unsigned long rdtsc(void) 51 | { 52 | unsigned int a, d; 53 | asm volatile("rdtsc" : "=a"(a), "=d"(d)); 54 | return ((unsigned long)a) | (((unsigned long)d) << 32); 55 | } 56 | -------------------------------------------------------------------------------- /inc/lancet/error.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #pragma once 26 | 27 | #include 28 | #include 29 | 30 | #define lancet_fprintf(f, ...) \ 31 | { \ 32 | char hostname[64]; \ 33 | gethostname(hostname, 64); \ 34 | fprintf(f, "[%s] ", hostname); \ 35 | fprintf(f, __VA_ARGS__); \ 36 | } 37 | 38 | #define lancet_perror(...) \ 39 | { \ 40 | char hostname[64]; \ 41 | gethostname(hostname, 64); \ 42 | fprintf(stderr, "[%s] ", hostname); \ 43 | perror(__VA_ARGS__); \ 44 | } 45 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | .PHONY: coordinator agents style manager manager_r2p2 all clean 24 | 25 | .DEFAULT_GOAL := all 26 | all: agents 27 | ${MAKE} manager 28 | ${MAKE} coordinator 29 | 30 | current_dir= $(shell pwd) 31 | 32 | manager: 33 | ${MAKE} -C ${current_dir}/agent-manager 34 | 35 | coordinator: 36 | ${MAKE} -C coordinator/ 37 | 38 | agents: clean 39 | mkdir agents/assets 40 | cmake -DCMAKE_BUILD_TYPE=ReleaseAssert -S . -B agents/assets 41 | cmake --build agents/assets 42 | 43 | agents_r2p2: clean 44 | mkdir agents/assets 45 | cmake -DBUILD_R2P2=ON -DR2P2_ROOT=${R2P2} -S . -B agents/assets 46 | cmake --build agents/assets 47 | 48 | agents_r2p2_nic_ts: clean 49 | mkdir agents/assets 50 | cmake -DBUILD_R2P2=ON -DR2P2_ROOT=${R2P2} -DR2P2_NIC_TS=ON -S . -B agents/assets 51 | cmake --build agents/assets 52 | 53 | deploy: clean_remote 54 | scripts/deploy_lancet.sh ${HOSTS} ${DIR} 55 | 56 | prepare_clients: 57 | scripts/prepare_clients.sh ${HOSTS} 58 | 59 | clean_remote: 60 | scripts/clean_up.sh ${HOSTS} ${DIR} 61 | 62 | clean: 63 | rm -rf agents/assets 64 | 65 | style: 66 | clang-format -i -style=file agents/*.c 67 | clang-format -i -style=file inc/lancet/*.h 68 | -------------------------------------------------------------------------------- /agents/key_gen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | static struct iovec *uniform_get_key(struct key_gen *kg) 35 | { 36 | long int idx = rand(); 37 | return &kg->keys[idx % kg->key_count]; 38 | } 39 | 40 | static void generate_keys(struct key_gen *kg) 41 | { 42 | 43 | int i; 44 | long key_size; 45 | 46 | for (i = 0; i < kg->key_count; i++) { 47 | key_size = lround( 48 | kg->key_size_gen->inv_cdf(kg->key_size_gen, i / kg->key_count)); 49 | kg->keys[i].iov_base = calloc(key_size + 1, sizeof(char)); 50 | kg->keys[i].iov_len = key_size; 51 | snprintf(kg->keys[i].iov_base, (int)key_size + 1, "%0*d", (int)key_size, 52 | i); 53 | } 54 | } 55 | 56 | struct key_gen *init_key_gen(char *type, int key_count) 57 | { 58 | struct key_gen *kg = malloc(sizeof(struct key_gen)); 59 | struct rand_gen *size_gen; 60 | 61 | size_gen = init_rand(type); 62 | 63 | kg->key_count = key_count; 64 | kg->get_key = uniform_get_key; 65 | kg->key_size_gen = size_gen; 66 | kg->keys = malloc(kg->key_count * sizeof(struct iovec)); 67 | 68 | generate_keys(kg); 69 | return kg; 70 | } 71 | -------------------------------------------------------------------------------- /inc/lancet/tp_proto.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | /* 26 | * Interface for the communication protocol e.g. TCP, UDP, R2P2 etc 27 | */ 28 | #pragma once 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | struct transport_protocol { 37 | void (*tp_main[AGENT_NR])(void); 38 | }; 39 | 40 | struct transport_protocol *init_tcp(void); 41 | #ifdef ENABLE_R2P2 42 | struct transport_protocol *init_r2p2(void); 43 | #endif 44 | struct transport_protocol *init_udp(void); 45 | struct transport_protocol *init_tls(void); 46 | 47 | /* 48 | * TCP specific 49 | */ 50 | #define MAX_PAYLOAD 16384 51 | struct tcp_connection { 52 | uint32_t fd; 53 | uint16_t idx; 54 | uint16_t closed; 55 | uint16_t pending_reqs; 56 | uint16_t buffer_idx; 57 | char buffer[MAX_PAYLOAD]; 58 | }; 59 | struct byte_req_pair handle_response(struct tcp_connection *conn); 60 | 61 | /* 62 | * UDP specific 63 | */ 64 | #define UDP_MAX_PAYLOAD 1500 65 | struct udp_socket { 66 | uint32_t fd; 67 | uint32_t taken; 68 | struct timespec tx_timestamp; 69 | struct timespec rx_timestamp; 70 | char buffer[UDP_MAX_PAYLOAD]; 71 | }; 72 | 73 | /* 74 | * TLS specific 75 | */ 76 | struct tls_connection { 77 | struct tcp_connection conn; 78 | SSL *ssl; 79 | }; 80 | -------------------------------------------------------------------------------- /agents/cpp_rand.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | extern "C" { 30 | struct cpp_gen *new_normal_gen() { 31 | std::random_device rd {}; 32 | std::mt19937 *gen = new std::mt19937(rd()); 33 | std::normal_distribution *d = new std::normal_distribution(); 34 | struct cpp_gen *ng = new struct cpp_gen; 35 | ng->d = d; 36 | ng->gen = gen; 37 | 38 | return ng; 39 | } 40 | 41 | double get_normal_rand(struct cpp_gen *ng) { 42 | std::normal_distribution *d = (std::normal_distribution *)ng->d; 43 | std::mt19937 *gen = (std::mt19937 *)ng->gen; 44 | 45 | return (*d)(*gen); 46 | } 47 | 48 | struct cpp_gen *new_gamma_gen(double alpha, double beta) { 49 | std::random_device rd {}; 50 | std::mt19937 *gen = new std::mt19937(rd()); 51 | //std::default_random_engine *gen = new std::default_random_engine; 52 | 53 | std::gamma_distribution *d = new std::gamma_distribution(alpha, beta); 54 | struct cpp_gen *gg = new struct cpp_gen; 55 | gg->d = d; 56 | gg->gen = gen; 57 | return gg; 58 | 59 | } 60 | double get_gamma_rand(struct cpp_gen *gg) { 61 | std::gamma_distribution *d = (std::gamma_distribution *)gg->gen; 62 | std::mt19937 *gen = (std::mt19937 *)gg->gen; 63 | return (*d)(*gen); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /external/R2P2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | message(STATUS "Importing R2P2") 23 | cmake_minimum_required(VERSION 3.12...3.13) 24 | 25 | # TODO not sure if this is the best way to do this. have to double-specify R2P2_ROOT AND BUILD_R2P2 26 | if(NOT R2P2_ROOT) 27 | message(FATAL_ERROR "importing R2P2 and R2P2_ROOT variable unspecified") 28 | endif() 29 | 30 | if(R2P2_NIC_TS) 31 | set(WITH_TIMESTAMPING "-DWITH_TIMESTAMPING") 32 | endif() 33 | 34 | message(STATUS "WITH TIMESTAMPING: " ${WITH_TIMESTAMPING}) 35 | 36 | project(r2p2 VERSION 1.0.0 37 | DESCRIPTION "R2P2" 38 | LANGUAGES C CXX) 39 | message(STATUS "provided r2p2 root: ${R2P2_ROOT}") 40 | 41 | if(IS_DIRECTORY ${R2P2_ROOT}) 42 | get_filename_component(R2P2_ROOT_DIR ${R2P2_ROOT} ABSOLUTE BASE_DIR ${CMAKE_CURRENT_BINARY_DIR}) 43 | message(STATUS "R2P2_ROOT_DIR: ${R2P2_ROOT_DIR}") 44 | else() 45 | message(FATAL_ERROR "R2P2_ROOT does not point to a directory: ${R2P2_ROOT}") 46 | endif() 47 | 48 | file(GLOB r2p2_sources ${R2P2_ROOT_DIR}/r2p2-common.c ${R2P2_ROOT_DIR}/mempool.c ${R2P2_ROOT_DIR}/cfg.c ${R2P2_ROOT_DIR}/linux-backend.c ${R2P2_ROOT_DIR}/timestamping.c) 49 | 50 | add_library( r2p2 OBJECT ${r2p2_sources} ) # Not sure if this or static 51 | target_include_directories( r2p2 PUBLIC ${R2P2_ROOT_DIR}/inc) 52 | target_compile_features( r2p2 PUBLIC c_std_11 ) 53 | target_compile_options(r2p2 PUBLIC "-DLINUX") # PUBLIC = it will propagate as a compile option to all dependencies, too 54 | target_compile_options(r2p2 PUBLIC ${WITH_TIMESTAMPING}) 55 | -------------------------------------------------------------------------------- /inc/lancet/timestamping.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #pragma once 26 | 27 | #include 28 | 29 | struct timestamp_info { 30 | struct timespec time; 31 | uint32_t optid; 32 | }; 33 | 34 | /* 35 | * There is one such structure per connection 36 | */ 37 | struct pending_tx_timestamps { 38 | uint32_t tx_byte_counter; // increment this on every write/send by the 39 | // number of bytes (TCP) */ 40 | uint32_t head; // waiting for timestamps 41 | uint32_t tail; // timestamp received 42 | uint32_t consumed; // matched with reply 43 | struct timestamp_info *pending; 44 | }; 45 | 46 | int enable_nic_timestamping(char *if_name); 47 | int disable_nic_timestamping(char *if_name); 48 | int sock_enable_timestamping(int fd); 49 | ssize_t timestamp_recv(int sockfd, void *buf, size_t len, int flags, 50 | struct timestamp_info *last_rx_time); 51 | int get_tx_timestamp(int sockfd, struct pending_tx_timestamps *tx_timestamps); 52 | int udp_get_tx_timestamp(int sockfd, struct timespec *tx_timestamp); 53 | void add_pending_tx_timestamp(struct pending_tx_timestamps *tx_timestamps, 54 | uint32_t bytes); 55 | struct timestamp_info * 56 | pop_pending_tx_timestamps(struct pending_tx_timestamps *tx_timestamps); 57 | int timespec_diff(struct timespec *res, struct timespec *a, struct timespec *b); 58 | /* 59 | * Used only in userspace symmetric timestamping 60 | */ 61 | void push_complete_tx_timestamp(struct pending_tx_timestamps *tx_timestamps, 62 | struct timespec *to_add); 63 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlinesLeft: false 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: None 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: false 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: true 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | BreakBeforeBinaryOperators: None 36 | BreakBeforeBraces: Linux 37 | BreakBeforeTernaryOperators: true 38 | BreakConstructorInitializersBeforeComma: false 39 | ColumnLimit: 80 40 | CommentPragmas: '^ IWYU pragma:' 41 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 42 | ConstructorInitializerIndentWidth: 4 43 | ContinuationIndentWidth: 4 44 | Cpp11BracedListStyle: true 45 | DerivePointerAlignment: false 46 | DisableFormat: false 47 | ExperimentalAutoDetectBinPacking: false 48 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 49 | IncludeCategories: 50 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 51 | Priority: 2 52 | - Regex: '^(<|"(gtest|isl|json)/)' 53 | Priority: 3 54 | - Regex: '.*' 55 | Priority: 1 56 | IndentCaseLabels: false 57 | IndentWidth: 4 58 | IndentWrappedFunctionNames: false 59 | KeepEmptyLinesAtTheStartOfBlocks: true 60 | MacroBlockBegin: '' 61 | MacroBlockEnd: '' 62 | MaxEmptyLinesToKeep: 1 63 | NamespaceIndentation: None 64 | ObjCBlockIndentWidth: 4 65 | ObjCSpaceAfterProperty: false 66 | ObjCSpaceBeforeProtocolList: true 67 | PenaltyBreakBeforeFirstCallParameter: 19 68 | PenaltyBreakComment: 300 69 | PenaltyBreakFirstLessLess: 120 70 | PenaltyBreakString: 1000 71 | PenaltyExcessCharacter: 1000000 72 | PenaltyReturnTypeOnItsOwnLine: 60 73 | PointerAlignment: Right 74 | ReflowComments: true 75 | SortIncludes: true 76 | SpaceAfterCStyleCast: false 77 | SpaceBeforeAssignmentOperators: true 78 | SpaceBeforeParens: ControlStatements 79 | SpaceInEmptyParentheses: false 80 | SpacesBeforeTrailingComments: 1 81 | SpacesInAngles: false 82 | SpacesInContainerLiterals: true 83 | SpacesInCStyleCastParentheses: false 84 | SpacesInParentheses: false 85 | SpacesInSquareBrackets: false 86 | Standard: Cpp11 87 | TabWidth: 4 88 | UseTab: Always 89 | ... 90 | -------------------------------------------------------------------------------- /inc/lancet/agent.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #pragma once 26 | 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | #define MAX_THREADS 16 33 | 34 | struct host_tuple { 35 | uint32_t ip; 36 | uint16_t port; 37 | }; 38 | 39 | enum agent_type { 40 | THROUGHPUT_AGENT, 41 | LATENCY_AGENT, 42 | SYMMETRIC_NIC_TIMESTAMP_AGENT, 43 | SYMMETRIC_AGENT, 44 | AGENT_NR, 45 | }; 46 | 47 | enum transport_protocol_type { 48 | TCP, 49 | R2P2, 50 | UDP, 51 | TLS, 52 | }; 53 | 54 | struct agent_config { 55 | int thread_count; 56 | int conn_count; 57 | struct host_tuple targets[8192]; 58 | int target_count; 59 | enum agent_type atype; 60 | enum transport_protocol_type tp_type; 61 | struct transport_protocol *tp; 62 | struct rand_gen *idist; 63 | struct application_protocol *app_proto; 64 | char if_name[64]; 65 | int per_conn_reqs; 66 | }; 67 | 68 | struct __attribute__((packed)) agent_control_block { 69 | struct rand_gen idist; // it should be the first field 70 | int should_load; 71 | int should_measure; 72 | int thread_count; 73 | int agent_type; 74 | uint32_t per_thread_samples; 75 | double sampling; 76 | int conn_open; 77 | }; 78 | 79 | int should_load(void); 80 | int should_measure(void); 81 | struct agent_config *parse_arguments(int argc, char **argv); 82 | int get_conn_count(void); 83 | int get_thread_count(void); 84 | int get_target_count(void); 85 | struct application_protocol * get_app_proto(void); 86 | struct host_tuple *get_targets(void); 87 | long get_ia(void); 88 | enum agent_type get_agent_type(void); 89 | int get_agent_tid(void); 90 | uint32_t get_per_thread_samples(void); 91 | double get_sampling_rate(void); 92 | char *get_if_name(void); 93 | int get_max_pending_reqs(void); 94 | void set_conn_open(int val); 95 | struct request *prepare_request(void); 96 | struct byte_req_pair process_response(char *buf, int size); 97 | 98 | extern pthread_barrier_t conn_open_barrier; 99 | -------------------------------------------------------------------------------- /agent-manager/setup.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | import logging 23 | import os 24 | import shutil 25 | logging.basicConfig() 26 | log = logging.getLogger(__file__) 27 | log.setLevel(logging.DEBUG) 28 | 29 | try: 30 | import pathlib 31 | except ImportError: 32 | log.error("Must use Python 3!") 33 | raise 34 | 35 | from setuptools import setup, find_packages 36 | manager_path = (pathlib.Path(__file__).parent / "manager").absolute() 37 | assert manager_path.exists() and manager_path.is_dir(), "manager path is missing at {}".format(manager_path.absolute()) 38 | cmake_build_path = pathlib.Path("../agents/assets").absolute() 39 | 40 | shutil.rmtree("manager/assets", ignore_errors=True) 41 | os.mkdir("manager/assets") 42 | target = cmake_build_path / "agents/agent" 43 | shutil.copyfile(target.absolute().as_posix(), "manager/assets/agent") 44 | shutil.copymode(target.absolute().as_posix(), "manager/assets/agent") 45 | target = cmake_build_path / "agents/librand.so" 46 | shutil.copyfile(target.absolute().as_posix(), "manager/assets/librand.so") 47 | shutil.copymode(target.absolute().as_posix(), "manager/assets/librand.so") 48 | 49 | if not cmake_build_path.exists(): 50 | raise Exception("binary assets folder needs to be built using the CMake build system. See the README") 51 | 52 | setup( 53 | name="lancet-manager", 54 | version="0.1.0", 55 | url="https://github.com/epfl-dcsl/lancet-tool", 56 | author="Marios Kogias", 57 | author_email="marios.kogias@epfl.ch", 58 | packages=find_packages(), 59 | zip_safe=False, 60 | install_requires=[ 61 | "scipy", 62 | "posix_ipc", 63 | "statsmodels" 64 | ], 65 | package_data={'manager': ["assets/agent", 66 | "assets/librand.so"]}, 67 | include_package_data=True, 68 | entry_points = { 69 | 'console_scripts': ['lancet=manager.lancet:main'] 70 | }, 71 | python_requires=">=3", # TODO not totally sure about the language requirements here 72 | setup_requires=['wheel'], 73 | ) 74 | -------------------------------------------------------------------------------- /inc/lancet/stats.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #pragma once 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | #define MAX_PER_THREAD_SAMPLES 131072 33 | #define MAX_PER_THREAD_TX_SAMPLES 4096 34 | 35 | struct byte_req_pair { 36 | uint64_t bytes; 37 | uint64_t reqs; 38 | }; 39 | 40 | struct __attribute__((packed)) tx_samples { 41 | uint32_t count; 42 | struct timespec samples[MAX_PER_THREAD_SAMPLES]; 43 | }; 44 | 45 | struct __attribute__((packed)) throughput_stats { 46 | struct byte_req_pair rx; 47 | struct byte_req_pair tx; 48 | }; 49 | 50 | struct __attribute__((packed)) lat_sample { 51 | uint64_t nsec; 52 | struct timespec tx; // used for iid-ness checks 53 | }; 54 | 55 | struct __attribute__((packed)) latency_stats { 56 | struct throughput_stats th_s; 57 | uint32_t inc_idx; // increment idx 58 | struct lat_sample samples[MAX_PER_THREAD_SAMPLES]; 59 | }; 60 | 61 | union stats { 62 | struct throughput_stats th_s; 63 | struct latency_stats lt_s; 64 | }; 65 | 66 | int init_per_thread_stats(void); 67 | int add_throughput_tx_sample(struct byte_req_pair tx_p); 68 | int add_throughput_rx_sample(struct byte_req_pair rx_p); 69 | int add_tx_timestamp(struct timespec *tx_ts); 70 | int add_latency_sample(long diff, struct timespec *tx); 71 | // void clear_stats(union stats *stats); 72 | // void compute_latency_percentiles(struct latency_stats *lt_s); 73 | // void compute_latency_percentiles_ci(struct latency_stats *lt_s); 74 | // void set_per_thread_samples(int samples, double sr); 75 | // uint32_t compute_convergence(struct lat_sample *samples, int size); 76 | // void clear_all_stats(void); 77 | // void aggregate_throughput_stats(union stats *agg_stats); 78 | // void aggregate_latency_samples(union stats *agg_stats); 79 | // int check_ia(void); 80 | // void collect_reference_ia(struct rand_gen *gen); 81 | // void init_reference_ia_dist(struct rand_gen *gen); 82 | // void set_reference_load(uint32_t load); 83 | // double check_iid(struct latency_stats *lt_s); 84 | -------------------------------------------------------------------------------- /inc/lancet/coord_proto.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #pragma once 26 | 27 | #include 28 | 29 | /* 30 | * Message types 31 | */ 32 | enum { 33 | START_LOAD = 0, 34 | START_MEASURE, 35 | REPORT_REQ, 36 | REPLY, 37 | TERMINATE, 38 | CONN_OPEN, 39 | }; 40 | 41 | /* 42 | * Types of REPORT_REQ messages 43 | */ 44 | enum { 45 | REPORT_THROUGHPUT = 0, 46 | REPORT_LATENCY, 47 | }; 48 | 49 | /* 50 | * Types of REPLY messages 51 | */ 52 | enum { 53 | REPLY_ACK = 0, 54 | REPLY_STATS_THROUGHPUT, 55 | REPLY_STATS_LATENCY, 56 | REPLY_CONVERGENCE, 57 | REPLY_IA_COMP, 58 | REPLY_IID, 59 | // REPLY_KV_STATS etc... 60 | }; 61 | 62 | struct __attribute__((__packed__)) msg_hdr { 63 | uint32_t MessageType; 64 | uint32_t MessageLength; 65 | }; 66 | 67 | struct __attribute__((__packed__)) msg1 { 68 | struct msg_hdr Hdr; 69 | uint32_t Info; 70 | }; 71 | 72 | struct __attribute__((__packed__)) msg2 { 73 | struct msg_hdr Hdr; 74 | uint32_t Info1; 75 | uint32_t Info2; 76 | }; 77 | 78 | // the reply payload starts with the reply type which is a uint32_t 79 | struct __attribute__((__packed__)) throughput_reply { 80 | uint64_t Rx_bytes; 81 | uint64_t Tx_bytes; 82 | uint64_t Req_count; 83 | uint64_t Duration; 84 | uint64_t CorrectIAD; // to avoid padding 85 | }; 86 | 87 | struct __attribute__((__packed__)) latency_reply { 88 | struct throughput_reply Th_data; 89 | uint64_t Avg_lat; 90 | uint64_t P50_i; 91 | uint64_t P50; 92 | uint64_t P50_k; 93 | uint64_t P90_i; 94 | uint64_t P90; 95 | uint64_t P90_k; 96 | uint64_t P95_i; 97 | uint64_t P95; 98 | uint64_t P95_k; 99 | uint64_t P99_i; 100 | uint64_t P99; 101 | uint64_t P99_k; 102 | uint64_t P999_i; 103 | uint64_t P999; 104 | uint64_t P999_k; 105 | uint64_t P9999_i; 106 | uint64_t P9999; 107 | uint64_t P9999_k; 108 | uint64_t P99999_i; 109 | uint64_t P99999; 110 | uint64_t P99999_k; 111 | uint64_t P999999_i; 112 | uint64_t P999999; 113 | uint64_t P999999_k; 114 | uint32_t ToReduceSampling; 115 | uint8_t IsIid; 116 | uint8_t IsStationary; 117 | }; 118 | -------------------------------------------------------------------------------- /agents/loader.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include 40 | #include 41 | 42 | int open_connection(const char *host, const char *port) 43 | { 44 | int fd; 45 | struct in_addr ip; 46 | uint16_t nport = strtol(port, NULL, 10); 47 | struct sockaddr_in addr; 48 | 49 | fd = socket(AF_INET, SOCK_STREAM, 0); 50 | if (fd < 0) { 51 | perror("socket"); 52 | return -1; 53 | } 54 | 55 | if (inet_aton(host, &ip) == 0) { 56 | perror("inet_aton"); 57 | return -1; 58 | } 59 | 60 | addr.sin_port = htons(nport); 61 | addr.sin_addr = ip; 62 | addr.sin_family = AF_INET; 63 | 64 | if (connect(fd, (struct sockaddr *)&addr, sizeof(addr))) { 65 | perror("connect"); 66 | return -1; 67 | } 68 | 69 | return fd; 70 | } 71 | 72 | int main(int argc, char **argv) 73 | { 74 | int sock, len, key_count; 75 | struct application_protocol *proto; 76 | struct request req; 77 | struct iovec received; 78 | char buf[1024]; 79 | struct byte_req_pair pair; 80 | 81 | if (argc != 4) { 82 | fprintf(stderr, "usage: %s \n", argv[0]); 83 | return EXIT_FAILURE; 84 | } 85 | 86 | proto = init_app_proto(argv[1]); 87 | 88 | assert(proto != NULL); 89 | 90 | sock = open_connection(argv[2], argv[3]); 91 | assert(sock > 0); 92 | 93 | key_count = kv_get_key_count(proto); 94 | 95 | for (int i = 0; i < key_count; i++) { 96 | if (create_request(proto, &req)) { 97 | fprintf(stderr, "failed to create request %d\n", i); 98 | return EXIT_FAILURE; 99 | } 100 | 101 | len = writev(sock, req.iovs, req.iov_cnt); 102 | assert(len > 0); 103 | 104 | len = read(sock, buf, 1024); 105 | received.iov_base = buf; 106 | received.iov_len = len; 107 | pair = consume_response(proto, &received); 108 | assert(pair.reqs >= 1); 109 | } 110 | 111 | return EXIT_SUCCESS; 112 | } 113 | -------------------------------------------------------------------------------- /inc/lancet/rand_gen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #pragma once 26 | 27 | #include 28 | #include 29 | 30 | enum { 31 | GEN_OTHER = 0, 32 | GEN_FIXED, 33 | GEN_EXP, 34 | }; 35 | 36 | struct param_1 { 37 | double a; 38 | }; 39 | 40 | struct param_2 { 41 | double a; 42 | double b; 43 | }; 44 | 45 | struct param_3 { 46 | double a; 47 | double b; 48 | double c; 49 | }; 50 | 51 | struct param_lss { 52 | double loc; 53 | double scale; 54 | double shape; 55 | }; 56 | 57 | struct bimodal_param { 58 | double low; 59 | double up; 60 | double prob; 61 | }; 62 | 63 | struct lognorm_params { 64 | double sigma; 65 | double mu; 66 | struct cpp_gen *ng; 67 | }; 68 | 69 | struct gamma_params { 70 | struct cpp_gen *gg; 71 | }; 72 | 73 | union rand_params { 74 | struct param_1 p1; 75 | struct param_2 p2; 76 | struct param_3 p3; 77 | struct param_lss lss; 78 | struct bimodal_param bp; 79 | struct lognorm_params lgp; 80 | struct gamma_params gp; 81 | }; 82 | 83 | struct __attribute__((packed)) rand_gen { 84 | /* Void pointer to hold any relevant data for each distribution */ 85 | int gen_type; 86 | /* Set distribution's average */ 87 | void (*set_avg)(struct rand_gen *gen, double avg); 88 | /* 89 | * Inverse CDF takes a number in [0,1] (cummulative probability) and 90 | * returns the corresponding number 91 | */ 92 | double (*inv_cdf)(struct rand_gen *gen, double y); 93 | /* Set only if the random + inv_cdf pattern is not followed */ 94 | double (*generate)(struct rand_gen *generator); 95 | union rand_params params; 96 | }; 97 | 98 | /* Initialise random generator */ 99 | struct rand_gen *init_rand(char *gen_type); 100 | /* Set the average */ 101 | #ifdef __cplusplus 102 | extern "C" 103 | #endif 104 | void 105 | set_avg_ext(struct rand_gen *gen, double avg); 106 | 107 | static inline void set_avg(struct rand_gen *gen, double avg) 108 | { 109 | return gen->set_avg(gen, avg); 110 | } 111 | 112 | /* Generate a random number */ 113 | static inline double generate(struct rand_gen *generator) 114 | { 115 | double y; 116 | 117 | if (generator->generate) { 118 | return generator->generate(generator); 119 | } else { 120 | y = drand48(); 121 | return generator->inv_cdf(generator, y); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /inc/lancet/app_proto.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #pragma once 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | // A pointer to a random char for kv-store vals 38 | #define MAX_VAL_SIZE 2 * 1024 * 1024 39 | extern char random_char[MAX_VAL_SIZE]; 40 | extern __thread void *per_thread_arg; 41 | 42 | #define MAX_IOVS 64 43 | struct request { 44 | void *meta; 45 | int iov_cnt; 46 | struct iovec iovs[MAX_IOVS]; 47 | }; 48 | 49 | enum app_proto_type { 50 | PROTO_ECHO, 51 | PROTO_SYNTHETIC, 52 | PROTO_REDIS, 53 | PROTO_REDIS_YCSBE, 54 | PROTO_MEMCACHED_BIN, 55 | PROTO_MEMCACHED_ASCII, 56 | PROTO_HTTP, 57 | PROTO_STSS, 58 | }; 59 | 60 | struct application_protocol { 61 | enum app_proto_type type; 62 | void *arg; 63 | int (*create_request)(struct application_protocol *proto, 64 | struct request *req); 65 | struct byte_req_pair (*consume_response)(struct application_protocol *proto, 66 | struct iovec *response); 67 | }; 68 | 69 | struct application_protocol *init_app_proto(char *proto); 70 | static inline int create_request(struct application_protocol *proto, 71 | struct request *req) 72 | { 73 | return proto->create_request(proto, req); 74 | }; 75 | 76 | static inline struct byte_req_pair 77 | consume_response(struct application_protocol *proto, struct iovec *response) 78 | { 79 | return proto->consume_response(proto, response); 80 | }; 81 | 82 | struct kv_info { 83 | struct key_gen *key; 84 | struct rand_gen *val_len; 85 | struct rand_gen *key_sel; 86 | double get_ratio; 87 | }; 88 | 89 | static inline int kv_get_key_count(struct application_protocol *proto) 90 | { 91 | struct kv_info *info; 92 | 93 | info = (struct kv_info *)proto->arg; 94 | return info->key->key_count; 95 | } 96 | 97 | /* 98 | * Redis 99 | * redis_____ 100 | */ 101 | int redis_init(char *proto, struct application_protocol *app_proto); 102 | 103 | /* 104 | * Memcached 105 | * _____ 106 | */ 107 | int memcache_init(char *proto, struct application_protocol *app_proto); 108 | 109 | /* 110 | * HTTP 111 | */ 112 | int http_proto_init(char *proto, struct application_protocol *app_proto); 113 | 114 | #ifdef __cplusplus 115 | }; 116 | #endif 117 | -------------------------------------------------------------------------------- /agents/picohttpparser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, 3 | * Shigeo Mitsunari 4 | * 5 | * The software is licensed under either the MIT License (below) or the Perl 6 | * license. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef picohttpparser_h 28 | #define picohttpparser_h 29 | 30 | #include 31 | 32 | #ifdef _MSC_VER 33 | #define ssize_t intptr_t 34 | #endif 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | /* contains name and value of a header (name == NULL if is a continuing line 41 | * of a multiline header */ 42 | struct phr_header { 43 | const char *name; 44 | size_t name_len; 45 | const char *value; 46 | size_t value_len; 47 | }; 48 | 49 | /* returns number of bytes consumed if successful, -2 if request is partial, 50 | * -1 if failed */ 51 | int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, 52 | int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len); 53 | 54 | /* ditto */ 55 | int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len, 56 | struct phr_header *headers, size_t *num_headers, size_t last_len); 57 | 58 | /* ditto */ 59 | int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len); 60 | 61 | /* should be zero-filled before start */ 62 | struct phr_chunked_decoder { 63 | size_t bytes_left_in_chunk; /* number of bytes left in current chunk */ 64 | char consume_trailer; /* if trailing headers should be consumed */ 65 | char _hex_count; 66 | char _state; 67 | }; 68 | 69 | /* the function rewrites the buffer given as (buf, bufsz) removing the chunked- 70 | * encoding headers. When the function returns without an error, bufsz is 71 | * updated to the length of the decoded data available. Applications should 72 | * repeatedly call the function while it returns -2 (incomplete) every time 73 | * supplying newly arrived data. If the end of the chunked-encoded data is 74 | * found, the function returns a non-negative number indicating the number of 75 | * octets left undecoded at the tail of the supplied buffer. Returns -1 on 76 | * error. 77 | */ 78 | ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz); 79 | 80 | /* returns if the chunked decoder is in middle of chunked data */ 81 | int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder); 82 | 83 | #ifdef __cplusplus 84 | } 85 | #endif 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /agents/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | cmake_minimum_required( VERSION 3.12...3.13 ) 23 | 24 | project( Agent 25 | VERSION 1.0.0 26 | DESCRIPTION "Agent for Lancet") 27 | 28 | set(CMAKE_C_FLAGS_RELEASEASSERT "-O3") 29 | 30 | # first, lets find the flags we need 31 | set(THREADS_PREFER_PTHREAD_FLAG ON) 32 | find_package(Threads REQUIRED) 33 | find_library(LIBRT rt REQUIRED) 34 | find_library(LIBM m REQUIRED) 35 | find_library(LIB_SSL ssl REQUIRED) 36 | find_library(LIB_CRYPTO crypto REQUIRED) 37 | 38 | find_library(LIBCONFIG config) 39 | if(NOT LIBM) 40 | message(FATAL_ERROR "libconfig not found") 41 | endif() 42 | 43 | # Setting this variable because both librand.so and agent link them in 44 | set(RAND_SRCS "rand_gen.c" "cpp_rand.cc") 45 | set(COMMON_CFLAGS "-Wall" ) 46 | 47 | # This is used by the Controller to do fast math 48 | # SHARED means that PIC is on automatically 49 | add_library( rand SHARED ) 50 | target_sources( rand 51 | PRIVATE ${RAND_SRCS} ) 52 | target_link_libraries( rand PRIVATE lancet ) 53 | target_compile_features( rand PRIVATE cxx_std_11 ) 54 | target_compile_features( rand PRIVATE c_std_11 ) 55 | target_compile_options( rand PRIVATE ${COMMON_CFLAGS} ) 56 | 57 | if(BUILD_R2P2) 58 | set(R2P2_TP_SOURCE "tp_r2p2.c") 59 | set(ENABLE_R2P2 "-DENABLE_R2P2") 60 | endif() 61 | 62 | if(R2P2_NIC_TS) 63 | set(ENABLE_R2P2_NIC_TS "-DR2P2_NIC_TS") 64 | endif() 65 | 66 | set(HTTP_SOURCES "http_app.cc" "picohttpparser.c") 67 | 68 | add_executable( loader ) 69 | target_link_libraries( loader PRIVATE lancet ) 70 | target_sources( loader PRIVATE 71 | ${RAND_SRCS} 72 | "app_proto.c" "key_gen.c" 73 | "loader.c" "redis.c" "memcache.c" 74 | ${HTTP_SOURCES} 75 | ) 76 | target_link_libraries( loader PRIVATE rand ) 77 | target_include_directories( loader PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" ) 78 | target_compile_features( loader PUBLIC c_std_11 ) 79 | target_compile_features( loader PUBLIC cxx_std_14 ) 80 | target_compile_options( loader PRIVATE ${COMMON_CFLAGS} ) 81 | target_include_directories( loader PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" ) 82 | 83 | add_executable( agent ) 84 | target_sources( agent PRIVATE 85 | ${RAND_SRCS} 86 | "agent.c" "args.c" 87 | "app_proto.c" 88 | "tp_tcp.c" "tp_udp.c" "tp_ssl.c" "key_gen.c" 89 | "stats.c" "timestamping.c" "redis.c" "memcache.c" 90 | ${HTTP_SOURCES} 91 | ${R2P2_TP_SOURCE} 92 | ) 93 | target_compile_features( agent PUBLIC c_std_11 ) 94 | target_compile_features( agent PUBLIC cxx_std_14 ) 95 | target_include_directories( agent PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" ) 96 | target_link_libraries( agent PRIVATE lancet ) 97 | target_compile_options(agent PRIVATE ${COMMON_CFLAGS} ${ENABLE_R2P2} ${ENABLE_R2P2_NIC_TS}) 98 | target_link_libraries( agent PRIVATE 99 | Threads::Threads ${LIBM} ${LIBRT} ${LIB_SSL} ${LIB_CRYPTO}) 100 | if(BUILD_R2P2) 101 | target_link_libraries( agent PRIVATE r2p2 ${LIBCONFIG}) 102 | endif() 103 | -------------------------------------------------------------------------------- /coordinator/deploy.go: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package main 25 | 26 | import ( 27 | "fmt" 28 | "golang.org/x/crypto/ssh" 29 | "io" 30 | "io/ioutil" 31 | "os" 32 | "os/user" 33 | "path" 34 | ) 35 | 36 | func publicKeyFile(file string) (ssh.AuthMethod, error) { 37 | buffer, err := ioutil.ReadFile(file) 38 | if err != nil { 39 | return nil, fmt.Errorf("Error reading key file %s\n", err) 40 | } 41 | key, err := ssh.ParsePrivateKey(buffer) 42 | if err != nil { 43 | return nil, err 44 | } 45 | return ssh.PublicKeys(key), nil 46 | } 47 | 48 | func createSession(connection *ssh.Client) (*ssh.Session, error) { 49 | session, err := connection.NewSession() 50 | if err != nil { 51 | return nil, fmt.Errorf("Failed to create session: %s", err) 52 | } 53 | modes := ssh.TerminalModes{ 54 | // ssh.ECHO: 0, // disable echoing 55 | ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud 56 | ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud 57 | } 58 | 59 | if err := session.RequestPty("xterm", 80, 40, modes); err != nil { 60 | session.Close() 61 | return nil, fmt.Errorf("Error in pty") 62 | } 63 | 64 | err = configIO(session) 65 | if err != nil { 66 | session.Close() 67 | return nil, err 68 | } 69 | return session, nil 70 | } 71 | 72 | func configIO(session *ssh.Session) error { 73 | /* 74 | stdin, err := session.StdinPipe() 75 | if err != nil { 76 | return fmt.Errorf("Unable to setup stdin for session: %v", err) 77 | } 78 | go io.Copy(stdin, os.Stdin) 79 | */ 80 | stdout, err := session.StdoutPipe() 81 | if err != nil { 82 | return fmt.Errorf("Unable to setup stdout for session: %v", err) 83 | } 84 | go io.Copy(os.Stdout, stdout) 85 | 86 | stderr, err := session.StderrPipe() 87 | if err != nil { 88 | return fmt.Errorf("Unable to setup stderr for session: %v", err) 89 | } 90 | go io.Copy(os.Stderr, stderr) 91 | return nil 92 | } 93 | 94 | func runAgent(dst, privateKey string, args string) (*ssh.Session, error) { 95 | currentUser, _ := user.Current() 96 | keyFile, err := publicKeyFile(privateKey) 97 | if err != nil { 98 | return nil, err 99 | } 100 | 101 | sshConfig := &ssh.ClientConfig{ 102 | User: currentUser.Username, 103 | Auth: []ssh.AuthMethod{keyFile}, 104 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 105 | } 106 | 107 | connection, err := ssh.Dial("tcp", fmt.Sprintf("%v:22", dst), sshConfig) 108 | if err != nil { 109 | return nil, fmt.Errorf("Failed to dial: %s", err) 110 | } 111 | 112 | session, err := createSession(connection) 113 | if err != nil { 114 | return nil, err 115 | } 116 | 117 | dstPath := path.Join("/tmp", currentUser.Username, "lancet") 118 | venvPath := path.Join(dstPath, "venv/bin/activate") 119 | 120 | cmd := fmt.Sprintf("sudo bash -c 'ulimit -c unlimited && source %s && lancet -- %s'", 121 | venvPath, args) 122 | if _, err := fmt.Printf("My command '%s'\n", cmd); err != nil { 123 | return nil, err 124 | } 125 | go session.Run(cmd) 126 | return session, nil 127 | } 128 | -------------------------------------------------------------------------------- /agents/stats.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #define TX_TIMESTAMP_SAMPLING 0.01 43 | 44 | static __thread union stats *thread_stats; 45 | static __thread struct timespec prev_tx_timestamp; 46 | static __thread struct tx_samples *tx_s; 47 | static __thread uint32_t tx_sample_selector = 0; 48 | 49 | static int configure_stats_shm(void) 50 | { 51 | int fd, ret; 52 | void *vaddr; 53 | char fname[64]; 54 | 55 | sprintf(fname, "/lancet-stats%d", get_agent_tid()); 56 | fd = shm_open(fname, O_RDWR | O_CREAT | O_TRUNC, 0660); 57 | if (fd == -1) 58 | return 1; 59 | 60 | if (get_agent_type() == THROUGHPUT_AGENT) { 61 | ret = ftruncate(fd, sizeof(struct throughput_stats) + 62 | sizeof(struct tx_samples)); 63 | if (ret) 64 | return ret; 65 | 66 | vaddr = mmap(NULL, sizeof(struct throughput_stats) + 67 | sizeof(struct tx_samples), 68 | PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 69 | if (vaddr == MAP_FAILED) 70 | return 1; 71 | 72 | bzero(vaddr, 73 | sizeof(struct throughput_stats) + sizeof(struct tx_samples)); 74 | 75 | tx_s = (struct tx_samples *)(((char *)vaddr) + 76 | sizeof(struct throughput_stats)); 77 | } else { 78 | ret = ftruncate(fd, sizeof(struct latency_stats) + 79 | sizeof(struct tx_samples)); 80 | if (ret) 81 | return ret; 82 | 83 | vaddr = 84 | mmap(NULL, sizeof(struct latency_stats) + sizeof(struct tx_samples), 85 | PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 86 | if (vaddr == MAP_FAILED) 87 | return 1; 88 | 89 | bzero(vaddr, sizeof(struct latency_stats) + sizeof(struct tx_samples)); 90 | 91 | tx_s = (struct tx_samples *)(((char *)vaddr) + 92 | sizeof(struct latency_stats)); 93 | } 94 | 95 | thread_stats = vaddr; 96 | 97 | return 0; 98 | } 99 | 100 | int init_per_thread_stats(void) 101 | { 102 | int ret; 103 | 104 | ret = configure_stats_shm(); 105 | if (ret) 106 | return ret; 107 | 108 | return 0; 109 | } 110 | 111 | int add_throughput_tx_sample(struct byte_req_pair tx_p) 112 | { 113 | if (!should_measure()) 114 | return 0; 115 | 116 | thread_stats->th_s.tx.bytes += tx_p.bytes; 117 | thread_stats->th_s.tx.reqs += tx_p.reqs; 118 | 119 | return 0; 120 | } 121 | 122 | int add_throughput_rx_sample(struct byte_req_pair rx_p) 123 | { 124 | if (!should_measure()) 125 | return 0; 126 | 127 | thread_stats->th_s.rx.bytes += rx_p.bytes; 128 | thread_stats->th_s.rx.reqs += rx_p.reqs; 129 | 130 | return 0; 131 | } 132 | 133 | int add_tx_timestamp(struct timespec *tx_ts) 134 | { 135 | int res; 136 | struct timespec *dest; 137 | 138 | if (drand48() < TX_TIMESTAMP_SAMPLING) { 139 | dest = &tx_s->samples[tx_s->count++ % MAX_PER_THREAD_TX_SAMPLES]; 140 | res = timespec_diff(dest, tx_ts, &prev_tx_timestamp); 141 | if (res) 142 | tx_s->count--; 143 | } 144 | 145 | prev_tx_timestamp = *tx_ts; 146 | return 0; 147 | } 148 | 149 | int add_latency_sample(long diff, struct timespec *tx) 150 | { 151 | struct lat_sample *lts; 152 | 153 | assert(diff > 0); 154 | if (!should_measure() || 155 | (tx_sample_selector++ % lround(1 / get_sampling_rate()))) 156 | return 0; 157 | lts = &thread_stats->lt_s 158 | .samples[thread_stats->lt_s.inc_idx++ % MAX_PER_THREAD_SAMPLES]; 159 | lts->nsec = diff; 160 | if (tx) 161 | lts->tx = *tx; 162 | 163 | return 0; 164 | } 165 | -------------------------------------------------------------------------------- /agents/args.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | static struct transport_protocol * 38 | init_transport_protocol(enum transport_protocol_type tp_type) 39 | { 40 | struct transport_protocol *res; 41 | 42 | switch (tp_type) { 43 | case TCP: 44 | res = init_tcp(); 45 | break; 46 | case UDP: 47 | res = init_udp(); 48 | break; 49 | case TLS: 50 | res = init_tls(); 51 | break; 52 | #ifdef ENABLE_R2P2 53 | case R2P2: 54 | res = init_r2p2(); 55 | break; 56 | #endif 57 | default: 58 | res = NULL; 59 | break; 60 | } 61 | return res; 62 | } 63 | 64 | struct agent_config *parse_arguments(int argc, char **argv) 65 | { 66 | int c, agent_type; 67 | struct agent_config *cfg; 68 | char *token1, *token2; 69 | struct sockaddr_in sa; 70 | // char proto[128]; 71 | 72 | cfg = calloc(1, sizeof(struct agent_config)); 73 | if (!cfg) { 74 | lancet_fprintf(stderr, "Failed to allocate cfg\n"); 75 | return NULL; 76 | } 77 | 78 | while ((c = getopt(argc, argv, "t:s:c:a:p:i:r:n:o:")) != -1) { 79 | switch (c) { 80 | case 't': 81 | // Thread count 82 | cfg->thread_count = atoi(optarg); 83 | break; 84 | case 's': 85 | // Targets ip:port,ip:port 86 | token1 = strtok_r(optarg, ",", &optarg); 87 | while (token1) { 88 | /* Prepare the target */ 89 | token2 = strtok_r(token1, ":", &token1); 90 | inet_pton(AF_INET, token2, &(sa.sin_addr)); 91 | cfg->targets[cfg->target_count].ip = sa.sin_addr.s_addr; 92 | token2 = strtok_r(token1, ":", &token1); 93 | cfg->targets[cfg->target_count++].port = atoi(token2); 94 | 95 | assert(cfg->target_count < 64); 96 | token1 = strtok_r(optarg, ",", &optarg); 97 | } 98 | break; 99 | case 'c': 100 | // Connection count 101 | cfg->conn_count = atoi(optarg); 102 | break; 103 | case 'a': 104 | // Agent type 105 | agent_type = atoi(optarg); 106 | if (agent_type == THROUGHPUT_AGENT) 107 | cfg->atype = THROUGHPUT_AGENT; 108 | else if (agent_type == LATENCY_AGENT) 109 | cfg->atype = LATENCY_AGENT; 110 | else if (agent_type == SYMMETRIC_NIC_TIMESTAMP_AGENT) 111 | cfg->atype = SYMMETRIC_NIC_TIMESTAMP_AGENT; 112 | else if (agent_type == SYMMETRIC_AGENT) 113 | cfg->atype = SYMMETRIC_AGENT; 114 | else { 115 | lancet_fprintf(stderr, "Unknown agent type\n"); 116 | return NULL; 117 | } 118 | break; 119 | case 'p': 120 | // Communication protocol 121 | if (strcmp(optarg, "TCP") == 0) 122 | cfg->tp_type = TCP; 123 | #ifdef ENABLE_R2P2 124 | else if (strcmp(optarg, "R2P2") == 0) 125 | cfg->tp_type = R2P2; 126 | #endif 127 | else if (strcmp(optarg, "UDP") == 0) 128 | cfg->tp_type = UDP; 129 | else if (strcmp(optarg, "TLS") == 0) 130 | cfg->tp_type = TLS; 131 | else { 132 | lancet_fprintf(stderr, "Unknown transport protocol\n"); 133 | return NULL; 134 | } 135 | break; 136 | case 'i': 137 | // Interarrival distribution 138 | cfg->idist = init_rand(optarg); 139 | if (!cfg->idist) { 140 | lancet_fprintf(stderr, "Failed to create iadist\n"); 141 | return NULL; 142 | } 143 | break; 144 | case 'r': 145 | // Application protocol (request response types) 146 | cfg->app_proto = init_app_proto(optarg); 147 | if (!cfg->app_proto) { 148 | lancet_fprintf(stderr, "Failed to create app proto\n"); 149 | return NULL; 150 | } 151 | break; 152 | case 'n': 153 | strncpy(cfg->if_name, optarg, 64); 154 | break; 155 | case 'o': 156 | cfg->per_conn_reqs = atoi(optarg); 157 | break; 158 | default: 159 | lancet_fprintf(stderr, "Unknown argument\n"); 160 | abort(); 161 | } 162 | } 163 | #ifdef ENABLE_R2P2 164 | // Generators interfacing with R2P2 must use host endianness (except latency) 165 | if (cfg->tp_type == R2P2 && cfg->atype != LATENCY_AGENT) { 166 | for (int i=0;itarget_count;i++) 167 | cfg->targets[i].ip = ntohl(cfg->targets[i].ip); 168 | } 169 | #endif 170 | cfg->tp = init_transport_protocol(cfg->tp_type); 171 | if (!cfg->tp) { 172 | lancet_fprintf(stderr, "Failed to init transport\n"); 173 | return NULL; 174 | } 175 | 176 | return cfg; 177 | } 178 | -------------------------------------------------------------------------------- /coordinator/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package main 25 | 26 | import ( 27 | "flag" 28 | "fmt" 29 | "os" 30 | "os/user" 31 | "path" 32 | "strings" 33 | ) 34 | 35 | type ServerConfig struct { 36 | target string 37 | thThreads int 38 | ltThreads int 39 | thConn int 40 | ltConn int 41 | idist string 42 | appProto string 43 | comProto string 44 | ifName string 45 | reqPerConn int 46 | } 47 | 48 | type ExperimentConfig struct { 49 | thAgents []string 50 | ltAgents []string 51 | symAgents []string 52 | agentPort int 53 | ltRate int 54 | loadPattern string 55 | ciSize int 56 | nicTS bool 57 | privateKeyPath string 58 | } 59 | 60 | type GeneralConfig struct { 61 | runAgents bool 62 | printAgentArgs bool 63 | } 64 | 65 | func ParseConfig() (*ServerConfig, *ExperimentConfig, *GeneralConfig, error) { 66 | currentUser, _ := user.Current() 67 | id_rsa_path := path.Join(currentUser.HomeDir, ".ssh/id_rsa") 68 | var agentPort = flag.Int("agentPort", 5001, "listening port of the agent") 69 | var target = flag.String("targetHost", "127.0.0.1:8000", "host:port comma-separated list to run experiment against") 70 | var thAgents = flag.String("loadAgents", "", "ip of loading agents separated by commas, e.g. ip1,ip2,...") 71 | var ltAgents = flag.String("ltAgents", "", "ip of latency agents separated by commas, e.g. ip1,ip2,...") 72 | var symAgents = flag.String("symAgents", "", "ip of symmetric agents separated by commas, e.g. ip1,ip2,...") 73 | var thThreads = flag.Int("loadThreads", 1, "loading threads per agent (used for load and sym agents)") 74 | var ltThreads = flag.Int("ltThreads", 1, "latency threads per agent") 75 | var thConn = flag.Int("loadConns", 1, "number of loading connections per agent") 76 | var ltConn = flag.Int("ltConns", 1, "number of latency connections") 77 | var idist = flag.String("idist", "exp", "interarrival distibution: fixed, exp") 78 | var appProto = flag.String("appProto", "echo:4", "application protocol") 79 | var comProto = flag.String("comProto", "TCP", "TCP|R2P2|UDP|TLS") 80 | var ltRate = flag.Int("lqps", 4000, "latency qps") 81 | var loadPattern = flag.String("loadPattern", "fixed:10000", "load pattern") 82 | var ciSize = flag.Int("ciSize", 10, "size of 95-confidence interval in us") 83 | var nicTS = flag.Bool("nicTS", false, "NIC timestamping for symmetric agents") 84 | var privateKey = flag.String("privateKey", id_rsa_path, "location of the (local) private key to deploy the agents. Will find a default if not specified") 85 | var ifName = flag.String("ifName", "enp65s0", "interface name for hardware timestamping") 86 | var reqPerConn = flag.Int("reqPerConn", 1, "Number of outstanding requests per TCP connection") 87 | var runAgents = flag.Bool("runAgents", true, "Automatically run agents") 88 | var printAgentArgs = flag.Bool("printAgentArgs", false, "Print in JSON format the arguments for each agent") 89 | 90 | flag.Parse() 91 | 92 | serverCfg := &ServerConfig{} 93 | expCfg := &ExperimentConfig{} 94 | generalCfg := &GeneralConfig{} 95 | 96 | if *runAgents { 97 | if x, err := os.Stat(*privateKey); err != nil { 98 | fmt.Printf("Unable to find private ssh key at path '%s'\n", *privateKey) 99 | return nil, nil, nil, err 100 | } else if x.IsDir() { 101 | return nil, nil, nil, fmt.Errorf("Have a directory at ssh path %s\n", *privateKey) 102 | } 103 | } 104 | 105 | serverCfg.target = *target 106 | serverCfg.thThreads = *thThreads 107 | serverCfg.ltThreads = *ltThreads 108 | serverCfg.thConn = *thConn 109 | serverCfg.ltConn = *ltConn 110 | serverCfg.idist = *idist 111 | serverCfg.appProto = *appProto 112 | serverCfg.comProto = *comProto 113 | serverCfg.ifName = *ifName 114 | serverCfg.reqPerConn = *reqPerConn 115 | 116 | if *thAgents == "" { 117 | expCfg.thAgents = nil 118 | } else { 119 | expCfg.thAgents = strings.Split(*thAgents, ",") 120 | } 121 | if *ltAgents == "" { 122 | expCfg.ltAgents = nil 123 | } else { 124 | expCfg.ltAgents = strings.Split(*ltAgents, ",") 125 | } 126 | if *symAgents == "" { 127 | expCfg.symAgents = nil 128 | } else { 129 | expCfg.symAgents = strings.Split(*symAgents, ",") 130 | } 131 | expCfg.agentPort = *agentPort 132 | expCfg.ltRate = *ltRate 133 | expCfg.loadPattern = *loadPattern 134 | expCfg.ciSize = *ciSize 135 | expCfg.nicTS = *nicTS 136 | expCfg.privateKeyPath = *privateKey 137 | 138 | generalCfg.runAgents = *runAgents 139 | generalCfg.printAgentArgs = *printAgentArgs 140 | 141 | return serverCfg, expCfg, generalCfg, nil 142 | } 143 | -------------------------------------------------------------------------------- /agent-manager/manager/lancet.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | #!/usr/bin/env python3 23 | 24 | # import sys 25 | import socket 26 | import time 27 | import argparse 28 | import pathlib 29 | 30 | from manager.proto import LancetProto 31 | from manager.agentcontroller import LancetController 32 | from manager.stats import aggregate_throughput, aggregate_latency 33 | 34 | MANAGER_PORT = 5001 35 | this_dir = pathlib.Path(__file__).absolute().parent 36 | default_binary_asset_dir = this_dir / "assets/" 37 | agent_bin_name = "agent" 38 | lib_rand_name = "librand.so" 39 | 40 | def get_args(): 41 | parser = argparse.ArgumentParser( 42 | description="The Lancet Agent" 43 | ) 44 | parser.add_argument("-b", "--binary-assets", default=default_binary_asset_dir, 45 | help="default base directory for binary assets") 46 | parser.add_argument("--agent", help="override for custom path to args") 47 | parser.add_argument("--rand", help="override for custom path to librand.so") 48 | parser.add_argument("agent_args", nargs="*", help="arguments to directly pass into the agent") 49 | args = parser.parse_args() 50 | 51 | ba = args.binary_assets 52 | if not (ba.exists() and ba.is_dir()): 53 | raise Exception("Could not find binary assets base directory at {}".format(ba)) 54 | if args.agent is None: 55 | args.agent = ba / agent_bin_name 56 | agent = args.agent 57 | if not (agent.exists() and agent.is_file()): 58 | raise Exception("Bad agent path at {}".format(agent)) 59 | if args.rand is None: 60 | args.rand = ba / lib_rand_name 61 | librand = args.rand 62 | if not (librand.exists() and librand.is_file()): 63 | raise Exception("Bad librand.so path at {}".format(librand)) 64 | return args 65 | 66 | class LancetServer: 67 | def __init__(self, librand): 68 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 69 | self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 70 | self.socket.bind(("", MANAGER_PORT)) 71 | self.socket.listen(1) 72 | self.controller = LancetController(librand_path=librand) 73 | 74 | def run(self, args): 75 | self.controller.launch_agent(args) 76 | conn, addr = self.socket.accept() 77 | self.proto = LancetProto(conn) 78 | while 1: 79 | msg = self.proto.recv_msg() 80 | res = self.process_msg(msg) 81 | if res == -1: 82 | print("Lancet will terminate") 83 | ret = self.controller.check_agent() 84 | if ret: 85 | print("Agent failed with {}".format(ret)) 86 | else: 87 | print("Agent is running") 88 | break 89 | self.proto.close() 90 | self.controller.terminate() 91 | 92 | def process_msg(self, msg): 93 | if msg.msg_type == 0: 94 | self.controller.start_load(msg.info) 95 | elif msg.msg_type == 1: 96 | self.controller.start_measure(msg.info.SampleCount, msg.info.SamplingRate) 97 | self.start_time = time.time() 98 | # Configure sampling 99 | elif msg.msg_type == 2: 100 | if msg.info == 0: 101 | self.end_time = time.time() 102 | throughput_stats = self.controller.get_stats() 103 | agg_stats = aggregate_throughput(throughput_stats) 104 | agg_stats.duration = self.end_time - self.start_time 105 | self.proto.reply_throughput(agg_stats) 106 | elif msg.info == 1: 107 | self.end_time = time.time() 108 | latency_stats = self.controller.get_stats() 109 | try: 110 | agg_stats = aggregate_latency(latency_stats, 111 | self.controller.get_per_thread_samples()) 112 | except ValueError: 113 | return -1 114 | agg_stats.duration = self.end_time - self.start_time 115 | self.proto.reply_latency(agg_stats) # should pass something here 116 | else: 117 | print("Unknown report msg") 118 | return -1 119 | elif msg.msg_type == 3: 120 | self.proto.reply_value(self.controller.get_conn_open()) 121 | elif msg.msg_type == -1: 122 | return -1 123 | return 0 124 | 125 | 126 | def main(): 127 | args = get_args() 128 | server = LancetServer(librand=args.rand) 129 | server.run(args=args) 130 | # try: 131 | # server.run() 132 | # except: 133 | # server.proto.close() 134 | # self.controller.terminate() 135 | 136 | if __name__ == "__main__": 137 | main() 138 | -------------------------------------------------------------------------------- /agents/agent.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #define _GNU_SOURCE 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | static struct agent_config *cfg; 44 | static struct agent_control_block *acb; 45 | static __thread struct request to_send; 46 | static __thread struct iovec received; 47 | static __thread int thread_idx; 48 | pthread_barrier_t conn_open_barrier; 49 | 50 | int should_load(void) 51 | { 52 | return acb->should_load; 53 | } 54 | 55 | int should_measure(void) 56 | { 57 | return acb->should_measure; 58 | } 59 | 60 | int get_conn_count(void) 61 | { 62 | return cfg->conn_count; 63 | } 64 | 65 | int get_thread_count(void) 66 | { 67 | return cfg->thread_count; 68 | } 69 | 70 | int get_target_count(void) 71 | { 72 | return cfg->target_count; 73 | } 74 | 75 | struct application_protocol * get_app_proto(void) 76 | { 77 | return cfg->app_proto; 78 | } 79 | 80 | struct host_tuple *get_targets(void) 81 | { 82 | return cfg->targets; 83 | } 84 | 85 | long get_ia(void) 86 | { 87 | return lround(generate(cfg->idist) * 1000); 88 | } 89 | 90 | enum agent_type get_agent_type(void) 91 | { 92 | return cfg->atype; 93 | } 94 | 95 | int get_agent_tid(void) 96 | { 97 | return thread_idx; 98 | } 99 | 100 | uint32_t get_per_thread_samples(void) 101 | { 102 | return acb->per_thread_samples; 103 | } 104 | 105 | double get_sampling_rate(void) 106 | { 107 | return acb->sampling; 108 | } 109 | 110 | char *get_if_name(void) 111 | { 112 | return cfg->if_name; 113 | } 114 | 115 | int get_max_pending_reqs(void) 116 | { 117 | return cfg->per_conn_reqs; 118 | } 119 | 120 | void set_conn_open(int val) 121 | { 122 | acb->conn_open = val; 123 | } 124 | 125 | struct request *prepare_request(void) 126 | { 127 | create_request(cfg->app_proto, &to_send); 128 | 129 | return &to_send; 130 | } 131 | 132 | struct byte_req_pair process_response(char *buf, int size) 133 | { 134 | received.iov_base = buf; 135 | received.iov_len = size; 136 | return consume_response(cfg->app_proto, &received); 137 | } 138 | 139 | static void *agent_main(void *arg) 140 | { 141 | cpu_set_t cpuset; 142 | pthread_t thread; 143 | int s; 144 | 145 | thread = pthread_self(); 146 | thread_idx = (int)(long)arg; 147 | init_per_thread_stats(); 148 | 149 | srand(time(NULL) + thread_idx * 12345); 150 | 151 | CPU_ZERO(&cpuset); 152 | CPU_SET(thread_idx, &cpuset); 153 | 154 | s = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset); 155 | if (s != 0) { 156 | lancet_perror("pthread_setaffinity_np"); 157 | return NULL; 158 | } 159 | cfg->tp->tp_main[cfg->atype](); 160 | 161 | return NULL; 162 | } 163 | 164 | static int configure_control_block(void) 165 | { 166 | int fd, ret; 167 | void *vaddr; 168 | 169 | fd = shm_open("/lancetcontrol", O_RDWR | O_CREAT | O_TRUNC, 0660); 170 | if (fd == -1) 171 | return 1; 172 | 173 | ret = ftruncate(fd, sizeof(struct agent_control_block)); 174 | if (ret) 175 | return ret; 176 | 177 | vaddr = mmap(NULL, sizeof(struct agent_control_block), 178 | PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 179 | if (vaddr == MAP_FAILED) 180 | return 1; 181 | 182 | acb = vaddr; 183 | 184 | bzero((void *)acb, sizeof(struct agent_control_block)); 185 | acb->thread_count = get_thread_count(); 186 | acb->idist = *cfg->idist; 187 | free(cfg->idist); 188 | cfg->idist = &acb->idist; 189 | acb->agent_type = get_agent_type(); 190 | 191 | return 0; 192 | } 193 | 194 | int main(int argc, char **argv) 195 | { 196 | int i; 197 | pthread_t *tids; 198 | 199 | cfg = parse_arguments(argc, argv); 200 | if (!cfg) 201 | exit(-1); 202 | 203 | if (cfg->atype == SYMMETRIC_NIC_TIMESTAMP_AGENT) 204 | enable_nic_timestamping(cfg->if_name); 205 | 206 | if (configure_control_block()) { 207 | lancet_fprintf(stderr, "failed to init the control block\n"); 208 | exit(-1); 209 | } 210 | 211 | tids = malloc(cfg->thread_count * sizeof(pthread_t)); 212 | if (!tids) { 213 | lancet_fprintf(stderr, "Failed to allocate tids\n"); 214 | exit(-1); 215 | } 216 | 217 | int ret = pthread_barrier_init(&conn_open_barrier, NULL, cfg->thread_count); 218 | assert(!ret); 219 | 220 | for (i = 1; i < cfg->thread_count; i++) { 221 | if (pthread_create(&tids[i], NULL, agent_main, (void *)(long)i)) { 222 | lancet_fprintf(stderr, "failed to spawn thread %d\n", i); 223 | exit(-1); 224 | } 225 | } 226 | 227 | agent_main(0); 228 | 229 | return 0; 230 | } 231 | -------------------------------------------------------------------------------- /agents/http_app.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | // Lancet-specific stuff 34 | extern "C" { 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include "picohttpparser.h" 41 | } 42 | 43 | class RequestCreator 44 | { 45 | private: 46 | const std::string mRequestString; 47 | std::unique_ptr mStrBuf; 48 | public: 49 | explicit RequestCreator(const std::string &request_string) : mRequestString(request_string), mStrBuf(new char[request_string.size()]) { 50 | } 51 | 52 | iovec renewRequest() { 53 | auto const sz = mRequestString.size(); 54 | strncpy(mStrBuf.get(), mRequestString.data(), sz); 55 | return { .iov_base = mStrBuf.get(), .iov_len = sz }; 56 | } 57 | }; 58 | 59 | int http_create_request(application_protocol *proto, 60 | request *req) 61 | { 62 | using namespace std; 63 | static __thread RequestCreator* requestCreator = nullptr; 64 | 65 | auto const &http_params = *static_cast(proto->arg); // if needed 66 | if (!requestCreator) { 67 | requestCreator = new RequestCreator(http_params); 68 | } 69 | req->iovs[0] = requestCreator->renewRequest(); 70 | req->iov_cnt = 1; 71 | req->meta = nullptr; // callee must zero 72 | return 0; 73 | } 74 | 75 | const size_t max_headers = 32; 76 | const char* content_length = "Content-Length"; 77 | const size_t content_length_length = 14; 78 | byte_req_pair http_consume_response(application_protocol *proto, iovec *response) { 79 | using namespace std; 80 | const char *msg; 81 | size_t msg_len, num_headers = max_headers; 82 | phr_header headers[max_headers]; 83 | int minor_version, status; 84 | 85 | auto ret = phr_parse_response( 86 | static_cast(response->iov_base), response->iov_len, 87 | &minor_version, &status, 88 | &msg, &msg_len, 89 | headers, &num_headers, 0); 90 | if (ret < 0) { 91 | // -2 == how it indicates a partial request. anything else is unexpected 92 | if (ret != -2) { 93 | lancet_fprintf(stderr, "failed to parse HTTP response. Got return code %d\n", ret); 94 | assert(0); 95 | } 96 | return {0,0}; 97 | } 98 | assert(ret > 0); 99 | 100 | bool found = false; 101 | size_t reported_content_length = 0; 102 | for (decltype(num_headers) i = 0; i < num_headers; ++i) { 103 | auto &hdr = headers[i]; 104 | if (strncmp(hdr.name, content_length, min(content_length_length, hdr.name_len)) == 0) { 105 | reported_content_length = atoll(hdr.value); 106 | found = true; 107 | break; 108 | } 109 | } 110 | if (not found) { 111 | lancet_fprintf(stderr, "Unable to determine content of HTTP response from header\n"); 112 | assert(0); 113 | } 114 | 115 | auto reported_total_len = ret + reported_content_length; 116 | auto leftover_bytes = response->iov_len - reported_total_len; 117 | if (reported_total_len > response->iov_len) { 118 | // in this case, we may need to wait for more if MAX_PAYLOAD has some left 119 | // if this is greater than MAX_PAYLOAD to buffer, then the calling method should error 120 | return {0,0}; 121 | } 122 | 123 | assert(leftover_bytes >= 0); 124 | 125 | // otherwise, ret is number of bytes consumed 126 | return { 127 | .bytes = static_cast(reported_total_len), 128 | .reqs = 1 129 | }; 130 | } 131 | 132 | // All interfacing stuff with the rest of the Lancet code goes below 133 | 134 | extern "C" int http_proto_init(char *proto, application_protocol *app_proto) { 135 | using namespace std; 136 | 137 | assert(proto != nullptr); 138 | assert(app_proto != nullptr); 139 | 140 | regex http_resource(R"(^http:([\w\.]*)((?:/[\w\.]+)+)\s*$)"); 141 | 142 | cmatch match; 143 | regex_match(proto, match, http_resource); 144 | if (not match.ready()) { 145 | lancet_fprintf(stderr, "Unable to parse http protocol\n"); 146 | return -1; 147 | } 148 | assert(match.size() == 3); // so that the regex isn't malformed 149 | auto const&& request_host = match[1].str(); 150 | auto const&& asset_path = match[2].str(); 151 | stringstream http_stream; 152 | http_stream << "GET " << asset_path << " HTTP/1.1\r\nHost: " << request_host << "\r\n\r\n"; 153 | 154 | auto http_params = make_unique(http_stream.str()); 155 | app_proto->arg = static_castarg)>(http_params.release()); 156 | app_proto->type = PROTO_HTTP; 157 | app_proto->create_request = http_create_request; 158 | app_proto->consume_response = http_consume_response; 159 | 160 | return 0; 161 | } 162 | -------------------------------------------------------------------------------- /coordinator/stats.go: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package main 25 | 26 | // #include "../inc/lancet/coord_proto.h" 27 | import "C" 28 | import ( 29 | "fmt" 30 | ) 31 | 32 | func computeStatsThroughput(replies []*C.struct_throughput_reply) *C.struct_throughput_reply { 33 | agg_stats := &C.struct_throughput_reply{} 34 | for _, r := range replies { 35 | agg_stats.Rx_bytes += r.Rx_bytes 36 | agg_stats.Tx_bytes += r.Tx_bytes 37 | agg_stats.Req_count += r.Req_count 38 | agg_stats.CorrectIAD += r.CorrectIAD 39 | } 40 | agg_stats.Duration = replies[0].Duration 41 | 42 | return agg_stats 43 | } 44 | 45 | func computeStatsLatency(replies []*C.struct_latency_reply) *C.struct_latency_reply { 46 | agg_stats := &C.struct_latency_reply{} 47 | for _, r := range replies { 48 | printLatencyStats(r) 49 | agg_stats.Avg_lat += r.Avg_lat 50 | agg_stats.P50_i += r.P50_i 51 | agg_stats.P50 += r.P50 52 | agg_stats.P50_k += r.P50_k 53 | agg_stats.P90_i += r.P90_i 54 | agg_stats.P90 += r.P90 55 | agg_stats.P90_k += r.P90_k 56 | agg_stats.P95_i += r.P95_i 57 | agg_stats.P95 += r.P95 58 | agg_stats.P95_k += r.P95_k 59 | agg_stats.P99_i += r.P99_i 60 | agg_stats.P99 += r.P99 61 | agg_stats.P99_k += r.P99_k 62 | agg_stats.P999_i += r.P999_i 63 | agg_stats.P999 += r.P999 64 | agg_stats.P999_k += r.P999_k 65 | agg_stats.P9999_i += r.P9999_i 66 | agg_stats.P9999 += r.P9999 67 | agg_stats.P9999_k += r.P9999_k 68 | agg_stats.P99999_i += r.P99999_i 69 | agg_stats.P99999 += r.P99999 70 | agg_stats.P99999_k += r.P99999_k 71 | agg_stats.P999999_i += r.P999999_i 72 | agg_stats.P999999 += r.P999999 73 | agg_stats.P999999_k += r.P999999_k 74 | agg_stats.IsStationary += r.IsStationary 75 | agg_stats.IsIid += r.IsIid 76 | } 77 | agg_stats.Avg_lat /= C.uint64_t(len(replies)) 78 | agg_stats.P50_i /= C.uint64_t(len(replies)) 79 | agg_stats.P50 /= C.uint64_t(len(replies)) 80 | agg_stats.P50_k /= C.uint64_t(len(replies)) 81 | agg_stats.P90_i /= C.uint64_t(len(replies)) 82 | agg_stats.P90 /= C.uint64_t(len(replies)) 83 | agg_stats.P90_k /= C.uint64_t(len(replies)) 84 | agg_stats.P95_i /= C.uint64_t(len(replies)) 85 | agg_stats.P95 /= C.uint64_t(len(replies)) 86 | agg_stats.P95_k /= C.uint64_t(len(replies)) 87 | agg_stats.P99_i /= C.uint64_t(len(replies)) 88 | agg_stats.P99 /= C.uint64_t(len(replies)) 89 | agg_stats.P99_k /= C.uint64_t(len(replies)) 90 | agg_stats.P999_i /= C.uint64_t(len(replies)) 91 | agg_stats.P999 /= C.uint64_t(len(replies)) 92 | agg_stats.P999_k /= C.uint64_t(len(replies)) 93 | agg_stats.P9999_i /= C.uint64_t(len(replies)) 94 | agg_stats.P9999 /= C.uint64_t(len(replies)) 95 | agg_stats.P9999_k /= C.uint64_t(len(replies)) 96 | agg_stats.P99999_i /= C.uint64_t(len(replies)) 97 | agg_stats.P99999 /= C.uint64_t(len(replies)) 98 | agg_stats.P99999_k /= C.uint64_t(len(replies)) 99 | agg_stats.P999999_i /= C.uint64_t(len(replies)) 100 | agg_stats.P999999 /= C.uint64_t(len(replies)) 101 | agg_stats.P999999_k /= C.uint64_t(len(replies)) 102 | 103 | if agg_stats.IsIid == 0 { 104 | agg_stats.ToReduceSampling = 1000000 105 | for _, r := range replies { 106 | if r.ToReduceSampling < agg_stats.ToReduceSampling { 107 | agg_stats.ToReduceSampling = r.ToReduceSampling 108 | } 109 | } 110 | } 111 | return agg_stats 112 | 113 | } 114 | 115 | func printThroughputStats(stats *C.struct_throughput_reply) { 116 | fmt.Println("Next line includes both load and measurement") 117 | fmt.Println("#ReqCount\tQPS\tRxBw\tTxBw") 118 | fmt.Printf("%v\t%v\t%v\t%v\n", stats.Req_count, 119 | 1e6*float64(stats.Req_count)/float64(stats.Duration), 120 | 1e6*float64(stats.Rx_bytes)/float64(stats.Duration), 121 | 1e6*float64(stats.Tx_bytes)/float64(stats.Duration)) 122 | } 123 | 124 | func printLatencyStats(stats *C.struct_latency_reply) { 125 | fmt.Println("#Avg Lat\t50th\t90th\t95th\t99th\t99.9th\t99.99th\t99.999th\t99.9999th") 126 | fmt.Printf("%v\t%v(%v, %v)\t%v(%v, %v)\t%v(%v, %v)\t%v(%v, %v)\t%v(%v, %v)\t%v(%v, %v)\t%v(%v, %v)\t%v(%v, %v)\n", 127 | float64(stats.Avg_lat)/1e3, 128 | float64(stats.P50)/1e3, float64(stats.P50_i)/1e3, float64(stats.P50_k)/1e3, 129 | float64(stats.P90)/1e3, float64(stats.P90_i)/1e3, float64(stats.P90_k)/1e3, 130 | float64(stats.P95)/1e3, float64(stats.P95_i)/1e3, float64(stats.P95_k)/1e3, 131 | float64(stats.P99)/1e3, float64(stats.P99_i)/1e3, float64(stats.P99_k)/1e3, 132 | float64(stats.P999)/1e3, float64(stats.P999_i)/1e3, float64(stats.P999_k)/1e3, 133 | float64(stats.P9999)/1e3, float64(stats.P9999_i)/1e3, float64(stats.P9999_k)/1e3, 134 | float64(stats.P99999)/1e3, float64(stats.P99999_i)/1e3, float64(stats.P99999_k)/1e3, 135 | float64(stats.P999999)/1e3, float64(stats.P999999_i)/1e3, float64(stats.P999999_k)/1e3) 136 | } 137 | 138 | func getRPS(stats *C.struct_throughput_reply) float64 { 139 | return 1e6 * float64(stats.Req_count) / float64(stats.Duration) 140 | } 141 | 142 | func getLatCISize(stats []*C.struct_latency_reply, percentile int) int { 143 | if percentile == 99 { 144 | return int(stats[0].P99_k - stats[0].P99_i) 145 | } else { 146 | panic("Unknown percentile") 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /coordinator/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package main 25 | 26 | // #include "../inc/lancet/coord_proto.h" 27 | import "C" 28 | import ( 29 | "encoding/json" 30 | "fmt" 31 | "net" 32 | "os" 33 | "time" 34 | ) 35 | 36 | const ( 37 | tHROUGHPUT_AGENT = iota 38 | lATENCY_AGENT 39 | ) 40 | 41 | type agent struct { 42 | name string 43 | conn *net.TCPConn 44 | aType int 45 | } 46 | 47 | func main() { 48 | 49 | serverCfg, expCfg, generalCfg, err := ParseConfig() 50 | if err != nil { 51 | fmt.Println(err) 52 | os.Exit(1) 53 | } 54 | 55 | c := coordinator{} 56 | 57 | if expCfg.thAgents != nil { 58 | c.thAgents = make([]*agent, len(expCfg.thAgents)) 59 | } 60 | if expCfg.ltAgents != nil { 61 | c.ltAgents = make([]*agent, len(expCfg.ltAgents)) 62 | } 63 | if expCfg.symAgents != nil { 64 | c.symAgents = make([]*agent, len(expCfg.symAgents)) 65 | } 66 | 67 | c.agentPort = expCfg.agentPort 68 | 69 | var agentArgsMap map[string]string 70 | if generalCfg.printAgentArgs { 71 | agentArgsMap = make(map[string]string) 72 | } 73 | 74 | // Run throughput agents 75 | agentArgs := fmt.Sprintf("-s %s -t %d -c %d -o %d -i %s -p %s -r %s -a 0", 76 | serverCfg.target, serverCfg.thThreads, serverCfg.thConn, serverCfg.reqPerConn, 77 | serverCfg.idist, serverCfg.comProto, serverCfg.appProto) 78 | for i, a := range expCfg.thAgents { 79 | if generalCfg.printAgentArgs { 80 | agentArgsMap[a] = agentArgs 81 | } else if generalCfg.runAgents { 82 | session, err := runAgent(a, expCfg.privateKeyPath, agentArgs) 83 | if err != nil { 84 | fmt.Println(err) 85 | os.Exit(1) 86 | } 87 | defer session.Close() 88 | } 89 | c.thAgents[i] = &agent{name: a, aType: tHROUGHPUT_AGENT} 90 | } 91 | 92 | // Run latency agents 93 | ltArgs := fmt.Sprintf("-s %s -t %d -c %d -i %s -p %s -r %s -a 1 -o 1", 94 | serverCfg.target, serverCfg.ltThreads, serverCfg.ltConn, 95 | serverCfg.idist, serverCfg.comProto, serverCfg.appProto) 96 | for i, a := range expCfg.ltAgents { 97 | if generalCfg.printAgentArgs { 98 | agentArgsMap[a] = ltArgs 99 | } else if generalCfg.runAgents { 100 | session, err := runAgent(a, expCfg.privateKeyPath, ltArgs) 101 | if err != nil { 102 | fmt.Println(err) 103 | os.Exit(1) 104 | } 105 | defer session.Close() 106 | } 107 | c.ltAgents[i] = &agent{name: a, aType: lATENCY_AGENT} 108 | } 109 | 110 | symArgsPre := fmt.Sprintf("-s %s -t %d -c %d -o %d -i %s -p %s -r %s", 111 | serverCfg.target, serverCfg.thThreads, serverCfg.thConn, serverCfg.reqPerConn, 112 | serverCfg.idist, serverCfg.comProto, serverCfg.appProto) 113 | var symArgs string 114 | if expCfg.nicTS { 115 | symArgs = fmt.Sprintf("%s -a %d -n %s", symArgsPre, 2, serverCfg.ifName) 116 | } else { 117 | symArgs = fmt.Sprintf("%s -a %d", symArgsPre, 3) 118 | } 119 | for i, a := range expCfg.symAgents { 120 | if generalCfg.printAgentArgs { 121 | agentArgsMap[a] = symArgs 122 | } else if generalCfg.runAgents { 123 | session, err := runAgent(a, expCfg.privateKeyPath, symArgs) 124 | if err != nil { 125 | fmt.Println(err) 126 | os.Exit(1) 127 | } 128 | defer session.Close() 129 | } 130 | c.symAgents[i] = &agent{name: a, aType: lATENCY_AGENT} 131 | } 132 | 133 | if generalCfg.printAgentArgs { 134 | agentArgsMapStr, _ := json.Marshal(agentArgsMap) 135 | fmt.Println(string(agentArgsMapStr)) 136 | os.Exit(0) 137 | } 138 | 139 | if generalCfg.runAgents { 140 | time.Sleep(5000 * time.Millisecond) 141 | } 142 | 143 | // Initialize management connections 144 | for _, a := range c.thAgents { 145 | tcpAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", a.name, c.agentPort)) 146 | if err != nil { 147 | fmt.Println("ResolveTCPAddr failed:", err) 148 | os.Exit(1) 149 | 150 | } 151 | conn, err := net.DialTCP("tcp", nil, tcpAddr) 152 | if err != nil { 153 | fmt.Println("Dial failed:", err) 154 | os.Exit(1) 155 | 156 | } 157 | defer conn.Close() 158 | a.conn = conn 159 | } 160 | for _, a := range c.ltAgents { 161 | tcpAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", a.name, c.agentPort)) 162 | if err != nil { 163 | fmt.Println("ResolveTCPAddr failed:", err) 164 | os.Exit(1) 165 | 166 | } 167 | conn, err := net.DialTCP("tcp", nil, tcpAddr) 168 | if err != nil { 169 | fmt.Println("Dial failed:", err) 170 | os.Exit(1) 171 | 172 | } 173 | defer conn.Close() 174 | a.conn = conn 175 | } 176 | for _, a := range c.symAgents { 177 | tcpAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", a.name, c.agentPort)) 178 | if err != nil { 179 | fmt.Println("ResolveTCPAddr failed:", err) 180 | os.Exit(1) 181 | 182 | } 183 | conn, err := net.DialTCP("tcp", nil, tcpAddr) 184 | if err != nil { 185 | fmt.Println("Dial failed:", err) 186 | os.Exit(1) 187 | 188 | } 189 | defer conn.Close() 190 | a.conn = conn 191 | } 192 | 193 | // Run experiment 194 | c.shouldWaitConn = false 195 | if serverCfg.comProto == "TCP" { 196 | c.shouldWaitConn = true 197 | } 198 | err = c.runExp(expCfg.loadPattern, expCfg.ltRate, expCfg.ciSize) 199 | if err != nil { 200 | fmt.Println(err) 201 | os.Exit(1) 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /agent-manager/manager/agentcontroller.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | import subprocess 23 | import time 24 | import posix_ipc 25 | import mmap 26 | import ctypes 27 | import shlex 28 | 29 | import logging 30 | logging.basicConfig() 31 | log = logging.getLogger(__file__) 32 | log.setLevel(logging.DEBUG) 33 | 34 | MAX_PER_THREAD_SAMPLES = 131072 35 | MAX_PER_THREAD_TX_SAMPLES = 4096 36 | 37 | class AgentControlBlock(ctypes.Structure): 38 | _pack_ = 1 39 | _fields_ = [ 40 | ('idist', ctypes.c_char * 52), 41 | ('should_load', ctypes.c_int), 42 | ('should_measure', ctypes.c_int), 43 | ('thread_count', ctypes.c_int), 44 | ('agent_type', ctypes.c_int), 45 | ('sample_count', ctypes.c_uint32), 46 | ('sampling_rate', ctypes.c_double), 47 | ('conn_open', ctypes.c_int), 48 | ] 49 | 50 | class Timespec(ctypes.Structure): 51 | _pack_ = 1 52 | _fields_ = [ 53 | ('sec', ctypes.c_uint64), 54 | ('nsec', ctypes.c_uint64), 55 | ] 56 | 57 | class TxTimestamps(ctypes.Structure): 58 | _pack_ = 1 59 | _fields_ = [ 60 | ('Count', ctypes.c_uint32), 61 | ('Samples', Timespec * MAX_PER_THREAD_SAMPLES) 62 | ] 63 | 64 | class ThroughputStats(ctypes.Structure): 65 | _pack_ = 1 66 | _fields_ = [ 67 | ('RxBytes', ctypes.c_uint64), 68 | ('RxReqs', ctypes.c_uint64), 69 | ('TxBytes', ctypes.c_uint64), 70 | ('TxReqs', ctypes.c_uint64), 71 | ('TxTs', TxTimestamps), 72 | ] 73 | 74 | 75 | class LatSample(ctypes.Structure): 76 | _pack_ = 1 77 | _fields_ = [ 78 | ('nsec_latency', ctypes.c_uint64), 79 | ('sec_send', ctypes.c_uint64), 80 | ('nsec_send', ctypes.c_uint64), 81 | ] 82 | 83 | class LatencyStats(ctypes.Structure): 84 | _pack_ = 1 85 | _fields_ = [ 86 | ('RxBytes', ctypes.c_uint64), 87 | ('RxReqs', ctypes.c_uint64), 88 | ('TxBytes', ctypes.c_uint64), 89 | ('TxReqs', ctypes.c_uint64), 90 | ('IncIdx', ctypes.c_uint32), 91 | ('Samples', LatSample * MAX_PER_THREAD_SAMPLES), 92 | ('TxTs', TxTimestamps), 93 | ] 94 | 95 | class LancetController: 96 | 97 | def __init__(self, librand_path): 98 | assert librand_path.exists() and librand_path.is_file(), "Bad librand path at {}".format(librand_path) 99 | extc = ctypes.CDLL(librand_path.absolute().as_posix()) 100 | self.set_load_fn = extc.set_avg_ext 101 | self.thread_stats = [] 102 | 103 | def retry_open_shmem(self, path, tries=10, delay=1): 104 | shm = None 105 | for _i in range(tries): 106 | try: 107 | shm = posix_ipc.SharedMemory(path, 0) 108 | except posix_ipc.ExistentialError: 109 | time.sleep(delay) 110 | continue 111 | break 112 | assert shm is not None, path 113 | return shm 114 | 115 | def launch_agent(self, args): 116 | launch_args = [str(args.agent.as_posix())] + shlex.split(" ".join(args.agent_args)) 117 | log.debug("Agent launch command: \"{}\"".format(launch_args)) 118 | self.agent = subprocess.Popen(launch_args) 119 | shm = self.retry_open_shmem('/lancetcontrol') 120 | buffer = mmap.mmap(shm.fd, ctypes.sizeof(AgentControlBlock), 121 | mmap.MAP_SHARED, mmap.PROT_WRITE) 122 | self.acb = AgentControlBlock.from_buffer(buffer) 123 | # Map the stats 124 | if self.acb.agent_type == 0: 125 | for i in range(self.acb.thread_count): 126 | shm = self.retry_open_shmem('/lancet-stats{}'.format(i)) 127 | buffer = mmap.mmap(shm.fd, ctypes.sizeof(ThroughputStats), 128 | mmap.MAP_SHARED, mmap.PROT_WRITE) 129 | self.thread_stats.append(ThroughputStats.from_buffer(buffer)) 130 | elif (self.acb.agent_type == 1 or self.acb.agent_type == 2 or self.acb.agent_type == 3): 131 | for i in range(self.acb.thread_count): 132 | shm = self.retry_open_shmem('/lancet-stats{}'.format(i)) 133 | buffer = mmap.mmap(shm.fd, ctypes.sizeof(LatencyStats), 134 | mmap.MAP_SHARED, mmap.PROT_WRITE) 135 | self.thread_stats.append(LatencyStats.from_buffer(buffer)) 136 | else: 137 | assert False 138 | 139 | 140 | def start_load(self, load): 141 | self.acb.should_measure = 0 142 | per_thread_load = float(load.value) / self.acb.thread_count 143 | l = ctypes.c_double(1e6 / per_thread_load) 144 | self.set_load_fn(ctypes.byref(self.acb), l) 145 | self.acb.should_load = 1 146 | 147 | def start_measure(self, sample_count, sampling_rate): 148 | self.clear_stats() 149 | print("Will start load for {} samples with {} sampling rate".format(sample_count, sampling_rate)) 150 | self.acb.sample_count = int(sample_count / self.acb.thread_count) 151 | self.acb.sampling_rate = sampling_rate / 100.0 152 | self.acb.should_measure = 1 153 | 154 | def get_conn_open(self): 155 | return self.acb.conn_open 156 | 157 | def terminate(self): 158 | self.agent.kill() 159 | 160 | def get_stats(self): 161 | self.acb.should_measure = 0 162 | return self.thread_stats 163 | 164 | def check_agent(self): 165 | return self.agent.poll() 166 | 167 | def get_per_thread_samples(self): 168 | return self.acb.sample_count 169 | 170 | def clear_stats(self): 171 | for stats in self.thread_stats: 172 | stats.RxBytes = 0 173 | stats.RxReqs = 0 174 | stats.TxBytes = 0 175 | stats.TxReqs = 0 176 | stats.TxTs.Count = 0 177 | 178 | if self.acb.agent_type > 0: # clear latency stats 179 | stats.IncIdx = 0 180 | -------------------------------------------------------------------------------- /agent-manager/manager/stats.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | import numpy 23 | import math 24 | import sys 25 | from scipy.stats import spearmanr, anderson, kstest, ks_2samp 26 | from statsmodels.tsa.stattools import adfuller 27 | 28 | from manager.agentcontroller import MAX_PER_THREAD_SAMPLES, MAX_PER_THREAD_TX_SAMPLES 29 | 30 | IID_A_VAL = 1e-10 31 | 32 | class LancetThroughputStats: 33 | def __init__(self): 34 | self.RxBytes = 0 35 | self.RxReqs = 0 36 | self.TxBytes = 0 37 | self.TxReqs = 0 38 | self.ia_is_correct = False 39 | 40 | class LancetLatencyStats: 41 | def _init_(self): 42 | self.throughput_stats = LancetThroughputStats() 43 | self.Avg_latency = 0 44 | self.P50i = 0 45 | self.P50 = 0 46 | self.P50k = 0 47 | self.P90i = 0 48 | self.P90 = 0 49 | self.P90k = 0 50 | self.P95i = 0 51 | self.P95 = 0 52 | self.P95k = 0 53 | self.P99i = 0 54 | self.P99 = 0 55 | self.P99k = 0 56 | self.P999i = 0 57 | self.P999 = 0 58 | self.P999k = 0 59 | self.P9999i = 0 60 | self.P9999 = 0 61 | self.P9999k = 0 62 | self.P99999i = 0 63 | self.P99999 = 0 64 | self.P99999k = 0 65 | self.P999999i = 0 66 | self.P999999 = 0 67 | self.P999999k = 0 68 | self.IsIID = 0 69 | self.ToReduce = 0 70 | 71 | def get_ci(samples, percentile): 72 | size = len(samples) 73 | heta = 1.96 # for 95th confidence 74 | prod = size*percentile 75 | j = math.floor(prod - heta*math.sqrt(prod*(1-percentile))) 76 | k = math.ceil(prod + heta*math.sqrt(prod*(1-percentile))) + 1 77 | j, k = int(j), int(k) 78 | j, k = [int(samples[x]) if 0 <= x < size else 0 for x in [j, k]] 79 | return j, k 80 | 81 | def check_iid(all_latency_stats, per_thread_samples): 82 | if (len(all_latency_stats) == 0): 83 | print("No latency stats") 84 | return False, -1 85 | lags = [2,5,10,25,50,100, 200, 500, 1000] 86 | data = [] 87 | for s in all_latency_stats: 88 | sample_count = min(per_thread_samples, s.IncIdx) 89 | data += map(lambda x: (x.nsec_latency, x.sec_send*1e9+x.nsec_send), 90 | s.Samples[:sample_count]) 91 | data.sort(key=lambda x: x[1]) 92 | latencies = list(map(lambda x: x[0], data)) 93 | corr, p = spearmanr(latencies[:-1], latencies[1:]) 94 | print("Spearman {} {}".format(corr, p)) 95 | 96 | if p > IID_A_VAL: 97 | # Data are iid 98 | return True, 0 99 | for lag in lags: 100 | corr, p = spearmanr(latencies[:-lag], latencies[lag:]) 101 | if p > IID_A_VAL: 102 | break 103 | 104 | return False, lag 105 | 106 | def check_interarrival(all_stats): 107 | all_tx_samples = [] 108 | for s in all_stats: 109 | sample_count = min(MAX_PER_THREAD_TX_SAMPLES, s.TxTs.Count) 110 | all_tx_samples += map(lambda x: x.sec*1e9+x.nsec, s.TxTs.Samples[:sample_count]) 111 | 112 | if len(all_tx_samples) == 0: 113 | print("No tx samples") 114 | return False 115 | res = anderson(all_tx_samples, dist='expon') 116 | print(res) 117 | return res[0] < 2*res[1][4] 118 | 119 | def check_stationarity(all_latency_stats, per_thread_samples): 120 | return True 121 | data = [] 122 | for s in all_latency_stats: 123 | sample_count = min(per_thread_samples, s.IncIdx) 124 | data += map(lambda x: (x.nsec_latency, x.sec_send*1e9+x.nsec_send), 125 | s.Samples[:sample_count]) 126 | data.sort(key=lambda x: x[1]) 127 | latencies = list(map(lambda x: x[0], data)) 128 | 129 | try: 130 | adf_res = adfuller(latencies) 131 | except ValueError as e: 132 | print("adfuller error {} {}".format(len(latencies), e), file=sys.stderr) 133 | return False 134 | return adf_res[0] < 0 135 | 136 | def aggregate_throughput(stats): 137 | agg = LancetThroughputStats() 138 | for s in stats: 139 | agg.RxBytes += s.RxBytes 140 | agg.RxReqs += s.RxReqs 141 | agg.TxBytes += s.TxBytes 142 | agg.TxReqs += s.TxReqs 143 | 144 | agg.ia_is_correct = check_interarrival(stats) 145 | 146 | return agg 147 | 148 | def aggregate_latency(stats, per_thread_samples): 149 | agg = LancetLatencyStats() 150 | agg.throughput_stats = aggregate_throughput(stats) 151 | 152 | all_samples = [] 153 | for s in stats: 154 | sample_count = min(per_thread_samples, s.IncIdx) 155 | all_samples += map(lambda x: x.nsec_latency, s.Samples[:sample_count]) 156 | 157 | # Uncomment in order to collect samples 158 | #with open("/tmp/kogias/lancet-samples", 'w') as f: 159 | # f.write("\n".join(map(str, all_samples))) 160 | 161 | print("There are {} samples".format(len(all_samples))) 162 | all_samples.sort() 163 | agg.Avg_latency = int(numpy.mean(all_samples)) 164 | agg.P50 = int(numpy.percentile(all_samples, 50)) 165 | agg.P50i, agg.P50k = get_ci(all_samples, 0.5) 166 | agg.P90 = int(numpy.percentile(all_samples, 90)) 167 | agg.P90i, agg.P90k = get_ci(all_samples, 0.9) 168 | agg.P95 = int(numpy.percentile(all_samples, 95)) 169 | agg.P95i, agg.P95k = get_ci(all_samples, 0.95) 170 | agg.P99 = int(numpy.percentile(all_samples, 99)) 171 | agg.P99i, agg.P99k = get_ci(all_samples, 0.99) 172 | agg.P999 = int(numpy.percentile(all_samples, 99.9)) 173 | agg.P999i, agg.P999k = get_ci(all_samples, 0.999) 174 | agg.P9999 = int(numpy.percentile(all_samples, 99.99)) 175 | agg.P9999i, agg.P9999k = get_ci(all_samples, 0.9999) 176 | agg.P99999 = int(numpy.percentile(all_samples, 99.999)) 177 | agg.P99999i, agg.P99999k = get_ci(all_samples, 0.99999) 178 | agg.P999999 = int(numpy.percentile(all_samples, 99.9999)) 179 | agg.P999999i, agg.P999999k = get_ci(all_samples, 0.999999) 180 | agg.is_stationary = check_stationarity(stats, per_thread_samples) 181 | is_iid, to_reduce = check_iid(stats, per_thread_samples) 182 | agg.IsIID = is_iid 183 | agg.ToReduce = to_reduce 184 | 185 | return agg 186 | -------------------------------------------------------------------------------- /agent-manager/manager/proto.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | import ctypes 23 | import io 24 | 25 | class MsgHdr(ctypes.Structure): 26 | _pack_ = 1 27 | _fields_ = [ 28 | ('MessageType', ctypes.c_uint32), 29 | ('MessageLength', ctypes.c_uint32), 30 | ] 31 | 32 | class Msg1(ctypes.Structure): 33 | _pack_ = 1 34 | _fields_ = [ 35 | ('MessageType', ctypes.c_uint32), 36 | ('MessageLength', ctypes.c_uint32), 37 | ('Info', ctypes.c_uint32), 38 | ] 39 | 40 | class StartLoadMsg(ctypes.Structure): 41 | _pack_ = 1 42 | _fields_ = [ 43 | ('SampleCount', ctypes.c_uint32), 44 | ('SamplingRate', ctypes.c_double), 45 | ] 46 | 47 | class ThroughputReply(ctypes.Structure): 48 | _pack_ = 1 49 | _fields_ = [ 50 | ('RxBytes', ctypes.c_uint64), 51 | ('TxBytes', ctypes.c_uint64), 52 | ('ReqCount', ctypes.c_uint64), 53 | ('Duration', ctypes.c_uint64), 54 | ('CorrectIAD', ctypes.c_uint64), 55 | ] 56 | 57 | class LatencyReply(ctypes.Structure): 58 | _pack_ = 1 59 | _fields_ = [ 60 | ('Th_data', ThroughputReply), 61 | ('Avg_latency', ctypes.c_uint64), 62 | ('P50i', ctypes.c_uint64), 63 | ('P50', ctypes.c_uint64), 64 | ('P50k', ctypes.c_uint64), 65 | ('P90i', ctypes.c_uint64), 66 | ('P90', ctypes.c_uint64), 67 | ('P90k', ctypes.c_uint64), 68 | ('P95i', ctypes.c_uint64), 69 | ('P95', ctypes.c_uint64), 70 | ('P95k', ctypes.c_uint64), 71 | ('P99i', ctypes.c_uint64), 72 | ('P99', ctypes.c_uint64), 73 | ('P99k', ctypes.c_uint64), 74 | ('P999i', ctypes.c_uint64), 75 | ('P999', ctypes.c_uint64), 76 | ('P999k', ctypes.c_uint64), 77 | ('P9999i', ctypes.c_uint64), 78 | ('P9999', ctypes.c_uint64), 79 | ('P9999k', ctypes.c_uint64), 80 | ('P99999i', ctypes.c_uint64), 81 | ('P99999', ctypes.c_uint64), 82 | ('P99999k', ctypes.c_uint64), 83 | ('P999999i', ctypes.c_uint64), 84 | ('P999999', ctypes.c_uint64), 85 | ('P999999k', ctypes.c_uint64), 86 | ('ToReduceSampling', ctypes.c_uint32), 87 | ('IsIID', ctypes.c_uint8), 88 | ('IsStationary', ctypes.c_uint8), 89 | ] 90 | 91 | class MsgInternal: 92 | def __init__(self, msg_type, info): 93 | self.msg_type = msg_type 94 | self.info = info 95 | 96 | class LancetProto: 97 | def __init__(self, conn): 98 | self.conn = conn 99 | 100 | def recv_msg(self): 101 | data = self.conn.recv(8) 102 | if len(data) == 0: 103 | self.conn.close() 104 | lancet_msg = MsgInternal(-1, 0) 105 | return lancet_msg 106 | assert len(data) == 8 107 | buf = ctypes.create_string_buffer(data) 108 | msg = MsgHdr.from_buffer(buf) 109 | 110 | if msg.MessageType == 0: # START_LOAD 111 | data = self.conn.recv(4) 112 | assert len(data) == 4 113 | buf = ctypes.create_string_buffer(data) 114 | load = ctypes.c_uint32.from_buffer(buf) 115 | lancet_msg = MsgInternal(0, load) 116 | self.reply_ack() 117 | elif msg.MessageType == 1: # START_MEASURE 118 | data = self.conn.recv(12) 119 | assert len(data) == 12 120 | buf = ctypes.create_string_buffer(data) 121 | msg = StartLoadMsg.from_buffer(buf) 122 | lancet_msg = MsgInternal(1, msg) 123 | self.reply_ack() 124 | elif msg.MessageType == 2: # REPORT_REQ 125 | data = self.conn.recv(4) 126 | assert len(data) == 4 127 | buf = ctypes.create_string_buffer(data) 128 | report_type = ctypes.c_uint32.from_buffer(buf) 129 | lancet_msg = MsgInternal(2, report_type.value) 130 | elif msg.MessageType == 5: # CONN_OPEN 131 | data = self.conn.recv(4) 132 | assert len(data) == 4 133 | lancet_msg = MsgInternal(3, None) 134 | else: 135 | assert False 136 | return lancet_msg 137 | 138 | def reply_ack(self): 139 | msg = Msg1() 140 | msg.MessageType = 3 # Reply 141 | msg.MessageLength = 4 142 | msg.Info = 0 # REPLY_ACK 143 | self.conn.send(msg) 144 | 145 | def reply_value(self, value): 146 | msg = Msg1() 147 | msg.MessageType = 3 # Reply 148 | msg.MessageLength = 4 149 | msg.Info = value 150 | self.conn.send(msg) 151 | 152 | def reply_throughput(self, stats): 153 | msg = Msg1() 154 | msg.MessageType = 3 # Reply 155 | msg.MessageLength = 36 # throughput stats + type 156 | msg.Info = 1 # REPLY_STATS_THROUGHPUT 157 | reply = ThroughputReply() 158 | reply.Duration = int(1e6*stats.duration) 159 | reply.RxBytes = stats.RxBytes 160 | reply.TxBytes = stats.TxBytes 161 | reply.ReqCount = stats.RxReqs 162 | reply.CorrectIAD = stats.ia_is_correct 163 | replyBuf = io.BytesIO() 164 | replyBuf.write(msg) 165 | replyBuf.write(reply) 166 | self.conn.send(replyBuf.getvalue()) 167 | replyBuf.close() 168 | 169 | def reply_latency(self, stats): 170 | msg = Msg1() 171 | msg.MessageType = 3 # Reply 172 | msg.MessageLength = 140 # latency stats + type 173 | msg.Info = 2 # REPLY_STATS_LATENCY 174 | reply = LatencyReply() 175 | reply.Th_data.Duration = int(1e6*stats.duration) 176 | reply.Th_data.RxBytes = stats.throughput_stats.RxBytes 177 | reply.Th_data.TxBytes = stats.throughput_stats.TxBytes 178 | reply.Th_data.ReqCount = stats.throughput_stats.RxReqs 179 | reply.Th_data.CorrectIAD = stats.throughput_stats.ia_is_correct 180 | reply.Avg_latency = stats.Avg_latency 181 | reply.P50i = stats.P50i 182 | reply.P50 = stats.P50 183 | reply.P50k = stats.P50k 184 | reply.P90i = stats.P90i 185 | reply.P90 = stats.P90 186 | reply.P90k = stats.P90k 187 | reply.P95i = stats.P95i 188 | reply.P95 = stats.P95 189 | reply.P95k = stats.P95k 190 | reply.P99i = stats.P99i 191 | reply.P99 = stats.P99 192 | reply.P99k = stats.P99k 193 | reply.P999i = stats.P999i 194 | reply.P999 = stats.P999 195 | reply.P999k = stats.P999k 196 | reply.P9999i = stats.P9999i 197 | reply.P9999 = stats.P9999 198 | reply.P9999k = stats.P9999k 199 | reply.P99999i = stats.P99999i 200 | reply.P99999 = stats.P99999 201 | reply.P99999k = stats.P99999k 202 | reply.P999999i = stats.P999999i 203 | reply.P999999 = stats.P999999 204 | reply.P999999k = stats.P999999k 205 | reply.IsIID = stats.IsIID 206 | reply.ToReduceSampling = stats.ToReduce 207 | reply.IsStationary = stats.is_stationary 208 | replyBuf = io.BytesIO() 209 | replyBuf.write(msg) 210 | replyBuf.write(reply) 211 | self.conn.send(replyBuf.getvalue()) 212 | replyBuf.close() 213 | 214 | def close(self): 215 | self.conn.close() 216 | -------------------------------------------------------------------------------- /agents/app_proto.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #ifdef ENABLE_R2P2 37 | #include 38 | #endif 39 | 40 | __thread void *per_thread_arg = NULL; 41 | char random_char[MAX_VAL_SIZE]; 42 | 43 | /* 44 | * Echo protocol 45 | */ 46 | int echo_create_request(struct application_protocol *proto, struct request *req) 47 | { 48 | struct iovec *fixed_req = (struct iovec *)proto->arg; 49 | req->iovs[0] = *fixed_req; 50 | req->iov_cnt = 1; 51 | req->meta = NULL; 52 | 53 | return 0; 54 | } 55 | 56 | struct byte_req_pair echo_consume_response(struct application_protocol *proto, 57 | struct iovec *response) 58 | { 59 | struct byte_req_pair res; 60 | 61 | struct iovec *msg = (struct iovec *)proto->arg; 62 | 63 | res.reqs = response->iov_len / msg->iov_len; 64 | res.bytes = res.reqs * msg->iov_len; 65 | 66 | return res; 67 | } 68 | 69 | static int echo_init(char *proto, struct application_protocol *app_proto) 70 | { 71 | char *token; 72 | struct iovec *arg; 73 | int message_len; 74 | 75 | token = strtok(proto, ":"); 76 | token = strtok(NULL, ":"); 77 | 78 | message_len = atoi(token); 79 | arg = malloc(sizeof(struct iovec)); 80 | assert(arg); 81 | arg->iov_base = malloc(message_len); 82 | assert(arg->iov_base); 83 | memset(arg->iov_base, '#', message_len); 84 | arg->iov_len = message_len; 85 | 86 | app_proto->type = PROTO_ECHO; 87 | // The proto arg is the iovec with the message 88 | app_proto->arg = arg; 89 | app_proto->create_request = echo_create_request; 90 | app_proto->consume_response = echo_consume_response; 91 | 92 | return 0; 93 | } 94 | 95 | /* 96 | * Synthetic protocol 97 | */ 98 | int synthetic_create_request(struct application_protocol *proto, 99 | struct request *req) 100 | { 101 | struct rand_gen *generator = (struct rand_gen *)proto->arg; 102 | 103 | if (!per_thread_arg) { 104 | per_thread_arg = malloc(sizeof(long)); 105 | assert(per_thread_arg); 106 | } 107 | per_thread_arg = (void *)lround(generate(generator)); 108 | req->iovs[0].iov_base = &per_thread_arg; 109 | req->iovs[0].iov_len = sizeof(long); 110 | req->iov_cnt = 1; 111 | req->meta = NULL; 112 | 113 | return 0; 114 | } 115 | 116 | struct byte_req_pair 117 | synthetic_consume_response(struct application_protocol *proto, 118 | struct iovec *response) 119 | { 120 | struct byte_req_pair res; 121 | 122 | res.reqs = response->iov_len / sizeof(long); 123 | res.bytes = res.reqs * sizeof(long); 124 | return res; 125 | } 126 | 127 | static int synthetic_init(char *proto, struct application_protocol *app_proto) 128 | { 129 | char *token; 130 | struct rand_gen *gen = NULL; 131 | 132 | // Remove the type. 133 | token = strtok(proto, ":"); 134 | token = strtok(NULL, ""); 135 | 136 | gen = init_rand(token); 137 | assert(gen); 138 | 139 | app_proto->type = PROTO_SYNTHETIC; 140 | // The proto arg is the random generator 141 | app_proto->arg = gen; 142 | app_proto->create_request = synthetic_create_request; 143 | app_proto->consume_response = synthetic_consume_response; 144 | 145 | return 0; 146 | } 147 | 148 | /* 149 | * STSS - synthetic time sythetic size protocol 150 | */ 151 | struct stss_data { 152 | struct rand_gen *time_gen; 153 | struct rand_gen *req_size_gen; 154 | struct rand_gen *rep_size_gen; 155 | int replicated; 156 | double read_ratio; 157 | }; 158 | 159 | /* 160 | * Request 3 longs service_time, request_size, reply_size 161 | */ 162 | int stss_create_request(struct application_protocol *proto, 163 | struct request *req) 164 | { 165 | struct stss_data *data = (struct stss_data *)proto->arg; 166 | long *req_payload; 167 | 168 | if (!per_thread_arg) { 169 | per_thread_arg = malloc(3*sizeof(long)); 170 | assert(per_thread_arg); 171 | } 172 | req_payload = (long *)per_thread_arg; 173 | req_payload[0] = lround(generate(data->time_gen)); 174 | req_payload[1] = lround(generate(data->req_size_gen)); 175 | req_payload[2] = lround(generate(data->rep_size_gen)); 176 | req->iovs[0].iov_base = per_thread_arg; 177 | req->iovs[0].iov_len = 3*sizeof(long); 178 | req->iovs[1].iov_base = random_char; 179 | req->iovs[1].iov_len = req_payload[1]; 180 | req->iov_cnt = 2; 181 | if (data->replicated) { 182 | #ifdef ENABLE_R2P2 183 | if (drand48() <= data->read_ratio) 184 | req->meta = (void *)(unsigned long)REPLICATED_ROUTE_NO_SE; 185 | else 186 | req->meta = (void *)(unsigned long)REPLICATED_ROUTE; 187 | #else 188 | assert(0); 189 | #endif 190 | } else 191 | req->meta = NULL; 192 | 193 | return 0; 194 | } 195 | 196 | /* 197 | * Reply size 1 long (#bytes to follow) + payload 198 | */ 199 | struct byte_req_pair stss_consume_response(struct application_protocol *proto, 200 | struct iovec *response) 201 | { 202 | struct byte_req_pair res = {0}; 203 | long *payload_size; 204 | char *tmp; 205 | int processed; 206 | 207 | assert(response->iov_len >= sizeof(long)); 208 | tmp = response->iov_base; 209 | while (1) { 210 | payload_size = (long *)&tmp[processed]; 211 | processed += sizeof(long); 212 | if (*payload_size < (response->iov_len - processed)) 213 | break; 214 | processed += *payload_size; 215 | res.reqs += 1; 216 | res.bytes += (sizeof(long) + *payload_size); 217 | if (sizeof(long) < (response->iov_len - processed)) 218 | break; 219 | } 220 | return res; 221 | } 222 | 223 | static int stss_init(char *proto, struct application_protocol *app_proto) 224 | { 225 | struct stss_data *data; 226 | char *token, *saveptr; 227 | 228 | data = calloc(1, sizeof(struct stss_data)); 229 | assert(data); 230 | 231 | saveptr = proto; 232 | token = strtok_r(saveptr, "_", &saveptr); 233 | if (strncmp(token, "stssr", 5) == 0) { 234 | data->replicated = 1; 235 | } else if (strncmp(token, "stss", 4) == 0) { 236 | data->replicated = 0; 237 | } else 238 | assert(0); 239 | token = strtok_r(saveptr, "_", &saveptr); 240 | data->time_gen = init_rand(token); 241 | assert(data->time_gen); 242 | token = strtok_r(saveptr, "_", &saveptr); 243 | data->req_size_gen = init_rand(token); 244 | assert(data->req_size_gen); 245 | token = strtok_r(saveptr, "_", &saveptr); 246 | data->rep_size_gen = init_rand(token); 247 | assert(data->rep_size_gen); 248 | if (data->replicated) { 249 | token = strtok_r(saveptr, "_", &saveptr); 250 | data->read_ratio = atof(token); 251 | } 252 | 253 | app_proto->arg = data; 254 | app_proto->create_request = stss_create_request; 255 | app_proto->consume_response = stss_consume_response; 256 | app_proto->type = PROTO_STSS; 257 | 258 | return 0; 259 | } 260 | 261 | struct application_protocol *init_app_proto(char *proto) 262 | { 263 | struct application_protocol *app_proto; 264 | 265 | app_proto = malloc(sizeof(struct application_protocol)); 266 | assert(app_proto); 267 | 268 | // Init random char 269 | memset(random_char, 'x', MAX_VAL_SIZE); 270 | 271 | if (strncmp(proto, "echo", 4) == 0) 272 | echo_init(proto, app_proto); 273 | else if (strncmp(proto, "synthetic", 9) == 0) 274 | synthetic_init(proto, app_proto); 275 | else if (strncmp(proto, "redis", 5) == 0) 276 | redis_init(proto, app_proto); 277 | else if (strncmp(proto, "memcache", 8) == 0) 278 | memcache_init(proto, app_proto); 279 | else if (strncmp(proto, "http", 4) == 0) { 280 | int i = http_proto_init(proto, app_proto); 281 | if (i != 0) 282 | return NULL; 283 | } else if (strncmp(proto, "stss", 4) == 0) 284 | stss_init(proto, app_proto); 285 | else { 286 | lancet_fprintf(stderr, "Unknown application protocol\n"); 287 | return NULL; 288 | } 289 | 290 | return app_proto; 291 | } 292 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lancet 2 | Lancet is a distributed latency measuring tool. It leverages NIC-based timestamping to improve measuring accuracy and it depends on a self-correcting measuring methodology to eliminate measuring pitfalls. 3 | For more refer to the Lancet [paper](https://www.usenix.org/conference/atc19/presentation/kogias-lancet). 4 | 5 | # Building Lancet 6 | 7 | Lancet has multiple components: 8 | 9 | * The coordinator that launches and manages all agents is a self-contained Golang program found in `coordinator/`. It is built using standard `go build` 10 | * The agents that perform throughput and latency are written in C and are found in the `agents/` directory. They are built using `cmake` (and in turn `ninja` or `make`, depending on what you have installed). 11 | * The agent manager is a pure Python program that is built into a package (wheel in Python parlance) and is in `agent-manager`. 12 | 13 | You need to build the different components separately. 14 | Making the agents manually involves using CMake. 15 | You may need a newer version of CMake than your distribution provides. 16 | We recommend installing CMake via `pip`: 17 | 18 | ``` 19 | pip3 install cmake 20 | ``` 21 | ## Vanilla Lancet build 22 | Lancet supports TCP and UDP with hardware timestamping. To build run the following: 23 | ``` 24 | make coordinator 25 | make agents 26 | make manager 27 | ``` 28 | 29 | ## Building with R2P2 30 | If you also want to build with [R2P2](https://github.com/epfl-dcsl/r2p2) run the following: 31 | ``` 32 | make coordinator 33 | make agents_r2p2 R2P2= 34 | make manager 35 | ``` 36 | 37 | ## Building with R2P2 with hardware timestamping support 38 | If you want to use the hardware timestamping support for R2P2 run the following: 39 | ``` 40 | make coordinator 41 | make agents_r2p2_nic_ts R2P2= 42 | make manager 43 | ``` 44 | Note: For TCP and UDP hardware timestamping you don't need to build differently 45 | 46 | # Running Lancet 47 | Lancet is a distributed tool. There are several agents and one coordinator. The coordinator is in charge of spawning and controlling the agents. So, users are expected first deploy the lancet agents and then only interact with them through the coordinator. 48 | 49 | **Note:** In order to use Lancet's hardware timestamping feature you will need a Linux kernel >= 4.19.4. Prior kernel versions might lead to incorrect results. Also, you need a NIC with hardware timestamping support. We've tested Lancet with Mellanox Connect-x4. 50 | 51 | ## Deploy Lancet Agents 52 | Before running Lancet you need to deploy the agents. 53 | ``` 54 | make deploy HOSTS= 55 | ``` 56 | The above command will copy the necessary Lancet assets and install them in ``/tmp//lancet``. Lancet uses a python virtualenv. So, it assumes there is a ``virtualenv`` is installed in the agent machines. 57 | 58 | ## Arguments 59 | Lancet supports a series of arguments, most of them are self explanatory. 60 | ``` 61 | $./coordinator/coordinator -h 62 | Usage of ./coordinator/coordinator: 63 | -agentPort int 64 | listening port of the agent (default 5001) 65 | -appProto string 66 | application protocol (default "echo:4") 67 | -ciSize int 68 | size of 95-confidence interval in us (default 10) 69 | -comProto string 70 | TCP|R2P2|UDP (default "TCP") 71 | -idist string 72 | interarrival distibution: fixed, exp (default "exp") 73 | -ifName string 74 | interface name for hardware timestamping (default "enp65s0") 75 | -loadAgents string 76 | ip of loading agents separated by commas, e.g. ip1,ip2,... (this can be specified along with symAgents. These would add additional load) 77 | -loadConn int 78 | number of loading connections per agent (used for both load and sym agents) (default 1) 79 | -loadPattern string 80 | load pattern (default "fixed:10000") 81 | -loadThreads int 82 | loading threads per agent (used for load and sym agents) (default 1) 83 | -lqps int 84 | latency qps (default 4000) 85 | -ltAgents string 86 | ip of latency agents separated by commas, e.g. ip1,ip2,... 87 | -ltConn int 88 | number of latency connections (default 1) 89 | -ltThreads int 90 | latency threads per agent (default 1) 91 | -nicTS 92 | NIC timestamping for symmetric agents 93 | -privateKey string 94 | location of the (local) private key to deploy the agents. Will find a default if not specified (default "$HOME/.ssh/id_rsa") 95 | -reqPerConn int 96 | Number of outstanding requests per TCP connection (used for both load and sym agents) (default 1) 97 | -symAgents string 98 | ip of latency agents separated by commas, e.g. ip1,ip2,... 99 | -targetHost string 100 | host:port comma-separated list to run experiment against (default "127.0.0.1:8000") 101 | ``` 102 | 103 | ## Application Protocols 104 | Lancet supports a few application protocols, while it can be easily extended with new ones. Currently, we support the following protocols: 105 | 106 | ### Echo protocol 107 | ``-appProto echo:``
108 | This protocol sends the specified number of bytes and waits the same number back as an answer. For example, ``echo:4`` will send and expect 4 bytes as a response. 109 | 110 | ### Synthetic protocol 111 | ``-appProto synthetic:``
112 | This protocol sends a random long integer (8 bytes) as a payload and expects an long integer as a reply, too. This protocol is used in synthetic service time microbenchmarks. For example, ``synthetic:fixed:10`` always sends the number 10 as a payload, while ``synthetic:exp:10`` generates and sends random numbers from an exponential distribution with an average of 10. 113 | 114 | ### KV-store Protocols 115 | Currently lancet supports 3 different key-value store protocols: **binary memcached**, **ascii memcached**, and **Redis**. Defining the KV-store workload is the same across all protocols. 116 | ``` 117 | -appProto _____ 118 | ``` 119 | 120 | For example ``memcache-bin_fixed:10_fixed:2_1000000_0.998_uni`` specifies a KV-store workload with 1000000 keys of with fixed size of 10 bytes, fixed values of 2 bytes, 0.2 % writes, and random uniform key access pattern (as opposed to round robin). 121 | 122 | ### HTTP Protocol 123 | 124 | Running with the HTTP agent requires the following parameters to be passed to the `coordinator`: 125 | 126 | * `-comProto TCP`: http only works over TCP 127 | * `-targetHost HOSTNAME:80`, where `HOSTNAME` is the name of the server (and presumably serving HTTP over the standard port 80) 128 | * `-appProto http:SITE/path/to/asset.html`: simply put, the app proto is `http:` followed by a URL. The parameters to make a valid HTTP request are placed in the `appProto` argument. `SITE` is the name of the site (e.g. `example.com`) that is placed in the `Host:` field of the HTTP request. `/path/to/asset.html` is the path to the asset requested, e.g. just `/index.html`; this is used as parameters to the `GET` method in the HTTP request. 129 | 130 | #### Troubleshooting 131 | 132 | Lancet uses long-running connections that opens at the beginning of the experiment. 133 | If you receive an error message saying `"Connection closed"` when running Lancet against your webserver, keep in mind that some webservers limit the number of total requests allowed on a single connection. 134 | For example, in `nginx` you will have to [adjust this number to something _much_ larger than 100](https://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_requests). 135 | 136 | ## Random generators 137 | Lancet implements a series of random generators that are used for the workload configuration, e.g. the inter-arrival time and application protocols. The most commonly used are the following: 138 | 139 | 1. Fixed ``fixed:`` 140 | 2. Exponential ``exp:`` 141 | 3. Bimodal ``bimodal:::`` 142 | 4. Uniform Random ``uni:`` positive integers up to the upper limit 143 | 5. Round Robin ``rr:`` 144 | 145 | For more check the ``init_rand`` function in ``agents/rand_gen.c``. 146 | 147 | ## Load Patterns 148 | Lancet supports the following load patterns: 149 | 150 | 1. ``fixed:[:<#Samples>[:]]``
151 | Fixed load level **without** the self-correcting methodology. Sampling_Rate is specified as a percentage. So 5 means 5%. 152 | 153 | 2. ``fixedQual:[:<#Samples>[:]]``
154 | Fixed load level **with** the self-correcting methodology. The extra arguments only configure the initial values for #Samples and sampling rate. 155 | 156 | 3. ``step:::[:<#Samples>[:]]``
157 | Step load pattern **without** the self-correcting methodology. The extra arguments only configure the initial values for #Samples and sampling rate. 158 | 159 | 4. ``stepQual:::[:<#Samples>[:]]``
160 | Step load pattern **with** the self-correcting methodology used in every step. 161 | 162 | Note: Arguments in parentesis are optional. 163 | The default #Samples is 10000. Default Sampling_Rate is %20. 164 | When running without self-correcting methodology, the test stops after the required number of <#Samples> is collected. The non self-correcting test also doesn't ensure that actual throughput equals expected throughput. For example, actual throughput might be much lower than expected throughput because the throughput-agent cannot generate the required load. 165 | For example, if ltAgents is used and lqps is 4000, #Samples is 10000, and sampling rate is %20. Then it would take roughly 10000/(4000*0.20) seconds = 12.5 seconds to finish the test. 166 | -------------------------------------------------------------------------------- /agents/memcache.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #define _GNU_SOURCE 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | static __thread struct bmc_header header; 39 | static __thread uint64_t extras; 40 | static __thread char val_len_str[64]; 41 | static char get_cmd[] = "get "; 42 | static char set_cmd[] = "set "; 43 | static char rn[] = "\r\n"; 44 | static char set_zeros[] = " 0 0 "; 45 | 46 | enum { WAIT_FOR_HEADER = 0, WAIT_FOR_BODY, FINISHED }; 47 | 48 | static char *strchnth(char *s, char c, int occ) 49 | { 50 | char *ptr; 51 | 52 | ptr = s - 1; 53 | for (int i = 0; i < occ; i++) { 54 | ptr = strchr(ptr + 1, c); 55 | if (!ptr) 56 | return NULL; 57 | } 58 | return ptr; 59 | } 60 | 61 | static struct byte_req_pair 62 | memcache_ascii_consume_response(struct application_protocol *proto, 63 | struct iovec *resp) 64 | { 65 | struct byte_req_pair res; 66 | int bytes_to_process, get_rep_size; 67 | char *buf, *ptr; 68 | 69 | res.bytes = 0; 70 | res.reqs = 0; 71 | bytes_to_process = resp->iov_len; 72 | buf = resp->iov_base; 73 | 74 | while (bytes_to_process) { 75 | if (bytes_to_process < 5) // minimum reply is EDN\r\n 76 | goto OUT; 77 | if (strncmp(&buf[resp->iov_len - bytes_to_process], "END\r\n", 5) == 78 | 0) { 79 | // key not found 80 | res.bytes += 5; 81 | res.reqs += 1; 82 | bytes_to_process -= 5; 83 | continue; 84 | } 85 | if (bytes_to_process < 8) // try STORED\r\n 86 | goto OUT; 87 | if (strncmp(&buf[resp->iov_len - bytes_to_process], "STORED\r\n", 8) == 88 | 0) { 89 | // successful set 90 | res.bytes += 8; 91 | res.reqs += 1; 92 | bytes_to_process -= 8; 93 | continue; 94 | } 95 | // try for get reply - look for 3 \n 96 | ptr = strchnth(&buf[resp->iov_len - bytes_to_process], '\n', 3); 97 | if (!ptr) 98 | goto OUT; 99 | get_rep_size = ptr - &buf[resp->iov_len - bytes_to_process] + 1; 100 | bytes_to_process -= get_rep_size; 101 | res.bytes += get_rep_size; 102 | res.reqs += 1; 103 | } 104 | 105 | OUT: 106 | return res; 107 | } 108 | 109 | static int memcache_ascii_create_request(struct application_protocol *proto, 110 | struct request *req) 111 | { 112 | struct kv_info *info; 113 | long val_len; 114 | int key_idx; 115 | struct iovec *key; 116 | 117 | info = (struct kv_info *)proto->arg; 118 | key_idx = generate(info->key_sel); 119 | key = &info->key->keys[key_idx]; 120 | 121 | if (drand48() > info->get_ratio) { 122 | // set 123 | val_len = lround(generate(info->val_len)); 124 | assert(val_len <= MAX_VAL_SIZE); 125 | snprintf(val_len_str, 64, "%ld", val_len); 126 | 127 | req->iovs[0].iov_base = set_cmd; 128 | req->iovs[0].iov_len = 4; 129 | req->iovs[1].iov_base = key->iov_base; 130 | req->iovs[1].iov_len = key->iov_len; 131 | req->iovs[2].iov_base = set_zeros; 132 | req->iovs[2].iov_len = 5; 133 | req->iovs[3].iov_base = val_len_str; 134 | req->iovs[3].iov_len = strlen(val_len_str); 135 | req->iovs[4].iov_base = rn; 136 | req->iovs[4].iov_len = 2; 137 | req->iovs[5].iov_base = random_char; 138 | req->iovs[5].iov_len = val_len; 139 | req->iovs[6].iov_base = rn; 140 | req->iovs[6].iov_len = 2; 141 | 142 | req->iov_cnt = 7; 143 | } else { 144 | // get 145 | req->iovs[0].iov_base = get_cmd; 146 | req->iovs[0].iov_len = 4; 147 | req->iovs[1].iov_base = key->iov_base; 148 | req->iovs[1].iov_len = key->iov_len; 149 | req->iovs[2].iov_base = rn; 150 | req->iovs[2].iov_len = 2; 151 | 152 | req->iov_cnt = 3; 153 | } 154 | 155 | return 0; 156 | } 157 | 158 | static struct byte_req_pair 159 | memcache_bin_consume_response(struct application_protocol *proto, 160 | struct iovec *resp) 161 | { 162 | struct byte_req_pair res; 163 | int bytes_to_process, state; 164 | struct bmc_header *bmc_header; 165 | char *buf; 166 | long body_len; 167 | 168 | res.bytes = 0; 169 | res.reqs = 0; 170 | 171 | bytes_to_process = resp->iov_len; 172 | 173 | state = WAIT_FOR_HEADER; 174 | buf = resp->iov_base; 175 | while (bytes_to_process) { 176 | switch (state) { 177 | case WAIT_FOR_HEADER: 178 | if (bytes_to_process < sizeof(struct bmc_header)) 179 | goto OUT; 180 | bmc_header = 181 | (struct bmc_header *)&buf[resp->iov_len - bytes_to_process]; 182 | body_len = ntohl(bmc_header->body_len); 183 | state = WAIT_FOR_BODY; 184 | break; 185 | case WAIT_FOR_BODY: 186 | if (bytes_to_process < 187 | (body_len + sizeof(struct bmc_header))) 188 | goto OUT; 189 | state = FINISHED; 190 | break; 191 | case FINISHED: 192 | bytes_to_process -= 193 | (body_len + sizeof(struct bmc_header)); 194 | res.reqs += 1; 195 | res.bytes += (sizeof(struct bmc_header) + body_len); 196 | state = WAIT_FOR_HEADER; 197 | break; 198 | } 199 | } 200 | OUT: 201 | return res; 202 | } 203 | 204 | static int memcache_bin_create_request(struct application_protocol *proto, 205 | struct request *req) 206 | { 207 | struct kv_info *info; 208 | long val_len; 209 | int key_idx; 210 | struct iovec *key; 211 | 212 | bzero(&header, sizeof(struct bmc_header)); 213 | extras = 0; 214 | 215 | info = (struct kv_info *)proto->arg; 216 | key_idx = generate(info->key_sel); 217 | key = &info->key->keys[key_idx]; 218 | 219 | header.magic = 0x80; 220 | header.key_len = htons(key->iov_len); 221 | header.data_type = 0x00; 222 | header.vbucket = 0x00; 223 | assert(key != NULL); 224 | 225 | if (drand48() > info->get_ratio) { 226 | // set 227 | val_len = lround(generate(info->val_len)); 228 | assert(val_len <= MAX_VAL_SIZE); 229 | 230 | header.opcode = CMD_SET; 231 | header.extra_len = 0x08; // sets have extras for flags and expiration 232 | header.body_len = htonl(key->iov_len + val_len + header.extra_len); 233 | 234 | req->iovs[0].iov_base = &header; 235 | req->iovs[0].iov_len = sizeof(struct bmc_header); 236 | req->iovs[1].iov_base = &extras; 237 | req->iovs[1].iov_len = sizeof(uint64_t); 238 | req->iovs[2].iov_base = key->iov_base; 239 | req->iovs[2].iov_len = key->iov_len; 240 | req->iovs[3].iov_base = random_char; 241 | req->iovs[3].iov_len = val_len; 242 | 243 | req->iov_cnt = 4; 244 | } else { 245 | // get 246 | header.opcode = CMD_GETK; 247 | header.extra_len = 0x00; 248 | header.body_len = htonl(key->iov_len); 249 | 250 | req->iovs[0].iov_base = &header; 251 | req->iovs[0].iov_len = sizeof(struct bmc_header); 252 | req->iovs[1].iov_base = key->iov_base; 253 | req->iovs[1].iov_len = key->iov_len; 254 | 255 | req->iov_cnt = 2; 256 | } 257 | 258 | return 0; 259 | } 260 | 261 | int memcache_init(char *proto, struct application_protocol *app_proto) 262 | { 263 | struct kv_info *data; 264 | char *token, *key_dist; 265 | int key_count; 266 | char *saveptr; 267 | char key_sel[64]; 268 | 269 | data = malloc(sizeof(struct kv_info)); 270 | assert(data != NULL); 271 | 272 | assert(strncmp("memcache-", proto, 9) == 0); 273 | 274 | /* key size dist */ 275 | strtok_r(proto, "_", &saveptr); 276 | 277 | key_dist = strtok_r(NULL, "_", &saveptr); 278 | 279 | token = strtok_r(NULL, "_", &saveptr); 280 | 281 | /* value length dist */ 282 | data->val_len = init_rand(token); 283 | 284 | assert(data->val_len != NULL); 285 | 286 | /* key count */ 287 | token = strtok_r(NULL, "_", &saveptr); 288 | 289 | key_count = atoi(token); 290 | 291 | /* init key generator */ 292 | data->key = init_key_gen(key_dist, key_count); 293 | assert(data->key != NULL); 294 | 295 | /* get request ratio */ 296 | token = strtok_r(NULL, "_", &saveptr); 297 | data->get_ratio = strtod(token, NULL); 298 | 299 | /* key selector distribution */ 300 | token = strtok_r(NULL, "_", &saveptr); 301 | sprintf(key_sel, "%s:%d\n", token, key_count); 302 | data->key_sel = init_rand(key_sel); 303 | assert(data->key_sel != NULL); 304 | 305 | app_proto->arg = data; 306 | if (strncmp("memcache-bin", proto, 12) == 0) { 307 | app_proto->type = PROTO_MEMCACHED_BIN; 308 | app_proto->consume_response = memcache_bin_consume_response; 309 | app_proto->create_request = memcache_bin_create_request; 310 | } else if (strncmp("memcache-ascii", proto, 14) == 0) { 311 | app_proto->type = PROTO_MEMCACHED_ASCII; 312 | app_proto->consume_response = memcache_ascii_consume_response; 313 | app_proto->create_request = memcache_ascii_create_request; 314 | } else { 315 | fprintf(stderr, "Wrong memcached protocol\n"); 316 | return -1; 317 | } 318 | 319 | return 0; 320 | } 321 | -------------------------------------------------------------------------------- /coordinator/proto.go: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package main 25 | 26 | // #include "../inc/lancet/coord_proto.h" 27 | import "C" 28 | import ( 29 | "bytes" 30 | "encoding/binary" 31 | "fmt" 32 | "time" 33 | /* 34 | "strings" 35 | */) 36 | 37 | func broadcastMessage(msg *bytes.Buffer, agents []*agent) error { 38 | for _, a := range agents { 39 | count, err := a.conn.Write(msg.Bytes()) 40 | if err != nil { 41 | return fmt.Errorf("Write to server failed: %v\n", err) 42 | } 43 | if count != msg.Len() { 44 | panic("Conn write mismatch") 45 | } 46 | } 47 | return nil 48 | } 49 | 50 | func collectAcks(agents []*agent) error { 51 | // Wait for ACK with a 2 second deadline 52 | timeOut := 2000 * time.Millisecond 53 | for _, a := range agents { 54 | a.conn.SetReadDeadline(time.Now().Add(timeOut)) 55 | reply := &C.struct_msg1{} 56 | data := make([]byte, 64) 57 | _, err := a.conn.Read(data) 58 | if err != nil { 59 | return fmt.Errorf("Read from agent failed: %v\n", err) 60 | } 61 | r := bytes.NewReader(data) 62 | err = binary.Read(r, binary.LittleEndian, reply) 63 | if err != nil { 64 | return fmt.Errorf("Error parsing ACK: %v\n", err) 65 | } 66 | if reply.Info != C.REPLY_ACK { 67 | return fmt.Errorf("Didn't receive ACK.\n") 68 | } 69 | } 70 | return nil 71 | } 72 | 73 | func collectValues(agents []*agent) ([]int, error) { 74 | ret := make([]int, len(agents)) 75 | 76 | // Wait for value with a 2 second deadline 77 | timeOut := 2000 * time.Millisecond 78 | for i, a := range agents { 79 | a.conn.SetReadDeadline(time.Now().Add(timeOut)) 80 | reply := &C.struct_msg1{} 81 | data := make([]byte, 64) 82 | _, err := a.conn.Read(data) 83 | if err != nil { 84 | return nil, fmt.Errorf("Read from agent failed: %v\n", err) 85 | } 86 | r := bytes.NewReader(data) 87 | err = binary.Read(r, binary.LittleEndian, reply) 88 | if err != nil { 89 | return nil, fmt.Errorf("Error parsing value: %v\n", err) 90 | } 91 | ret[i] = int(reply.Info) 92 | } 93 | return ret, nil 94 | } 95 | 96 | func collectThroughputResults(agents []*agent) ([]*C.struct_throughput_reply, error) { 97 | result := make([]*C.struct_throughput_reply, 0) 98 | timeOut := 30000 * time.Millisecond 99 | for _, a := range agents { 100 | a.conn.SetReadDeadline(time.Now().Add(timeOut)) 101 | reply := &C.struct_throughput_reply{} 102 | prelude := &C.struct_msg1{} 103 | data := make([]byte, 1024) 104 | _, err := a.conn.Read(data) 105 | if err != nil { 106 | return nil, fmt.Errorf("Read from agent failed: %v\n", err) 107 | } 108 | r := bytes.NewReader(data) 109 | 110 | // Read throughput reply 111 | err = binary.Read(r, binary.LittleEndian, prelude) 112 | if err != nil { 113 | return nil, fmt.Errorf("Error parsing throughput_reply header: %v\n", err) 114 | } 115 | if prelude.Info != C.REPLY_STATS_THROUGHPUT { 116 | return nil, fmt.Errorf("Didn't receive throughput stats\n") 117 | } 118 | err = binary.Read(r, binary.LittleEndian, reply) 119 | if err != nil { 120 | return nil, fmt.Errorf("Error parsing throughput_reply: %v\n", err) 121 | } 122 | result = append(result, reply) 123 | } 124 | return result, nil 125 | } 126 | 127 | func collectLatencyResults(agents []*agent) ([]*C.struct_latency_reply, error) { 128 | result := make([]*C.struct_latency_reply, 0) 129 | timeOut := 10000 * time.Millisecond 130 | aggregate := 0 131 | for _, a := range agents { 132 | a.conn.SetReadDeadline(time.Now().Add(timeOut)) 133 | reply := &C.struct_latency_reply{} 134 | prelude := &C.struct_msg1{} 135 | data := make([]byte, 1024) 136 | _, err := a.conn.Read(data) 137 | 138 | if err != nil { 139 | return nil, fmt.Errorf("Read from agent failed: %v\n", err) 140 | } 141 | r := bytes.NewReader(data) 142 | err = binary.Read(r, binary.LittleEndian, prelude) 143 | if err != nil { 144 | return nil, fmt.Errorf("Error parsing latency_reply header: %v\n", err) 145 | } 146 | if prelude.Info != C.REPLY_STATS_LATENCY { 147 | return nil, fmt.Errorf("Didn't receive latency stats\n") 148 | } 149 | err = binary.Read(r, binary.LittleEndian, reply) 150 | if err != nil { 151 | return nil, fmt.Errorf("Error parsing latency_reply: %v\n", err) 152 | } 153 | aggregate += int(reply.Th_data.CorrectIAD) 154 | result = append(result, reply) 155 | } 156 | fmt.Printf("Overall IA check: %v\n", aggregate) 157 | return result, nil 158 | } 159 | 160 | func collectConvergenceResults(agents []*agent) ([]int, error) { 161 | // Wait for ACK with a 2 second deadline 162 | timeOut := 2000 * time.Millisecond 163 | res := make([]int, len(agents)) 164 | for i, a := range agents { 165 | a.conn.SetReadDeadline(time.Now().Add(timeOut)) 166 | prelude := &C.struct_msg1{} 167 | var conv uint32 168 | data := make([]byte, 64) 169 | _, err := a.conn.Read(data) 170 | if err != nil { 171 | return nil, fmt.Errorf("Read from agent failed: %v\n", err) 172 | } 173 | r := bytes.NewReader(data) 174 | err = binary.Read(r, binary.LittleEndian, prelude) 175 | if err != nil { 176 | return nil, fmt.Errorf("Error parsing convergence: %v\n", err) 177 | } 178 | if prelude.Info != C.REPLY_CONVERGENCE { 179 | return nil, fmt.Errorf("Didn't receive convergence\n") 180 | } 181 | err = binary.Read(r, binary.LittleEndian, &conv) 182 | if err != nil { 183 | return nil, fmt.Errorf("Error parsing convergence: %v\n", err) 184 | } 185 | res[i] = int(conv) 186 | } 187 | return res, nil 188 | } 189 | 190 | // Messages sent from the coordinator to the agents 191 | func startLoad(agents []*agent, load int) error { 192 | msg := C.struct_msg1{ 193 | Hdr: C.struct_msg_hdr{ 194 | MessageType: C.uint32_t(C.START_LOAD), 195 | MessageLength: C.uint32_t(4), 196 | }, 197 | Info: C.uint32_t(load), 198 | } 199 | buf := &bytes.Buffer{} 200 | err := binary.Write(buf, binary.LittleEndian, msg) 201 | if err != nil { 202 | return fmt.Errorf("Error formating message: %v", err) 203 | } 204 | err = broadcastMessage(buf, agents) 205 | if err != nil { 206 | return err 207 | } 208 | err = collectAcks(agents) 209 | if err != nil { 210 | return err 211 | } 212 | 213 | return nil 214 | } 215 | 216 | func startMeasure(agents []*agent, sampleCount int, samplingRate float64) error { 217 | Hdr := C.struct_msg_hdr{ 218 | MessageType: C.uint32_t(C.START_MEASURE), 219 | MessageLength: C.uint32_t(12), 220 | } 221 | Info1 := C.uint32_t(sampleCount) 222 | Info2 := C.double(samplingRate) 223 | buf := &bytes.Buffer{} 224 | err := binary.Write(buf, binary.LittleEndian, Hdr) 225 | if err != nil { 226 | return fmt.Errorf("Error formating message: %v", err) 227 | } 228 | err = binary.Write(buf, binary.LittleEndian, Info1) 229 | if err != nil { 230 | return fmt.Errorf("Error formating message: %v", err) 231 | } 232 | err = binary.Write(buf, binary.LittleEndian, Info2) 233 | if err != nil { 234 | return fmt.Errorf("Error formating message: %v", err) 235 | } 236 | err = broadcastMessage(buf, agents) 237 | if err != nil { 238 | return err 239 | } 240 | err = collectAcks(agents) 241 | if err != nil { 242 | return err 243 | } 244 | return nil 245 | } 246 | 247 | func reportThroughput(agents []*agent) ([]*C.struct_throughput_reply, error) { 248 | msg := C.struct_msg1{ 249 | Hdr: C.struct_msg_hdr{ 250 | MessageType: C.uint32_t(C.REPORT_REQ), 251 | MessageLength: C.uint32_t(4), 252 | }, 253 | Info: C.uint32_t(C.REPORT_THROUGHPUT), 254 | } 255 | buf := &bytes.Buffer{} 256 | err := binary.Write(buf, binary.LittleEndian, msg) 257 | if err != nil { 258 | return nil, fmt.Errorf("Error formating message: %v", err) 259 | } 260 | err = broadcastMessage(buf, agents) 261 | if err != nil { 262 | return nil, err 263 | } 264 | return collectThroughputResults(agents) 265 | } 266 | 267 | func reportLatency(agents []*agent) ([]*C.struct_latency_reply, error) { 268 | msg := C.struct_msg1{ 269 | Hdr: C.struct_msg_hdr{ 270 | MessageType: C.uint32_t(C.REPORT_REQ), 271 | MessageLength: C.uint32_t(4), 272 | }, 273 | Info: C.uint32_t(C.REPORT_LATENCY), 274 | } 275 | buf := &bytes.Buffer{} 276 | err := binary.Write(buf, binary.LittleEndian, msg) 277 | if err != nil { 278 | return nil, fmt.Errorf("Error formating message: %v", err) 279 | } 280 | err = broadcastMessage(buf, agents) 281 | if err != nil { 282 | return nil, err 283 | } 284 | return collectLatencyResults(agents) 285 | } 286 | 287 | func check_conn_open(agents []*agent) (bool, error) { 288 | msg := C.struct_msg1{ 289 | Hdr: C.struct_msg_hdr{ 290 | MessageType: C.uint32_t(C.CONN_OPEN), 291 | MessageLength: C.uint32_t(4), 292 | }, 293 | Info: C.uint32_t(0), 294 | } 295 | buf := &bytes.Buffer{} 296 | err := binary.Write(buf, binary.LittleEndian, msg) 297 | if err != nil { 298 | return false, fmt.Errorf("Error formating message: %v", err) 299 | } 300 | err = broadcastMessage(buf, agents) 301 | if err != nil { 302 | return false, err 303 | } 304 | conn_open, err := collectValues(agents) 305 | if err != nil { 306 | return false, err 307 | } 308 | for _, c := range conn_open { 309 | if c == 0 { 310 | return false, nil 311 | } 312 | } 313 | return true, nil 314 | } 315 | -------------------------------------------------------------------------------- /agents/redis.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #define _GNU_SOURCE 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #ifdef ENABLE_R2P2 37 | #include 38 | #endif 39 | 40 | #define YCSBE_SCAN_RATIO 0.95 41 | #define YCSBE_INSERT_RATIO 0.05 42 | #define YCSBE_KEY_COUNT 1000000 43 | #define YCSBE_MAX_SCAN_LEN 10 44 | #define YCSBE_FIELD_COUNT 10 45 | #define YCSBE_FIELD_SIZE 100 46 | 47 | struct ycsbe_info { 48 | double scan_ratio; 49 | double insert_ratio; 50 | int key_count; 51 | int scan_len; 52 | int field_count; 53 | int field_size; 54 | int replicated; 55 | char *fixed_req_body; 56 | }; 57 | 58 | static char set_prem[] = "*3\r\n$3\r\nSET\r\n$"; 59 | static char get_prem[] = "*2\r\n$3\r\nGET\r\n$"; 60 | static char ln[] = "\r\n"; 61 | static char dollar[] = "$"; 62 | static char ycsbe_insert_prem[] = "ycsbe.insert "; 63 | static char ycsbe_scan_prem[] = "ycsbe.scan "; 64 | static __thread char ycsbe_key[64]; 65 | static __thread char ycsbe_scan[64]; 66 | static __thread char key_len_str[64]; 67 | static __thread char val_len_str[64]; 68 | 69 | static int parse_string(char *buf, int bytes_left) 70 | { 71 | int processed = 0; 72 | char *p; 73 | 74 | if (bytes_left < 1) 75 | return processed; 76 | 77 | assert(buf[0] == '+'); 78 | 79 | p = memchr(buf, '\n', bytes_left); 80 | if (!p) 81 | return processed; 82 | 83 | processed = p - buf + 1; 84 | return processed; 85 | } 86 | 87 | static int parse_bulk_string(char *buf, int bytes_left) 88 | { 89 | int len, processed = 0, extra; 90 | char *p; 91 | 92 | if (bytes_left < 1) 93 | return processed; 94 | 95 | assert(buf[0] == '$'); 96 | 97 | p = memchr(buf, '\n', bytes_left); 98 | if (!p) 99 | return processed; 100 | 101 | len = atoi(&buf[1]); 102 | if (len == -1) 103 | processed = 5; // key not found 104 | else { 105 | extra = p - buf + 1 + 2; 106 | if ((len + extra) <= bytes_left) 107 | processed = len + extra; 108 | } 109 | 110 | return processed; 111 | } 112 | 113 | static struct byte_req_pair redis_kv_consume_response(struct application_protocol *proto, 114 | struct iovec *resp) 115 | { 116 | struct byte_req_pair res; 117 | int bytes_to_process, processed; 118 | char *buf; 119 | 120 | bytes_to_process = resp->iov_len; 121 | res.bytes = 0; 122 | res.reqs = 0; 123 | buf = resp->iov_base; 124 | 125 | while (bytes_to_process) { 126 | if (buf[resp->iov_len - bytes_to_process] == '+') { 127 | processed = parse_string(&buf[resp->iov_len - bytes_to_process], 128 | bytes_to_process); 129 | if (processed == 0) 130 | break; 131 | res.bytes += processed; 132 | res.reqs += 1; 133 | bytes_to_process -= processed; 134 | } else if (buf[resp->iov_len - bytes_to_process] == '$') { 135 | processed = parse_bulk_string( 136 | &buf[resp->iov_len - bytes_to_process], bytes_to_process); 137 | if (processed == 0) 138 | break; 139 | res.bytes += processed; 140 | res.reqs += 1; 141 | bytes_to_process -= processed; 142 | } else 143 | assert(0); 144 | } 145 | 146 | return res; 147 | } 148 | 149 | static int redis_kv_create_request(struct application_protocol *proto, 150 | struct request *req) 151 | { 152 | struct kv_info *info; 153 | long val_len; 154 | int key_idx; 155 | struct iovec *key; 156 | 157 | info = (struct kv_info *)proto->arg; 158 | key_idx = generate(info->key_sel); 159 | key = &info->key->keys[key_idx]; 160 | 161 | assert(key != NULL); 162 | 163 | // Fix key 164 | sprintf(key_len_str, "%ld", key->iov_len); 165 | req->iovs[1].iov_base = key_len_str; 166 | req->iovs[1].iov_len = strlen(key_len_str); 167 | req->iovs[2].iov_base = ln; 168 | req->iovs[2].iov_len = 2; 169 | req->iovs[3].iov_base = key->iov_base; 170 | req->iovs[3].iov_len = key->iov_len; 171 | req->iovs[4].iov_base = ln; 172 | req->iovs[4].iov_len = 2; 173 | 174 | if (drand48() > info->get_ratio) { 175 | val_len = lround(generate(info->val_len)); 176 | assert(val_len <= MAX_VAL_SIZE); 177 | 178 | req->iovs[0].iov_base = set_prem; 179 | req->iovs[0].iov_len = 14; 180 | 181 | req->iovs[5].iov_base = dollar; 182 | req->iovs[5].iov_len = 1; 183 | 184 | // Fix val 185 | sprintf(val_len_str, "%ld", val_len); 186 | req->iovs[6].iov_base = val_len_str; 187 | req->iovs[6].iov_len = strlen(val_len_str); 188 | req->iovs[7].iov_base = ln; 189 | req->iovs[7].iov_len = 2; 190 | req->iovs[8].iov_base = random_char; 191 | req->iovs[8].iov_len = val_len; 192 | req->iovs[9].iov_base = ln; 193 | req->iovs[9].iov_len = 2; 194 | 195 | req->iov_cnt = 10; 196 | #ifdef ENABLE_R2P2 197 | req->meta = (void *)(unsigned long)FIXED_ROUTE; 198 | #endif 199 | } else { 200 | req->iovs[0].iov_base = get_prem; 201 | req->iovs[0].iov_len = 14; 202 | 203 | req->iov_cnt = 5; 204 | #ifdef ENABLE_R2P2 205 | req->meta = (void *)(unsigned long)LB_ROUTE; 206 | #endif 207 | } 208 | 209 | return 0; 210 | } 211 | 212 | static int init_redis_kv(char *proto, struct application_protocol *app_proto) 213 | { 214 | struct kv_info *data; 215 | char *token, *key_dist; 216 | int key_count; 217 | char *saveptr; 218 | char key_sel[64]; 219 | 220 | data = malloc(sizeof(struct kv_info)); 221 | assert(data != NULL); 222 | 223 | /* key size dist */ 224 | strtok_r(proto, "_", &saveptr); 225 | 226 | key_dist = strtok_r(NULL, "_", &saveptr); 227 | 228 | token = strtok_r(NULL, "_", &saveptr); 229 | 230 | /* value length dist */ 231 | data->val_len = init_rand(token); 232 | 233 | assert(data->val_len != NULL); 234 | 235 | /* key count */ 236 | token = strtok_r(NULL, "_", &saveptr); 237 | 238 | key_count = atoi(token); 239 | 240 | /* init key generator */ 241 | data->key = init_key_gen(key_dist, key_count); 242 | assert(data->key != NULL); 243 | 244 | /* get request ratio */ 245 | token = strtok_r(NULL, "_", &saveptr); 246 | data->get_ratio = strtod(token, NULL); 247 | 248 | /* key selector distribution */ 249 | token = strtok_r(NULL, "_", &saveptr); 250 | sprintf(key_sel, "%s:%d\n", token, key_count); 251 | data->key_sel = init_rand(key_sel); 252 | assert(data->key_sel != NULL); 253 | 254 | app_proto->type = PROTO_REDIS; 255 | app_proto->arg = data; 256 | app_proto->consume_response = redis_kv_consume_response; 257 | app_proto->create_request = redis_kv_create_request; 258 | 259 | return 0; 260 | } 261 | 262 | static int redis_ycsbe_create_request(struct application_protocol *proto, 263 | struct request *req) 264 | { 265 | int key, scan_count; 266 | struct ycsbe_info *info; 267 | 268 | info = (struct ycsbe_info *)proto->arg; 269 | 270 | key = rand() % info->key_count; 271 | sprintf(ycsbe_key, "%d ", key); 272 | 273 | if (drand48()<=info->scan_ratio) { 274 | // perform a scan 275 | scan_count = rand() % info->scan_len + 1; 276 | sprintf(ycsbe_scan, "%d\n", scan_count); 277 | 278 | req->iovs[0].iov_base = ycsbe_scan_prem; 279 | req->iovs[0].iov_len = 11; 280 | req->iovs[1].iov_base = ycsbe_key; 281 | req->iovs[1].iov_len = strlen(ycsbe_key); 282 | req->iovs[2].iov_base = ycsbe_scan; 283 | req->iovs[2].iov_len = strlen(ycsbe_scan); 284 | req->iov_cnt = 3; 285 | 286 | if (info->replicated) { 287 | #ifdef ENABLE_R2P2 288 | req->meta = (void *)(unsigned long)REPLICATED_ROUTE_NO_SE; 289 | #else 290 | assert(0); 291 | #endif 292 | } else 293 | req->meta = NULL; 294 | } else { 295 | // perform an insert 296 | req->iovs[0].iov_base = ycsbe_insert_prem; 297 | req->iovs[0].iov_len = 13; 298 | req->iovs[1].iov_base = ycsbe_key; 299 | req->iovs[1].iov_len = strlen(ycsbe_key); 300 | req->iovs[2].iov_base = info->fixed_req_body; 301 | req->iovs[2].iov_len = info->field_count*(info->field_size+1); 302 | req->iov_cnt = 3; 303 | if (info->replicated) { 304 | #ifdef ENABLE_R2P2 305 | req->meta = (void *)(unsigned long)REPLICATED_ROUTE; 306 | #else 307 | assert(0); 308 | #endif 309 | 310 | } else 311 | req->meta = NULL; 312 | } 313 | 314 | return 0; 315 | } 316 | 317 | static int init_redis_ycsbe(char *proto, struct application_protocol *app_proto) 318 | { 319 | struct ycsbe_info *yinfo; 320 | int i; 321 | char *body; 322 | 323 | yinfo = malloc(sizeof(struct ycsbe_info)); 324 | assert(yinfo); 325 | 326 | yinfo->scan_ratio = YCSBE_SCAN_RATIO; 327 | yinfo->insert_ratio = YCSBE_INSERT_RATIO; 328 | yinfo->key_count = YCSBE_KEY_COUNT; 329 | yinfo->scan_len = YCSBE_MAX_SCAN_LEN; 330 | yinfo->field_count = YCSBE_FIELD_COUNT; 331 | yinfo->field_size = YCSBE_FIELD_SIZE; 332 | yinfo->fixed_req_body = malloc(yinfo->field_count*(yinfo->field_size+1)); 333 | assert(yinfo->fixed_req_body); 334 | if (strncmp("redis-ycsber", proto, 12) == 0) 335 | yinfo->replicated = 1; 336 | else 337 | yinfo->replicated = 0; 338 | 339 | body = yinfo->fixed_req_body; 340 | for (i=0;ifield_count;i++) { 341 | memset(body, 'x', yinfo->field_size); 342 | body += yinfo->field_size; 343 | *body++ = ' '; 344 | } 345 | *(body-1) = '\n'; 346 | 347 | app_proto->type = PROTO_REDIS_YCSBE; 348 | app_proto->arg = yinfo; 349 | app_proto->consume_response = NULL; 350 | app_proto->create_request = redis_ycsbe_create_request; 351 | 352 | return 0; 353 | } 354 | 355 | int redis_init(char *proto, struct application_protocol *app_proto) 356 | { 357 | assert(strncmp("redis", proto, 5) == 0); 358 | 359 | if (strncmp("redis-ycsbe", proto, 11) == 0) 360 | init_redis_ycsbe(proto, app_proto); 361 | else 362 | init_redis_kv(proto, app_proto); 363 | 364 | return 0; 365 | } 366 | -------------------------------------------------------------------------------- /agents/timestamping.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include 46 | #include 47 | 48 | #define CONTROL_LEN 1024 49 | 50 | static int set_timestamping_filter(int fd, char *if_name, int rx_filter, 51 | int tx_type) 52 | { 53 | struct ifreq ifr; 54 | struct hwtstamp_config config; 55 | 56 | config.flags = 0; 57 | config.tx_type = tx_type; 58 | config.rx_filter = rx_filter; 59 | 60 | strcpy(ifr.ifr_name, if_name); 61 | ifr.ifr_data = (caddr_t)&config; 62 | 63 | if (ioctl(fd, SIOCSHWTSTAMP, &ifr)) { 64 | lancet_perror("ERROR setting NIC timestamping: ioctl SIOCSHWTSTAMP"); 65 | return -1; 66 | } 67 | return 0; 68 | } 69 | 70 | /* 71 | * Returns 1 if timestamp found 0 otherwise 72 | */ 73 | static int extract_timestamp(struct msghdr *hdr, struct timestamp_info *dest) 74 | { 75 | struct cmsghdr *cmsg; 76 | struct scm_timestamping *ts; 77 | int found = -1; 78 | 79 | for (cmsg = CMSG_FIRSTHDR(hdr); cmsg != NULL; 80 | cmsg = CMSG_NXTHDR(hdr, cmsg)) { 81 | if (cmsg->cmsg_type == SCM_TIMESTAMPING) { 82 | ts = (struct scm_timestamping *)CMSG_DATA(cmsg); 83 | if (ts->ts[2].tv_sec != 0) { 84 | // make sure we don't get multiple timestamps for the same 85 | assert(found == -1); 86 | dest->time = ts->ts[2]; 87 | found = 1; 88 | } 89 | } else if (cmsg->cmsg_type == IP_RECVERR) { 90 | struct sock_extended_err *se = 91 | (struct sock_extended_err *)CMSG_DATA(cmsg); 92 | /* 93 | * Make sure we got the timestamp for the right request 94 | */ 95 | if (se->ee_errno == ENOMSG && 96 | se->ee_origin == SO_EE_ORIGIN_TIMESTAMPING) 97 | dest->optid = se->ee_data; 98 | else 99 | lancet_fprintf(stderr, "Received IP_RECVERR: errno = %d %s\n", 100 | se->ee_errno, strerror(se->ee_errno)); 101 | } else 102 | assert(0); 103 | } 104 | return found; 105 | } 106 | 107 | /* 108 | * Returns -1 if no new timestamp found 109 | * 1 if timestamp found 110 | */ 111 | static int udp_extract_timestamps(struct msghdr *hdr, struct timespec *dest) 112 | { 113 | struct cmsghdr *cmsg; 114 | struct scm_timestamping *ts; 115 | int found = -1; 116 | 117 | for (cmsg = CMSG_FIRSTHDR(hdr); cmsg != NULL; 118 | cmsg = CMSG_NXTHDR(hdr, cmsg)) { 119 | if (cmsg->cmsg_type == SCM_TIMESTAMPING) { 120 | ts = (struct scm_timestamping *)CMSG_DATA(cmsg); 121 | if (ts->ts[2].tv_sec != 0) { 122 | // Make sure we don't get multiple timestamps for the same 123 | assert(found == -1); 124 | dest->tv_sec = ts->ts[2].tv_sec; 125 | dest->tv_nsec = ts->ts[2].tv_nsec; 126 | found = 1; 127 | } 128 | } 129 | } 130 | return found; 131 | } 132 | 133 | int enable_nic_timestamping(char *if_name) 134 | { 135 | int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 136 | int filter = HWTSTAMP_FILTER_ALL; 137 | int tx_type = HWTSTAMP_TX_ON; 138 | int ret; 139 | 140 | ret = set_timestamping_filter(fd, if_name, filter, tx_type); 141 | close(fd); 142 | return ret; 143 | } 144 | 145 | int disable_nic_timestamping(char *if_name) 146 | { 147 | int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 148 | int ret = set_timestamping_filter(fd, if_name, HWTSTAMP_FILTER_NONE, 149 | HWTSTAMP_TX_OFF); 150 | close(fd); 151 | return ret; 152 | } 153 | 154 | int sock_enable_timestamping(int fd) 155 | { 156 | int ts_mode = 0; 157 | 158 | ts_mode |= SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE | 159 | SOF_TIMESTAMPING_TX_HARDWARE; 160 | ts_mode |= SOF_TIMESTAMPING_OPT_TSONLY | SOF_TIMESTAMPING_OPT_ID; 161 | 162 | if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &ts_mode, sizeof(ts_mode)) < 163 | 0) { 164 | lancet_perror( 165 | "ERROR enabling socket timestamping: setsockopt SO_TIMESTAMPING."); 166 | return -1; 167 | } 168 | return 0; 169 | } 170 | 171 | ssize_t timestamp_recv(int sockfd, void *buf, size_t len, int flags, 172 | struct timestamp_info *last_rx_time) 173 | { 174 | char recv_control[CONTROL_LEN] = {0}; 175 | int nbytes, ret; 176 | struct msghdr hdr = {0}; 177 | struct iovec recv_iov = {buf, len}; 178 | 179 | hdr.msg_iov = &recv_iov; 180 | hdr.msg_iovlen = 1; 181 | hdr.msg_control = recv_control; 182 | hdr.msg_controllen = CONTROL_LEN; 183 | 184 | nbytes = recvmsg(sockfd, &hdr, flags); 185 | 186 | if (nbytes <= 0) 187 | return nbytes; 188 | 189 | bzero(last_rx_time, sizeof(struct timestamp_info)); 190 | ret = extract_timestamp(&hdr, last_rx_time); 191 | assert(ret == 1); 192 | 193 | return nbytes; 194 | } 195 | 196 | /* 197 | * Used only for NIC timestamping with UDP 198 | * Returns -1 if no new timestamp found 199 | * 1 if timestamp found 200 | */ 201 | int udp_get_tx_timestamp(int sockfd, struct timespec *tx_timestamp) 202 | { 203 | char tx_control[CONTROL_LEN] = {0}; 204 | struct msghdr mhdr = {0}; 205 | struct iovec junk_iov = {NULL, 0}; 206 | int n; 207 | 208 | mhdr.msg_iov = &junk_iov; 209 | mhdr.msg_iovlen = 1; 210 | mhdr.msg_control = tx_control; 211 | mhdr.msg_controllen = CONTROL_LEN; 212 | 213 | n = recvmsg(sockfd, &mhdr, MSG_ERRQUEUE); 214 | if (n < 0) { 215 | return -1; 216 | } 217 | 218 | assert(n == 0); 219 | 220 | return udp_extract_timestamps(&mhdr, tx_timestamp); 221 | } 222 | 223 | /* 224 | * Used only for NIC timestamping 225 | * 1 if timestamp found 226 | * 0 if timestamp not found 227 | */ 228 | int get_tx_timestamp(int sockfd, struct pending_tx_timestamps *tx_timestamps) 229 | { 230 | char tx_control[CONTROL_LEN] = {0}; 231 | struct msghdr mhdr = {0}; 232 | struct iovec junk_iov = {NULL, 0}; 233 | int n; 234 | struct timestamp_info *ts_info; 235 | struct timestamp_info recv_info; 236 | 237 | assert(tx_timestamps->head >= tx_timestamps->tail); 238 | // Not waiting for a tx timestamp 239 | if (tx_timestamps->head == tx_timestamps->tail) { 240 | // printf("Not waiting for tx timestamp\n"); 241 | return 0; 242 | } 243 | 244 | mhdr.msg_iov = &junk_iov; 245 | mhdr.msg_iovlen = 1; 246 | mhdr.msg_control = tx_control; 247 | mhdr.msg_controllen = CONTROL_LEN; 248 | 249 | n = recvmsg(sockfd, &mhdr, MSG_ERRQUEUE); 250 | if (n < 0) { 251 | // printf("Nothing to read: %d\n", n); 252 | return 0; 253 | } 254 | 255 | assert(n == 0); 256 | 257 | n = extract_timestamp(&mhdr, &recv_info); 258 | assert(n == 1); 259 | ts_info = 260 | &tx_timestamps->pending[tx_timestamps->tail % get_max_pending_reqs()]; 261 | if (recv_info.optid + 1 < ts_info->optid) { 262 | // resubmission 263 | // printf("Received out of order: %d %d\n", recv_info.optid, 264 | // ts_info->optid); 265 | // struct timestamp_info *tmp = 266 | // &tx_timestamps->pending[(tx_timestamps->tail-1) % 267 | // get_max_pending_reqs()]; 268 | // printf("Received %ld %ld prev %ld %ld\n", recv_info.time.tv_sec, 269 | // recv_info.time.tv_nsec, tmp->time.tv_sec, tmp->time.tv_nsec); 270 | return get_tx_timestamp(sockfd, tx_timestamps); 271 | } 272 | assert(ts_info->optid <= recv_info.optid + 1); 273 | while (ts_info->optid <= recv_info.optid + 1) { 274 | ts_info->time = recv_info.time; 275 | add_tx_timestamp(&ts_info->time); 276 | tx_timestamps->tail++; 277 | if (ts_info->optid == recv_info.optid + 1) 278 | break; 279 | ts_info = &tx_timestamps 280 | ->pending[tx_timestamps->tail % get_max_pending_reqs()]; 281 | } 282 | return n; 283 | } 284 | 285 | void add_pending_tx_timestamp(struct pending_tx_timestamps *tx_timestamps, 286 | uint32_t bytes) 287 | { 288 | tx_timestamps->tx_byte_counter += bytes; 289 | tx_timestamps->pending[tx_timestamps->head++ % get_max_pending_reqs()] 290 | .optid = tx_timestamps->tx_byte_counter; 291 | } 292 | 293 | struct timestamp_info * 294 | pop_pending_tx_timestamps(struct pending_tx_timestamps *tx_timestamps) 295 | { 296 | struct timestamp_info *ret = NULL; 297 | 298 | assert(tx_timestamps->consumed <= tx_timestamps->head); 299 | if (tx_timestamps->consumed < tx_timestamps->tail) 300 | ret = 301 | &tx_timestamps 302 | ->pending[tx_timestamps->consumed++ % get_max_pending_reqs()]; 303 | 304 | return ret; 305 | } 306 | 307 | void push_complete_tx_timestamp(struct pending_tx_timestamps *tx_timestamps, 308 | struct timespec *to_add) 309 | { 310 | struct timestamp_info *ts_info; 311 | 312 | ts_info = 313 | &tx_timestamps->pending[tx_timestamps->tail % get_max_pending_reqs()]; 314 | ts_info->time = *to_add; 315 | // this is confusing but the consumed is used when receiving the reply 316 | tx_timestamps->head++; 317 | tx_timestamps->tail++; 318 | add_tx_timestamp(&ts_info->time); 319 | } 320 | 321 | int timespec_diff(struct timespec *res, struct timespec *a, struct timespec *b) 322 | { 323 | uint32_t billion = 1e9; 324 | 325 | if (!a || !b) 326 | return -1; 327 | if (a->tv_sec == 0 || b->tv_sec == 0) 328 | return -1; 329 | 330 | if (a->tv_nsec < b->tv_nsec) { 331 | res->tv_nsec = b->tv_nsec - a->tv_nsec; 332 | res->tv_nsec = billion - res->tv_nsec; 333 | res->tv_sec = a->tv_sec - 1 - b->tv_sec; 334 | 335 | } else { 336 | res->tv_nsec = a->tv_nsec - b->tv_nsec; 337 | res->tv_sec = a->tv_sec - b->tv_sec; 338 | } 339 | 340 | return 0; 341 | } 342 | -------------------------------------------------------------------------------- /agents/rand_gen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2021 Ecole Polytechnique Federale Lausanne (EPFL) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | /* 35 | * Deterministic distribution 36 | * params holds the number to return 37 | */ 38 | 39 | static __thread uint64_t prev_rr; 40 | 41 | static double fixed_inv_cdf(struct rand_gen *gen, 42 | __attribute__((unused)) double y) 43 | { 44 | return gen->params.p1.a; 45 | } 46 | 47 | static void fixed_set_avg(struct rand_gen *gen, double avg) 48 | { 49 | gen->params.p1.a = avg; 50 | } 51 | 52 | static void fixed_init(struct rand_gen *gen, struct param_1 *param) 53 | { 54 | gen->set_avg = fixed_set_avg; 55 | gen->inv_cdf = fixed_inv_cdf; 56 | gen->generate = NULL; 57 | gen->gen_type = GEN_FIXED; 58 | 59 | gen->set_avg(gen, param->a); 60 | free(param); 61 | } 62 | 63 | /* 64 | * Round robin 65 | * param holds the max 66 | */ 67 | static double rr_generate(struct rand_gen *gen) 68 | { 69 | uint64_t max = gen->params.p1.a; 70 | 71 | return prev_rr++ % max; 72 | } 73 | 74 | static void rr_init(struct rand_gen *gen, struct param_1 *param) 75 | { 76 | gen->generate = rr_generate; 77 | gen->params.p1.a = param->a; 78 | 79 | free(param); 80 | } 81 | 82 | /* 83 | * Uniform random 84 | * param holds the max 85 | */ 86 | static double uni_generate(struct rand_gen *gen) 87 | { 88 | uint64_t max = gen->params.p1.a; 89 | 90 | return rand() % max; 91 | } 92 | 93 | static void uni_init(struct rand_gen *gen, struct param_1 *param) 94 | { 95 | gen->generate = uni_generate; 96 | gen->params.p1.a = param->a; 97 | 98 | free(param); 99 | } 100 | 101 | /* 102 | * Exponential distribution 103 | * params holds lambda (1/avg) 104 | */ 105 | 106 | static double exp_inv_cdf(struct rand_gen *gen, double y) 107 | { 108 | return -log(y) / gen->params.p1.a; 109 | } 110 | 111 | static void exp_set_avg(struct rand_gen *gen, double avg) 112 | { 113 | gen->params.p1.a = (double)1.0 / avg; 114 | } 115 | 116 | static void exp_init(struct rand_gen *gen, struct param_1 *param) 117 | { 118 | gen->set_avg = exp_set_avg; 119 | gen->inv_cdf = exp_inv_cdf; 120 | gen->generate = NULL; 121 | gen->gen_type = GEN_EXP; 122 | 123 | gen->set_avg(gen, param->a); 124 | free(param); 125 | } 126 | 127 | /* 128 | * Generalised pareto distribution 129 | * params hold a struct of param_lss with loc, shape and scale 130 | */ 131 | static double gpar_inv_cdf(struct rand_gen *gen, double y) 132 | { 133 | return gen->params.lss.loc + 134 | gen->params.lss.scale * (pow(1 - y, -gen->params.lss.shape) - 1) / 135 | gen->params.lss.shape; 136 | } 137 | 138 | /* 139 | * Change only the scale parameter (legacy) 140 | */ 141 | static void gpar_set_avg(struct rand_gen *gen, double avg) 142 | { 143 | gen->params.lss.scale = 144 | (avg - gen->params.lss.loc) * (1 - gen->params.lss.shape); 145 | } 146 | 147 | static void gpar_init(struct rand_gen *gen, struct param_3 *param) 148 | { 149 | gen->params.p3 = *param; 150 | gen->set_avg = gpar_set_avg; 151 | gen->inv_cdf = gpar_inv_cdf; 152 | gen->generate = NULL; 153 | gen->gen_type = GEN_OTHER; 154 | 155 | free(param); 156 | } 157 | 158 | /* 159 | * GEV distribution 160 | * params holds a struct param_lss 161 | */ 162 | static double gev_inv_cdf(struct rand_gen *gen, double y) 163 | { 164 | return gen->params.lss.loc + 165 | gen->params.lss.scale * (pow(-exp(y), -gen->params.lss.shape) - 1) / 166 | gen->params.lss.shape; 167 | } 168 | 169 | /* 170 | * Not implemented 171 | */ 172 | static void gev_set_avg(__attribute__((unused)) struct rand_gen *gen, 173 | __attribute__((unused)) double avg) 174 | { 175 | assert(0); 176 | } 177 | 178 | static void gev_init(struct rand_gen *gen, struct param_3 *param) 179 | { 180 | gen->params.p3 = *param; 181 | gen->set_avg = gev_set_avg; 182 | gen->inv_cdf = gev_inv_cdf; 183 | gen->generate = NULL; 184 | gen->gen_type = GEN_OTHER; 185 | } 186 | 187 | /* 188 | * Bimodal distribution. 189 | * maxi1:maxi2:Prob1 190 | */ 191 | static void bimodal_set_avg(__attribute__((unused)) struct rand_gen *gen, 192 | __attribute__((unused)) double avg) 193 | { 194 | // Should never be called. 195 | assert(0); 196 | } 197 | 198 | static double bimodal_inv_cdf(struct rand_gen *gen, double y) 199 | { 200 | if (y <= gen->params.bp.prob) 201 | return gen->params.bp.low; 202 | return gen->params.bp.up; 203 | } 204 | 205 | static void bimodal_init(struct rand_gen *gen, struct param_3 *param) 206 | { 207 | gen->params.p3 = *param; 208 | gen->set_avg = bimodal_set_avg; 209 | gen->inv_cdf = bimodal_inv_cdf; 210 | gen->generate = NULL; 211 | gen->gen_type = GEN_OTHER; 212 | free(param); 213 | } 214 | 215 | /* 216 | * Lognormal distribution 217 | */ 218 | static double lognorm_generate(struct rand_gen *gen) 219 | { 220 | double y = get_normal_rand(gen->params.lgp.ng); 221 | return exp(gen->params.lgp.mu + y * gen->params.lgp.sigma); 222 | } 223 | 224 | static double lognorm_inv_cdf(struct rand_gen *gen, double y) 225 | { 226 | assert(0); 227 | } 228 | 229 | static void lognorm_set_avg(__attribute__((unused)) struct rand_gen *gen, 230 | double avg) 231 | { 232 | assert(0); 233 | } 234 | 235 | static void lognormal_init(struct rand_gen *gen, struct param_2 *param) 236 | { 237 | 238 | gen->params.lgp.ng = new_normal_gen(); 239 | gen->params.lgp.mu = param->a; 240 | gen->params.lgp.sigma = param->b; 241 | 242 | gen->generate = lognorm_generate; 243 | gen->set_avg = lognorm_set_avg; 244 | gen->inv_cdf = lognorm_inv_cdf; 245 | gen->gen_type = GEN_OTHER; 246 | free(param); 247 | } 248 | 249 | /* 250 | * Gamma distribution 251 | */ 252 | static double gamma_generate(struct rand_gen *gen) 253 | { 254 | int res = get_gamma_rand(gen->params.gp.gg); 255 | return res; 256 | } 257 | 258 | static double gamma_inv_cdf(struct rand_gen *gen, double y) 259 | { 260 | assert(0); 261 | } 262 | 263 | static void gamma_set_avg(__attribute__((unused)) struct rand_gen *gen, 264 | double avg) 265 | { 266 | assert(0); 267 | } 268 | 269 | static void gamma_init(struct rand_gen *gen, struct param_2 *param) 270 | { 271 | assert(0); 272 | 273 | gen->params.gp.gg = new_gamma_gen(param->a, param->b); 274 | 275 | gen->generate = gamma_generate; 276 | gen->set_avg = gamma_set_avg; 277 | gen->inv_cdf = gamma_inv_cdf; 278 | gen->gen_type = GEN_OTHER; 279 | free(param); 280 | } 281 | 282 | static struct param_1 *parse_param_1(char *type) 283 | { 284 | char *tok; 285 | struct param_1 *res; 286 | 287 | res = (struct param_1 *)malloc(sizeof(struct param_1)); 288 | assert(res); 289 | 290 | tok = strtok(type, ":"); 291 | tok = strtok(NULL, ":"); 292 | res->a = (tok == NULL) ? 0 : atof(tok); 293 | 294 | return res; 295 | } 296 | 297 | static struct param_2 *parse_param_2(char *type) 298 | { 299 | char *tok; 300 | struct param_2 *params; 301 | 302 | params = (struct param_2 *)malloc(sizeof(struct param_2)); 303 | assert(params); 304 | 305 | tok = strtok(type, ":"); 306 | tok = strtok(NULL, ":"); 307 | params->a = atof(tok); 308 | tok = strtok(NULL, ":"); 309 | params->b = atof(tok); 310 | 311 | return params; 312 | } 313 | 314 | static struct param_3 *parse_param_3(char *type) 315 | { 316 | char *tok; 317 | struct param_3 *params; 318 | 319 | params = (struct param_3 *)malloc(sizeof(struct param_3)); 320 | assert(params); 321 | 322 | tok = strtok(type, ":"); 323 | tok = strtok(NULL, ":"); 324 | params->a = atof(tok); 325 | tok = strtok(NULL, ":"); 326 | params->b = atof(tok); 327 | tok = strtok(NULL, ":"); 328 | params->c = atof(tok); 329 | 330 | return params; 331 | } 332 | 333 | struct rand_gen *init_rand(char *gen_type) 334 | { 335 | struct rand_gen *gen = (struct rand_gen *)malloc(sizeof(struct rand_gen)); 336 | assert(gen); 337 | 338 | if (strncmp(gen_type, "fixed", 5) == 0) 339 | fixed_init(gen, parse_param_1(gen_type)); 340 | else if (strncmp(gen_type, "rr", 2) == 0) 341 | rr_init(gen, parse_param_1(gen_type)); 342 | else if (strncmp(gen_type, "uni", 3) == 0) 343 | uni_init(gen, parse_param_1(gen_type)); 344 | else if (strncmp(gen_type, "exp", 3) == 0) 345 | exp_init(gen, parse_param_1(gen_type)); 346 | else if (strncmp(gen_type, "pareto", 6) == 0) 347 | gpar_init(gen, parse_param_3(gen_type)); 348 | else if (strncmp(gen_type, "gev", 3) == 0) 349 | gev_init(gen, parse_param_3(gen_type)); 350 | else if (strcmp(gen_type, "fb_key") == 0) { 351 | char type[] = "gev:30.7984:8.20449:0.078688"; 352 | gev_init(gen, parse_param_3(type)); 353 | } else if (strcmp(gen_type, "fb_ia") == 0) { 354 | char type[] = "gpar:0:16.0292:0.154971"; 355 | gpar_init(gen, parse_param_3(type)); 356 | } else if (strcmp(gen_type, "fb_val") == 0) { 357 | /* WARNING: this is not exactly the same as mutilate */ 358 | char type[] = "gpar:15.0:214.476:0.348238"; 359 | gpar_init(gen, parse_param_3(type)); 360 | } else if (strncmp(gen_type, "bimodal", 7) == 0) 361 | bimodal_init(gen, parse_param_3(gen_type)); 362 | else if (strncmp(gen_type, "lognorm", 7) == 0) 363 | lognormal_init(gen, parse_param_2(gen_type)); 364 | else if (strncmp(gen_type, "gamma", 5) == 0) 365 | gamma_init(gen, parse_param_2(gen_type)); 366 | else { 367 | lancet_fprintf(stderr, "Unknown generator type %s\n", gen_type); 368 | return NULL; 369 | } 370 | return gen; 371 | } 372 | 373 | void set_avg_ext(struct rand_gen *gen, double avg) 374 | { 375 | switch (gen->gen_type) { 376 | case GEN_FIXED: 377 | fixed_set_avg(gen, avg); 378 | break; 379 | case GEN_EXP: 380 | exp_set_avg(gen, avg); 381 | break; 382 | default: 383 | assert(0); 384 | } 385 | } 386 | --------------------------------------------------------------------------------