diff options
-rw-r--r-- | .github/workflows/ci.yml | 2 | ||||
-rw-r--r-- | DEVELOPMENT.md | 14 | ||||
-rw-r--r-- | Makefile | 26 | ||||
-rwxr-xr-x | scripts/flamegraph.sh | 1 | ||||
-rw-r--r-- | src/CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/ci.c | 12 | ||||
-rw-r--r-- | src/ci.h | 4 | ||||
-rw-r--r-- | src/cmd_line.c | 2 | ||||
-rw-r--r-- | src/file.c | 4 | ||||
-rw-r--r-- | src/file.h | 8 | ||||
-rw-r--r-- | src/net.h | 10 | ||||
-rw-r--r-- | src/process.c | 13 | ||||
-rw-r--r-- | src/process.h | 15 | ||||
-rw-r--r-- | src/protocol.c | 10 | ||||
-rw-r--r-- | src/protocol.h | 6 | ||||
-rw-r--r-- | src/server.c | 4 | ||||
-rw-r--r-- | src/storage.c | 4 | ||||
-rw-r--r-- | src/storage.h | 2 | ||||
-rw-r--r-- | src/storage_sqlite.c | 2 | ||||
-rw-r--r-- | src/storage_sqlite.h | 2 | ||||
-rw-r--r-- | src/worker.c | 8 |
21 files changed, 91 insertions, 63 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 205a033..91bdc70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,7 +107,7 @@ jobs: # sudo is used to resolve kernel symbols. Plus, it would be required # if we didn't fix perf_event_paranoid. PATH needs to be preserved # for FlameGraph. - sudo --preserve-env=PATH make debug/flame_graphs + sudo --preserve-env=PATH DEBUGINFOD_URLS='https://debuginfod.ubuntu.com' make debug/flame_graphs - name: Upload flame graphs uses: actions/upload-artifact@v4 with: diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 59c5e9e..df3d0d8 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,3 +1,11 @@ +### Setting up + +Set up git hooks by running + + ./scripts/setup-hooks.sh + +### Building + There's a Makefile with useful shortcuts to build the project in the "build/" directory: @@ -55,6 +63,12 @@ build/coverage/html: The latest code coverage report for the `master` branch can be found at https://egor-tensin.github.io/cimple/coverage/. +### Static analysis + +Build w/ GCC's `-fanalyzer`: + + make analyzer + ### Flame graphs Some performance analysis can be done by looking at flame graphs. @@ -1,16 +1,18 @@ include prelude.mk -COMPILER ?= clang -CONFIGURATION ?= debug -DEFAULT_HOST ?= 127.0.0.1 -DEFAULT_PORT ?= 5556 -COVERAGE ?= +COMPILER ?= clang +CONFIGURATION ?= debug +DEFAULT_HOST ?= 127.0.0.1 +DEFAULT_PORT ?= 5556 +COVERAGE ?= +STATIC_ANALYZER ?= $(eval $(call noexpand,COMPILER)) $(eval $(call noexpand,CONFIGURATION)) $(eval $(call noexpand,DEFAULT_HOST)) $(eval $(call noexpand,DEFAULT_PORT)) $(eval $(call noexpand,COVERAGE)) +$(eval $(call noexpand,STATIC_ANALYZER)) src_dir := $(abspath .) build_dir := $(src_dir)/build @@ -40,6 +42,7 @@ build: -D 'DEFAULT_PORT=$(call escape,$(DEFAULT_PORT))' \ -D 'TEST_REPORT_DIR=$(call escape,$(build_dir)/test_report)' \ -D 'COVERAGE=$(call escape,$(COVERAGE))' \ + -D 'STATIC_ANALYZER=$(call escape,$(STATIC_ANALYZER))' \ -D 'FLAME_GRAPHS_DIR=$(call escape,$(build_dir)/flame_graphs)' \ -S '$(call escape,$(src_dir))' \ -B '$(call escape,$(build_dir)/cmake)' @@ -71,6 +74,16 @@ ifndef CI xdg-open '$(call escape,$(build_dir))/html/index.html' &> /dev/null endif +# This a separate target, because CMake is kinda dumb; if you run `make debug` +# and then `make debug COMPILER=gcc STATIC_ANALYZER=1`, it'll run a rebuild, +# but won't include the -fanalyzer option for some reason. +.PHONY: analyzer +analyzer analyzer/%: CONFIGURATION := debug +analyzer analyzer/%: COMPILER := gcc +analyzer analyzer/%: STATIC_ANALYZER := 1 +analyzer analyzer/%: build_dir := $(build_dir)/analyzer +analyzer: build + %/install: % DO cmake --install '$(call escape,$(build_dir))/cmake' @@ -107,9 +120,6 @@ endif ctest --test-dir '$(call escape,$(build_dir))/cmake' \ --verbose --tests-regex python_tests_valgrind -# When building a Docker image for a different platform, exclude stress tests: -# they simply take way too long. - %/flame_graphs: DO @echo ----------------------------------------------------------------- @echo Generating flame graphs diff --git a/scripts/flamegraph.sh b/scripts/flamegraph.sh index 3a6c9e2..7baf084 100755 --- a/scripts/flamegraph.sh +++ b/scripts/flamegraph.sh @@ -101,6 +101,7 @@ main() { --freq=99 \ --call-graph dwarf,65528 \ --pid="$pids" \ + ${DEBUGINFOD_URLS:+--debuginfod} \ --no-inherit & record_pid="$!" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ca19416..7bd8e78 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,11 @@ if(COVERAGE) add_link_options(--coverage) endif() +option(STATIC_ANALYZER "Enable static analysis") +if(STATIC_ANALYZER) + add_compile_options(-fanalyzer) +endif() + # Valgrind on Ubuntu Focal doesn't like DWARF version 5 debug info generated # by Clang 14. if("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") @@ -31,13 +31,13 @@ static const char *ci_env[] = { }; /* clang-format on */ -static int ci_run_script(const char *script, struct proc_output *result) +static int ci_run_script(const char *script, struct process_output *result) { const char *args[] = {script, NULL}; - return proc_capture(args, ci_env, result); + return process_execute_and_capture(args, ci_env, result); } -int ci_run(struct proc_output *result) +int ci_run(struct process_output *result) { for (const char **script = ci_scripts; *script; ++script) { if (!file_exists(*script)) @@ -76,7 +76,7 @@ cleanup_repo: return ret; } -int ci_run_git_repo(const char *url, const char *rev, struct proc_output *output) +int ci_run_git_repo(const char *url, const char *rev, struct process_output *output) { char *oldpwd = NULL; git_repository *repo = NULL; @@ -86,7 +86,7 @@ int ci_run_git_repo(const char *url, const char *rev, struct proc_output *output if (ret < 0) goto exit; - ret = my_chdir(git_repository_workdir(repo), &oldpwd); + ret = chdir_wrapper(git_repository_workdir(repo), &oldpwd); if (ret < 0) goto free_repo; @@ -95,7 +95,7 @@ int ci_run_git_repo(const char *url, const char *rev, struct proc_output *output goto oldpwd; oldpwd: - my_chdir(oldpwd, NULL); + chdir_wrapper(oldpwd, NULL); free(oldpwd); free_repo: @@ -10,7 +10,7 @@ #include "process.h" -int ci_run(struct proc_output *); +int ci_run(struct process_output *); /* * This is a high-level function. It's basically equivalent to the following @@ -25,6 +25,6 @@ int ci_run(struct proc_output *); * rm -rf "$dir" * */ -int ci_run_git_repo(const char *url, const char *rev, struct proc_output *); +int ci_run_git_repo(const char *url, const char *rev, struct process_output *); #endif diff --git a/src/cmd_line.c b/src/cmd_line.c index 35b27a7..bf3aa24 100644 --- a/src/cmd_line.c +++ b/src/cmd_line.c @@ -16,7 +16,7 @@ static char *get_current_binary_path(void) { - return my_readlink("/proc/self/exe"); + return readlink_wrapper("/proc/self/exe"); } static char *get_current_binary_name(void) @@ -36,7 +36,7 @@ int rm_rf(const char *dir) return nftw(dir, unlink_cb, 64, FTW_DEPTH | FTW_PHYS); } -int my_chdir(const char *dir, char **old) +int chdir_wrapper(const char *dir, char **old) { int ret = 0; @@ -63,7 +63,7 @@ free_old: return ret; } -char *my_readlink(const char *path) +char *readlink_wrapper(const char *path) { size_t current_size = 256; char *buf = NULL; @@ -12,8 +12,12 @@ int rm_rf(const char *dir); -int my_chdir(const char *dir, char **old); -char *my_readlink(const char *path); +/* This chdir(2) wrapper optionally saves the previous working directory in the + * `old` pointer, allowing the user to switch back to it if necessary. */ +int chdir_wrapper(const char *dir, char **old); + +/* This readlink(2) wrapper allocates enough memory dynamically. */ +char *readlink_wrapper(const char *path); int file_dup(int fd); void file_close(int fd); @@ -11,7 +11,6 @@ #include "buf.h" #include <stddef.h> -#include <stdint.h> int net_bind(const char *port); int net_accept(int fd); @@ -21,15 +20,6 @@ void net_close(int fd); int net_send(int fd, const void *, size_t); int net_recv(int fd, void *, size_t); -struct buf; - -int buf_create(struct buf **, const void *, uint32_t); -int buf_create_from_string(struct buf **, const char *); -void buf_destroy(struct buf *); - -uint32_t buf_get_size(const struct buf *); -const void *buf_get_data(const struct buf *); - int net_send_buf(int fd, const struct buf *); int net_recv_buf(int fd, struct buf **); diff --git a/src/process.c b/src/process.c index 580daaf..1c452ac 100644 --- a/src/process.c +++ b/src/process.c @@ -58,7 +58,7 @@ static int wait_for_child(pid_t pid, int *ec) return 0; } -int proc_spawn(const char *args[], const char *envp[], int *ec) +int process_execute(const char *args[], const char *envp[], int *ec) { pid_t child_pid = fork(); if (child_pid < 0) { @@ -93,7 +93,8 @@ static int redirect_and_exec_child(int pipe_fds[2], const char *args[], const ch return exec_child(args, envp); } -int proc_capture(const char *args[], const char *envp[], struct proc_output *result) +int process_execute_and_capture(const char *args[], const char *envp[], + struct process_output *result) { static const int flags = O_CLOEXEC; int pipe_fds[2]; @@ -137,9 +138,9 @@ close_pipe: return ret; } -int proc_output_create(struct proc_output **_output) +int process_output_create(struct process_output **_output) { - struct proc_output *output = calloc(1, sizeof(struct proc_output)); + struct process_output *output = calloc(1, sizeof(struct process_output)); if (!output) { log_errno("calloc"); return -1; @@ -153,13 +154,13 @@ int proc_output_create(struct proc_output **_output) return 0; } -void proc_output_destroy(struct proc_output *output) +void process_output_destroy(struct process_output *output) { free(output->data); free(output); } -void proc_output_dump(const struct proc_output *output) +void process_output_dump(const struct process_output *output) { log("Process exit code: %d\n", output->ec); log("Process output: %zu bytes\n", output->data_size); diff --git a/src/process.h b/src/process.h index 810010d..965cb5e 100644 --- a/src/process.h +++ b/src/process.h @@ -10,24 +10,25 @@ #include <stddef.h> -struct proc_output { +struct process_output { int ec; unsigned char *data; size_t data_size; }; /* The exit code is only valid if the functions returns a non-negative number. */ -int proc_spawn(const char *args[], const char *envp[], int *ec); +int process_execute(const char *args[], const char *envp[], int *ec); -/* Similarly, the contents of the proc_output structure is only valid if the function returns a +/* Similarly, the contents of the process_output structure is only valid if the function returns a * non-negative number. * * In that case, you'll need to free the output. */ -int proc_capture(const char *args[], const char *envp[], struct proc_output *result); +int process_execute_and_capture(const char *args[], const char *envp[], + struct process_output *result); -int proc_output_create(struct proc_output **); -void proc_output_destroy(struct proc_output *); +int process_output_create(struct process_output **); +void process_output_destroy(struct process_output *); -void proc_output_dump(const struct proc_output *); +void process_output_dump(const struct process_output *); #endif diff --git a/src/protocol.c b/src/protocol.c index d950004..0fa06f1 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -120,7 +120,7 @@ static const char *const finished_key_ec = "exit_code"; static const char *const finished_key_data = "output"; int request_create_finished_run(struct jsonrpc_request **request, int run_id, - const struct proc_output *output) + const struct process_output *output) { int ret = 0; @@ -153,12 +153,12 @@ free_request: } int request_parse_finished_run(const struct jsonrpc_request *request, int *_run_id, - struct proc_output **_output) + struct process_output **_output) { int ret = 0; - struct proc_output *output = NULL; - ret = proc_output_create(&output); + struct process_output *output = NULL; + ret = process_output_create(&output); if (ret < 0) return ret; @@ -187,7 +187,7 @@ int request_parse_finished_run(const struct jsonrpc_request *request, int *_run_ return ret; free_output: - proc_output_destroy(output); + process_output_destroy(output); return ret; } diff --git a/src/protocol.h b/src/protocol.h index 2277998..c496449 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -21,8 +21,10 @@ int request_parse_new_worker(const struct jsonrpc_request *); int request_create_start_run(struct jsonrpc_request **, const struct run *); int request_parse_start_run(const struct jsonrpc_request *, struct run **); -int request_create_finished_run(struct jsonrpc_request **, int run_id, const struct proc_output *); -int request_parse_finished_run(const struct jsonrpc_request *, int *run_id, struct proc_output **); +int request_create_finished_run(struct jsonrpc_request **, int run_id, + const struct process_output *); +int request_parse_finished_run(const struct jsonrpc_request *, int *run_id, + struct process_output **); int request_create_get_runs(struct jsonrpc_request **); int request_parse_get_runs(const struct jsonrpc_request *); diff --git a/src/server.c b/src/server.c index 89ffcb1..b917f7c 100644 --- a/src/server.c +++ b/src/server.c @@ -297,7 +297,7 @@ static int server_handle_cmd_finished_run(const struct jsonrpc_request *request, int ret = 0; int run_id = 0; - struct proc_output *output; + struct process_output *output; ret = request_parse_finished_run(request, &run_id, &output); if (ret < 0) @@ -312,7 +312,7 @@ static int server_handle_cmd_finished_run(const struct jsonrpc_request *request, log("Marked run %d as finished\n", run_id); free_output: - proc_output_destroy(output); + process_output_destroy(output); return ret; } diff --git a/src/storage.c b/src/storage.c index 43e871f..54a62c2 100644 --- a/src/storage.c +++ b/src/storage.c @@ -18,7 +18,7 @@ typedef int (*storage_create_t)(struct storage *, const struct storage_settings typedef void (*storage_destroy_t)(struct storage *); typedef int (*storage_run_create_t)(struct storage *, const char *repo_url, const char *rev); -typedef int (*storage_run_finished_t)(struct storage *, int repo_id, const struct proc_output *); +typedef int (*storage_run_finished_t)(struct storage *, int repo_id, const struct process_output *); typedef int (*storage_get_runs_t)(struct storage *, struct run_queue *); typedef storage_get_runs_t storage_get_run_queue_t; @@ -105,7 +105,7 @@ int storage_run_create(struct storage *storage, const char *repo_url, const char return api->run_create(storage, repo_url, rev); } -int storage_run_finished(struct storage *storage, int run_id, const struct proc_output *output) +int storage_run_finished(struct storage *storage, int run_id, const struct process_output *output) { const struct storage_api *api = get_api(storage->type); if (!api) diff --git a/src/storage.h b/src/storage.h index c50444d..01ec25a 100644 --- a/src/storage.h +++ b/src/storage.h @@ -36,7 +36,7 @@ int storage_create(struct storage *, const struct storage_settings *); void storage_destroy(struct storage *); int storage_run_create(struct storage *, const char *repo_url, const char *rev); -int storage_run_finished(struct storage *, int run_id, const struct proc_output *); +int storage_run_finished(struct storage *, int run_id, const struct process_output *); int storage_get_runs(struct storage *, struct run_queue *); int storage_get_run_queue(struct storage *, struct run_queue *); diff --git a/src/storage_sqlite.c b/src/storage_sqlite.c index 6c8a26f..efefaf0 100644 --- a/src/storage_sqlite.c +++ b/src/storage_sqlite.c @@ -410,7 +410,7 @@ int storage_sqlite_run_create(struct storage *storage, const char *repo_url, con } int storage_sqlite_run_finished(struct storage *storage, int run_id, - const struct proc_output *output) + const struct process_output *output) { struct prepared_stmt *stmt = &storage->sqlite->stmt_run_finished; int ret = 0; diff --git a/src/storage_sqlite.h b/src/storage_sqlite.h index 2d82496..2a4df2b 100644 --- a/src/storage_sqlite.h +++ b/src/storage_sqlite.h @@ -24,7 +24,7 @@ int storage_sqlite_create(struct storage *, const struct storage_settings *); void storage_sqlite_destroy(struct storage *); int storage_sqlite_run_create(struct storage *, const char *repo_url, const char *rev); -int storage_sqlite_run_finished(struct storage *, int id, const struct proc_output *); +int storage_sqlite_run_finished(struct storage *, int id, const struct process_output *); int storage_sqlite_get_runs(struct storage *, struct run_queue *runs); int storage_sqlite_get_run_queue(struct storage *, struct run_queue *runs); diff --git a/src/worker.c b/src/worker.c index 5c0ab96..8ee0703 100644 --- a/src/worker.c +++ b/src/worker.c @@ -179,8 +179,8 @@ static int worker_do_run(struct worker *worker) { int ret = 0; - struct proc_output *result = NULL; - ret = proc_output_create(&result); + struct process_output *result = NULL; + ret = process_output_create(&result); if (ret < 0) return ret; @@ -190,7 +190,7 @@ static int worker_do_run(struct worker *worker) goto free_output; } - proc_output_dump(result); + process_output_dump(result); struct jsonrpc_request *finished_request = NULL; @@ -214,7 +214,7 @@ free_request: jsonrpc_request_destroy(finished_request); free_output: - proc_output_destroy(result); + process_output_destroy(result); run_destroy(worker->run); |