diff --git a/MODULE.bazel b/MODULE.bazel index a9ccd471622..b019d99d643 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -40,6 +40,7 @@ include("//bazel_common:score_modules_target_sw.MODULE.bazel") # Score test images include("//bazel_common:score_images.MODULE.bazel") +bazel_dep(name = "google_benchmark", version = "1.9.4") bazel_dep(name = "rules_boost", repo_name = "com_github_nelhage_rules_boost") archive_override( module_name = "rules_boost", diff --git a/benchmark/BUILD b/benchmark/BUILD new file mode 100644 index 00000000000..1a55461b332 --- /dev/null +++ b/benchmark/BUILD @@ -0,0 +1,34 @@ +load(":opt_transition.bzl", "opt_binary") + +cc_binary( + name = "scr_bench_log", + srcs = [ + "scr_bench_log.cpp" + ], + data = ["etc/logging.json"], + visibility = ["//visibility:public"], + deps = [ + "@google_benchmark//:benchmark", + "@google_benchmark//:benchmark_main", + "@score_logging//score/mw/log", + ], +) + +# Forces the benchmark to be built with -c opt even when the rest is debug +opt_binary( + name = "scr_bench_log_opt", + binary = ":scr_bench_log", + data = ["etc/logging.json"], + visibility = ["//visibility:public"], +) + +exports_files([ + "etc/logging.json", +]) + +filegroup( + name = "etc_configs", + srcs = ["etc/logging.json"], + visibility = ["//visibility:public"], +) + diff --git a/benchmark/etc/logging.json b/benchmark/etc/logging.json new file mode 100644 index 00000000000..0009eccd164 --- /dev/null +++ b/benchmark/etc/logging.json @@ -0,0 +1,17 @@ +{ + "appId": "SCBL", + "appDesc": "score_bench_log", + "logLevel": "kInfo", + "logLevelThresholdConsole": "kInfo", + "logMode": "kRemote", + "contextConfigs":[ + { + "name": "ESBL", + "logLevel": "kInfo" + }, + { + "name": "DSBL" + } + ], + "logFilePath": "/tmp_ram" +} diff --git a/benchmark/opt_transition.bzl b/benchmark/opt_transition.bzl new file mode 100644 index 00000000000..8b164a56a61 --- /dev/null +++ b/benchmark/opt_transition.bzl @@ -0,0 +1,46 @@ +# Transition rule that forces a target to be built with -c opt + +def _opt_transition_impl(settings, attr): + return {"//command_line_option:compilation_mode": "opt"} + +_opt_transition = transition( + implementation = _opt_transition_impl, + inputs = [], + outputs = ["//command_line_option:compilation_mode"], +) + +def _opt_binary_impl(ctx): + # Note: ctx.attr.binary is a list when using transitions + binary_target = ctx.attr.binary[0] + src_executable = ctx.executable.binary + + # Create a symlink to the actual executable (executable must be created by this rule) + out = ctx.actions.declare_file(ctx.label.name) + ctx.actions.symlink(output = out, target_file = src_executable, is_executable = True) + + runfiles = ctx.runfiles(files = ctx.files.data) + runfiles = runfiles.merge(binary_target[DefaultInfo].default_runfiles) + + return [DefaultInfo( + executable = out, + files = depset([out]), + runfiles = runfiles, + )] + +opt_binary = rule( + implementation = _opt_binary_impl, + attrs = { + "binary": attr.label( + cfg = _opt_transition, + executable = True, + mandatory = True, + ), + "data": attr.label_list( + allow_files = True, + ), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + }, + executable = True, +) diff --git a/benchmark/scr_bench_log.cpp b/benchmark/scr_bench_log.cpp new file mode 100644 index 00000000000..158f9e614e4 --- /dev/null +++ b/benchmark/scr_bench_log.cpp @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "score/mw/log/logger.h" + +std::atomic g_side_effects{0}; +std::string get_heavy_metadata() { + g_side_effects.fetch_add(1, std::memory_order_relaxed); + + std::stringstream ss; + + // Simulate formatting 64 bytes of data + for(int i = 0; i < 64; ++i) { + ss << std::hex << std::setw(2) << std::setfill('0') << (i % 255); + } + + return ss.str(); +} + +// --- THE FIXTURE --- +class LoggerFixture : public benchmark::Fixture { +public: + // 1. One-time Initialization per benchmark run + void SetUp(const ::benchmark::State& state) override { + //msg = "Score Logging framework benchmark test with metrics"; + msg = get_heavy_metadata(); + g_side_effects = 0; // reset counter + elogger = std::make_unique("ESBL"); + dlogger = std::make_unique("DSBL"); + } + + // 2. Cleanup after the benchmark finishes + void TearDown(const ::benchmark::State& state) override { + // framework_shutdown(); + } + + std::unique_ptr elogger; + std::unique_ptr dlogger; + std::string msg; +}; + +// --- THE BENCHMARK (Using the Fixture) --- +BENCHMARK_F(LoggerFixture, BM_ScoreSteadyStateLogging)(benchmark::State& state) { + size_t total_bytes = 0; +// int simulated_allocs = 0; + size_t alloc_counter = 0; + + for (auto _ : state) { + // 3. THE HOT PATH (What we are actually measuring) + // framework_log(msg); + // msg = get_heavy_metadata(); + alloc_counter++; + elogger->LogInfo() << msg; + benchmark::DoNotOptimize(msg.data()); + + // Tracking data for metrics + total_bytes += msg.size(); + // if (state.iterations() % 500 == 0) simulated_allocs++; + } + + // --- ADDITIONAL METRICS --- + + // Throughput: Bytes per second (will show as MB/s) + state.SetBytesProcessed(int64_t(total_bytes)); + + // Throughput: Logs per second + state.SetItemsProcessed(int64_t(state.iterations())); + + // Custom Counter: Allocations (Zero-Copy Check) + // kIsRate shows average allocs per second + //state.counters["AllocRate"] = benchmark::Counter( + // simulated_allocs, benchmark::Counter::kIsRate); + + state.counters["AllocPerLog"] = benchmark::Counter( + static_cast(alloc_counter) / state.iterations(), + benchmark::Counter::kAvgThreads + ); + + // kAvgThreads shows average allocs per log call + //state.counters["AllocPerLog"] = benchmark::Counter( + // simulated_allocs, benchmark::Counter::kAvgThreads); + + state.counters["SideEffects"] = benchmark::Counter(g_side_effects.load()); +} + +// --- TEST SCORE (DEBUG log while level is FATAL) --- +BENCHMARK_F(LoggerFixture, BM_Score_DisabledLog)(benchmark::State& state) { + for (auto _ : state) { + dlogger->LogDebug() << msg; + benchmark::DoNotOptimize(msg.data()); + } + state.counters["SideEffects"] = benchmark::Counter(g_side_effects.load()); +} + +// Register and run +BENCHMARK_MAIN(); diff --git a/images/qnx_x86_64/build/BUILD b/images/qnx_x86_64/build/BUILD index a9aabe11678..9eb2a5a4fb3 100644 --- a/images/qnx_x86_64/build/BUILD +++ b/images/qnx_x86_64/build/BUILD @@ -49,6 +49,9 @@ qnx_ifs( ":scripts", ":system.build", ":system_dir", + "//benchmark:scr_bench_log", + "//benchmark:scr_bench_log_opt", + "//benchmark:etc_configs", "//feature_integration_tests/configs:etc_configs", "//feature_integration_tests/configs/datarouter:etc_configs", "//showcases", @@ -57,6 +60,8 @@ qnx_ifs( ], build_file = "init.build", ext_repo_maping = { + "BENCHMARK_PATH": "$(location //benchmark:scr_bench_log)", + "BENCHMARK_OPT_PATH": "$(location //benchmark:scr_bench_log_opt)", "BUNDLE_PATH": "$(location //showcases:showcases)", "DATAROUTER_PATH": "$(location @score_logging//score/datarouter:datarouter)", }, diff --git a/images/qnx_x86_64/build/init.build b/images/qnx_x86_64/build/init.build index fdc1d273390..1444511104f 100644 --- a/images/qnx_x86_64/build/init.build +++ b/images/qnx_x86_64/build/init.build @@ -14,7 +14,7 @@ ############################################################################### # -# Example image built based on minimal configuration from QNX +# Example image built based on minimal configuration from QNX # ############################################################################### @@ -25,7 +25,7 @@ # Use startup-x86 by default startup-x86 -v -D8250..115200 -zz # Start x86 kernel with verbose output, serial console at 115200 baud PATH=/proc/boot # Set executable search path to boot directory - LD_LIBRARY_PATH=/proc/boot # Set library search path to boot directory + LD_LIBRARY_PATH=/proc/boot # Set library search path to boot directory [+keeplinked] procnto-smp-instr # Keep process manager linked and instrumented for SMP } @@ -39,8 +39,8 @@ SYSNAME=nto # Set system name to "nto" (Neutrino) TERM=qansi # Set terminal type to QNX ANSI - devc-ser8250 & # Start serial driver in background - waitfor /dev/ser1 # Wait for serial device to be available + devc-ser8250 -e -b115200 0x3f8,4 & # Start serial driver on COM1 (I/O 0x3f8, IRQ 4) + waitfor /dev/ser1 5 # Wait for serial device with 5 second timeout reopen /dev/ser1 # Reopen serial device for console I/O display_msg Placeholder for startup script # Display startup message @@ -96,6 +96,6 @@ devb-eide # Block device driver for IDE/SATA hard drive # Required for mounting QNX6 file systems from disk partitions # Orchestrator example needed -[type=link] /data=/tmp_ram +[type=link] /data=/tmp_ram [+include] ${MAIN_BUILD_FILE_DIR}/system.build # Include additional system build configurations diff --git a/images/qnx_x86_64/build/system.build b/images/qnx_x86_64/build/system.build index 16f55a4607f..3badb68a5f4 100644 --- a/images/qnx_x86_64/build/system.build +++ b/images/qnx_x86_64/build/system.build @@ -63,6 +63,18 @@ libpam.so.2 # Pluggable Authentication Modules libr # Core file system utilities - most are provided by toybox (minimal Unix utilities) [type=link] /bin/ls=/proc/boot/ls # Link ls to IFS version for compatibility +[type=link] /bin/mkdir=/proc/boot/mkdir # Link mkdir to /bin for PATH access +[type=link] /bin/cat=/proc/boot/cat # Link cat to /bin for PATH access +[type=link] /bin/rm=/proc/boot/rm # Link rm to /bin for PATH access +[type=link] /bin/grep=/proc/boot/grep # Link grep to /bin for PATH access +[type=link] /bin/date=/proc/boot/date # Link date to /bin for PATH access +[type=link] /bin/kill=/proc/boot/slay # Link kill to slay for PATH access +[type=link] /bin/nc=/proc/boot/nc # Link nc to /bin for PATH access +[type=link] /bin/tcpdump=/proc/boot/tcpdump # Link tcpdump to /bin for PATH access +[type=link] /bin/pidin=/proc/boot/pidin # Link pidin to /bin for PATH access +[type=link] /bin/awk=/proc/boot/awk # Link awk to /bin for PATH access +[type=link] /bin/ifconfig=/proc/boot/ifconfig # Link ifconfig to /bin for PATH access +[type=link] /bin/mkfifo=/proc/boot/mkfifo # Link mkfifo to /bin for PATH access # Note: /bin/sh symlink already defined in init.build as /proc/boot/ksh toybox # Minimal Unix utilities collection (replaces many GNU tools) [type=link] cp=toybox # Copy files and directories @@ -288,6 +300,10 @@ pci/pci_debug2.so # Enhanced PCI debugging support [perms=644] /usr/bin/datarouter/etc/logging.json = ${MAIN_BUILD_FILE_DIR}/../../../feature_integration_tests/configs/datarouter/etc/logging.json [perms=644] /usr/bin/datarouter/etc/log-channels.json = ${MAIN_BUILD_FILE_DIR}/../../../feature_integration_tests/configs/datarouter/etc/log-channels.json +[perms=777] /usr/bin/benchmark/scr_bench_log = ${BENCHMARK_PATH} +[perms=777] /usr/bin/benchmark/scr_bench_log_opt = ${BENCHMARK_OPT_PATH} +[perms=644] /usr/bin/benchmark/etc/logging.json = ${MAIN_BUILD_FILE_DIR}/../../../benchmark/etc/logging.json + # Common showcases bundle [perms=777] / = ${BUNDLE_PATH} diff --git a/images/qnx_x86_64/configs/network_setup_dhcp.sh b/images/qnx_x86_64/configs/network_setup_dhcp.sh index da2b1db8b15..b4bf4c9b7f8 100644 --- a/images/qnx_x86_64/configs/network_setup_dhcp.sh +++ b/images/qnx_x86_64/configs/network_setup_dhcp.sh @@ -95,6 +95,6 @@ sysctl -w net.inet.icmp.bmcastecho=1 > /dev/null # Enable ICMP broadcast # multicast traffic ('224.0.0.0/4') out the guest network # interface so it reaches the host via the QEMU TAP device. echo "Adding multicast route" -route add -net 224.0.0.0 -netmask 240.0.0.0 -interface vtnet0 +/proc/boot/route add -net 224.0.0.0 -netmask 240.0.0.0 -interface vtnet0 echo "---> Network configuration completed" diff --git a/images/qnx_x86_64/configs/startup.sh b/images/qnx_x86_64/configs/startup.sh index c4b9fd79bb4..bc1b7e9ce84 100644 --- a/images/qnx_x86_64/configs/startup.sh +++ b/images/qnx_x86_64/configs/startup.sh @@ -82,4 +82,10 @@ mkdir -p /tmp_ram/tmp_discovery ln -sP /tmp_ram/tmp_discovery /tmp_discovery /proc/boot/sshd -f /var/ssh/sshd_config # Start SSH daemon with specified configuration file -/showcases/bin/cli # Start the CLI application from the mounted showcases directory +#/showcases/bin/cli & # Start the CLI application in background + +mkdir -p /tmp_ram/benchmark +mkdir -p /tmp_ram/benchmark/etc + +echo "---> Starting interactive shell on serial console" +exec /bin/sh # Start interactive shell for serial console access diff --git a/runners/qemu_x86_64/scripts/run_qemu.sh b/runners/qemu_x86_64/scripts/run_qemu.sh index 6482d232665..fb771a6e196 100755 --- a/runners/qemu_x86_64/scripts/run_qemu.sh +++ b/runners/qemu_x86_64/scripts/run_qemu.sh @@ -32,9 +32,6 @@ qemu-system-x86_64 \ -pidfile /tmp/qemu.pid \ -nographic \ -kernel "${IFS_IMAGE}" \ - -chardev stdio,id=char0,signal=on,mux=on \ - -mon chardev=char0,mode=readline \ - -serial chardev:char0 \ -object rng-random,filename=/dev/urandom,id=rng0 \ -netdev bridge,id=net0,br=virbr0 -device virtio-net-pci,netdev=net0 \ -device virtio-rng-pci,rng=rng0