diff --git a/.clang-tidy b/.clang-tidy
index 0acc102f238dee75730258350c99633da2fb83e4..9b6114abc990f6b86399c0bb7bffb2657a33dcb1 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -11,6 +11,7 @@ Checks: >
   -clang-diagnostic-empty-translation-unit,
   -readability-braces-around-statements,
   -readability-function-cognitive-complexity,
+  -readability-identifier-length,
   -readability-implicit-bool-conversion,
   -readability-isolate-declaration,
   -readability-magic-numbers,
diff --git a/.gitignore b/.gitignore
index be4498e268312665d6b94b5a0a53ce8097d69c85..e564f15fcfea1156e510b4f4774828b5093aee36 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,8 @@
 /.cache/
 /.clangd/
 
+/clang-tidy-report
+
 tools/gdb/__pycache__/
 
 subprojects/packagecache/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e0ce0b7ba6865ff2a52c6421059dd0293b5faf9a..c2a3741518ade374f462df1f11612fb699b38fbe 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: "flowdalic/debian-testing-dev:1.20"
+image: "flowdalic/debian-testing-dev:1.23"
 
 before_script:
   - ulimit -a
diff --git a/apps/EchoServer.cpp b/apps/EchoServer.cpp
index 70a1cf5133930cf5918fb115455eeada3691d5e4..8ef7b9b31876fd953bc11358235d06639a5ff443 100644
--- a/apps/EchoServer.cpp
+++ b/apps/EchoServer.cpp
@@ -35,6 +35,7 @@ static float max_computations_probability = -1;
 
 static std::atomic<bool> quit = false;
 
+// NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp)
 static thread_local std::mt19937 randGenerator;
 static auto getComputation() -> unsigned {
 	// fixed computation is computations_us
diff --git a/apps/fsearch/fsearch.cpp b/apps/fsearch/fsearch.cpp
index 58d7183c501f50c7a0090e060ac2b22cdb1bdac2..c8f3581be22c182345c1f35e9ee135920c312ea1 100644
--- a/apps/fsearch/fsearch.cpp
+++ b/apps/fsearch/fsearch.cpp
@@ -50,7 +50,7 @@ void search(const std::string& path) {
 
 	ssize_t bytes_read = emper::io::readFileAndWait(fd, buf.data(), buf.size(), 0);
 	while (bytes_read > 0) {
-		if (memmem(&buf[0], bytes_read, needle, needle_len)) {
+		if (memmem(buf.data(), bytes_read, needle, needle_len)) {
 			outBuf->getStream() << path << std::endl;
 			goto out;
 		}
diff --git a/apps/fsearch/fsearch_callback.cpp b/apps/fsearch/fsearch_callback.cpp
index b5961cf5e7b39ba952b555ab65bbe4ecab909d40..d75103d36ab9fe941421c24b30c5a0360196f421 100644
--- a/apps/fsearch/fsearch_callback.cpp
+++ b/apps/fsearch/fsearch_callback.cpp
@@ -76,7 +76,7 @@ class FileSearcher {
 			DIE_MSG_ERRNO("read failed");
 		}
 
-		if (memmem(&buf[0], bytes_read, needle, needle_len)) {
+		if (memmem(buf.data(), bytes_read, needle, needle_len)) {
 			printf("%s\n", path.c_str());
 			delete this;
 			return;
diff --git a/emper/Runtime.cpp b/emper/Runtime.cpp
index 8625d70416aa14661efd927d8bb224e1b1167b0a..5e72917f339f044f4e33a3ab2f7896340ca69fdb 100644
--- a/emper/Runtime.cpp
+++ b/emper/Runtime.cpp
@@ -4,16 +4,16 @@
 
 #include <numa.h>
 #include <pthread.h>	// for pthread_t, pthread_attr_init
-
-#include <cerrno>	 // for errno
 // Non portable.
 #include <sched.h>				// for cpu_set_t, CPU_SET, CPU_ZERO
 #include <sys/sysinfo.h>	// for get_nprocs
 #include <unistd.h>
 
 #include <array>
+#include <cerrno>
 #include <cstdlib>	// for rand, srand, abort
 #include <cstring>
+#include <exception>
 #include <fstream>	// IWYU pragma: keep
 #include <iostream>
 #include <memory>	 // for __shared_ptr_access, shared_ptr
@@ -40,6 +40,7 @@
 #include "lib/ShardedFileBuffer.hpp"
 #include "lib/env.hpp"
 #include "log/LogBuffer.hpp"
+#include "sleep_strategy/SemaphoreWorkerSleepStrategy.hpp"
 #include "stats/FromAnywhere.hpp"
 #include "stats/Worker.hpp"
 
@@ -57,6 +58,7 @@
 
 #ifndef EMPER_HAS_GETTID
 #include <syscall.h>
+
 #define gettid() (syscall(SYS_gettid))
 #endif
 
@@ -265,22 +267,26 @@ Runtime::Runtime(workerid_t workerCount, const std::vector<NewWorkerHook>& newWo
 
 Runtime::~Runtime() {
 	DBG("Runtime " << this << " is terminating");
-	if (threadsRunning) {
-		initiateAndWaitUntilTermination();
-	}
+	try {
+		if (threadsRunning) {
+			initiateAndWaitUntilTermination();
+		}
 
-	emper::lib::ShardedFileBuffer::unregisterRuntime(*this);
+		emper::lib::ShardedFileBuffer::unregisterRuntime(*this);
 
-	// Print the stats after all worker threads have terminated and before we delete
-	// objects bound to the runtime's lifetime
-	if constexpr (emper::STATS) {
-		const char* envValueStatsFile = std::getenv("EMPER_STATS_FILE");
-		if (envValueStatsFile) {
-			std::ofstream out(envValueStatsFile, std::ofstream::out);
-			printStats(out);
-		} else {
-			printStats(std::cout);
+		// Print the stats after all worker threads have terminated and before we delete
+		// objects bound to the runtime's lifetime
+		if constexpr (emper::STATS) {
+			const char* envValueStatsFile = std::getenv("EMPER_STATS_FILE");
+			if (envValueStatsFile) {
+				std::ofstream out(envValueStatsFile, std::ofstream::out);
+				printStats(out);
+			} else {
+				printStats(std::cout);
+			}
 		}
+	} catch (const std::exception& e) {
+		LOGW("exception: " << e.what());
 	}
 
 	for (unsigned int i = 0; i < workerCount; ++i) {
diff --git a/emper/io/GlobalIoContext.cpp b/emper/io/GlobalIoContext.cpp
index 73452ac0911cc653b9422b5d0e6e8302eb081684..91e9c60ba511d1ecb522313ba5a50ee2878bc91f 100644
--- a/emper/io/GlobalIoContext.cpp
+++ b/emper/io/GlobalIoContext.cpp
@@ -25,7 +25,7 @@
 #include "io/SubmitActor.hpp"
 #include "lib/TaggedPtr.hpp"
 #include "lib/sync/Semaphore.hpp"
-#include "sleep_strategy/WorkerSleepStrategy.hpp"
+#include "sleep_strategy/SemaphoreWorkerSleepStrategy.hpp"
 
 using emper::lib::TaggedPtr;
 
diff --git a/emper/io/io.cpp b/emper/io/io.cpp
index bbbf10c252cc67a98626df68cb9c389bde2b0696..86cec9351b4bc0680bd393ce52dcd31fc7d21c73 100644
--- a/emper/io/io.cpp
+++ b/emper/io/io.cpp
@@ -84,6 +84,7 @@ emper_fibril void fibril_recursive_directory_walk(
 	for (const auto& pathRef : fs::directory_iterator(dirpath)) {
 		// We must copy 'pathRef', to not hold a reference, which may
 		// become invalid at the end of the iteration.
+		// NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
 		const fs::directory_entry path = pathRef;
 		if (filter(path)) {
 			fibril.spawn(fn, path);
diff --git a/emper/lib/DebugUtil.cpp b/emper/lib/DebugUtil.cpp
index 85bd93a354f16a9b28ce976df7e474a1e2f1e5e2..c23d513ed32f7d8d66301d982abae66f96b401fb 100644
--- a/emper/lib/DebugUtil.cpp
+++ b/emper/lib/DebugUtil.cpp
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: LGPL-3.0-or-later
-// Copyright © 2020-2021 Florian Schmaus
+// Copyright © 2020-2022 Florian Schmaus
 #include "DebugUtil.hpp"
 
 #include <execinfo.h>
@@ -9,6 +9,8 @@
 #include <cstdio>
 #include <cstdlib>
 
+#include "Common.hpp"
+
 static void handler(int sig) {
 	const int backtrace_size = 20;
 	// NOLINTNEXTLINE(modernize-avoid-c-arrays)
@@ -16,12 +18,17 @@ static void handler(int sig) {
 
 	int size = backtrace(array, backtrace_size);
 
-	fprintf(stderr, "Error: signal %d:\n", sig);
+	int res = fprintf(stderr, "Error: signal %d:\n", sig);
+	if (res) DIE_MSG_ERRNO("fprintf() failed");
+
 	backtrace_symbols_fd(array, size, STDERR_FILENO);
 	exit(EXIT_FAILURE);
 }
 
 void enableStacktraceOnAborts() {
-	signal(SIGSEGV, handler);
-	signal(SIGABRT, handler);
+	sighandler_t prev_handler;
+	prev_handler = signal(SIGSEGV, handler);
+	if (prev_handler == SIG_ERR) DIE_MSG_ERRNO("signal() failed");
+	prev_handler = signal(SIGABRT, handler);
+	if (prev_handler == SIG_ERR) DIE_MSG_ERRNO("signal() failed");
 }
diff --git a/emper/lib/sync/SpuriousFutex2Semaphore.cpp b/emper/lib/sync/SpuriousFutex2Semaphore.cpp
index 72ffa3901253a873465e376c551ca3a02076698d..349e0a0bffd1b3503e4ec69d86d05ba287fb4ad6 100644
--- a/emper/lib/sync/SpuriousFutex2Semaphore.cpp
+++ b/emper/lib/sync/SpuriousFutex2Semaphore.cpp
@@ -10,7 +10,6 @@
 #include <cerrno>
 
 #include "Worker.hpp"
-#include "emper-config.h"
 
 #ifndef SYS_futex_waitv
 #define SYS_futex_waitv 449
diff --git a/iwyu-mappings.imp b/iwyu-mappings.imp
index bbe2308cdadfde1136ebfff1d41c3c8c6dd07c53..a3161cf2e57eaf67e3555bc74870b5fff0d1dbba 100644
--- a/iwyu-mappings.imp
+++ b/iwyu-mappings.imp
@@ -23,5 +23,6 @@
 	{ include: ["<boost/cstdint.hpp>", "private", "<cstdint>", "public"], },
 
 	{ symbol: ["__kernel_timespec", "private", "<liburing.h>", "public" ] },
+	{ symbol: ["__s32", "private", "<liburing.h>", "public" ] },
 	{ symbol: ["std::filesystem", "private", "<filesystem>", "public" ] },
 ]
diff --git a/tests/ContextOutOfBoundsTest.cpp b/tests/ContextOutOfBoundsTest.cpp
index 0cfcb8196b2207b7544b4b3ac67bd2e68f91e8b2..6d5020fa89979cce23676951769789121e50f98c 100644
--- a/tests/ContextOutOfBoundsTest.cpp
+++ b/tests/ContextOutOfBoundsTest.cpp
@@ -13,7 +13,7 @@ static auto recur(uintptr_t depth, int val) -> int {
 	constexpr size_t stack_user_size = 1024;
 	std::array<int, stack_user_size> stack_user;
 	for (int& i : stack_user) {
-		// NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp)
+		// NOLINTNEXTLINE(cert-msc30-c,cert-msc50-cpp)
 		i = rand();
 	}
 
diff --git a/tests/io/SimpleDiskAndNetworkTest.cpp b/tests/io/SimpleDiskAndNetworkTest.cpp
index 72438d5d0c958589dc2ae859385f2958a7feefde..69c7e549b63026ab36a1f8ede26bff5cc7cf3552 100644
--- a/tests/io/SimpleDiskAndNetworkTest.cpp
+++ b/tests/io/SimpleDiskAndNetworkTest.cpp
@@ -125,7 +125,7 @@ static void testIov() {
 
 	DBG("Prepared iov");
 
-	auto writevFuture = emper::io::writev(file_fd, &iov[0], iovcnt);
+	auto writevFuture = emper::io::writev(file_fd, iov.data(), iovcnt);
 	ssize_t written = writevFuture->waitAndSetErrno();
 	if (written < 0) {
 		DIE_MSG_ERRNO("writev failed");
diff --git a/tools/fix-includes b/tools/fix-includes
index 225adaf8377a788e161a82df9c72f8f74dac561b..a4d2f9970dd39fe72704e668aa4f3cb438ee564e 100755
--- a/tools/fix-includes
+++ b/tools/fix-includes
@@ -56,7 +56,9 @@ trap cleanup exit
 cd "${BUILDDIR}"
 
 echo "Running IWYU"
-ninja iwyu > "${TMPFILE}"
+set +e
+ninja iwyu |tee "${TMPFILE}"
+set -e
 
 echo "Running fix_includes.py"
 fix_includes.py < "${TMPFILE}"
diff --git a/tools/run-clang-tidy b/tools/run-clang-tidy
index ebaeb67379b6fb0b2cb198e365ad227dc084650a..e54b087bb93409a80e3b21ea46d5683c405447fa 100755
--- a/tools/run-clang-tidy
+++ b/tools/run-clang-tidy
@@ -42,4 +42,5 @@ JOBS=$(nproc)
 
 ${RUN_CLANG_TIDY} \
 	-p "${ROOTDIR}/compile_commands_wo_subprojects/" \
-	-j "${JOBS}"
+	-j "${JOBS}" |\
+	tee "${ROOTDIR}/clang-tidy-report"