From 604528151e0c36a412562015f89a90c6971582c5 Mon Sep 17 00:00:00 2001 From: Bastian Krol Date: Fri, 21 Jun 2024 13:48:44 +0200 Subject: [PATCH 01/12] feat(preload): initial version of LD_PRELOAD env hook --- .../dash0-instrumentation/preload/.gitignore | 1 + .../preload/Dockerfile-x86_64-glibc | 12 ++++++ images/dash0-instrumentation/preload/Makefile | 25 +++++++++++ .../dash0-instrumentation/preload/README.md | 5 +++ .../preload/appundertest.c | 25 +++++++++++ .../preload/libdash0envhook.c | 37 +++++++++++++++++ .../preload/smoke-test.sh | 41 +++++++++++++++++++ .../preload/start-build-container.sh | 39 ++++++++++++++++++ 8 files changed, 185 insertions(+) create mode 100644 images/dash0-instrumentation/preload/.gitignore create mode 100644 images/dash0-instrumentation/preload/Dockerfile-x86_64-glibc create mode 100644 images/dash0-instrumentation/preload/Makefile create mode 100644 images/dash0-instrumentation/preload/README.md create mode 100644 images/dash0-instrumentation/preload/appundertest.c create mode 100644 images/dash0-instrumentation/preload/libdash0envhook.c create mode 100755 images/dash0-instrumentation/preload/smoke-test.sh create mode 100755 images/dash0-instrumentation/preload/start-build-container.sh diff --git a/images/dash0-instrumentation/preload/.gitignore b/images/dash0-instrumentation/preload/.gitignore new file mode 100644 index 00000000..a70e5d2a --- /dev/null +++ b/images/dash0-instrumentation/preload/.gitignore @@ -0,0 +1 @@ +/*.so diff --git a/images/dash0-instrumentation/preload/Dockerfile-x86_64-glibc b/images/dash0-instrumentation/preload/Dockerfile-x86_64-glibc new file mode 100644 index 00000000..206ea4f5 --- /dev/null +++ b/images/dash0-instrumentation/preload/Dockerfile-x86_64-glibc @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +# SPDX-License-Identifier: Apache-2.0 + +FROM ubuntu:24.10 + +RUN apt update && \ + apt-get install -y \ + build-essential + +WORKDIR /usr/src/dash0/preload/ + +CMD ["watch", "-n1", "./smoke-test.sh"] diff --git a/images/dash0-instrumentation/preload/Makefile b/images/dash0-instrumentation/preload/Makefile new file mode 100644 index 00000000..c4274206 --- /dev/null +++ b/images/dash0-instrumentation/preload/Makefile @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +# SPDX-License-Identifier: Apache-2.0 + +CFLAGS ?= -Wall -Werror -O2 +PREFIX = /usr +LIBDIR = $(PREFIX)/lib +BINDIR = $(PREFIX)/bin + +all: libdash0envhook.so appundertest.so + +libdash0envhook.so: libdash0envhook.c + $(CC) $(CFLAGS) -fPIC -rdynamic -shared -ldl -o $@ libdash0envhook.c + +appundertest.so: appundertest.c + $(CC) $(CFLAGS) -o $@ appundertest.c + +clean: + @rm -f *.so + +install: + install -d $(DESTDIR)$(LIBDIR) $(DESTDIR)$(BINDIR) + install -m 644 libdash0envhook.so $(DESTDIR)$(LIBDIR)/ + install -m 755 dash0-env-hook.sh $(DESTDIR)$(BINDIR)/ + +.PHONY: all clean install diff --git a/images/dash0-instrumentation/preload/README.md b/images/dash0-instrumentation/preload/README.md new file mode 100644 index 00000000..99ff9d26 --- /dev/null +++ b/images/dash0-instrumentation/preload/README.md @@ -0,0 +1,5 @@ +Dash0 LD_PRELOAD Env Hook +========================= + +Overrides [getenv](https://man7.org/linux/man-pages/man3/getenv.3.html) to dynamically add environment variables to +executables after the fact. diff --git a/images/dash0-instrumentation/preload/appundertest.c b/images/dash0-instrumentation/preload/appundertest.c new file mode 100644 index 00000000..3846d800 --- /dev/null +++ b/images/dash0-instrumentation/preload/appundertest.c @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +void echo_env_var(const char* name) { + fputs(name, stdout); + fputs(": ", stdout); + + char* value = getenv(name); + if (value != NULL) { + fputs(value, stdout); + } else { + fputs("not set", stdout); + } + fputs("; ", stdout); +} + +int main(void) { + echo_env_var("TERM"); + echo_env_var("NODE_OPTIONS"); +} + + diff --git a/images/dash0-instrumentation/preload/libdash0envhook.c b/images/dash0-instrumentation/preload/libdash0envhook.c new file mode 100644 index 00000000..f8493d45 --- /dev/null +++ b/images/dash0-instrumentation/preload/libdash0envhook.c @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include + +typedef char* (*getenv_fun_ptr)(const char *name); +typedef char* (*secure_getenv_fun_ptr)(const char *name); + +char* getenv(const char* name) +{ + getenv_fun_ptr original_function = (getenv_fun_ptr)dlsym(RTLD_NEXT, "getenv"); + if (strcmp(name, "NODE_OPTIONS") != 0) { + return original_function(name); + } + + char* dash0_stuff = "--require /dash0"; + + char* original_value = original_function(name); + if (original_value == NULL) { + return dash0_stuff; + } + + char* modified_value = malloc(strlen(dash0_stuff) + 1 + strlen(original_value) + 1); + sprintf(modified_value, "%s %s", dash0_stuff, original_value); + strcpy(modified_value, dash0_stuff); + strcat(modified_value, " "); + strcat(modified_value, original_value); + + // Note: it is probably okay to not free the modified_value, as long as we only malloc a very limited number of char* + // instances, i.e. only one for every environment variable name we want to override, *not* one per getenv call! + return modified_value; +} + diff --git a/images/dash0-instrumentation/preload/smoke-test.sh b/images/dash0-instrumentation/preload/smoke-test.sh new file mode 100755 index 00000000..f5afc09c --- /dev/null +++ b/images/dash0-instrumentation/preload/smoke-test.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +NC='\033[0m' + +relative_directory="$(dirname ${BASH_SOURCE})" +directory="$(realpath $relative_directory)" +cd "$directory" + +make clean +make + +exit_code=0 + +test_output_1=$(LD_PRELOAD="$directory/libdash0envhook.so" ./appundertest.so) + +if [[ "$test_output_1" == "TERM: xterm; NODE_OPTIONS: --require /dash0; " ]]; then + printf "${GREEN}test 1 successful${NC}\n" +else + printf "${RED}test 1 failed:${NC}\n" + echo "$test_output_1" + exit_code=1 +fi + +test_output_2=$(LD_PRELOAD="$directory/libdash0envhook.so" NODE_OPTIONS="existing NODE_OPTIONS" ./appundertest.so) +if [[ "$test_output_2" == "TERM: xterm; NODE_OPTIONS: --require /dash0 existing NODE_OPTIONS; " ]]; then + printf "${GREEN}test 2 successful${NC}\n" +else + printf "${RED}test 2 failed:${NC}\n" + echo "$test_output_2" + exit_code=1 +fi + +exit $exit_code + diff --git a/images/dash0-instrumentation/preload/start-build-container.sh b/images/dash0-instrumentation/preload/start-build-container.sh new file mode 100755 index 00000000..59c86faa --- /dev/null +++ b/images/dash0-instrumentation/preload/start-build-container.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +cd "$(dirname ${BASH_SOURCE})" + +if [[ -z ${ARCH:-} ]]; then + ARCH=x86_64 +fi +if [[ -z ${LIBC:-} ]]; then + LIBC=glibc +fi + +dockerfile_name="Dockerfile-$ARCH-$LIBC" +if [[ ! -f $dockerfile_name ]]; then + echo "The file \"$dockerfile_name\" does not exist, this combination of CPU architecture and libc flavor is not supported." + exit 1 +fi + +image_name=dash0-env-hook-builder-$ARCH-$LIBC +container_name=$image_name + +docker_run_extra_arguments="" +if [[ "${INTERACTIVE:-}" == "true" ]]; then + docker_run_extra_arguments=/bin/bash +fi + +docker rm -f $container_name +docker build . -f $dockerfile_name -t $image_name +docker run \ + --name $container_name \ + -it \ + --volume $(pwd):/usr/src/dash0/preload/ \ + $image_name \ + $docker_run_extra_arguments + From 13cb2ed01e7b525167b62186c2773603eea7573a Mon Sep 17 00:00:00 2001 From: Bastian Krol Date: Fri, 21 Jun 2024 15:32:22 +0200 Subject: [PATCH 02/12] chore(preload): restructure sources, improve Makefile --- .../dash0-instrumentation/preload/.gitignore | 1 - images/dash0-instrumentation/preload/Makefile | 45 +++++++++++++------ .../preload/lib/.gitignore | 1 + .../preload/obj/.gitignore | 1 + .../preload/smoke-test.sh | 4 +- .../preload/{ => src}/libdash0envhook.c | 0 .../preload/{ => test}/appundertest.c | 0 .../preload/testbin/.gitignore | 1 + 8 files changed, 36 insertions(+), 17 deletions(-) delete mode 100644 images/dash0-instrumentation/preload/.gitignore create mode 100644 images/dash0-instrumentation/preload/lib/.gitignore create mode 100644 images/dash0-instrumentation/preload/obj/.gitignore rename images/dash0-instrumentation/preload/{ => src}/libdash0envhook.c (100%) rename images/dash0-instrumentation/preload/{ => test}/appundertest.c (100%) create mode 100644 images/dash0-instrumentation/preload/testbin/.gitignore diff --git a/images/dash0-instrumentation/preload/.gitignore b/images/dash0-instrumentation/preload/.gitignore deleted file mode 100644 index a70e5d2a..00000000 --- a/images/dash0-instrumentation/preload/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.so diff --git a/images/dash0-instrumentation/preload/Makefile b/images/dash0-instrumentation/preload/Makefile index c4274206..c3223fa8 100644 --- a/images/dash0-instrumentation/preload/Makefile +++ b/images/dash0-instrumentation/preload/Makefile @@ -1,25 +1,42 @@ # SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. # SPDX-License-Identifier: Apache-2.0 -CFLAGS ?= -Wall -Werror -O2 -PREFIX = /usr -LIBDIR = $(PREFIX)/lib -BINDIR = $(PREFIX)/bin +CFLAGS ?= -Wall -Werror -Wextra -O2 -all: libdash0envhook.so appundertest.so +SRC_DIR := src +BIN_DIR := bin +OBJ_DIR := obj +LIB_DIR := lib +TEST_DIR := test +SRC_EXT := c -libdash0envhook.so: libdash0envhook.c - $(CC) $(CFLAGS) -fPIC -rdynamic -shared -ldl -o $@ libdash0envhook.c +OS := $(shell uname -s) +SHELL := bash -appundertest.so: appundertest.c - $(CC) $(CFLAGS) -o $@ appundertest.c +NAMES := $(notdir $(basename $(wildcard $(SRC_DIR)/*.$(SRC_EXT)))) +$(info NAMES: $(NAMES)) +OBJECTS :=$(patsubst %,$(OBJ_DIR)/%.o,$(NAMES)) +$(info OBJECTS: $(OBJECTS)) + +TEST_NAMES := $(notdir $(basename $(wildcard $(TEST_DIR)/*.$(SRC_EXT)))) +$(info TEST_NAMES: $(TEST_NAMES)) +TEST_OBJECTS :=$(patsubst %,$(OBJ_DIR)/%.o,$(TEST_NAMES)) +$(info TEST_OBJECTS: $(TEST_OBJECTS)) + + +all: $(OBJECTS) $(TEST_OBJECTS) testbin/appundertest.so + $(CC) $(CFLAGS) -rdynamic -shared -ldl $(OBJECTS) -o $(LIB_DIR)/libdash0envhook.so + +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.$(SRC_EXT) + $(CC) -c -fPIC $^ -o $@ $(DEBUG) $(CFLAGS) $(LIBS) + +$(OBJ_DIR)/%.o: $(TEST_DIR)/%.$(SRC_EXT) + $(CC) -c $^ -o $@ $(DEBUG) $(CFLAGS) $(LIBS) + +testbin/appundertest.so: test/appundertest.c + $(CC) $(CFLAGS) -o $@ test/appundertest.c clean: @rm -f *.so -install: - install -d $(DESTDIR)$(LIBDIR) $(DESTDIR)$(BINDIR) - install -m 644 libdash0envhook.so $(DESTDIR)$(LIBDIR)/ - install -m 755 dash0-env-hook.sh $(DESTDIR)$(BINDIR)/ - .PHONY: all clean install diff --git a/images/dash0-instrumentation/preload/lib/.gitignore b/images/dash0-instrumentation/preload/lib/.gitignore new file mode 100644 index 00000000..140f8cf8 --- /dev/null +++ b/images/dash0-instrumentation/preload/lib/.gitignore @@ -0,0 +1 @@ +*.so diff --git a/images/dash0-instrumentation/preload/obj/.gitignore b/images/dash0-instrumentation/preload/obj/.gitignore new file mode 100644 index 00000000..5761abcf --- /dev/null +++ b/images/dash0-instrumentation/preload/obj/.gitignore @@ -0,0 +1 @@ +*.o diff --git a/images/dash0-instrumentation/preload/smoke-test.sh b/images/dash0-instrumentation/preload/smoke-test.sh index f5afc09c..5e6f1e9c 100755 --- a/images/dash0-instrumentation/preload/smoke-test.sh +++ b/images/dash0-instrumentation/preload/smoke-test.sh @@ -18,7 +18,7 @@ make exit_code=0 -test_output_1=$(LD_PRELOAD="$directory/libdash0envhook.so" ./appundertest.so) +test_output_1=$(LD_PRELOAD="$directory/lib/libdash0envhook.so" ./testbin/appundertest.so) if [[ "$test_output_1" == "TERM: xterm; NODE_OPTIONS: --require /dash0; " ]]; then printf "${GREEN}test 1 successful${NC}\n" @@ -28,7 +28,7 @@ else exit_code=1 fi -test_output_2=$(LD_PRELOAD="$directory/libdash0envhook.so" NODE_OPTIONS="existing NODE_OPTIONS" ./appundertest.so) +test_output_2=$(LD_PRELOAD="$directory/lib/libdash0envhook.so" NODE_OPTIONS="existing NODE_OPTIONS" ./testbin/appundertest.so) if [[ "$test_output_2" == "TERM: xterm; NODE_OPTIONS: --require /dash0 existing NODE_OPTIONS; " ]]; then printf "${GREEN}test 2 successful${NC}\n" else diff --git a/images/dash0-instrumentation/preload/libdash0envhook.c b/images/dash0-instrumentation/preload/src/libdash0envhook.c similarity index 100% rename from images/dash0-instrumentation/preload/libdash0envhook.c rename to images/dash0-instrumentation/preload/src/libdash0envhook.c diff --git a/images/dash0-instrumentation/preload/appundertest.c b/images/dash0-instrumentation/preload/test/appundertest.c similarity index 100% rename from images/dash0-instrumentation/preload/appundertest.c rename to images/dash0-instrumentation/preload/test/appundertest.c diff --git a/images/dash0-instrumentation/preload/testbin/.gitignore b/images/dash0-instrumentation/preload/testbin/.gitignore new file mode 100644 index 00000000..140f8cf8 --- /dev/null +++ b/images/dash0-instrumentation/preload/testbin/.gitignore @@ -0,0 +1 @@ +*.so From 5ec2a6bd50917b8192a5cc3637b04fa6b27ee9f9 Mon Sep 17 00:00:00 2001 From: Bastian Krol Date: Fri, 21 Jun 2024 17:10:16 +0200 Subject: [PATCH 03/12] feat(preload): override GNU secure_getenv as well --- .../preload/Dockerfile-x86_64-glibc | 2 +- .../preload/smoke-test.sh | 41 ------------------- .../preload/src/libdash0envhook.c | 18 ++++++-- .../preload/test/appundertest.c | 17 ++++++++ 4 files changed, 32 insertions(+), 46 deletions(-) delete mode 100755 images/dash0-instrumentation/preload/smoke-test.sh diff --git a/images/dash0-instrumentation/preload/Dockerfile-x86_64-glibc b/images/dash0-instrumentation/preload/Dockerfile-x86_64-glibc index 206ea4f5..16caff22 100644 --- a/images/dash0-instrumentation/preload/Dockerfile-x86_64-glibc +++ b/images/dash0-instrumentation/preload/Dockerfile-x86_64-glibc @@ -9,4 +9,4 @@ RUN apt update && \ WORKDIR /usr/src/dash0/preload/ -CMD ["watch", "-n1", "./smoke-test.sh"] +CMD ["watch", "-n1", "test/smoke-test.sh"] diff --git a/images/dash0-instrumentation/preload/smoke-test.sh b/images/dash0-instrumentation/preload/smoke-test.sh deleted file mode 100755 index 5e6f1e9c..00000000 --- a/images/dash0-instrumentation/preload/smoke-test.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -# SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. -# SPDX-License-Identifier: Apache-2.0 - -set -euo pipefail - -RED='\033[0;31m' -GREEN='\033[0;32m' -NC='\033[0m' - -relative_directory="$(dirname ${BASH_SOURCE})" -directory="$(realpath $relative_directory)" -cd "$directory" - -make clean -make - -exit_code=0 - -test_output_1=$(LD_PRELOAD="$directory/lib/libdash0envhook.so" ./testbin/appundertest.so) - -if [[ "$test_output_1" == "TERM: xterm; NODE_OPTIONS: --require /dash0; " ]]; then - printf "${GREEN}test 1 successful${NC}\n" -else - printf "${RED}test 1 failed:${NC}\n" - echo "$test_output_1" - exit_code=1 -fi - -test_output_2=$(LD_PRELOAD="$directory/lib/libdash0envhook.so" NODE_OPTIONS="existing NODE_OPTIONS" ./testbin/appundertest.so) -if [[ "$test_output_2" == "TERM: xterm; NODE_OPTIONS: --require /dash0 existing NODE_OPTIONS; " ]]; then - printf "${GREEN}test 2 successful${NC}\n" -else - printf "${RED}test 2 failed:${NC}\n" - echo "$test_output_2" - exit_code=1 -fi - -exit $exit_code - diff --git a/images/dash0-instrumentation/preload/src/libdash0envhook.c b/images/dash0-instrumentation/preload/src/libdash0envhook.c index f8493d45..ef50e5e5 100644 --- a/images/dash0-instrumentation/preload/src/libdash0envhook.c +++ b/images/dash0-instrumentation/preload/src/libdash0envhook.c @@ -7,12 +7,12 @@ #include #include -typedef char* (*getenv_fun_ptr)(const char *name); -typedef char* (*secure_getenv_fun_ptr)(const char *name); +typedef char* (*getenv_fun_ptr)(const char* name); -char* getenv(const char* name) +typedef char* (*secure_getenv_fun_ptr)(const char* name); + +char* _getenv(char* (*original_function)(const char* name), const char* name) { - getenv_fun_ptr original_function = (getenv_fun_ptr)dlsym(RTLD_NEXT, "getenv"); if (strcmp(name, "NODE_OPTIONS") != 0) { return original_function(name); } @@ -35,3 +35,13 @@ char* getenv(const char* name) return modified_value; } +char* getenv(const char* name) { + getenv_fun_ptr original_function = (getenv_fun_ptr)dlsym(RTLD_NEXT, "getenv"); + return _getenv(original_function, name); +} + +char* secure_getenv(const char* name) { + secure_getenv_fun_ptr original_function = (secure_getenv_fun_ptr)dlsym(RTLD_NEXT, "secure_getenv"); + return _getenv(original_function, name); +} + diff --git a/images/dash0-instrumentation/preload/test/appundertest.c b/images/dash0-instrumentation/preload/test/appundertest.c index 3846d800..7b77cf2b 100644 --- a/images/dash0-instrumentation/preload/test/appundertest.c +++ b/images/dash0-instrumentation/preload/test/appundertest.c @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. // SPDX-License-Identifier: Apache-2.0 +#define _GNU_SOURCE + #include #include @@ -17,9 +19,24 @@ void echo_env_var(const char* name) { fputs("; ", stdout); } +void echo_env_var_secure(const char* name) { + fputs(name, stdout); + fputs(": ", stdout); + + char* value = secure_getenv(name); + if (value != NULL) { + fputs(value, stdout); + } else { + fputs("not set", stdout); + } + fputs("; ", stdout); +} + int main(void) { echo_env_var("TERM"); echo_env_var("NODE_OPTIONS"); + echo_env_var("TERM"); + echo_env_var_secure("NODE_OPTIONS"); } From 7e83ac731c612353b256390c5b1f4ae37a1f4d77 Mon Sep 17 00:00:00 2001 From: Bastian Krol Date: Fri, 21 Jun 2024 18:01:50 +0200 Subject: [PATCH 04/12] test(preload): improve smoke test script --- images/dash0-instrumentation/preload/Makefile | 4 -- .../preload/test/appundertest.c | 30 +++++++--- .../preload/test/smoke-test.sh | 58 +++++++++++++++++++ 3 files changed, 81 insertions(+), 11 deletions(-) create mode 100755 images/dash0-instrumentation/preload/test/smoke-test.sh diff --git a/images/dash0-instrumentation/preload/Makefile b/images/dash0-instrumentation/preload/Makefile index c3223fa8..d0715567 100644 --- a/images/dash0-instrumentation/preload/Makefile +++ b/images/dash0-instrumentation/preload/Makefile @@ -14,14 +14,10 @@ OS := $(shell uname -s) SHELL := bash NAMES := $(notdir $(basename $(wildcard $(SRC_DIR)/*.$(SRC_EXT)))) -$(info NAMES: $(NAMES)) OBJECTS :=$(patsubst %,$(OBJ_DIR)/%.o,$(NAMES)) -$(info OBJECTS: $(OBJECTS)) TEST_NAMES := $(notdir $(basename $(wildcard $(TEST_DIR)/*.$(SRC_EXT)))) -$(info TEST_NAMES: $(TEST_NAMES)) TEST_OBJECTS :=$(patsubst %,$(OBJ_DIR)/%.o,$(TEST_NAMES)) -$(info TEST_OBJECTS: $(TEST_OBJECTS)) all: $(OBJECTS) $(TEST_OBJECTS) testbin/appundertest.so diff --git a/images/dash0-instrumentation/preload/test/appundertest.c b/images/dash0-instrumentation/preload/test/appundertest.c index 7b77cf2b..80a81132 100644 --- a/images/dash0-instrumentation/preload/test/appundertest.c +++ b/images/dash0-instrumentation/preload/test/appundertest.c @@ -5,6 +5,7 @@ #include #include +#include void echo_env_var(const char* name) { fputs(name, stdout); @@ -14,9 +15,8 @@ void echo_env_var(const char* name) { if (value != NULL) { fputs(value, stdout); } else { - fputs("not set", stdout); + fputs("NULL", stdout); } - fputs("; ", stdout); } void echo_env_var_secure(const char* name) { @@ -27,16 +27,32 @@ void echo_env_var_secure(const char* name) { if (value != NULL) { fputs(value, stdout); } else { - fputs("not set", stdout); + fputs("NULL", stdout); } - fputs("; ", stdout); } -int main(void) { +int main(int argc, char* argv[]) { + if (argc < 2) { + fputs("not enough arguments, the name of the test case needs to be specifed", stdout); + fputs("\n", stdout); + exit(1); + } + char* test_case = argv[1]; + if (strcmp(test_case, "non-existing") == 0) { + echo_env_var("DOES_NOT_EXIST"); + } else if (strcmp(test_case, "term") == 0) { echo_env_var("TERM"); + } else if (strcmp(test_case, "node_options") == 0) { echo_env_var("NODE_OPTIONS"); - echo_env_var("TERM"); + } else if (strcmp(test_case, "term-gnu-secure") == 0) { + echo_env_var_secure("TERM"); + } else if (strcmp(test_case, "node_options-gnu-secure") == 0) { echo_env_var_secure("NODE_OPTIONS"); + } else { + fputs("unknown test case:", stdout); + fputs(test_case, stdout); + fputs("\n", stdout); + exit(1); + } } - diff --git a/images/dash0-instrumentation/preload/test/smoke-test.sh b/images/dash0-instrumentation/preload/test/smoke-test.sh new file mode 100755 index 00000000..22e88592 --- /dev/null +++ b/images/dash0-instrumentation/preload/test/smoke-test.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +NC='\033[0m' + +relative_directory="$(dirname ${BASH_SOURCE})"/.. +directory="$(realpath $relative_directory)" +cd "$directory" + +run_test_case() { + local test_case=$1 + local command=$2 + local expected=$3 + local existing_node_options_value=${4:-} + set +xe + if [[ "$existing_node_options_value" != "" ]]; then + local test_output=$(LD_PRELOAD="$directory/lib/libdash0envhook.so" NODE_OPTIONS="$existing_node_options_value" ./testbin/appundertest.so "$command") + else + local test_output=$(LD_PRELOAD="$directory/lib/libdash0envhook.so" ./testbin/appundertest.so "$command") + fi + local test_exit_code=$? + if [[ $test_exit_code != 0 ]]; then + printf "${RED}test \"$test_case\" crashed:${NC}\n" + echo "received exit code: $test_exit_code" + echo "output: $test_output" + exit_code=1 + elif [[ "$test_output" != "$expected" ]]; then + printf "${RED}test \"$test_case\" failed:${NC}\n" + echo "expected: $expected" + echo "actual: $test_output" + exit_code=1 + else + printf "${GREEN}test \"$test_case\" successful${NC}\n" + fi +} + +make + +exit_code=0 + +run_test_case "getenv: returns NULL for non-existing environment variable" non-existing "DOES_NOT_EXIST: NULL" +run_test_case "getenv: returns environment variable unchanged" term "TERM: xterm" +run_test_case "getenv: overrides NODE_OPTIONS if it is not present" node_options "NODE_OPTIONS: --require /dash0" +run_test_case "getenv: appends to NODE_OPTIONS if it is present" node_options "NODE_OPTIONS: --require /dash0 --existing-node-options" "--existing-node-options" + +run_test_case "secure_getenv: returns NULL for non-existing environment variable" non-existing "DOES_NOT_EXIST: NULL" +run_test_case "secure_getenv: returns environment variable unchanged" term-gnu-secure "TERM: xterm" +run_test_case "secure_getenv: overrides NODE_OPTIONS if it is not present" node_options-gnu-secure "NODE_OPTIONS: --require /dash0" +run_test_case "secure_getenv: appends to NODE_OPTIONS if it is present" node_options-gnu-secure "NODE_OPTIONS: --require /dash0 --existing-node-options" "--existing-node-options" + +exit $exit_code + From dc11c890a1ceb146af8100504d242edb66417a3a Mon Sep 17 00:00:00 2001 From: Bastian Krol Date: Sat, 22 Jun 2024 09:22:13 +0200 Subject: [PATCH 05/12] fix(preload): only malloc once per environment variable name (instead of once per getenv call) --- ...t-build-container.sh => build-and-test.sh} | 0 .../preload/src/libdash0envhook.c | 36 +++++++++++++++---- .../dash0-instrumentation/preload/src/map.c | 34 ++++++++++++++++++ .../dash0-instrumentation/preload/src/map.h | 11 ++++++ .../preload/test/appundertest.c | 10 +++++- .../preload/test/smoke-test.sh | 12 ++++--- 6 files changed, 92 insertions(+), 11 deletions(-) rename images/dash0-instrumentation/preload/{start-build-container.sh => build-and-test.sh} (100%) create mode 100644 images/dash0-instrumentation/preload/src/map.c create mode 100755 images/dash0-instrumentation/preload/src/map.h diff --git a/images/dash0-instrumentation/preload/start-build-container.sh b/images/dash0-instrumentation/preload/build-and-test.sh similarity index 100% rename from images/dash0-instrumentation/preload/start-build-container.sh rename to images/dash0-instrumentation/preload/build-and-test.sh diff --git a/images/dash0-instrumentation/preload/src/libdash0envhook.c b/images/dash0-instrumentation/preload/src/libdash0envhook.c index ef50e5e5..091b7ff2 100644 --- a/images/dash0-instrumentation/preload/src/libdash0envhook.c +++ b/images/dash0-instrumentation/preload/src/libdash0envhook.c @@ -2,36 +2,60 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include #include #include #include +#include "map.h" + typedef char* (*getenv_fun_ptr)(const char* name); typedef char* (*secure_getenv_fun_ptr)(const char* name); +int num_map_entries = 1; +Entry map[1]; +bool init = false; + +char* default_node_options_value = "--require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js"; + + +void init_map() { + if (init) { + return; + } + Entry node_options_entry = { .key = "NODE_OPTIONS", .value = NULL }; + map[0] = node_options_entry; + init = true; +} + char* _getenv(char* (*original_function)(const char* name), const char* name) { + init_map(); + if (strcmp(name, "NODE_OPTIONS") != 0) { return original_function(name); } - char* dash0_stuff = "--require /dash0"; + char* cached = get_map_entry(map, num_map_entries, name); + if (cached != NULL) { + return cached; + } char* original_value = original_function(name); if (original_value == NULL) { - return dash0_stuff; + return default_node_options_value; } - char* modified_value = malloc(strlen(dash0_stuff) + 1 + strlen(original_value) + 1); - sprintf(modified_value, "%s %s", dash0_stuff, original_value); - strcpy(modified_value, dash0_stuff); + char* modified_value = malloc(strlen(default_node_options_value) + 1 + strlen(original_value) + 1); + strcpy(modified_value, default_node_options_value); strcat(modified_value, " "); strcat(modified_value, original_value); // Note: it is probably okay to not free the modified_value, as long as we only malloc a very limited number of char* - // instances, i.e. only one for every environment variable name we want to override, *not* one per getenv call! + // instances, i.e. only one for every environment variable name we want to override. + put_map_entry(map, num_map_entries, name, modified_value); return modified_value; } diff --git a/images/dash0-instrumentation/preload/src/map.c b/images/dash0-instrumentation/preload/src/map.c new file mode 100644 index 00000000..13f93367 --- /dev/null +++ b/images/dash0-instrumentation/preload/src/map.c @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "map.h" + +Entry* find(Entry map[], int len, const char* key) { + for (int i = 0; i < len; i++) { + Entry* e = &map[i]; + if (strcmp(e->key, key) == 0) { + return e; + } + } + return NULL; +} + +char* get_map_entry(Entry map[], int len, const char* key) { + Entry* e = find(map, len, key); + if (e == NULL) { + return NULL; + } + return e->value; +} + +void put_map_entry(Entry* map, int len, const char* key, char* value) { + Entry* e = find(map, len, key); + if (e == NULL) { + return; + } + e->value = value; +} + diff --git a/images/dash0-instrumentation/preload/src/map.h b/images/dash0-instrumentation/preload/src/map.h new file mode 100755 index 00000000..67659986 --- /dev/null +++ b/images/dash0-instrumentation/preload/src/map.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +// SPDX-License-Identifier: Apache-2.0 + +typedef struct Entry { + const char* key; + char* value; +} Entry; + +char* get_map_entry(Entry map[], int len, const char* key); + +void put_map_entry(Entry* map, int len, const char* key, char* value); diff --git a/images/dash0-instrumentation/preload/test/appundertest.c b/images/dash0-instrumentation/preload/test/appundertest.c index 80a81132..2ea99d6b 100644 --- a/images/dash0-instrumentation/preload/test/appundertest.c +++ b/images/dash0-instrumentation/preload/test/appundertest.c @@ -44,12 +44,20 @@ int main(int argc, char* argv[]) { echo_env_var("TERM"); } else if (strcmp(test_case, "node_options") == 0) { echo_env_var("NODE_OPTIONS"); + } else if (strcmp(test_case, "node_options_twice") == 0) { + echo_env_var("NODE_OPTIONS"); + fputs("; ", stdout); + echo_env_var("NODE_OPTIONS"); } else if (strcmp(test_case, "term-gnu-secure") == 0) { echo_env_var_secure("TERM"); } else if (strcmp(test_case, "node_options-gnu-secure") == 0) { echo_env_var_secure("NODE_OPTIONS"); + } else if (strcmp(test_case, "node_options_twice-gnu-secure") == 0) { + echo_env_var_secure("NODE_OPTIONS"); + fputs("; ", stdout); + echo_env_var_secure("NODE_OPTIONS"); } else { - fputs("unknown test case:", stdout); + fputs("unknown test case: ", stdout); fputs(test_case, stdout); fputs("\n", stdout); exit(1); diff --git a/images/dash0-instrumentation/preload/test/smoke-test.sh b/images/dash0-instrumentation/preload/test/smoke-test.sh index 22e88592..76540843 100755 --- a/images/dash0-instrumentation/preload/test/smoke-test.sh +++ b/images/dash0-instrumentation/preload/test/smoke-test.sh @@ -46,13 +46,17 @@ exit_code=0 run_test_case "getenv: returns NULL for non-existing environment variable" non-existing "DOES_NOT_EXIST: NULL" run_test_case "getenv: returns environment variable unchanged" term "TERM: xterm" -run_test_case "getenv: overrides NODE_OPTIONS if it is not present" node_options "NODE_OPTIONS: --require /dash0" -run_test_case "getenv: appends to NODE_OPTIONS if it is present" node_options "NODE_OPTIONS: --require /dash0 --existing-node-options" "--existing-node-options" +run_test_case "getenv: overrides NODE_OPTIONS if it is not present" node_options "NODE_OPTIONS: --require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js" +run_test_case "getenv: ask for NODE_OPTIONS (unset) twice" node_options_twice "NODE_OPTIONS: --require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js; NODE_OPTIONS: --require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js" +run_test_case "getenv: appends to NODE_OPTIONS if it is present" node_options "NODE_OPTIONS: --require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js --existing-node-options" "--existing-node-options" +run_test_case "getenv: ask for NODE_OPTIONS (set) twice" node_options_twice "NODE_OPTIONS: --require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js --existing-node-options; NODE_OPTIONS: --require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js --existing-node-options" "--existing-node-options" run_test_case "secure_getenv: returns NULL for non-existing environment variable" non-existing "DOES_NOT_EXIST: NULL" run_test_case "secure_getenv: returns environment variable unchanged" term-gnu-secure "TERM: xterm" -run_test_case "secure_getenv: overrides NODE_OPTIONS if it is not present" node_options-gnu-secure "NODE_OPTIONS: --require /dash0" -run_test_case "secure_getenv: appends to NODE_OPTIONS if it is present" node_options-gnu-secure "NODE_OPTIONS: --require /dash0 --existing-node-options" "--existing-node-options" +run_test_case "secure_getenv: overrides NODE_OPTIONS if it is not present" node_options-gnu-secure "NODE_OPTIONS: --require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js" +run_test_case "secure_getenv: ask for NODE_OPTIONS (unset) twice" node_options_twice-gnu-secure "NODE_OPTIONS: --require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js; NODE_OPTIONS: --require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js" +run_test_case "secure_getenv: appends to NODE_OPTIONS if it is present" node_options-gnu-secure "NODE_OPTIONS: --require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js --existing-node-options" "--existing-node-options" +run_test_case "secure_getenv: ask for NODE_OPTIONS (set) twice" node_options_twice-gnu-secure "NODE_OPTIONS: --require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js --existing-node-options; NODE_OPTIONS: --require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js --existing-node-options" "--existing-node-options" exit $exit_code From 350eb3c9616d26d2460c567d1d22271b2d58b29d Mon Sep 17 00:00:00 2001 From: Bastian Krol Date: Sat, 22 Jun 2024 10:25:09 +0200 Subject: [PATCH 06/12] feat(preload): support x86_64 as well as arm64 --- ...ckerfile-x86_64-glibc => Dockerfile-glibc} | 2 +- images/dash0-instrumentation/preload/Makefile | 3 +-- .../preload/build-and-test.sh | 25 ++++++++++++++++--- .../dash0-instrumentation/preload/test-all.sh | 12 +++++++++ .../preload/test/smoke-test.sh | 25 ++++++++++++++++++- 5 files changed, 59 insertions(+), 8 deletions(-) rename images/dash0-instrumentation/preload/{Dockerfile-x86_64-glibc => Dockerfile-glibc} (82%) create mode 100755 images/dash0-instrumentation/preload/test-all.sh diff --git a/images/dash0-instrumentation/preload/Dockerfile-x86_64-glibc b/images/dash0-instrumentation/preload/Dockerfile-glibc similarity index 82% rename from images/dash0-instrumentation/preload/Dockerfile-x86_64-glibc rename to images/dash0-instrumentation/preload/Dockerfile-glibc index 16caff22..234317df 100644 --- a/images/dash0-instrumentation/preload/Dockerfile-x86_64-glibc +++ b/images/dash0-instrumentation/preload/Dockerfile-glibc @@ -9,4 +9,4 @@ RUN apt update && \ WORKDIR /usr/src/dash0/preload/ -CMD ["watch", "-n1", "test/smoke-test.sh"] +CMD ["test/smoke-test.sh"] diff --git a/images/dash0-instrumentation/preload/Makefile b/images/dash0-instrumentation/preload/Makefile index d0715567..c094172c 100644 --- a/images/dash0-instrumentation/preload/Makefile +++ b/images/dash0-instrumentation/preload/Makefile @@ -19,7 +19,6 @@ OBJECTS :=$(patsubst %,$(OBJ_DIR)/%.o,$(NAMES)) TEST_NAMES := $(notdir $(basename $(wildcard $(TEST_DIR)/*.$(SRC_EXT)))) TEST_OBJECTS :=$(patsubst %,$(OBJ_DIR)/%.o,$(TEST_NAMES)) - all: $(OBJECTS) $(TEST_OBJECTS) testbin/appundertest.so $(CC) $(CFLAGS) -rdynamic -shared -ldl $(OBJECTS) -o $(LIB_DIR)/libdash0envhook.so @@ -33,6 +32,6 @@ testbin/appundertest.so: test/appundertest.c $(CC) $(CFLAGS) -o $@ test/appundertest.c clean: - @rm -f *.so + @rm -f $(OBJECTS) $(OBJ_DIR)/*.o $(TEST_OBJECTS) $(LIB_DIR)/*.so testbin/appundertest.so .PHONY: all clean install diff --git a/images/dash0-instrumentation/preload/build-and-test.sh b/images/dash0-instrumentation/preload/build-and-test.sh index 59c86faa..a44c9c8e 100755 --- a/images/dash0-instrumentation/preload/build-and-test.sh +++ b/images/dash0-instrumentation/preload/build-and-test.sh @@ -8,15 +8,26 @@ set -euo pipefail cd "$(dirname ${BASH_SOURCE})" if [[ -z ${ARCH:-} ]]; then - ARCH=x86_64 + ARCH=arm64 fi +if [[ $ARCH == arm64 ]]; then + docker_platform=linux/arm64 + expected_cpu_architecture=aarch64 +elif [[ $ARCH == x86_64 ]]; then + docker_platform=linux/amd64 + expected_cpu_architecture=x86_64 +else + echo "The architecture $ARCH is not supported." + exit 1 +fi + if [[ -z ${LIBC:-} ]]; then LIBC=glibc fi -dockerfile_name="Dockerfile-$ARCH-$LIBC" +dockerfile_name="Dockerfile-$LIBC" if [[ ! -f $dockerfile_name ]]; then - echo "The file \"$dockerfile_name\" does not exist, this combination of CPU architecture and libc flavor is not supported." + echo "The file \"$dockerfile_name\" does not exist, the libc flavor $LIBC is not supported." exit 1 fi @@ -29,8 +40,14 @@ if [[ "${INTERACTIVE:-}" == "true" ]]; then fi docker rm -f $container_name -docker build . -f $dockerfile_name -t $image_name +docker build --platform $docker_platform . -f $dockerfile_name -t $image_name + +# note: building one image for both platforms is not suppored on Docker desktop +# docker build --platform linux/amd64,linux/arm64 . -f $dockerfile_name -t dash0-env-hook-builder-all-$LIBC + docker run \ + --platform $docker_platform \ + --env EXPECTED_CPU_ARCHITECTURE=$expected_cpu_architecture \ --name $container_name \ -it \ --volume $(pwd):/usr/src/dash0/preload/ \ diff --git a/images/dash0-instrumentation/preload/test-all.sh b/images/dash0-instrumentation/preload/test-all.sh new file mode 100755 index 00000000..e184db28 --- /dev/null +++ b/images/dash0-instrumentation/preload/test-all.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +cd "$(dirname ${BASH_SOURCE})" + +ARCH=arm64 ./build-and-test.sh +ARCH=x86_64 ./build-and-test.sh + diff --git a/images/dash0-instrumentation/preload/test/smoke-test.sh b/images/dash0-instrumentation/preload/test/smoke-test.sh index 76540843..4da4d20b 100755 --- a/images/dash0-instrumentation/preload/test/smoke-test.sh +++ b/images/dash0-instrumentation/preload/test/smoke-test.sh @@ -13,18 +13,40 @@ relative_directory="$(dirname ${BASH_SOURCE})"/.. directory="$(realpath $relative_directory)" cd "$directory" +if [[ -z ${EXPECTED_CPU_ARCHITECTURE:-} ]]; then + echo "EXPECTED_CPU_ARCHITECTURE is not set for $0." + exit 1 +fi + +arch_output=$(uname -p) +arch_exit_code=$? +if [[ $arch_exit_code != 0 ]]; then + printf "${RED}verifying CPU architecture failed:${NC}\n" + echo "exit code: $arch_exit_code" + echo "output: $arch_output" + exit 1 +elif [[ "$arch_output" != "$EXPECTED_CPU_ARCHITECTURE" ]]; then + printf "${RED}verifying CPU architecture failed:${NC}\n" + echo "expected: $EXPECTED_CPU_ARCHITECTURE" + echo "actual: $arch_output" + exit 1 +else + printf "${GREEN}verifying CPU architecture $EXPECTED_CPU_ARCHITECTURE successful${NC}\n" +fi + run_test_case() { local test_case=$1 local command=$2 local expected=$3 local existing_node_options_value=${4:-} - set +xe + set +e if [[ "$existing_node_options_value" != "" ]]; then local test_output=$(LD_PRELOAD="$directory/lib/libdash0envhook.so" NODE_OPTIONS="$existing_node_options_value" ./testbin/appundertest.so "$command") else local test_output=$(LD_PRELOAD="$directory/lib/libdash0envhook.so" ./testbin/appundertest.so "$command") fi local test_exit_code=$? + set -e if [[ $test_exit_code != 0 ]]; then printf "${RED}test \"$test_case\" crashed:${NC}\n" echo "received exit code: $test_exit_code" @@ -40,6 +62,7 @@ run_test_case() { fi } +make clean make exit_code=0 From 1ffd3f3ce197dbf419a8373b93078a9d4f332b4c Mon Sep 17 00:00:00 2001 From: Bastian Krol Date: Sat, 22 Jun 2024 10:53:17 +0200 Subject: [PATCH 07/12] test(preload): test against glibc and musl Also, convert all shell scripts to POSIX-compliant sh scripts and apply most shellcheck suggestions. --- .../preload/Dockerfile-musl | 10 +++++ images/dash0-instrumentation/preload/Makefile | 2 +- .../preload/build-and-test.sh | 45 ++++++++++++------- .../dash0-instrumentation/preload/test-all.sh | 12 ++--- .../preload/test/smoke-test.sh | 44 +++++++++--------- 5 files changed, 68 insertions(+), 45 deletions(-) create mode 100644 images/dash0-instrumentation/preload/Dockerfile-musl diff --git a/images/dash0-instrumentation/preload/Dockerfile-musl b/images/dash0-instrumentation/preload/Dockerfile-musl new file mode 100644 index 00000000..e5160ada --- /dev/null +++ b/images/dash0-instrumentation/preload/Dockerfile-musl @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +# SPDX-License-Identifier: Apache-2.0 + +FROM alpine:3.20.1 + +RUN apk add --no-cache build-base + +WORKDIR /usr/src/dash0/preload/ + +CMD ["test/smoke-test.sh"] diff --git a/images/dash0-instrumentation/preload/Makefile b/images/dash0-instrumentation/preload/Makefile index c094172c..6b1316af 100644 --- a/images/dash0-instrumentation/preload/Makefile +++ b/images/dash0-instrumentation/preload/Makefile @@ -11,7 +11,7 @@ TEST_DIR := test SRC_EXT := c OS := $(shell uname -s) -SHELL := bash +SHELL := sh NAMES := $(notdir $(basename $(wildcard $(SRC_DIR)/*.$(SRC_EXT)))) OBJECTS :=$(patsubst %,$(OBJ_DIR)/%.o,$(NAMES)) diff --git a/images/dash0-instrumentation/preload/build-and-test.sh b/images/dash0-instrumentation/preload/build-and-test.sh index a44c9c8e..754547b0 100755 --- a/images/dash0-instrumentation/preload/build-and-test.sh +++ b/images/dash0-instrumentation/preload/build-and-test.sh @@ -1,19 +1,19 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. # SPDX-License-Identifier: Apache-2.0 -set -euo pipefail +set -eu -cd "$(dirname ${BASH_SOURCE})" +cd "$(dirname "$0")" -if [[ -z ${ARCH:-} ]]; then +if [ -z "${ARCH:-}" ]; then ARCH=arm64 fi -if [[ $ARCH == arm64 ]]; then +if [ "$ARCH" = arm64 ]; then docker_platform=linux/arm64 expected_cpu_architecture=aarch64 -elif [[ $ARCH == x86_64 ]]; then +elif [ "$ARCH" = x86_64 ]; then docker_platform=linux/amd64 expected_cpu_architecture=x86_64 else @@ -21,12 +21,16 @@ else exit 1 fi -if [[ -z ${LIBC:-} ]]; then +if [ -z "${LIBC:-}" ]; then LIBC=glibc fi +echo +echo +echo ">>> Building and testing for $ARCH and $LIBC <<<" + dockerfile_name="Dockerfile-$LIBC" -if [[ ! -f $dockerfile_name ]]; then +if [ ! -f "$dockerfile_name" ]; then echo "The file \"$dockerfile_name\" does not exist, the libc flavor $LIBC is not supported." exit 1 fi @@ -35,22 +39,29 @@ image_name=dash0-env-hook-builder-$ARCH-$LIBC container_name=$image_name docker_run_extra_arguments="" -if [[ "${INTERACTIVE:-}" == "true" ]]; then - docker_run_extra_arguments=/bin/bash +if [ "${INTERACTIVE:-}" = "true" ]; then + if [ "$LIBC" = glibc ]; then + docker_run_extra_arguments=/bin/bash + elif [ "$LIBC" = musl ]; then + docker_run_extra_arguments=/bin/sh + else + echo "The libc flavor $LIBC is not supported." + exit 1 + fi fi -docker rm -f $container_name -docker build --platform $docker_platform . -f $dockerfile_name -t $image_name +docker rm -f "$container_name" +docker build --platform "$docker_platform" . -f "$dockerfile_name" -t "$image_name" # note: building one image for both platforms is not suppored on Docker desktop # docker build --platform linux/amd64,linux/arm64 . -f $dockerfile_name -t dash0-env-hook-builder-all-$LIBC docker run \ - --platform $docker_platform \ - --env EXPECTED_CPU_ARCHITECTURE=$expected_cpu_architecture \ - --name $container_name \ + --platform "$docker_platform" \ + --env EXPECTED_CPU_ARCHITECTURE="$expected_cpu_architecture" \ + --name "$container_name" \ -it \ - --volume $(pwd):/usr/src/dash0/preload/ \ - $image_name \ + --volume "$(pwd):/usr/src/dash0/preload/" \ + "$image_name" \ $docker_run_extra_arguments diff --git a/images/dash0-instrumentation/preload/test-all.sh b/images/dash0-instrumentation/preload/test-all.sh index e184db28..02ada664 100755 --- a/images/dash0-instrumentation/preload/test-all.sh +++ b/images/dash0-instrumentation/preload/test-all.sh @@ -1,12 +1,14 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. # SPDX-License-Identifier: Apache-2.0 -set -euo pipefail +set -eu -cd "$(dirname ${BASH_SOURCE})" +cd "$(dirname "$0")" -ARCH=arm64 ./build-and-test.sh -ARCH=x86_64 ./build-and-test.sh +ARCH=arm64 LIBC=glibc ./build-and-test.sh +ARCH=x86_64 LIBC=glibc ./build-and-test.sh +ARCH=arm64 LIBC=musl ./build-and-test.sh +ARCH=x86_64 LIBC=musl ./build-and-test.sh diff --git a/images/dash0-instrumentation/preload/test/smoke-test.sh b/images/dash0-instrumentation/preload/test/smoke-test.sh index 4da4d20b..46ebdc9f 100755 --- a/images/dash0-instrumentation/preload/test/smoke-test.sh +++ b/images/dash0-instrumentation/preload/test/smoke-test.sh @@ -1,64 +1,64 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. # SPDX-License-Identifier: Apache-2.0 -set -euo pipefail +set -eu RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' -relative_directory="$(dirname ${BASH_SOURCE})"/.. -directory="$(realpath $relative_directory)" +relative_directory="$(dirname "$0")"/.. +directory="$(realpath "$relative_directory")" cd "$directory" -if [[ -z ${EXPECTED_CPU_ARCHITECTURE:-} ]]; then +if [ -z "${EXPECTED_CPU_ARCHITECTURE:-}" ]; then echo "EXPECTED_CPU_ARCHITECTURE is not set for $0." exit 1 fi -arch_output=$(uname -p) +arch_output=$(uname -m) arch_exit_code=$? -if [[ $arch_exit_code != 0 ]]; then +if [ $arch_exit_code != 0 ]; then printf "${RED}verifying CPU architecture failed:${NC}\n" echo "exit code: $arch_exit_code" echo "output: $arch_output" exit 1 -elif [[ "$arch_output" != "$EXPECTED_CPU_ARCHITECTURE" ]]; then +elif [ "$arch_output" != "$EXPECTED_CPU_ARCHITECTURE" ]; then printf "${RED}verifying CPU architecture failed:${NC}\n" echo "expected: $EXPECTED_CPU_ARCHITECTURE" echo "actual: $arch_output" exit 1 else - printf "${GREEN}verifying CPU architecture $EXPECTED_CPU_ARCHITECTURE successful${NC}\n" + printf "${GREEN}verifying CPU architecture %s successful${NC}\n" "$EXPECTED_CPU_ARCHITECTURE" fi run_test_case() { - local test_case=$1 - local command=$2 - local expected=$3 - local existing_node_options_value=${4:-} + test_case=$1 + command=$2 + expected=$3 + existing_node_options_value=${4:-} set +e - if [[ "$existing_node_options_value" != "" ]]; then - local test_output=$(LD_PRELOAD="$directory/lib/libdash0envhook.so" NODE_OPTIONS="$existing_node_options_value" ./testbin/appundertest.so "$command") + if [ "$existing_node_options_value" != "" ]; then + test_output=$(LD_PRELOAD="$directory/lib/libdash0envhook.so" NODE_OPTIONS="$existing_node_options_value" ./testbin/appundertest.so "$command") else - local test_output=$(LD_PRELOAD="$directory/lib/libdash0envhook.so" ./testbin/appundertest.so "$command") + test_output=$(LD_PRELOAD="$directory/lib/libdash0envhook.so" ./testbin/appundertest.so "$command") fi - local test_exit_code=$? + test_exit_code=$? set -e - if [[ $test_exit_code != 0 ]]; then - printf "${RED}test \"$test_case\" crashed:${NC}\n" + if [ $test_exit_code != 0 ]; then + printf "${RED}test \"%s\" crashed:${NC}\n" "$test_case" echo "received exit code: $test_exit_code" echo "output: $test_output" exit_code=1 - elif [[ "$test_output" != "$expected" ]]; then - printf "${RED}test \"$test_case\" failed:${NC}\n" + elif [ "$test_output" != "$expected" ]; then + printf "${RED}test \"%s\" failed:${NC}\n" "$test_case" echo "expected: $expected" echo "actual: $test_output" exit_code=1 else - printf "${GREEN}test \"$test_case\" successful${NC}\n" + printf "${GREEN}test \"%s\" successful${NC}\n" "$test_case" fi } From 13b2fee88eb13ee3e4cb820410d449fefd29104e Mon Sep 17 00:00:00 2001 From: Bastian Krol Date: Sat, 22 Jun 2024 11:48:34 +0200 Subject: [PATCH 08/12] perf(preload): only look up original function once --- .../preload/src/libdash0envhook.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/images/dash0-instrumentation/preload/src/libdash0envhook.c b/images/dash0-instrumentation/preload/src/libdash0envhook.c index 091b7ff2..928eed11 100644 --- a/images/dash0-instrumentation/preload/src/libdash0envhook.c +++ b/images/dash0-instrumentation/preload/src/libdash0envhook.c @@ -14,13 +14,15 @@ typedef char* (*getenv_fun_ptr)(const char* name); typedef char* (*secure_getenv_fun_ptr)(const char* name); +getenv_fun_ptr original_getenv; +secure_getenv_fun_ptr original_secure_getenv; + int num_map_entries = 1; Entry map[1]; bool init = false; char* default_node_options_value = "--require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js"; - void init_map() { if (init) { return; @@ -60,12 +62,16 @@ char* _getenv(char* (*original_function)(const char* name), const char* name) } char* getenv(const char* name) { - getenv_fun_ptr original_function = (getenv_fun_ptr)dlsym(RTLD_NEXT, "getenv"); - return _getenv(original_function, name); + if (!original_getenv) { + original_getenv = (getenv_fun_ptr)dlsym(RTLD_NEXT, "getenv"); + } + return _getenv(original_getenv, name); } char* secure_getenv(const char* name) { - secure_getenv_fun_ptr original_function = (secure_getenv_fun_ptr)dlsym(RTLD_NEXT, "secure_getenv"); - return _getenv(original_function, name); + if (!original_secure_getenv) { + original_secure_getenv = (secure_getenv_fun_ptr)dlsym(RTLD_NEXT, "secure_getenv"); + } + return _getenv(original_secure_getenv, name); } From 17ab3c416b1ebb455c941690ad9dd42c43a54d92 Mon Sep 17 00:00:00 2001 From: Bastian Krol Date: Sat, 22 Jun 2024 11:56:44 +0200 Subject: [PATCH 09/12] refactor(preload): init map in constructor instead of lazy init --- .../preload/src/libdash0envhook.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/images/dash0-instrumentation/preload/src/libdash0envhook.c b/images/dash0-instrumentation/preload/src/libdash0envhook.c index 928eed11..4c8710a5 100644 --- a/images/dash0-instrumentation/preload/src/libdash0envhook.c +++ b/images/dash0-instrumentation/preload/src/libdash0envhook.c @@ -19,23 +19,17 @@ secure_getenv_fun_ptr original_secure_getenv; int num_map_entries = 1; Entry map[1]; -bool init = false; char* default_node_options_value = "--require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js"; -void init_map() { - if (init) { - return; - } + +__attribute__((constructor)) static void setup(void) { Entry node_options_entry = { .key = "NODE_OPTIONS", .value = NULL }; map[0] = node_options_entry; - init = true; } char* _getenv(char* (*original_function)(const char* name), const char* name) { - init_map(); - if (strcmp(name, "NODE_OPTIONS") != 0) { return original_function(name); } From 46256ab33b351110133d4cf440e733ac9e05043d Mon Sep 17 00:00:00 2001 From: Bastian Krol Date: Mon, 24 Jun 2024 07:47:36 +0200 Subject: [PATCH 10/12] test(preload): build once, then test against both libc flavors The previous build & test setup was invalid: So far we have built _different_ libdash0envhook.so binaries for the LD_PRELOAD hook per libc flavor. With this, the binary works in the target system. But actually this approach is not not possible in the real world. We can set one binary as LD_PRELOAD when instrumenting a container and we do not know which libc flavor it is ahead of time. Thus we ultimately need to build a libdash0envhook.so binary that works independently of which libc flavor the target system uses. This commit only fixes the test setup. The binary does not yet support both libc flavors. This will be handled in a follow up commit. [skip ci] --- images/dash0-instrumentation/preload/Makefile | 28 ++++++---- .../preload/docker/Dockerfile-build | 12 +++++ .../Dockerfile-test-glibc} | 2 +- .../Dockerfile-test-musl} | 2 +- .../preload/scripts/build-in-container.sh | 54 +++++++++++++++++++ .../preload/scripts/make-clean.sh | 12 +++++ .../run-tests-in-container.sh} | 23 ++++---- .../preload/scripts/test-all.sh | 16 ++++++ .../preload/src/libdash0envhook.c | 1 - .../dash0-instrumentation/preload/src/map.c | 3 +- .../dash0-instrumentation/preload/src/map.h | 1 + .../dash0-instrumentation/preload/test-all.sh | 14 ----- .../preload/test/appundertest.c | 3 +- .../test/{smoke-test.sh => run-tests.sh} | 18 +++++-- .../preload/testbin/.gitignore | 1 - .../preload/testbin/aarch64/.gitignore | 1 + .../preload/testbin/x86_64/.gitignore | 1 + 17 files changed, 144 insertions(+), 48 deletions(-) create mode 100644 images/dash0-instrumentation/preload/docker/Dockerfile-build rename images/dash0-instrumentation/preload/{Dockerfile-glibc => docker/Dockerfile-test-glibc} (88%) rename images/dash0-instrumentation/preload/{Dockerfile-musl => docker/Dockerfile-test-musl} (86%) create mode 100755 images/dash0-instrumentation/preload/scripts/build-in-container.sh create mode 100755 images/dash0-instrumentation/preload/scripts/make-clean.sh rename images/dash0-instrumentation/preload/{build-and-test.sh => scripts/run-tests-in-container.sh} (73%) create mode 100755 images/dash0-instrumentation/preload/scripts/test-all.sh delete mode 100755 images/dash0-instrumentation/preload/test-all.sh rename images/dash0-instrumentation/preload/test/{smoke-test.sh => run-tests.sh} (87%) delete mode 100644 images/dash0-instrumentation/preload/testbin/.gitignore create mode 100644 images/dash0-instrumentation/preload/testbin/aarch64/.gitignore create mode 100644 images/dash0-instrumentation/preload/testbin/x86_64/.gitignore diff --git a/images/dash0-instrumentation/preload/Makefile b/images/dash0-instrumentation/preload/Makefile index 6b1316af..23915430 100644 --- a/images/dash0-instrumentation/preload/Makefile +++ b/images/dash0-instrumentation/preload/Makefile @@ -1,13 +1,15 @@ # SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. # SPDX-License-Identifier: Apache-2.0 +ARCH := $(shell uname -m) CFLAGS ?= -Wall -Werror -Wextra -O2 +LIB_LINKER_FLAGS ?= -nostdlib -rdynamic -shared -ldl SRC_DIR := src -BIN_DIR := bin OBJ_DIR := obj LIB_DIR := lib TEST_DIR := test +TESTBIN_DIR := testbin/$(ARCH) SRC_EXT := c OS := $(shell uname -s) @@ -17,21 +19,25 @@ NAMES := $(notdir $(basename $(wildcard $(SRC_DIR)/*.$(SRC_EXT)))) OBJECTS :=$(patsubst %,$(OBJ_DIR)/%.o,$(NAMES)) TEST_NAMES := $(notdir $(basename $(wildcard $(TEST_DIR)/*.$(SRC_EXT)))) -TEST_OBJECTS :=$(patsubst %,$(OBJ_DIR)/%.o,$(TEST_NAMES)) +TEST_OBJECTS :=$(patsubst %,$(TESTBIN_DIR)/%.o,$(TEST_NAMES)) -all: $(OBJECTS) $(TEST_OBJECTS) testbin/appundertest.so - $(CC) $(CFLAGS) -rdynamic -shared -ldl $(OBJECTS) -o $(LIB_DIR)/libdash0envhook.so +all: $(LIB_DIR)/libdash0envhook.so + +$(LIB_DIR)/libdash0envhook.so: $(OBJECTS) + $(CC) $(CFLAGS) $(LIB_LINKER_FLAGS) $(OBJECTS) -o $(LIB_DIR)/libdash0envhook.so $(OBJ_DIR)/%.o: $(SRC_DIR)/%.$(SRC_EXT) $(CC) -c -fPIC $^ -o $@ $(DEBUG) $(CFLAGS) $(LIBS) -$(OBJ_DIR)/%.o: $(TEST_DIR)/%.$(SRC_EXT) - $(CC) -c $^ -o $@ $(DEBUG) $(CFLAGS) $(LIBS) +clean: clean-test + @rm -f $(OBJECTS) $(LIB_DIR)/libdash0envhook.so + +clean-test: + @rm -f $(TEST_OBJECTS) -testbin/appundertest.so: test/appundertest.c - $(CC) $(CFLAGS) -o $@ test/appundertest.c +build-test: $(TEST_OBJECTS) -clean: - @rm -f $(OBJECTS) $(OBJ_DIR)/*.o $(TEST_OBJECTS) $(LIB_DIR)/*.so testbin/appundertest.so +$(TESTBIN_DIR)/%.o: $(TEST_DIR)/%.$(SRC_EXT) + $(CC) $(CFLAGS) -o $@ $(DEBUG) $^ -.PHONY: all clean install +.PHONY: all clean install clean-test build-test diff --git a/images/dash0-instrumentation/preload/docker/Dockerfile-build b/images/dash0-instrumentation/preload/docker/Dockerfile-build new file mode 100644 index 00000000..4221c59c --- /dev/null +++ b/images/dash0-instrumentation/preload/docker/Dockerfile-build @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +# SPDX-License-Identifier: Apache-2.0 + +FROM ubuntu:24.10 + +RUN apt update && \ + apt-get install -y \ + build-essential + +WORKDIR /usr/src/dash0/preload/ + +CMD ["scripts/make-clean.sh"] diff --git a/images/dash0-instrumentation/preload/Dockerfile-glibc b/images/dash0-instrumentation/preload/docker/Dockerfile-test-glibc similarity index 88% rename from images/dash0-instrumentation/preload/Dockerfile-glibc rename to images/dash0-instrumentation/preload/docker/Dockerfile-test-glibc index 234317df..3d728af3 100644 --- a/images/dash0-instrumentation/preload/Dockerfile-glibc +++ b/images/dash0-instrumentation/preload/docker/Dockerfile-test-glibc @@ -9,4 +9,4 @@ RUN apt update && \ WORKDIR /usr/src/dash0/preload/ -CMD ["test/smoke-test.sh"] +CMD ["test/run-tests.sh"] diff --git a/images/dash0-instrumentation/preload/Dockerfile-musl b/images/dash0-instrumentation/preload/docker/Dockerfile-test-musl similarity index 86% rename from images/dash0-instrumentation/preload/Dockerfile-musl rename to images/dash0-instrumentation/preload/docker/Dockerfile-test-musl index e5160ada..a5befcb1 100644 --- a/images/dash0-instrumentation/preload/Dockerfile-musl +++ b/images/dash0-instrumentation/preload/docker/Dockerfile-test-musl @@ -7,4 +7,4 @@ RUN apk add --no-cache build-base WORKDIR /usr/src/dash0/preload/ -CMD ["test/smoke-test.sh"] +CMD ["test/run-tests.sh"] diff --git a/images/dash0-instrumentation/preload/scripts/build-in-container.sh b/images/dash0-instrumentation/preload/scripts/build-in-container.sh new file mode 100755 index 00000000..e03edfdd --- /dev/null +++ b/images/dash0-instrumentation/preload/scripts/build-in-container.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env sh + +# SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +# SPDX-License-Identifier: Apache-2.0 + +set -eu + +cd "$(dirname "$0")"/.. + +# TODO build multi platform image + +if [ -z "${ARCH:-}" ]; then + ARCH=arm64 +fi +if [ "$ARCH" = arm64 ]; then + docker_platform=linux/arm64 +elif [ "$ARCH" = x86_64 ]; then + docker_platform=linux/amd64 +else + echo "The architecture $ARCH is not supported." + exit 1 +fi + +dockerfile_name=docker/Dockerfile-build +image_name=dash0-env-hook-builder +container_name=$image_name + +docker_run_extra_arguments="" +if [ "${INTERACTIVE:-}" = "true" ]; then + docker_run_extra_arguments=/bin/bash +fi + +echo +echo +echo ">>> Building the library on $ARCH <<<" + +docker rm -f "$container_name" +docker build \ + --platform "$docker_platform" \ + . \ + -f "$dockerfile_name" \ + -t "$image_name" + +# note: building one image for both platforms is not supported by Docker desktop's standard "docker build" command. +# docker build --platform linux/amd64,linux/arm64 . -f $dockerfile_name -t dash0-env-hook-builder-all-$LIBC + +docker run \ + --platform "$docker_platform" \ + --name "$container_name" \ + -it \ + --volume "$(pwd):/usr/src/dash0/preload/" \ + "$image_name" \ + $docker_run_extra_arguments + diff --git a/images/dash0-instrumentation/preload/scripts/make-clean.sh b/images/dash0-instrumentation/preload/scripts/make-clean.sh new file mode 100755 index 00000000..5bd47a3d --- /dev/null +++ b/images/dash0-instrumentation/preload/scripts/make-clean.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env sh + +# SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +# SPDX-License-Identifier: Apache-2.0 + +set -eu + +cd "$(dirname "$0")"/.. + +make clean +make + diff --git a/images/dash0-instrumentation/preload/build-and-test.sh b/images/dash0-instrumentation/preload/scripts/run-tests-in-container.sh similarity index 73% rename from images/dash0-instrumentation/preload/build-and-test.sh rename to images/dash0-instrumentation/preload/scripts/run-tests-in-container.sh index 754547b0..8f5419c2 100755 --- a/images/dash0-instrumentation/preload/build-and-test.sh +++ b/images/dash0-instrumentation/preload/scripts/run-tests-in-container.sh @@ -5,7 +5,7 @@ set -eu -cd "$(dirname "$0")" +cd "$(dirname "$0")"/.. if [ -z "${ARCH:-}" ]; then ARCH=arm64 @@ -25,17 +25,13 @@ if [ -z "${LIBC:-}" ]; then LIBC=glibc fi -echo -echo -echo ">>> Building and testing for $ARCH and $LIBC <<<" - -dockerfile_name="Dockerfile-$LIBC" +dockerfile_name="docker/Dockerfile-test-$LIBC" if [ ! -f "$dockerfile_name" ]; then echo "The file \"$dockerfile_name\" does not exist, the libc flavor $LIBC is not supported." exit 1 fi -image_name=dash0-env-hook-builder-$ARCH-$LIBC +image_name=dash0-env-hook-test-$ARCH-$LIBC container_name=$image_name docker_run_extra_arguments="" @@ -50,11 +46,16 @@ if [ "${INTERACTIVE:-}" = "true" ]; then fi fi -docker rm -f "$container_name" -docker build --platform "$docker_platform" . -f "$dockerfile_name" -t "$image_name" +echo +echo +echo ">>> Testing the library on $ARCH and $LIBC <<<" -# note: building one image for both platforms is not suppored on Docker desktop -# docker build --platform linux/amd64,linux/arm64 . -f $dockerfile_name -t dash0-env-hook-builder-all-$LIBC +docker rm -f "$container_name" +docker build \ + --platform "$docker_platform" \ + . \ + -f "$dockerfile_name" \ + -t "$image_name" docker run \ --platform "$docker_platform" \ diff --git a/images/dash0-instrumentation/preload/scripts/test-all.sh b/images/dash0-instrumentation/preload/scripts/test-all.sh new file mode 100755 index 00000000..af878dcb --- /dev/null +++ b/images/dash0-instrumentation/preload/scripts/test-all.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env sh + +# SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +# SPDX-License-Identifier: Apache-2.0 + +set -eu + +cd "$(dirname "$0")"/.. + +scripts/build-in-container.sh + +ARCH=arm64 LIBC=glibc scripts/run-tests-in-container.sh +ARCH=x86_64 LIBC=glibc scripts/run-tests-in-container.sh +ARCH=arm64 LIBC=musl scripts/run-tests-in-container.sh +ARCH=x86_64 LIBC=musl scripts/run-tests-in-container.sh + diff --git a/images/dash0-instrumentation/preload/src/libdash0envhook.c b/images/dash0-instrumentation/preload/src/libdash0envhook.c index 4c8710a5..85ceab73 100644 --- a/images/dash0-instrumentation/preload/src/libdash0envhook.c +++ b/images/dash0-instrumentation/preload/src/libdash0envhook.c @@ -22,7 +22,6 @@ Entry map[1]; char* default_node_options_value = "--require /opt/dash0/instrumentation/node.js/node_modules/@dash0/opentelemetry/src/index.js"; - __attribute__((constructor)) static void setup(void) { Entry node_options_entry = { .key = "NODE_OPTIONS", .value = NULL }; map[0] = node_options_entry; diff --git a/images/dash0-instrumentation/preload/src/map.c b/images/dash0-instrumentation/preload/src/map.c index 13f93367..0fcb4e5a 100644 --- a/images/dash0-instrumentation/preload/src/map.c +++ b/images/dash0-instrumentation/preload/src/map.c @@ -1,13 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. // SPDX-License-Identifier: Apache-2.0 -#include #include #include "map.h" Entry* find(Entry map[], int len, const char* key) { - for (int i = 0; i < len; i++) { + for (int i = 0; i < len; i++) { Entry* e = &map[i]; if (strcmp(e->key, key) == 0) { return e; diff --git a/images/dash0-instrumentation/preload/src/map.h b/images/dash0-instrumentation/preload/src/map.h index 67659986..95f013f5 100755 --- a/images/dash0-instrumentation/preload/src/map.h +++ b/images/dash0-instrumentation/preload/src/map.h @@ -9,3 +9,4 @@ typedef struct Entry { char* get_map_entry(Entry map[], int len, const char* key); void put_map_entry(Entry* map, int len, const char* key, char* value); + diff --git a/images/dash0-instrumentation/preload/test-all.sh b/images/dash0-instrumentation/preload/test-all.sh deleted file mode 100755 index 02ada664..00000000 --- a/images/dash0-instrumentation/preload/test-all.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env sh - -# SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. -# SPDX-License-Identifier: Apache-2.0 - -set -eu - -cd "$(dirname "$0")" - -ARCH=arm64 LIBC=glibc ./build-and-test.sh -ARCH=x86_64 LIBC=glibc ./build-and-test.sh -ARCH=arm64 LIBC=musl ./build-and-test.sh -ARCH=x86_64 LIBC=musl ./build-and-test.sh - diff --git a/images/dash0-instrumentation/preload/test/appundertest.c b/images/dash0-instrumentation/preload/test/appundertest.c index 2ea99d6b..576c1507 100644 --- a/images/dash0-instrumentation/preload/test/appundertest.c +++ b/images/dash0-instrumentation/preload/test/appundertest.c @@ -33,8 +33,7 @@ void echo_env_var_secure(const char* name) { int main(int argc, char* argv[]) { if (argc < 2) { - fputs("not enough arguments, the name of the test case needs to be specifed", stdout); - fputs("\n", stdout); + fputs("error: not enough arguments, the name of the test case needs to be specifed\n", stdout); exit(1); } char* test_case = argv[1]; diff --git a/images/dash0-instrumentation/preload/test/smoke-test.sh b/images/dash0-instrumentation/preload/test/run-tests.sh similarity index 87% rename from images/dash0-instrumentation/preload/test/smoke-test.sh rename to images/dash0-instrumentation/preload/test/run-tests.sh index 46ebdc9f..77200ff2 100755 --- a/images/dash0-instrumentation/preload/test/smoke-test.sh +++ b/images/dash0-instrumentation/preload/test/run-tests.sh @@ -34,6 +34,15 @@ else printf "${GREEN}verifying CPU architecture %s successful${NC}\n" "$EXPECTED_CPU_ARCHITECTURE" fi +preload_lib=$directory/lib/libdash0envhook.so +if [ ! -f $preload_lib ]; then + printf "${RED}error: $preload_lib does not exist, not running any tests.${NC}\n" + exit 1 +fi + +appundertest=testbin/"${arch_output}"/appundertest.o +echo appundertest: $appundertest + run_test_case() { test_case=$1 command=$2 @@ -41,9 +50,9 @@ run_test_case() { existing_node_options_value=${4:-} set +e if [ "$existing_node_options_value" != "" ]; then - test_output=$(LD_PRELOAD="$directory/lib/libdash0envhook.so" NODE_OPTIONS="$existing_node_options_value" ./testbin/appundertest.so "$command") + test_output=$(LD_PRELOAD="$preload_lib" NODE_OPTIONS="$existing_node_options_value" "$appundertest" "$command") else - test_output=$(LD_PRELOAD="$directory/lib/libdash0envhook.so" ./testbin/appundertest.so "$command") + test_output=$(LD_PRELOAD="$preload_lib" "$appundertest" "$command") fi test_exit_code=$? set -e @@ -62,8 +71,9 @@ run_test_case() { fi } -make clean -make +# We always need to clean out the old appundertest.o, it might have been built for a different libc flavor. +make clean-test +make build-test exit_code=0 diff --git a/images/dash0-instrumentation/preload/testbin/.gitignore b/images/dash0-instrumentation/preload/testbin/.gitignore deleted file mode 100644 index 140f8cf8..00000000 --- a/images/dash0-instrumentation/preload/testbin/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.so diff --git a/images/dash0-instrumentation/preload/testbin/aarch64/.gitignore b/images/dash0-instrumentation/preload/testbin/aarch64/.gitignore new file mode 100644 index 00000000..5761abcf --- /dev/null +++ b/images/dash0-instrumentation/preload/testbin/aarch64/.gitignore @@ -0,0 +1 @@ +*.o diff --git a/images/dash0-instrumentation/preload/testbin/x86_64/.gitignore b/images/dash0-instrumentation/preload/testbin/x86_64/.gitignore new file mode 100644 index 00000000..5761abcf --- /dev/null +++ b/images/dash0-instrumentation/preload/testbin/x86_64/.gitignore @@ -0,0 +1 @@ +*.o From 7190db61f0af7b12db500a85be42469ad10abb64 Mon Sep 17 00:00:00 2001 From: Bastian Krol Date: Mon, 24 Jun 2024 12:37:08 +0200 Subject: [PATCH 11/12] test(preload): run tests for both CPU architectures [skip ci] --- images/dash0-instrumentation/preload/Makefile | 9 ++-- .../preload/scripts/build-in-container.sh | 12 +++--- .../preload/scripts/run-tests-in-container.sh | 5 ++- .../preload/scripts/test-all.sh | 42 ++++++++++++++++--- .../preload/test/run-tests.sh | 12 +++--- 5 files changed, 58 insertions(+), 22 deletions(-) diff --git a/images/dash0-instrumentation/preload/Makefile b/images/dash0-instrumentation/preload/Makefile index 23915430..0617ed5a 100644 --- a/images/dash0-instrumentation/preload/Makefile +++ b/images/dash0-instrumentation/preload/Makefile @@ -8,6 +8,7 @@ LIB_LINKER_FLAGS ?= -nostdlib -rdynamic -shared -ldl SRC_DIR := src OBJ_DIR := obj LIB_DIR := lib +LIB_NAME := $(LIB_DIR)/libdash0envhook_$(ARCH).so TEST_DIR := test TESTBIN_DIR := testbin/$(ARCH) SRC_EXT := c @@ -21,16 +22,16 @@ OBJECTS :=$(patsubst %,$(OBJ_DIR)/%.o,$(NAMES)) TEST_NAMES := $(notdir $(basename $(wildcard $(TEST_DIR)/*.$(SRC_EXT)))) TEST_OBJECTS :=$(patsubst %,$(TESTBIN_DIR)/%.o,$(TEST_NAMES)) -all: $(LIB_DIR)/libdash0envhook.so +all: $(LIB_NAME) -$(LIB_DIR)/libdash0envhook.so: $(OBJECTS) - $(CC) $(CFLAGS) $(LIB_LINKER_FLAGS) $(OBJECTS) -o $(LIB_DIR)/libdash0envhook.so +$(LIB_NAME): $(OBJECTS) + $(CC) $(CFLAGS) $(LIB_LINKER_FLAGS) $(OBJECTS) -o $(LIB_NAME) $(OBJ_DIR)/%.o: $(SRC_DIR)/%.$(SRC_EXT) $(CC) -c -fPIC $^ -o $@ $(DEBUG) $(CFLAGS) $(LIBS) clean: clean-test - @rm -f $(OBJECTS) $(LIB_DIR)/libdash0envhook.so + @rm -f $(OBJECTS) $(LIB_NAME) clean-test: @rm -f $(TEST_OBJECTS) diff --git a/images/dash0-instrumentation/preload/scripts/build-in-container.sh b/images/dash0-instrumentation/preload/scripts/build-in-container.sh index e03edfdd..a795c149 100755 --- a/images/dash0-instrumentation/preload/scripts/build-in-container.sh +++ b/images/dash0-instrumentation/preload/scripts/build-in-container.sh @@ -22,7 +22,7 @@ else fi dockerfile_name=docker/Dockerfile-build -image_name=dash0-env-hook-builder +image_name=dash0-env-hook-builder-$ARCH container_name=$image_name docker_run_extra_arguments="" @@ -34,16 +34,18 @@ echo echo echo ">>> Building the library on $ARCH <<<" -docker rm -f "$container_name" +docker rm -f "$container_name" 2> /dev/null + +# Note: This is not the multi-platform image that we will need eventually. The combination of docker build and docker +# run here basically only builds the library binary for the given CPU architecture and places it in the lib folder. And +# since the lib folder is mounted, the binary is then available in the host file system for further testing (for +# example, via other container images using a specific CPU architecture). docker build \ --platform "$docker_platform" \ . \ -f "$dockerfile_name" \ -t "$image_name" -# note: building one image for both platforms is not supported by Docker desktop's standard "docker build" command. -# docker build --platform linux/amd64,linux/arm64 . -f $dockerfile_name -t dash0-env-hook-builder-all-$LIBC - docker run \ --platform "$docker_platform" \ --name "$container_name" \ diff --git a/images/dash0-instrumentation/preload/scripts/run-tests-in-container.sh b/images/dash0-instrumentation/preload/scripts/run-tests-in-container.sh index 8f5419c2..b8ffc215 100755 --- a/images/dash0-instrumentation/preload/scripts/run-tests-in-container.sh +++ b/images/dash0-instrumentation/preload/scripts/run-tests-in-container.sh @@ -47,8 +47,9 @@ if [ "${INTERACTIVE:-}" = "true" ]; then fi echo -echo -echo ">>> Testing the library on $ARCH and $LIBC <<<" +echo --------------------------------------- +echo "testing the library on $ARCH and $LIBC" +echo --------------------------------------- docker rm -f "$container_name" docker build \ diff --git a/images/dash0-instrumentation/preload/scripts/test-all.sh b/images/dash0-instrumentation/preload/scripts/test-all.sh index af878dcb..80beed77 100755 --- a/images/dash0-instrumentation/preload/scripts/test-all.sh +++ b/images/dash0-instrumentation/preload/scripts/test-all.sh @@ -5,12 +5,44 @@ set -eu +RED='\033[0;31m' +GREEN='\033[0;32m' +NC='\033[0m' + cd "$(dirname "$0")"/.. -scripts/build-in-container.sh +ARCH=arm64 scripts/build-in-container.sh +ARCH=x86_64 scripts/build-in-container.sh + +exit_code=0 +summary="" + +run_tests_for_architecture_and_libc_flavor() { + arch=$1 + libc=$2 + set +e + ARCH=$arch LIBC=$libc scripts/run-tests-in-container.sh + test_exit_code=$? + set -e + echo + echo --------------------------------------- + if [ $test_exit_code != 0 ]; then + printf "${RED}tests for %s/%s failed (see above for details)${NC}\n" "$arch" "$libc" + exit_code=1 + summary="$summary\n$arch/$libc:\tfailed" + else + printf "${GREEN}tests for %s/%s were successful${NC}\n" "$arch" "$libc" + summary="$summary\n$arch/$libc:\tok" + fi + echo --------------------------------------- + echo +} + +run_tests_for_architecture_and_libc_flavor arm64 glibc +run_tests_for_architecture_and_libc_flavor x86_64 glibc +run_tests_for_architecture_and_libc_flavor arm64 musl +run_tests_for_architecture_and_libc_flavor x86_64 musl -ARCH=arm64 LIBC=glibc scripts/run-tests-in-container.sh -ARCH=x86_64 LIBC=glibc scripts/run-tests-in-container.sh -ARCH=arm64 LIBC=musl scripts/run-tests-in-container.sh -ARCH=x86_64 LIBC=musl scripts/run-tests-in-container.sh +echo "$summary" +exit $exit_code diff --git a/images/dash0-instrumentation/preload/test/run-tests.sh b/images/dash0-instrumentation/preload/test/run-tests.sh index 77200ff2..e575bb82 100755 --- a/images/dash0-instrumentation/preload/test/run-tests.sh +++ b/images/dash0-instrumentation/preload/test/run-tests.sh @@ -18,29 +18,29 @@ if [ -z "${EXPECTED_CPU_ARCHITECTURE:-}" ]; then exit 1 fi -arch_output=$(uname -m) +arch=$(uname -m) arch_exit_code=$? if [ $arch_exit_code != 0 ]; then printf "${RED}verifying CPU architecture failed:${NC}\n" echo "exit code: $arch_exit_code" - echo "output: $arch_output" + echo "output: $arch" exit 1 -elif [ "$arch_output" != "$EXPECTED_CPU_ARCHITECTURE" ]; then +elif [ "$arch" != "$EXPECTED_CPU_ARCHITECTURE" ]; then printf "${RED}verifying CPU architecture failed:${NC}\n" echo "expected: $EXPECTED_CPU_ARCHITECTURE" - echo "actual: $arch_output" + echo "actual: $arch" exit 1 else printf "${GREEN}verifying CPU architecture %s successful${NC}\n" "$EXPECTED_CPU_ARCHITECTURE" fi -preload_lib=$directory/lib/libdash0envhook.so +preload_lib=$directory/lib/libdash0envhook_$arch.so if [ ! -f $preload_lib ]; then printf "${RED}error: $preload_lib does not exist, not running any tests.${NC}\n" exit 1 fi -appundertest=testbin/"${arch_output}"/appundertest.o +appundertest=testbin/"${arch}"/appundertest.o echo appundertest: $appundertest run_test_case() { From 38aa8fc3228ce8eb8c01d0c6121c6f15594b5c6c Mon Sep 17 00:00:00 2001 From: Bastian Krol Date: Fri, 18 Oct 2024 13:10:25 +0200 Subject: [PATCH 12/12] chore(preload): move dash0-instrumentation -> instrumentation --- .../{dash0-instrumentation => instrumentation}/preload/Makefile | 0 .../{dash0-instrumentation => instrumentation}/preload/README.md | 0 .../preload/docker/Dockerfile-build | 0 .../preload/docker/Dockerfile-test-glibc | 0 .../preload/docker/Dockerfile-test-musl | 0 .../preload/lib/.gitignore | 0 .../preload/obj/.gitignore | 0 .../preload/scripts/build-in-container.sh | 0 .../preload/scripts/make-clean.sh | 0 .../preload/scripts/run-tests-in-container.sh | 0 .../preload/scripts/test-all.sh | 0 .../preload/src/libdash0envhook.c | 0 .../{dash0-instrumentation => instrumentation}/preload/src/map.c | 0 .../{dash0-instrumentation => instrumentation}/preload/src/map.h | 0 .../preload/test/appundertest.c | 0 .../preload/test/run-tests.sh | 0 .../preload/testbin/aarch64/.gitignore | 0 .../preload/testbin/x86_64/.gitignore | 0 18 files changed, 0 insertions(+), 0 deletions(-) rename images/{dash0-instrumentation => instrumentation}/preload/Makefile (100%) rename images/{dash0-instrumentation => instrumentation}/preload/README.md (100%) rename images/{dash0-instrumentation => instrumentation}/preload/docker/Dockerfile-build (100%) rename images/{dash0-instrumentation => instrumentation}/preload/docker/Dockerfile-test-glibc (100%) rename images/{dash0-instrumentation => instrumentation}/preload/docker/Dockerfile-test-musl (100%) rename images/{dash0-instrumentation => instrumentation}/preload/lib/.gitignore (100%) rename images/{dash0-instrumentation => instrumentation}/preload/obj/.gitignore (100%) rename images/{dash0-instrumentation => instrumentation}/preload/scripts/build-in-container.sh (100%) rename images/{dash0-instrumentation => instrumentation}/preload/scripts/make-clean.sh (100%) rename images/{dash0-instrumentation => instrumentation}/preload/scripts/run-tests-in-container.sh (100%) rename images/{dash0-instrumentation => instrumentation}/preload/scripts/test-all.sh (100%) rename images/{dash0-instrumentation => instrumentation}/preload/src/libdash0envhook.c (100%) rename images/{dash0-instrumentation => instrumentation}/preload/src/map.c (100%) rename images/{dash0-instrumentation => instrumentation}/preload/src/map.h (100%) rename images/{dash0-instrumentation => instrumentation}/preload/test/appundertest.c (100%) rename images/{dash0-instrumentation => instrumentation}/preload/test/run-tests.sh (100%) rename images/{dash0-instrumentation => instrumentation}/preload/testbin/aarch64/.gitignore (100%) rename images/{dash0-instrumentation => instrumentation}/preload/testbin/x86_64/.gitignore (100%) diff --git a/images/dash0-instrumentation/preload/Makefile b/images/instrumentation/preload/Makefile similarity index 100% rename from images/dash0-instrumentation/preload/Makefile rename to images/instrumentation/preload/Makefile diff --git a/images/dash0-instrumentation/preload/README.md b/images/instrumentation/preload/README.md similarity index 100% rename from images/dash0-instrumentation/preload/README.md rename to images/instrumentation/preload/README.md diff --git a/images/dash0-instrumentation/preload/docker/Dockerfile-build b/images/instrumentation/preload/docker/Dockerfile-build similarity index 100% rename from images/dash0-instrumentation/preload/docker/Dockerfile-build rename to images/instrumentation/preload/docker/Dockerfile-build diff --git a/images/dash0-instrumentation/preload/docker/Dockerfile-test-glibc b/images/instrumentation/preload/docker/Dockerfile-test-glibc similarity index 100% rename from images/dash0-instrumentation/preload/docker/Dockerfile-test-glibc rename to images/instrumentation/preload/docker/Dockerfile-test-glibc diff --git a/images/dash0-instrumentation/preload/docker/Dockerfile-test-musl b/images/instrumentation/preload/docker/Dockerfile-test-musl similarity index 100% rename from images/dash0-instrumentation/preload/docker/Dockerfile-test-musl rename to images/instrumentation/preload/docker/Dockerfile-test-musl diff --git a/images/dash0-instrumentation/preload/lib/.gitignore b/images/instrumentation/preload/lib/.gitignore similarity index 100% rename from images/dash0-instrumentation/preload/lib/.gitignore rename to images/instrumentation/preload/lib/.gitignore diff --git a/images/dash0-instrumentation/preload/obj/.gitignore b/images/instrumentation/preload/obj/.gitignore similarity index 100% rename from images/dash0-instrumentation/preload/obj/.gitignore rename to images/instrumentation/preload/obj/.gitignore diff --git a/images/dash0-instrumentation/preload/scripts/build-in-container.sh b/images/instrumentation/preload/scripts/build-in-container.sh similarity index 100% rename from images/dash0-instrumentation/preload/scripts/build-in-container.sh rename to images/instrumentation/preload/scripts/build-in-container.sh diff --git a/images/dash0-instrumentation/preload/scripts/make-clean.sh b/images/instrumentation/preload/scripts/make-clean.sh similarity index 100% rename from images/dash0-instrumentation/preload/scripts/make-clean.sh rename to images/instrumentation/preload/scripts/make-clean.sh diff --git a/images/dash0-instrumentation/preload/scripts/run-tests-in-container.sh b/images/instrumentation/preload/scripts/run-tests-in-container.sh similarity index 100% rename from images/dash0-instrumentation/preload/scripts/run-tests-in-container.sh rename to images/instrumentation/preload/scripts/run-tests-in-container.sh diff --git a/images/dash0-instrumentation/preload/scripts/test-all.sh b/images/instrumentation/preload/scripts/test-all.sh similarity index 100% rename from images/dash0-instrumentation/preload/scripts/test-all.sh rename to images/instrumentation/preload/scripts/test-all.sh diff --git a/images/dash0-instrumentation/preload/src/libdash0envhook.c b/images/instrumentation/preload/src/libdash0envhook.c similarity index 100% rename from images/dash0-instrumentation/preload/src/libdash0envhook.c rename to images/instrumentation/preload/src/libdash0envhook.c diff --git a/images/dash0-instrumentation/preload/src/map.c b/images/instrumentation/preload/src/map.c similarity index 100% rename from images/dash0-instrumentation/preload/src/map.c rename to images/instrumentation/preload/src/map.c diff --git a/images/dash0-instrumentation/preload/src/map.h b/images/instrumentation/preload/src/map.h similarity index 100% rename from images/dash0-instrumentation/preload/src/map.h rename to images/instrumentation/preload/src/map.h diff --git a/images/dash0-instrumentation/preload/test/appundertest.c b/images/instrumentation/preload/test/appundertest.c similarity index 100% rename from images/dash0-instrumentation/preload/test/appundertest.c rename to images/instrumentation/preload/test/appundertest.c diff --git a/images/dash0-instrumentation/preload/test/run-tests.sh b/images/instrumentation/preload/test/run-tests.sh similarity index 100% rename from images/dash0-instrumentation/preload/test/run-tests.sh rename to images/instrumentation/preload/test/run-tests.sh diff --git a/images/dash0-instrumentation/preload/testbin/aarch64/.gitignore b/images/instrumentation/preload/testbin/aarch64/.gitignore similarity index 100% rename from images/dash0-instrumentation/preload/testbin/aarch64/.gitignore rename to images/instrumentation/preload/testbin/aarch64/.gitignore diff --git a/images/dash0-instrumentation/preload/testbin/x86_64/.gitignore b/images/instrumentation/preload/testbin/x86_64/.gitignore similarity index 100% rename from images/dash0-instrumentation/preload/testbin/x86_64/.gitignore rename to images/instrumentation/preload/testbin/x86_64/.gitignore