00/33] Test suite hardening, correctness fixes, and BPF optimization
PATCH 1/1 00/33] Test suite hardening, correctness fixes, and BPF optimization
The stalld functional test suite has accumulated significant reliability and maintainability issues over time. Several tests were silently passing due to missing assertions, shell scoping bugs that discarded subshell results, and grep substring matches causing false positives on multi-digit CPU systems. Massive boilerplate duplication existed across the suite, timing-dependent sleeps caused flakiness, and a daemon bug was discovered where --force_fifo combined with single-threaded mode silently fell back to adaptive mode instead of erroring out. Additionally, the BPF queue_track backend suffered from an O(n) linear array scan on every sched_wakeup event, consuming significant real-time latency budget in Telco workloads.
This series addresses these issues in three areas. First, the daemon's --force_fifo validation is fixed to reject the incompatible single-threaded mode at startup. Second, the test suite is overhauled by introducing eight shared helpers (test_section, cleanup_scenario, find_starved_child, init_functional_test, assert_stalld_rejects, assert_log_contains, assert_success, and starvation/boost assertion helpers) that replace duplicated validation patterns across all functional tests. The tests are then hardened with proper assertions, transitioned to fail-fast semantics where any failure immediately aborts the run, and timing-dependent sleeps are replaced with event-driven synchronization. The starvation_gen helper's signal handler is fixed for async-signal safety. Legacy C test infrastructure, stale documentation, and unreachable code are removed. Third, the BPF queue_track backend replaces its per-CPU linear array with a hash map keyed by (cpu, pid) for O(1) task lookups, reducing thread latency from 5-6us to 3us on real-time systems.
As a result, approximately seven previously silent tests now correctly validate daemon behavior, the suite runs deterministically via event-driven waits rather than fixed sleeps, and the BPF backend eliminates its lookup overhead entirely. The series yields a net reduction of roughly 9,850 lines (+874/-10,725).
Wander Lairson Costa (33):
stalld: Reject --force_fifo in single-threaded mode
tests: Introduce test_section() helper
tests: Introduce cleanup_scenario() helper
tests: Introduce starvation and boost asserts
tests: Introduce find_starved_child() helper
tests: Fix task exit timing in test_boost_restoration
tests: Consolidate and adopt init_functional_test()
tests: Introduce assert_stalld_rejects() helper
tests: Fix boost verification in runtime and duration tests
tests: Fix subshell swallowing test results
tests: Fix repeated log match finding same line
chore: Remove legacy test infrastructure and stale docs
tests: Add assertions to SCHED_OTHER restoration test
tests: Fix CPU selection grep substring matches
tests: Add idle CPU skipping assertion
tests: Remove redundant pkill from cleanup
tests: Introduce and adopt assert_log_contains() helper
tests: Remove weak, redundant, and assertion-free test blocks
tests: Introduce and adopt assert_success() helper
tests: Replace wait conditionals with asserts
tests: Remove if-wrappers around assert calls
tests: Abort immediately on test failure
tests: Remove dead code after making fail() fatal
tests: Introduce and adopt process helpers
tests: Extract wait_for_process_exit helper
tests: Reduce default wait timeouts
tests: Reduce starvation_gen durations
tests: Replace init sleeps in test_affinity
tests: Drop redundant sleeps in test_pidfile
tests: Remove redundant sleeps after start_stalld
tests: Reduce timing and replace sleeps with event waits
tests: Fix async-signal-unsafe handler
bpf: Replace linear task scan with hash map
.claude/CLAUDE.md | 585 --------- .claude/agents/agent-prompt-engineer.md | 135 -- .claude/agents/c-expert.md | 53 - .claude/agents/code-reviewer.md | 104 -- .claude/agents/get-agent-hash | 99 -- .claude/agents/git-scm-master.md | 1154 ----------------- .claude/agents/kernel-hacker.md | 231 ---- .claude/agents/plan-validator.md | 130 -- .claude/agents/project-historian.md | 285 ---- .claude/agents/project-librarian.md | 604 --------- .claude/agents/project-manager.md | 388 ------ .claude/agents/project-scope-guardian.md | 326 ----- .claude/agents/python-expert.md | 57 - .claude/agents/test-specialist.md | 656 ---------- .claude/agents/update-agent-hashes | 96 -- .claude/context-snapshot.json | 103 -- .claude/rules | 42 - .gitignore | 5 +- bpf/stalld.bpf.c | 206 ++- src/queue_track.c | 95 +- src/queue_track.h | 107 +- src/utils.c | 9 +- tests/BACKEND_USAGE.md | 269 ---- tests/CONTEXT_SNAPSHOT_2025-10-31.md | 231 ---- tests/Makefile | 29 +- tests/README.md | 29 +- tests/TODO.md | 727 ----------- tests/functional/test_affinity.sh | 178 +-- tests/functional/test_backend_selection.sh | 24 +- tests/functional/test_boost_duration.sh | 164 +-- tests/functional/test_boost_period.sh | 151 +-- tests/functional/test_boost_restoration.sh | 310 +---- tests/functional/test_boost_runtime.sh | 179 +-- tests/functional/test_cpu_selection.sh | 76 +- tests/functional/test_deadline_boosting.sh | 266 +--- tests/functional/test_fifo_boosting.sh | 269 +--- .../test_fifo_priority_starvation.sh | 197 +-- tests/functional/test_force_fifo.sh | 196 +-- tests/functional/test_foreground.sh | 65 +- tests/functional/test_idle_detection.sh | 195 +-- tests/functional/test_log_only.sh | 30 +- tests/functional/test_logging_destinations.sh | 38 +- tests/functional/test_pidfile.sh | 166 +-- tests/functional/test_runqueue_parsing.sh | 417 ------ tests/functional/test_starvation_detection.sh | 281 +--- tests/functional/test_starvation_threshold.sh | 138 +- tests/functional/test_task_merging.sh | 161 +-- tests/helpers/starvation_gen.c | 2 +- tests/helpers/test_helpers.sh | 356 +++-- tests/legacy/README.md | 169 --- tests/legacy/test01.c | 506 -------- tests/legacy/test01_wrapper.sh | 161 --- tests/run_tests.sh | 149 +-- 53 files changed, 874 insertions(+), 10725 deletions(-) delete mode 100644 .claude/CLAUDE.md delete mode 100644 .claude/agents/agent-prompt-engineer.md delete mode 100644 .claude/agents/c-expert.md delete mode 100644 .claude/agents/code-reviewer.md delete mode 100755 .claude/agents/get-agent-hash delete mode 100644 .claude/agents/git-scm-master.md delete mode 100644 .claude/agents/kernel-hacker.md delete mode 100644 .claude/agents/plan-validator.md delete mode 100644 .claude/agents/project-historian.md delete mode 100644 .claude/agents/project-librarian.md delete mode 100644 .claude/agents/project-manager.md delete mode 100644 .claude/agents/project-scope-guardian.md delete mode 100644 .claude/agents/python-expert.md delete mode 100644 .claude/agents/test-specialist.md delete mode 100755 .claude/agents/update-agent-hashes delete mode 100644 .claude/context-snapshot.json delete mode 100644 .claude/rules delete mode 100644 tests/BACKEND_USAGE.md delete mode 100644 tests/CONTEXT_SNAPSHOT_2025-10-31.md delete mode 100644 tests/TODO.md delete mode 100755 tests/functional/test_runqueue_parsing.sh delete mode 100644 tests/legacy/README.md delete mode 100644 tests/legacy/test01.c delete mode 100755 tests/legacy/test01_wrapper.sh
Signature · 1 line
2.54.0
PATCH 1/1 01/33] stalld: Reject --force_fifo in single-threaded mode
When --force_fifo (-F) was combined with single-threaded mode (-O), stalld silently fell back to adaptive mode instead of honoring the requested configuration. This violates the principle of least surprise since the user explicitly requested single-threaded mode but gets a different threading model without warning.
Replace the silent fallback with a usage() error that exits immediately, consistent with the other argument validation checks in parse_args(). Also fix the corresponding test which was missing the -O flag and therefore tested adaptive mode instead of single-threaded mode.
--- src/utils.c | 9 ++------- tests/functional/test_force_fifo.sh | 18 ++---------------- 2 files changed, 4 insertions(+), 23 deletions(-)
src/utils.c +2 -7
@@ -1182,13 +1182,8 @@ int parse_args(int argc, char **argv)if (config_boost_duration > config_starving_threshold)usage ("the boost duration cannot be longer than the starving threshold " ); -if (config_force_fifo && config_single_threaded) { -log_msg ("-F/--force_fifo does not work in single-threaded mode\n" ); -log_msg ("falling back to the adaptive mode\n" ); - config_adaptive_multi_threaded =1 ; - config_single_threaded =0 ; - config_aggressive =0 ; - } +if (config_force_fifo && config_single_threaded) +usage ("--force_fifo does not work in single-threaded mode" );if (config_reservation && (config_aggressive || config_adaptive_multi_threaded))usage ("-R/--reservation only works in the single-threaded mode" );
tests/functional/test_force_fifo.sh +2 -16
@@ -207,22 +207,8 @@ log "==========================================" log "Test 5: Single-threaded mode with FIFO (should fail)" log "==========================================" -# Try to run stalld with -F but without -A (single-threaded mode) -# According to CLAUDE.md, this should die/exit -FIFO_SINGLE_LOG="/tmp/stalld_test_force_fifo_single_$$.log" -CLEANUP_FILES+=("${FIFO_SINGLE_LOG}") - -log "Testing single-threaded mode with -F (should exit)" -timeout 5 ${TEST_ROOT}/../stalld -f -v -c "${TEST_CPU}" -t ${threshold} -F > "${FIFO_SINGLE_LOG}" 2>&1 -ret=$? - -if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then - pass "single-threaded mode rejected FIFO" -elif grep -qiE "single.*thread|falling back|adaptive" "${FIFO_SINGLE_LOG}"; then - pass "stalld detected incompatibility and fell back to adaptive mode" -else - fail "stalld silently accepted FIFO in single-threaded mode" -fi +log "Testing single-threaded mode (-O) with -F (should exit)" +assert_stalld_rejects "Single-threaded mode rejected FIFO" -f -v -c "${TEST_CPU}" -t ${threshold} -F -O #============================================================================= # Test 6: Compare effectiveness (informational)
Signature · 1 line
2.54.0
PATCH 1/1 03/33] tests: Introduce cleanup_scenario() helper
In reply to Wander Lairson CostaFunctional tests currently duplicate boilerplate code to terminate background processes and stop the stalld daemon between test scenarios. This repetition clutters the test files and occasionally leads to errors, such as missing wait calls after sending termination signals.
Introduce a variadic cleanup_scenario() helper function in the test helpers script to consolidate this teardown logic. The function accepts an arbitrary number of process IDs, safely terminates each one, waits for them to exit, and then stops the stalld daemon. It is exported via export -f to ensure availability within subshells.
Applying this helper across the functional test suite removes redundant cleanup sequences and resolves missing wait calls in the boost restoration tests.
--- tests/functional/test_boost_duration.sh | 16 ++++--------- tests/functional/test_boost_period.sh | 16 ++++--------- tests/functional/test_boost_restoration.sh | 17 ++++--------- tests/functional/test_boost_runtime.sh | 16 ++++--------- tests/functional/test_deadline_boosting.sh | 22 ++++------------- tests/functional/test_fifo_boosting.sh | 20 ++++------------ .../test_fifo_priority_starvation.sh | 20 ++++------------ tests/functional/test_force_fifo.sh | 24 +++++-------------- tests/functional/test_idle_detection.sh | 8 ++----- tests/functional/test_log_only.sh | 4 +--- tests/functional/test_runqueue_parsing.sh | 24 +++++-------------- tests/functional/test_starvation_detection.sh | 17 ++++--------- tests/functional/test_starvation_threshold.sh | 12 +++------- tests/functional/test_task_merging.sh | 18 ++++---------- tests/helpers/test_helpers.sh | 14 ++++++++++- 15 files changed, 71 insertions(+), 177 deletions(-)
tests/functional/test_boost_duration.sh +4 -12
@@ -70,9 +70,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null || true -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 2: Short duration (1 second)
@@ -98,9 +96,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null || true -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 3: Long duration (10 seconds)
@@ -128,9 +124,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null || true -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 4: Verify task policy is restored after boost duration
@@ -157,9 +151,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null || true -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 5: Invalid duration values
tests/functional/test_boost_period.sh +4 -12
@@ -74,9 +74,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null || true -wait ${STARVE_PID} 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 2: Custom period (500ms = 500,000,000 ns)
@@ -100,9 +98,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null || true -wait ${STARVE_PID} 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 3: Very short period (100ms = 100,000,000 ns)
@@ -126,9 +122,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null || true -wait ${STARVE_PID} 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 4: Very long period (10s = 10,000,000,000 ns)
@@ -152,9 +146,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null || true -wait ${STARVE_PID} 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 5: Invalid period (0)
tests/functional/test_boost_restoration.sh +5 -12
@@ -150,8 +150,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 2: Restore Original RT Policy (SCHED_FIFO)
@@ -261,8 +260,7 @@ else fi # Cleanup -kill -TERM ${BLOCKER_PID} 2>/dev/null || true -stop_stalld +cleanup_scenario "${BLOCKER_PID}" #============================================================================= # Test 3: SCHED_OTHER Policy Restoration
@@ -289,8 +287,7 @@ wait ${STARVE_PID} 2>/dev/null || true # The starvation_gen output will show if blockees completed # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 4: Restoration Timing Verification
@@ -338,9 +335,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null || true -wait ${STARVE_PID} 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" # Give stalld time to fully exit before next test sleep 1
@@ -392,9 +387,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null || true -wait ${STARVE_PID} 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Final Summary
tests/functional/test_boost_runtime.sh +4 -12
@@ -70,9 +70,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null || true -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 2: Custom runtime (10,000 ns = 10 microseconds, less than default)
@@ -98,9 +96,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null || true -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 3: Larger runtime (100,000 ns = 100 microseconds)
@@ -126,9 +122,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null || true -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 4: Runtime < period (valid configuration)
@@ -156,9 +150,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null || true -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 5: Runtime > period (should error or be rejected)
tests/functional/test_deadline_boosting.sh +5 -17
@@ -88,9 +88,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 2: DEADLINE Parameters Verification
@@ -150,9 +148,7 @@ if [ ${boosted_task_found} -eq 0 ]; then fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 3: Task Makes Progress During Boost
@@ -218,9 +214,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 4: Policy Restoration After Boost
@@ -296,9 +290,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 5: Multiple Simultaneous Boosts
@@ -360,11 +352,7 @@ else fi # Cleanup - kill -TERM ${STARVE_PID0} 2>/dev/null - kill -TERM ${STARVE_PID1} 2>/dev/null - wait ${STARVE_PID0} 2>/dev/null - wait ${STARVE_PID1} 2>/dev/null - stop_stalld + cleanup_scenario "${STARVE_PID0}" "${STARVE_PID1}" fi #=============================================================================
tests/functional/test_fifo_boosting.sh +5 -15
@@ -78,9 +78,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 2: FIFO Priority Verification
@@ -131,9 +129,7 @@ if [ ${fifo_task_found} -eq 0 ]; then fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 3: FIFO Emulation Behavior
@@ -182,9 +178,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 4: FIFO vs DEADLINE Comparison
@@ -232,9 +226,7 @@ fi deadline_progress=$((ctxt_after_deadline - ctxt_before_deadline)) log "DEADLINE progress: ${deadline_progress} context switches" -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" # Small delay between tests sleep 2
@@ -277,9 +269,7 @@ fi fifo_progress=$((ctxt_after_fifo - ctxt_before_fifo)) log "FIFO progress: ${fifo_progress} context switches" -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" # Compare effectiveness log ""
tests/functional/test_fifo_priority_starvation.sh +5 -15
@@ -91,9 +91,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 2: Boosting Effectiveness
@@ -155,9 +153,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 3: Starvation Duration Tracking
@@ -216,9 +212,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 4: Close Priority Gap
@@ -251,9 +245,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 5: Correct Task Boosted
@@ -295,9 +287,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Final Summary
tests/functional/test_force_fifo.sh +6 -18
@@ -72,9 +72,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 2: Force FIFO mode (-F)
@@ -110,9 +108,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 3: Verify FIFO priority setting
@@ -144,9 +140,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 4: Verify FIFO emulation behavior (sleep runtime, restore, sleep remainder)
@@ -183,9 +177,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 5: Single-threaded mode with FIFO (should fail/exit)
@@ -215,9 +207,7 @@ wait_for_boost_detected "${STALLD_LOG_DL}" deadline_boosts=$(grep -c "boost" "${STALLD_LOG_DL}" || echo 0) log "ℹ INFO: SCHED_DEADLINE boosts: $deadline_boosts" -kill -TERM "${STARVE_PID}" 2>/dev/null -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" # Run with FIFO STALLD_LOG_FIFO="/tmp/stalld_test_force_fifo_comparison_$$.log"
@@ -231,9 +221,7 @@ wait_for_boost_detected "${STALLD_LOG_FIFO}" fifo_boosts=$(grep -c "boost" "${STALLD_LOG_FIFO}" || echo 0) log "ℹ INFO: SCHED_FIFO boosts: $fifo_boosts" -kill -TERM "${STARVE_PID}" 2>/dev/null -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" log "ℹ INFO: Comparison complete (DEADLINE: $deadline_boosts, FIFO: $fifo_boosts)"
tests/functional/test_idle_detection.sh +2 -6
@@ -165,9 +165,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 4: Idle Detection Overhead Reduction
@@ -236,9 +234,7 @@ else fi # Cleanup - kill -TERM ${STARVE_PID} 2>/dev/null - wait ${STARVE_PID} 2>/dev/null - stop_stalld + cleanup_scenario "${STARVE_PID}" fi #=============================================================================
tests/functional/test_log_only.sh +1 -3
@@ -76,9 +76,7 @@ else fi # Cleanup -kill ${STARVGEN_PID} 2>/dev/null -wait ${STARVGEN_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVGEN_PID}" echo "" echo "Log file contents:"
tests/functional/test_runqueue_parsing.sh +6 -18
@@ -121,9 +121,7 @@ if [ ${BPF_AVAILABLE} -eq 1 ]; then fi # Cleanup - kill -TERM ${STARVE_PID} 2>/dev/null - wait ${STARVE_PID} 2>/dev/null - stop_stalld + cleanup_scenario "${STARVE_PID}" else test_section "Test 1: eBPF Backend - SKIPPED" log "eBPF backend not available on this system"
@@ -174,9 +172,7 @@ if [ ${SCHED_DEBUG_AVAILABLE} -eq 1 ]; then fi # Cleanup - kill -TERM ${STARVE_PID} 2>/dev/null - wait ${STARVE_PID} 2>/dev/null - stop_stalld + cleanup_scenario "${STARVE_PID}" else test_section "Test 2: sched_debug Backend - SKIPPED" log "sched_debug backend not available on this system"
@@ -204,9 +200,7 @@ if [ ${BPF_AVAILABLE} -eq 1 ] && [ ${SCHED_DEBUG_AVAILABLE} -eq 1 ]; then bpf_detections=$(count_detected_tasks "${STALLD_LOG_BPF}") log "eBPF backend detected: ${bpf_detections} starvation events" - kill -TERM ${STARVE_PID} 2>/dev/null - wait ${STARVE_PID} 2>/dev/null - stop_stalld + cleanup_scenario "${STARVE_PID}" # Small delay between tests sleep 2
@@ -223,9 +217,7 @@ if [ ${BPF_AVAILABLE} -eq 1 ] && [ ${SCHED_DEBUG_AVAILABLE} -eq 1 ]; then sched_detections=$(count_detected_tasks "${STALLD_LOG_SCHED}") log "sched_debug backend detected: ${sched_detections} starvation events" - kill -TERM ${STARVE_PID} 2>/dev/null - wait ${STARVE_PID} 2>/dev/null - stop_stalld + cleanup_scenario "${STARVE_PID}" # Compare results log ""
@@ -314,9 +306,7 @@ if [ -n "$test_backend" ]; then fi # Cleanup - kill -TERM ${STARVE_PID} 2>/dev/null - wait ${STARVE_PID} 2>/dev/null - stop_stalld + cleanup_scenario "${STARVE_PID}" fi #=============================================================================
@@ -366,9 +356,7 @@ if [ ${SCHED_DEBUG_AVAILABLE} -eq 1 ]; then fi # Cleanup - kill -TERM ${STARVE_PID} 2>/dev/null - wait ${STARVE_PID} 2>/dev/null - stop_stalld + cleanup_scenario "${STARVE_PID}" else test_section "Test 5: Kernel Format Detection - SKIPPED" log "sched_debug backend required for format detection tests"
tests/functional/test_starvation_detection.sh +4 -13
@@ -94,9 +94,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 2: Context Switch Count Tracking
@@ -148,9 +146,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 3: Task Merging (Timestamp Preservation)
@@ -209,8 +205,7 @@ else fi # Cleanup starvation generator -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 4: Multiple CPUs Detection
@@ -275,11 +270,7 @@ else fi # Cleanup - kill -TERM ${STARVE_PID0} 2>/dev/null - kill -TERM ${STARVE_PID1} 2>/dev/null - wait ${STARVE_PID0} 2>/dev/null - wait ${STARVE_PID1} 2>/dev/null - stop_stalld + cleanup_scenario "${STARVE_PID0}" "${STARVE_PID1}" fi #=============================================================================
tests/functional/test_starvation_threshold.sh +3 -9
@@ -81,9 +81,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 2: Verify no detection before threshold
@@ -122,9 +120,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 3: Shorter threshold (3 seconds)
@@ -158,9 +154,7 @@ else fi # Cleanup -kill -TERM "${STARVE_PID}" 2>/dev/null -wait "${STARVE_PID}" 2>/dev/null || true -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 4: Invalid threshold values
tests/functional/test_task_merging.sh +4 -14
@@ -124,9 +124,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 2: Same PID + Same Context Switches = Merged
@@ -194,9 +192,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 3: Task Making Progress (No Merge)
@@ -246,9 +242,7 @@ else fi # Cleanup -kill -TERM ${STARVE_PID} 2>/dev/null -wait ${STARVE_PID} 2>/dev/null -stop_stalld +cleanup_scenario "${STARVE_PID}" #============================================================================= # Test 4: Multiple CPUs with Independent Task Merging
@@ -323,11 +317,7 @@ else fi # Cleanup - kill -TERM ${STARVE_PID0} 2>/dev/null - kill -TERM ${STARVE_PID1} 2>/dev/null - wait ${STARVE_PID0} 2>/dev/null - wait ${STARVE_PID1} 2>/dev/null - stop_stalld + cleanup_scenario "${STARVE_PID0}" "${STARVE_PID1}" fi #=============================================================================
tests/helpers/test_helpers.sh +13 -1
@@ -114,6 +114,18 @@ test_section() { log "==========================================" } +# Tear down starvation workloads and stalld between test sections. +# Usage: cleanup_scenario [PID ...] +cleanup_scenario() { + for pid in "$@"; do + if [ -n "$pid" ]; then + kill -TERM "$pid" 2>/dev/null || true + wait "$pid" 2>/dev/null || true + fi + done + stop_stalld +} + # Record a test pass with a description message. # # Usage: pass "description"
@@ -1138,7 +1150,7 @@ start_starvation_gen() { } # Export functions for use in tests -export -f start_test end_test test_section +export -f start_test end_test test_section cleanup_scenario export -f pass fail assert_equals assert_contains assert_not_contains export -f assert_file_exists assert_file_not_exists export -f assert_process_running assert_process_not_running
Signature · 1 line
2.54.0
PATCH 1/1 06/33] tests: Fix task exit timing in test_boost_restoration
In reply to Wander Lairson CostaTest 5 verifies that stalld handles a task exiting during an active boost. The starvation duration was set to threshold minus two seconds, causing the task to exit before the detection threshold was reached. As a result, the task was never boosted and the test silently passed without exercising the intended code path.
Lower the threshold and compute the starvation duration as threshold plus three seconds so that the task survives long enough to be detected and boosted, then exits mid-boost as originally intended.
--- tests/functional/test_boost_restoration.sh | 10 +++++----- tests/functional/test_runqueue_parsing.sh | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-)
tests/functional/test_boost_restoration.sh +5 -5
@@ -338,16 +338,16 @@ sleep 1 #============================================================================= test_section "Test 5: Graceful Handling of Task Exit During Boost" -threshold=10 -boost_duration=5 # Task will exit during boost (after 8s, boost is 5s) +threshold=5 +boost_duration=5 log "Starting stalld with ${threshold}s threshold, ${boost_duration}s boost (task will exit during boost)" rm -f "${STALLD_LOG}" start_stalld_with_log "${STALLD_LOG}" -f -v -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} -d ${boost_duration} -N -i "kworker" -# Create starvation that exits after threshold - 2s (so 8s) -# This ensures the task exits DURING the boost period -short_duration=$((threshold - 2)) +# Task must survive past the threshold to be detected and boosted, +# then exit during the boost window. +short_duration=$((threshold + 3)) log "Creating starvation that will exit after ${short_duration}s" start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d ${short_duration}
tests/functional/test_runqueue_parsing.sh +6 -6
@@ -90,7 +90,7 @@ if [ ${BPF_AVAILABLE} -eq 1 ]; then threshold=5 log "Starting stalld with eBPF backend (queue_track)" # Use -g 1 for 1-second granularity - start_stalld -f -v -g 1 -l -b queue_track -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} > "${STALLD_LOG_BPF}" 2>&1 + start_stalld -f -v -g 1 -N -l -i "kworker,ksoftirqd" -b queue_track -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} > "${STALLD_LOG_BPF}" 2>&1 # Create starvation to generate task data starvation_duration=$((threshold + 5))
@@ -135,7 +135,7 @@ if [ ${SCHED_DEBUG_AVAILABLE} -eq 1 ]; then threshold=5 log "Starting stalld with sched_debug backend" - start_stalld -f -v -g 1 -l -b sched_debug -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} > "${STALLD_LOG_SCHED}" 2>&1 + start_stalld -f -v -g 1 -N -l -i "kworker,ksoftirqd" -b sched_debug -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} > "${STALLD_LOG_SCHED}" 2>&1 # Create starvation starvation_duration=$((threshold + 5))
@@ -192,7 +192,7 @@ if [ ${BPF_AVAILABLE} -eq 1 ] && [ ${SCHED_DEBUG_AVAILABLE} -eq 1 ]; then log "" log "Running with eBPF backend..." rm -f "${STALLD_LOG_BPF}" - start_stalld -f -v -g 1 -l -b queue_track -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} > "${STALLD_LOG_BPF}" 2>&1 + start_stalld -f -v -g 1 -N -l -i "kworker,ksoftirqd" -b queue_track -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} > "${STALLD_LOG_BPF}" 2>&1 start_starvation_gen -c ${TEST_CPU} -p 80 -n 2 -d ${starvation_duration}
@@ -209,7 +209,7 @@ if [ ${BPF_AVAILABLE} -eq 1 ] && [ ${SCHED_DEBUG_AVAILABLE} -eq 1 ]; then log "" log "Running with sched_debug backend..." rm -f "${STALLD_LOG_SCHED}" - start_stalld -f -v -g 1 -l -b sched_debug -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} > "${STALLD_LOG_SCHED}" 2>&1 + start_stalld -f -v -g 1 -N -l -i "kworker,ksoftirqd" -b sched_debug -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} > "${STALLD_LOG_SCHED}" 2>&1 start_starvation_gen -c ${TEST_CPU} -p 80 -n 2 -d ${starvation_duration}
@@ -264,7 +264,7 @@ if [ -n "$test_backend" ]; then log "Testing task field extraction with ${test_backend} backend" rm -f "${log_file}" - start_stalld -f -v -g 1 -l -b ${test_backend} -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} > "${log_file}" 2>&1 + start_stalld -f -v -g 1 -N -l -i "kworker,ksoftirqd" -b ${test_backend} -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} > "${log_file}" 2>&1 # Create starvation with known parameters log "Creating starvation with known task name (starvation_gen)"
@@ -317,7 +317,7 @@ if [ ${SCHED_DEBUG_AVAILABLE} -eq 1 ]; then threshold=5 rm -f "${STALLD_LOG_SCHED}" - start_stalld -f -v -g 1 -l -b sched_debug -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} > "${STALLD_LOG_SCHED}" 2>&1 + start_stalld -f -v -g 1 -N -l -i "kworker,ksoftirqd" -b sched_debug -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} > "${STALLD_LOG_SCHED}" 2>&1 # Create brief starvation just to initialize the backend start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 8
Signature · 1 line
2.54.0
PATCH 1/1 05/33] tests: Introduce find_starved_child() helper
In reply to Wander Lairson CostaMultiple test scripts duplicate a pattern of using pgrep and a loop to identify the child process of starvation_gen that is most likely to be starving. This duplication clutters the tests and is prone to subtle errors. Notably, test_fifo_priority_starvation.sh contained a logic gap where it claimed to check for the lower-priority blockee but simply grabbed the first child process instead.
Introduce a centralized find_starved_child() helper in test_helpers.sh and export it for use across the test suite. This function reliably identifies the starving child by selecting the one with the lowest scheduling priority, determined by finding the highest kernel priority value exposed in /proc/PID/sched.
Replace twelve instances of the duplicated search pattern across six different test files with the new helper. This refactoring resolves the logic gap in the FIFO priority starvation test and improves test maintainability while reducing the overall line count.
--- tests/functional/test_boost_restoration.sh | 9 +--- tests/functional/test_deadline_boosting.sh | 51 ++++++------------- tests/functional/test_fifo_boosting.sh | 45 +++++----------- .../test_fifo_priority_starvation.sh | 11 +--- tests/functional/test_starvation_detection.sh | 41 ++++++--------- tests/functional/test_task_merging.sh | 23 ++------- tests/helpers/test_helpers.sh | 30 ++++++++++- 7 files changed, 79 insertions(+), 131 deletions(-)
tests/functional/test_boost_restoration.sh +1 -8
@@ -64,14 +64,7 @@ log "Creating starvation with SCHED_FIFO tasks (blocker prio 80, blockee prio 1) start_starvation_gen -c ${TEST_CPU} -p 80 -b 1 -n 1 -d 20 # Find the starved task -STARVE_CHILDREN=$(pgrep -P ${STARVE_PID} 2>/dev/null) -tracked_pid="" -for child_pid in ${STARVE_CHILDREN}; do - if [ -f "/proc/${child_pid}/sched" ]; then - tracked_pid=${child_pid} - break - fi -done +tracked_pid=$(find_starved_child "${STARVE_PID}") if [ -n "${tracked_pid}" ]; then log "Tracking task PID ${tracked_pid}"
tests/functional/test_deadline_boosting.sh +15 -36
77 lines not on this page · raw message
tests/functional/test_fifo_boosting.sh +13 -32
70 lines not on this page · raw message
tests/functional/test_fifo_priority_starvation.sh +1 -10
@@ -107,16 +107,7 @@ log "Creating FIFO-on-FIFO starvation on CPU ${TEST_CPU}" start_starvation_gen -c ${TEST_CPU} -p 10 -b 5 -n 1 -d 20 # Find the starved task (blockee) PID -STARVE_CHILDREN=$(pgrep -P ${STARVE_PID} 2>/dev/null) -blockee_pid="" -for child_pid in ${STARVE_CHILDREN}; do - if [ -f "/proc/${child_pid}/status" ]; then - # Check if it's the lower priority task (blockee) - # The blockee should have lower priority than blocker - blockee_pid=${child_pid} - break - fi -done +blockee_pid=$(find_starved_child "${STARVE_PID}") ctxt_before=0 if [ -n "${blockee_pid}" ]; then
tests/functional/test_starvation_detection.sh +16 -25
47 lines not on this page · raw message
tests/functional/test_task_merging.sh +4 -19
@@ -144,14 +144,7 @@ start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 20 wait_for_starvation_detected "${STALLD_LOG}" # Find the starved task PID -STARVE_CHILDREN=$(pgrep -P ${STARVE_PID} 2>/dev/null) -tracked_pid="" -for child_pid in ${STARVE_CHILDREN}; do - if [ -f "/proc/${child_pid}/status" ]; then - tracked_pid=${child_pid} - break - fi -done +tracked_pid=$(find_starved_child "${STARVE_PID}") if [ -n "${tracked_pid}" ]; then log "Tracking starved task PID ${tracked_pid}"
@@ -208,19 +201,11 @@ start_stalld_with_log "${STALLD_LOG}" -f -v -g 1 -t $threshold -c ${TEST_CPU} -a log "Creating starvation that will be boosted" start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 20 +# Find tracked task while it's guaranteed to be starving +tracked_pid=$(find_starved_child "${STARVE_PID}") + # Wait for starvation detection wait_for_starvation_detected "${STALLD_LOG}" - -# Find tracked task -STARVE_CHILDREN=$(pgrep -P ${STARVE_PID} 2>/dev/null) -tracked_pid="" -for child_pid in ${STARVE_CHILDREN}; do - if [ -f "/proc/${child_pid}/status" ]; then - tracked_pid=${child_pid} - break - fi -done - if [ -n "${tracked_pid}" ]; then # Wait for boost to complete and task to starve again sleep 5
tests/helpers/test_helpers.sh +29 -1
@@ -126,6 +126,34 @@ cleanup_scenario() { stop_stalld } +# Find the child of a starvation_gen process most likely to be starving. +# When multiple children exist, selects the one with the lowest scheduling +# priority (highest kernel prio value) since that task loses the CPU to +# higher-priority siblings. +# Usage: tracked_pid=$(find_starved_child <parent_pid>) +find_starved_child() { + local parent_pid=$1 + local candidate="" + local max_prio=-100 + local prio + for child in $(pgrep -P "${parent_pid}" 2>/dev/null); do + if [ -d "/proc/${child}" ]; then + prio=$(get_sched_priority "${child}" 2>/dev/null) + prio=${prio:--1} + if [ "${prio}" -gt "${max_prio}" ] 2>/dev/null; then + candidate="${child}" + max_prio="${prio}" + fi + fi + done + + if [ -n "${candidate}" ]; then + echo "${candidate}" + return 0 + fi + return 1 +} + # Assert that stalld detects a starving task within the timeout. # Usage: assert_starvation_detected <log_file> <message> [timeout] [cpu] assert_starvation_detected() {
@@ -1187,7 +1215,7 @@ start_starvation_gen() { } # Export functions for use in tests -export -f start_test end_test test_section cleanup_scenario assert_starvation_detected assert_boost_detected +export -f start_test end_test test_section cleanup_scenario find_starved_child assert_starvation_detected assert_boost_detected export -f pass fail assert_equals assert_contains assert_not_contains export -f assert_file_exists assert_file_not_exists export -f assert_process_running assert_process_not_running
Signature · 1 line
2.54.0
PATCH 1/1 04/33] tests: Introduce starvation and boost asserts
In reply to Wander Lairson CostaAdd assert_starvation_detected() and assert_boost_detected() wrappers to tests/helpers/test_helpers.sh. These functions wrap the common pattern of evaluating wait_for_* conditions, logging passes, and dumping logs on failure into a single function call. Both helpers return 0 or 1 to match the existing assert_* convention used throughout the test suite.
Update 6 test files to replace 16 simple if/else evaluation blocks with the new helpers, reducing boilerplate and yielding a net reduction of 33 lines. Complex evaluation blocks containing nested logic are intentionally left intact to preserve their specific behaviors.
--- tests/functional/test_boost_duration.sh | 24 ++---------- tests/functional/test_boost_period.sh | 18 ++------- tests/functional/test_boost_runtime.sh | 24 ++---------- tests/functional/test_log_only.sh | 8 +--- tests/functional/test_starvation_detection.sh | 12 +----- tests/functional/test_starvation_threshold.sh | 17 +------- tests/helpers/test_helpers.sh | 39 ++++++++++++++++++- 7 files changed, 54 insertions(+), 88 deletions(-)
tests/functional/test_boost_duration.sh +4 -20
@@ -63,11 +63,7 @@ log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} # Wait for starvation detection -if wait_for_starvation_detected "${STALLD_LOG}"; then - pass "Starvation detection occurred with default duration" -else - fail "No starvation detection" -fi +assert_starvation_detected "${STALLD_LOG}" "Starvation detection occurred with default duration" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -89,11 +85,7 @@ log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} # Wait for starvation detection -if wait_for_starvation_detected "${STALLD_LOG2}"; then - pass "Starvation detection with ${short_duration}s duration" -else - fail "No starvation detection with short duration" -fi +assert_starvation_detected "${STALLD_LOG2}" "Starvation detection with ${short_duration}s duration" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -117,11 +109,7 @@ log "Creating starvation on CPU ${TEST_CPU} for ${long_starvation}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${long_starvation} # Wait for starvation detection -if wait_for_starvation_detected "${STALLD_LOG3}"; then - pass "Starvation detection with ${long_duration}s duration" -else - fail "No starvation detection with long duration" -fi +assert_starvation_detected "${STALLD_LOG3}" "Starvation detection with ${long_duration}s duration" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -144,11 +132,7 @@ log "Creating starvation on CPU ${TEST_CPU} for 15s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 1 -d 15 # Wait for starvation detection -if wait_for_starvation_detected "${STALLD_LOG4}"; then - pass "Starvation detection with ${duration}s boost duration" -else - fail "No starvation detection" -fi +assert_starvation_detected "${STALLD_LOG4}" "Starvation detection with ${duration}s boost duration" # Cleanup cleanup_scenario "${STARVE_PID}"
tests/functional/test_boost_period.sh +3 -15
@@ -91,11 +91,7 @@ log "Creating starvation on CPU ${TEST_CPU}" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} # Wait for starvation detection and boosting -if wait_for_boost_detected "${STALLD_LOG}"; then - pass "Boosting occurred with custom period ${custom_period} ns" -else - fail "No boosting with custom period" -fi +assert_boost_detected "${STALLD_LOG}" "Boosting occurred with custom period ${custom_period} ns" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -115,11 +111,7 @@ log "Creating starvation on CPU ${TEST_CPU}" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} # Wait for starvation detection and boosting -if wait_for_boost_detected "${STALLD_LOG}"; then - pass "Boosting occurred with short period ${short_period} ns" -else - fail "No boosting with short period" -fi +assert_boost_detected "${STALLD_LOG}" "Boosting occurred with short period ${short_period} ns" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -139,11 +131,7 @@ log "Creating starvation on CPU ${TEST_CPU}" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} # Wait for starvation detection and boosting -if wait_for_boost_detected "${STALLD_LOG}"; then - pass "Boosting occurred with long period ${long_period} ns" -else - fail "No boosting with long period" -fi +assert_boost_detected "${STALLD_LOG}" "Boosting occurred with long period ${long_period} ns" # Cleanup cleanup_scenario "${STARVE_PID}"
tests/functional/test_boost_runtime.sh +4 -20
@@ -63,11 +63,7 @@ log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} # Wait for detection and boosting -if wait_for_starvation_detected "${STALLD_LOG}"; then - pass "Starvation detection with default runtime" -else - fail "No starvation detection" -fi +assert_starvation_detected "${STALLD_LOG}" "Starvation detection with default runtime" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -89,11 +85,7 @@ log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} # Wait for detection and boosting -if wait_for_starvation_detected "${STALLD_LOG2}"; then - pass "Starvation detection with custom runtime ${custom_runtime}ns" -else - fail "No starvation detection with custom runtime" -fi +assert_starvation_detected "${STALLD_LOG2}" "Starvation detection with custom runtime ${custom_runtime}ns" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -115,11 +107,7 @@ log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} # Wait for detection and boosting -if wait_for_starvation_detected "${STALLD_LOG3}"; then - pass "Starvation detection with large runtime ${large_runtime}ns" -else - fail "No starvation detection with large runtime" -fi +assert_starvation_detected "${STALLD_LOG3}" "Starvation detection with large runtime ${large_runtime}ns" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -143,11 +131,7 @@ log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} # Wait for detection and boosting -if wait_for_starvation_detected "${STALLD_LOG4}"; then - pass "Starvation detection with runtime < period" -else - fail "No starvation detection when runtime < period" -fi +assert_starvation_detected "${STALLD_LOG4}" "Starvation detection with runtime < period" # Cleanup cleanup_scenario "${STARVE_PID}"
tests/functional/test_log_only.sh +1 -7
@@ -58,13 +58,7 @@ echo "Starvation generator started (PID ${STARVGEN_PID})" echo "Waiting for starvation detection..." # Check if stalld detected the starvation (should log it) -if wait_for_starvation_detected "${LOG_FILE}"; then - pass "stalld detected and logged starvation" -else - fail "stalld did not detect starvation" - echo "Log contents:" - cat "${LOG_FILE}" -fi +assert_starvation_detected "${LOG_FILE}" "stalld detected and logged starvation" # Check that stalld did NOT boost (should not see "boosted" message with -l) if ! grep -q "boosted" "${LOG_FILE}"; then
tests/functional/test_starvation_detection.sh +2 -10
@@ -256,18 +256,10 @@ else # Wait for starvation detection on both CPUs log "Waiting for starvation detection on CPU ${CPU0}..." - if wait_for_starvation_detected "${STALLD_LOG}" 30 "${CPU0}"; then - pass "Starvation detected on CPU ${CPU0}" - else - fail "Starvation not detected on CPU ${CPU0}" - fi + assert_starvation_detected "${STALLD_LOG}" "Starvation detected on CPU ${CPU0}" "30" "${CPU0}" log "Waiting for starvation detection on CPU ${CPU1}..." - if wait_for_starvation_detected "${STALLD_LOG}" 30 "${CPU1}"; then - pass "Starvation detected on CPU ${CPU1}" - else - fail "Starvation not detected on CPU ${CPU1}" - fi + assert_starvation_detected "${STALLD_LOG}" "Starvation detected on CPU ${CPU1}" "30" "${CPU1}" # Cleanup cleanup_scenario "${STARVE_PID0}" "${STARVE_PID1}"
tests/functional/test_starvation_threshold.sh +2 -15
@@ -71,14 +71,7 @@ start_stalld_with_log "${STALLD_LOG}" -f -v -N -M -g 1 -i "ksoftirqd,kworker" -c # Wait for starvation detection log "Waiting for detection (threshold: ${threshold}s)" -# Check if starvation was detected - specifically look for starvation_gen tasks -if wait_for_starvation_detected "${STALLD_LOG}"; then - pass "Starvation detected after ${threshold}s threshold" -else - fail "Starvation not detected after ${threshold}s threshold" - log "Log contents:" - cat "${STALLD_LOG}" -fi +assert_starvation_detected "${STALLD_LOG}" "Starvation detected after ${threshold}s threshold" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -145,13 +138,7 @@ start_stalld_with_log "${STALLD_LOG3}" -f -v -N -M -g 1 -i "ksoftirqd,kworker" - log "Waiting for detection (threshold: ${threshold}s)" # Check if starvation_gen was detected -if wait_for_starvation_detected "${STALLD_LOG3}"; then - pass "Starvation detected with ${threshold}s threshold" -else - fail "Starvation not detected with ${threshold}s threshold" - log "Log contents:" - cat "${STALLD_LOG3}" -fi +assert_starvation_detected "${STALLD_LOG3}" "Starvation detected with ${threshold}s threshold" # Cleanup cleanup_scenario "${STARVE_PID}"
tests/helpers/test_helpers.sh +38 -1
@@ -126,6 +126,43 @@ cleanup_scenario() { stop_stalld } +# Assert that stalld detects a starving task within the timeout. +# Usage: assert_starvation_detected <log_file> <message> [timeout] [cpu] +assert_starvation_detected() { + local log_file=$1 + local message=${2:-"Starvation detected"} + local timeout=${3:-30} + local cpu=${4:-} + + if wait_for_starvation_detected "${log_file}" "${timeout}" "${cpu}"; then + pass "${message}" + return 0 + else + fail "${message}" + log "Log contents:" + cat "${log_file}" + return 1 + fi +} + +# Assert that stalld boosts a starving task within the timeout. +# Usage: assert_boost_detected <log_file> <message> [timeout] +assert_boost_detected() { + local log_file=$1 + local message=${2:-"Boost detected"} + local timeout=${3:-30} + + if wait_for_boost_detected "${log_file}" "${timeout}"; then + pass "${message}" + return 0 + else + fail "${message}" + log "Log contents:" + cat "${log_file}" + return 1 + fi +} + # Record a test pass with a description message. # # Usage: pass "description"
@@ -1150,7 +1187,7 @@ start_starvation_gen() { } # Export functions for use in tests -export -f start_test end_test test_section cleanup_scenario +export -f start_test end_test test_section cleanup_scenario assert_starvation_detected assert_boost_detected export -f pass fail assert_equals assert_contains assert_not_contains export -f assert_file_exists assert_file_not_exists export -f assert_process_running assert_process_not_running
Signature · 1 line
2.54.0
PATCH 1/1 07/33] tests: Consolidate and adopt init_functional_test()
In reply to Wander Lairson CostaSeveral functional tests use starvation_gen to create controlled starvation scenarios but do not verify the binary exists before attempting to run it. Add the existence check to init_functional_test() so that all tests using the shared initialization skip gracefully when the binary is missing and migrate standard tests to init_functional_test().
Additionally, standardize log file management by removing numbered log variable declarations in favor of reusing a single STALLD_LOG cleared between test sections. This fixes a broken log assertion in test_affinity where a numbered variable masked an unpopulated file.
Finally, move the BACKEND_FLAG construction into init_functional_test() and export it alongside the other shared variables. Five test files previously duplicated this block for direct stalld invocations.
--- tests/functional/test_affinity.sh | 49 ++----------- tests/functional/test_boost_duration.sh | 67 ++++------------- tests/functional/test_boost_period.sh | 46 +----------- tests/functional/test_boost_restoration.sh | 30 +------- tests/functional/test_boost_runtime.sh | 72 ++++--------------- tests/functional/test_deadline_boosting.sh | 30 +------- tests/functional/test_fifo_boosting.sh | 30 +------- .../test_fifo_priority_starvation.sh | 30 +------- tests/functional/test_force_fifo.sh | 28 +------- tests/functional/test_idle_detection.sh | 30 +------- tests/functional/test_pidfile.sh | 39 +--------- tests/functional/test_starvation_detection.sh | 30 +------- tests/functional/test_starvation_threshold.sh | 56 +++------------ tests/functional/test_task_merging.sh | 32 +-------- tests/helpers/test_helpers.sh | 13 +++- 15 files changed, 64 insertions(+), 518 deletions(-)
tests/functional/test_affinity.sh +4 -45
@@ -35,21 +35,8 @@ check_affinity() { fi } -start_test "CPU Affinity Option (-a)" +init_functional_test "CPU Affinity Option (-a)" "test_affinity" -# Setup test environment -setup_test_environment - -# Require root for this test -require_root - -# Check RT throttling -if ! check_rt_throttling; then - echo -e "${YELLOW}SKIP: RT throttling must be disabled for this test${NC}" - exit 77 -fi - -# Get number of CPUs num_cpus=$(nproc) log "System has $num_cpus CPUs"
@@ -58,15 +45,10 @@ if [ "$num_cpus" -lt 2 ]; then exit 77 fi -# Check if taskset is available if ! command -v taskset > /dev/null 2>&1; then log "⚠ WARNING: taskset not found, will use /proc fallback" fi -# Setup paths -STALLD_LOG="/tmp/stalld_test_affinity_$$.log" -CLEANUP_FILES+=("${STALLD_LOG}") - #============================================================================= # Test 1: Default behavior (no -a specified) #=============================================================================
@@ -92,9 +74,6 @@ stop_stalld #============================================================================= test_section "Test 2: Single CPU affinity (-a 0)" -STALLD_LOG2="/tmp/stalld_test_affinity_test2_$$.log" -CLEANUP_FILES+=("${STALLD_LOG2}") - start_stalld -f -v -l -t 5 -a 0 sleep 2
@@ -114,9 +93,6 @@ stop_stalld test_section "Test 3: Multi-CPU affinity (-a 0,2)" if [ "$num_cpus" -ge 4 ]; then - STALLD_LOG3="/tmp/stalld_test_affinity_test3_$$.log" - CLEANUP_FILES+=("${STALLD_LOG3}") - start_stalld -f -v -l -t 5 -a 0,2 sleep 2
@@ -141,9 +117,6 @@ fi test_section "Test 4: CPU range affinity (-a 0-2)" if [ "$num_cpus" -ge 4 ]; then - STALLD_LOG4="/tmp/stalld_test_affinity_test4_$$.log" - CLEANUP_FILES+=("${STALLD_LOG4}") - start_stalld -f -v -l -t 5 -a 0-2 sleep 2
@@ -168,9 +141,6 @@ test_section "Test 5: Verify stalld threads run on specified CPU" if [ "$num_cpus" -ge 2 ]; then test_cpu=1 - STALLD_LOG5="/tmp/stalld_test_affinity_test5_$$.log" - CLEANUP_FILES+=("${STALLD_LOG5}") - start_stalld -f -v -l -t 5 -a "$test_cpu" sleep 2
@@ -200,12 +170,10 @@ fi test_section "Test 6: Combined affinity and monitoring (-a 0 -c 1)" if [ "$num_cpus" -ge 2 ]; then - STALLD_LOG6="/tmp/stalld_test_affinity_test6_$$.log" - CLEANUP_FILES+=("${STALLD_LOG6}") + rm -f "${STALLD_LOG}" # Run stalld on CPU 0, but monitor CPU 1 - start_stalld -f -v -l -t 5 -a 0 -c 1 - sleep 2 + start_stalld_with_log "${STALLD_LOG}" -f -v -l -t 5 -a 0 -c 1 affinity=$(check_affinity "${STALLD_PID}")
@@ -216,7 +184,7 @@ if [ "$num_cpus" -ge 2 ]; then fi # Verify it's monitoring CPU 1 by checking logs - if grep -q "cpu 1" "${STALLD_LOG6}" || grep -q "monitoring.*1" "${STALLD_LOG6}"; then + if grep -q "cpu 1" "${STALLD_LOG}" || grep -q "monitoring.*1" "${STALLD_LOG}"; then log "ℹ INFO: stalld monitoring CPU 1 as requested" else log "ℹ INFO: CPU 1 monitoring not explicitly confirmed in logs"
@@ -236,12 +204,6 @@ invalid_cpu=999 INVALID_LOG="/tmp/stalld_test_affinity_invalid_$$.log" CLEANUP_FILES+=("${INVALID_LOG}") -# Add backend flag for consistency -BACKEND_FLAG="" -if [ -n "${STALLD_TEST_BACKEND}" ]; then - BACKEND_FLAG="-b ${STALLD_TEST_BACKEND}" -fi - timeout 5 ${TEST_ROOT}/../stalld -f -v ${BACKEND_FLAG} -l -t 5 -a ${invalid_cpu} > "${INVALID_LOG}" 2>&1 ret=$?
@@ -256,9 +218,6 @@ fi #============================================================================= test_section "Test 8: Verify affinity persists over time" -STALLD_LOG8="/tmp/stalld_test_affinity_test8_$$.log" -CLEANUP_FILES+=("${STALLD_LOG8}") - start_stalld -f -v -l -t 5 -a 0 sleep 2
tests/functional/test_boost_duration.sh +12 -55
128 lines not on this page · raw message
tests/functional/test_boost_period.sh +3 -43
65 lines not on this page · raw message
tests/functional/test_boost_restoration.sh +1 -29
@@ -17,35 +17,7 @@ source "${TEST_ROOT}/helpers/test_helpers.sh" # Parse command-line options parse_test_options "$@" || exit $? -start_test "Policy Restoration After Boosting" - -# Setup test environment -setup_test_environment - -# Require root for this test -require_root - -# Check RT throttling -if ! check_rt_throttling; then - echo -e "${YELLOW}SKIP: RT throttling must be disabled for this test${NC}" - exit 77 -fi - -# Pick a CPU for testing -TEST_CPU=$(pick_test_cpu) -log "Using CPU ${TEST_CPU} for testing" - -# Pick a different CPU for stalld to run on (avoid interference) -STALLD_CPU=0 -if [ ${TEST_CPU} -eq 0 ]; then - STALLD_CPU=1 -fi -log "Stalld will run on CPU ${STALLD_CPU}" - -# Setup paths -STARVE_GEN="${TEST_ROOT}/helpers/starvation_gen" -STALLD_LOG="/tmp/stalld_test_restoration_$$.log" -CLEANUP_FILES+=("${STALLD_LOG}") +init_functional_test "Policy Restoration After Boosting" "test_restoration" #============================================================================= # Test 1: Restore SCHED_FIFO Policy (starvation_gen creates SCHED_FIFO threads)
tests/functional/test_boost_runtime.sh +14 -58
142 lines not on this page · raw message
tests/functional/test_deadline_boosting.sh +1 -29
@@ -14,39 +14,11 @@ source "${TEST_ROOT}/helpers/test_helpers.sh" # Parse command-line options parse_test_options "$@" || exit $? -start_test "SCHED_DEADLINE Boosting Mechanism" - -# Setup test environment -setup_test_environment - -# Require root for this test -require_root - -# Check RT throttling -if ! check_rt_throttling; then - echo -e "${YELLOW}SKIP: RT throttling must be disabled for this test${NC}" - exit 77 -fi - -# Pick a CPU for testing -TEST_CPU=$(pick_test_cpu) -log "Using CPU ${TEST_CPU} for testing" - -# Pick a different CPU for stalld to run on (avoid interference) -STALLD_CPU=0 -if [ ${TEST_CPU} -eq 0 ]; then - STALLD_CPU=1 -fi -log "Stalld will run on CPU ${STALLD_CPU}" +init_functional_test "SCHED_DEADLINE Boosting Mechanism" "test_deadline_boost" # Get number of CPUs for multi-CPU tests NUM_CPUS=$(get_num_cpus) -# Setup paths -STARVE_GEN="${TEST_ROOT}/helpers/starvation_gen" -STALLD_LOG="/tmp/stalld_test_deadline_boost_$$.log" -CLEANUP_FILES+=("${STALLD_LOG}") - #============================================================================= # Test 1: Basic DEADLINE Boost Detection #=============================================================================
tests/functional/test_fifo_boosting.sh +1 -29
@@ -14,35 +14,7 @@ source "${TEST_ROOT}/helpers/test_helpers.sh" # Parse command-line options parse_test_options "$@" || exit $? -start_test "SCHED_FIFO Boosting Mechanism" - -# Setup test environment -setup_test_environment - -# Require root for this test -require_root - -# Check RT throttling -if ! check_rt_throttling; then - echo -e "${YELLOW}SKIP: RT throttling must be disabled for this test${NC}" - exit 77 -fi - -# Pick a CPU for testing -TEST_CPU=$(pick_test_cpu) -log "Using CPU ${TEST_CPU} for testing" - -# Pick a different CPU for stalld to run on (avoid interference) -STALLD_CPU=0 -if [ ${TEST_CPU} -eq 0 ]; then - STALLD_CPU=1 -fi -log "Stalld will run on CPU ${STALLD_CPU}" - -# Setup paths -STARVE_GEN="${TEST_ROOT}/helpers/starvation_gen" -STALLD_LOG="/tmp/stalld_test_fifo_boost_$$.log" -CLEANUP_FILES+=("${STALLD_LOG}") +init_functional_test "SCHED_FIFO Boosting Mechanism" "test_fifo_boost" #============================================================================= # Test 1: FIFO Boost with -F Flag
tests/functional/test_fifo_priority_starvation.sh +1 -29
@@ -18,35 +18,7 @@ source "${TEST_ROOT}/helpers/test_helpers.sh" # Parse command-line options parse_test_options "$@" || exit $? -start_test "FIFO-on-FIFO Priority Starvation Detection" - -# Setup test environment -setup_test_environment - -# Require root for this test -require_root - -# Check RT throttling -if ! check_rt_throttling; then - echo -e "${YELLOW}SKIP: RT throttling must be disabled for this test${NC}" - exit 77 -fi - -# Pick a CPU for testing -TEST_CPU=$(pick_test_cpu) -log "Using CPU ${TEST_CPU} for testing" - -# Pick a different CPU for stalld to run on (avoid interference) -STALLD_CPU=0 -if [ ${TEST_CPU} -eq 0 ]; then - STALLD_CPU=1 -fi -log "Stalld will run on CPU ${STALLD_CPU}" - -# Setup paths -STARVE_GEN="${TEST_ROOT}/helpers/starvation_gen" -STALLD_LOG="/tmp/stalld_test_fifo_prio_$$.log" -CLEANUP_FILES+=("${STALLD_LOG}") +init_functional_test "FIFO-on-FIFO Priority Starvation Detection" "test_fifo_prio" #============================================================================= # Test 1: Basic FIFO-on-FIFO Starvation Detection
tests/functional/test_force_fifo.sh +1 -27
@@ -13,33 +13,7 @@ source "${TEST_ROOT}/helpers/test_helpers.sh" # Parse command-line options parse_test_options "$@" || exit $? -start_test "Force FIFO Option (-F)" - -# Setup test environment -setup_test_environment - -# Require root for this test -require_root - -# Check RT throttling -if ! check_rt_throttling; then - echo -e "${YELLOW}SKIP: RT throttling must be disabled for this test${NC}" - exit 77 -fi - -# Pick a CPU for testing -TEST_CPU=$(pick_test_cpu) -log "Using CPU ${TEST_CPU} for testing" - -# Setup paths -STARVE_GEN="${TEST_ROOT}/helpers/starvation_gen" -STALLD_LOG="/tmp/stalld_test_force_fifo_$$.log" -CLEANUP_FILES+=("${STALLD_LOG}") - -if [ ! -x "${STARVE_GEN}" ]; then - echo -e "${YELLOW}SKIP: starvation_gen not found or not executable${NC}" - exit 77 -fi +init_functional_test "Force FIFO Option (-F)" "test_force_fifo" #============================================================================= # Test 1: Default behavior (should use SCHED_DEADLINE)
tests/functional/test_idle_detection.sh +1 -29
@@ -36,35 +36,7 @@ is_cpu_idle() { fi } -start_test "Idle CPU Detection" - -# Setup test environment -setup_test_environment - -# Require root for this test -require_root - -# Check RT throttling -if ! check_rt_throttling; then - echo -e "${YELLOW}SKIP: RT throttling must be disabled for this test${NC}" - exit 77 -fi - -# Pick a CPU for testing -TEST_CPU=$(pick_test_cpu) -log "Using CPU ${TEST_CPU} for testing" - -# Pick a different CPU for stalld to run on (avoid interference) -STALLD_CPU=0 -if [ ${TEST_CPU} -eq 0 ]; then - STALLD_CPU=1 -fi -log "Stalld will run on CPU ${STALLD_CPU}" - -# Setup paths -STARVE_GEN="${TEST_ROOT}/helpers/starvation_gen" -STALLD_LOG="/tmp/stalld_test_idle_$$.log" -CLEANUP_FILES+=("${STALLD_LOG}") +init_functional_test "Idle CPU Detection" "test_idle" # Check if idle detection is enabled by default log ""
tests/functional/test_pidfile.sh +1 -38
@@ -13,23 +13,7 @@ source "${TEST_ROOT}/helpers/test_helpers.sh" # Parse command-line options parse_test_options "$@" || exit $? -start_test "PID File Option (-P)" - -# Setup test environment -setup_test_environment - -# Require root for this test -require_root - -# Check RT throttling -if ! check_rt_throttling; then - echo -e "${YELLOW}SKIP: RT throttling must be disabled for this test${NC}" - exit 77 -fi - -# Setup paths -STALLD_LOG="/tmp/stalld_test_pidfile_$$.log" -CLEANUP_FILES+=("${STALLD_LOG}") +init_functional_test "PID File Option (-P)" "test_pidfile" #============================================================================= # Test 1: Default pidfile location (no -P specified)
@@ -76,9 +60,6 @@ CLEANUP_FILES+=("${custom_pidfile}") # Ensure pidfile doesn't exist before test rm -f "${custom_pidfile}" -STALLD_LOG2="/tmp/stalld_test_pidfile_test2_$$.log" -CLEANUP_FILES+=("${STALLD_LOG2}") - log "Starting stalld with custom pidfile: ${custom_pidfile}" start_stalld -l -t 5 --pidfile "${custom_pidfile}" sleep 2
@@ -118,9 +99,6 @@ tmp_pidfile="/tmp/stalld_test_tmp_$$.pid" CLEANUP_FILES+=("${tmp_pidfile}") rm -f "${tmp_pidfile}" -STALLD_LOG4="/tmp/stalld_test_pidfile_test4_$$.log" -CLEANUP_FILES+=("${STALLD_LOG4}") - log "Starting stalld with /tmp pidfile: ${tmp_pidfile}" start_stalld -l -t 5 --pidfile "${tmp_pidfile}" sleep 2
@@ -149,9 +127,6 @@ fg_pidfile="/tmp/stalld_test_pidfile_foreground_$$.pid" CLEANUP_FILES+=("${fg_pidfile}") rm -f "${fg_pidfile}" -STALLD_LOG5="/tmp/stalld_test_pidfile_test5_$$.log" -CLEANUP_FILES+=("${STALLD_LOG5}") - log "Starting stalld in foreground mode with pidfile: ${fg_pidfile}" start_stalld -f -v -l -t 5 --pidfile "${fg_pidfile}" sleep 2
@@ -182,12 +157,6 @@ invalid_pidfile="/nonexistent_${$}/stalld.pid" INVALID_LOG="/tmp/stalld_test_pidfile_invalid_$$.log" CLEANUP_FILES+=("${INVALID_LOG}") -# Add backend flag for consistency -BACKEND_FLAG="" -if [ -n "${STALLD_TEST_BACKEND}" ]; then - BACKEND_FLAG="-b ${STALLD_TEST_BACKEND}" -fi - log "Testing invalid pidfile path: ${invalid_pidfile}" timeout 5 ${TEST_ROOT}/../stalld -f -v ${BACKEND_FLAG} -l -t 5 --pidfile "${invalid_pidfile}" > "${INVALID_LOG}" 2>&1 ret=$?
@@ -198,9 +167,6 @@ else fail "stalld did not reject invalid pidfile path" fi -# Cleanup -chmod 755 "${test_dir}" - #============================================================================= # Test 7: Verify pidfile is readable by other processes #=============================================================================
@@ -210,9 +176,6 @@ readable_pidfile="/tmp/stalld_test_pidfile_readable_$$.pid" CLEANUP_FILES+=("${readable_pidfile}") rm -f "${readable_pidfile}" -STALLD_LOG7="/tmp/stalld_test_pidfile_test7_$$.log" -CLEANUP_FILES+=("${STALLD_LOG7}") - log "Starting stalld with readable pidfile: ${readable_pidfile}" start_stalld -l -t 5 --pidfile "${readable_pidfile}" sleep 2
tests/functional/test_starvation_detection.sh +1 -29
@@ -21,39 +21,11 @@ get_starved_pid() { grep "starved on CPU" "$log_file" | tail -1 | sed -E 's/.*\[([0-9]+)\].*/\1/' | head -1 } -start_test "Starvation Detection Logic" - -# Setup test environment -setup_test_environment - -# Require root for this test -require_root - -# Check RT throttling -if ! check_rt_throttling; then - echo -e "${YELLOW}SKIP: RT throttling must be disabled for this test${NC}" - exit 77 -fi - -# Pick a CPU for testing -TEST_CPU=$(pick_test_cpu) -log "Using CPU ${TEST_CPU} for testing" - -# Pick a different CPU for stalld to run on (avoid interference) -STALLD_CPU=0 -if [ ${TEST_CPU} -eq 0 ]; then - STALLD_CPU=1 -fi -log "Stalld will run on CPU ${STALLD_CPU}" +init_functional_test "Starvation Detection Logic" "test_detection" # Get number of CPUs for multi-CPU tests NUM_CPUS=$(get_num_cpus) -# Setup paths -STARVE_GEN="${TEST_ROOT}/helpers/starvation_gen" -STALLD_LOG="/tmp/stalld_test_detection_$$.log" -CLEANUP_FILES+=("${STALLD_LOG}") - #============================================================================= # Test 1: Basic Starvation Detection #=============================================================================
tests/functional/test_starvation_threshold.sh +10 -46
108 lines not on this page · raw message
tests/functional/test_task_merging.sh +1 -31
@@ -14,45 +14,15 @@ source "${TEST_ROOT}/helpers/test_helpers.sh" # Parse command-line options parse_test_options "$@" || exit $? -start_test "Task Merging Logic" - -# Setup test environment -setup_test_environment - -# Require root for this test -require_root - -# Check RT throttling -if ! check_rt_throttling; then - echo -e "${YELLOW}SKIP: RT throttling must be disabled for this test${NC}" - exit 77 -fi - -# Pick a CPU for testing -TEST_CPU=$(pick_test_cpu) -log "Using CPU ${TEST_CPU} for testing" - -# Pick a different CPU for stalld to run on (avoid interference) -STALLD_CPU=0 -if [ ${TEST_CPU} -eq 0 ]; then - STALLD_CPU=1 -fi -log "Stalld will run on CPU ${STALLD_CPU}" +init_functional_test "Task Merging Logic" "test_merge" # Check for DL-server (kernel automatic starvation handling) -# If DL-server is present, the kernel handles starvation automatically, -# so stalld won't detect starvation and we can't test task merging logic if [ -d "/sys/kernel/debug/sched/fair_server" ]; then echo -e "${YELLOW}SKIP: DL-server detected - kernel handles starvation automatically${NC}" echo " Task merging cannot be tested when DL-server prevents starvation" exit 77 fi -# Setup paths -STARVE_GEN="${TEST_ROOT}/helpers/starvation_gen" -STALLD_LOG="/tmp/stalld_test_merge_$$.log" -CLEANUP_FILES+=("${STALLD_LOG}") - #============================================================================= # Test 1: Timestamp Preservation for Non-Progressing Tasks #=============================================================================
tests/helpers/test_helpers.sh +12 -1
@@ -1154,8 +1154,19 @@ init_functional_test() { STALLD_LOG="/tmp/stalld_${log_suffix}_$$.log" CLEANUP_FILES+=("${STALLD_LOG}") + if [ ! -x "${STARVE_GEN}" ]; then + echo -e "${YELLOW}SKIP: starvation_gen not found or not executable${NC}" + exit 77 + fi + + # Build backend flag for direct stalld invocations + BACKEND_FLAG="" + if [ -n "${STALLD_TEST_BACKEND}" ]; then + BACKEND_FLAG="-b ${STALLD_TEST_BACKEND}" + fi + # Export variables for use in test - export TEST_CPU STALLD_CPU STARVE_GEN STALLD_LOG + export TEST_CPU STALLD_CPU STARVE_GEN STALLD_LOG BACKEND_FLAG } # Start starvation_gen in background with readiness detection
Signature · 1 line
2.54.0
PATCH 1/1 02/33] tests: Introduce test_section() helper
In reply to Wander Lairson CostaThe functional test suite contains over a hundred instances of repetitive boilerplate for printing section banners. This clutters the test files and leads to inconsistent logging, with some tests using standard echo while others use the journal-integrated log function.
Introduce a centralized test_section() helper function in the test helpers script to standardize section banner formatting. Update all functional test files to replace the multi-line banner boilerplate with single-line calls to this new helper.
The helper is exported to ensure it remains available within subshells used throughout the test suite. This migration standardizes all section banners to use the journal-integrated log function, improving test traceability while removing nearly 300 lines of redundant code.
--- tests/functional/test_affinity.sh | 40 +++------------ tests/functional/test_backend_selection.sh | 13 ++--- tests/functional/test_boost_duration.sh | 25 ++-------- tests/functional/test_boost_period.sh | 35 +++---------- tests/functional/test_boost_restoration.sh | 30 +++-------- tests/functional/test_boost_runtime.sh | 35 +++---------- tests/functional/test_cpu_selection.sh | 18 +++---- tests/functional/test_deadline_boosting.sh | 30 +++-------- tests/functional/test_fifo_boosting.sh | 30 +++-------- .../test_fifo_priority_starvation.sh | 30 +++-------- tests/functional/test_force_fifo.sh | 30 +++-------- tests/functional/test_foreground.sh | 8 ++- tests/functional/test_idle_detection.sh | 30 +++-------- tests/functional/test_logging_destinations.sh | 11 ++-- tests/functional/test_pidfile.sh | 33 +++--------- tests/functional/test_runqueue_parsing.sh | 50 ++++--------------- tests/functional/test_starvation_detection.sh | 35 +++---------- tests/functional/test_starvation_threshold.sh | 20 ++------ tests/functional/test_task_merging.sh | 25 ++-------- tests/helpers/test_helpers.sh | 11 +++- 20 files changed, 124 insertions(+), 415 deletions(-)
tests/functional/test_affinity.sh +8 -32
@@ -70,10 +70,7 @@ CLEANUP_FILES+=("${STALLD_LOG}") #============================================================================= # Test 1: Default behavior (no -a specified) #============================================================================= -log "" -log "==========================================" -log "Test 1: Default behavior (no affinity restriction)" -log "==========================================" +test_section "Test 1: Default behavior (no affinity restriction)" start_stalld -f -v -l -t 5 sleep 2
@@ -93,10 +90,7 @@ stop_stalld #============================================================================= # Test 2: Single CPU affinity #============================================================================= -log "" -log "==========================================" -log "Test 2: Single CPU affinity (-a 0)" -log "==========================================" +test_section "Test 2: Single CPU affinity (-a 0)" STALLD_LOG2="/tmp/stalld_test_affinity_test2_$$.log" CLEANUP_FILES+=("${STALLD_LOG2}")
@@ -117,10 +111,7 @@ stop_stalld #============================================================================= # Test 3: Multi-CPU affinity (CPU list) #============================================================================= -log "" -log "==========================================" -log "Test 3: Multi-CPU affinity (-a 0,2)" -log "==========================================" +test_section "Test 3: Multi-CPU affinity (-a 0,2)" if [ "$num_cpus" -ge 4 ]; then STALLD_LOG3="/tmp/stalld_test_affinity_test3_$$.log"
@@ -147,10 +138,7 @@ fi #============================================================================= # Test 4: CPU range affinity #============================================================================= -log "" -log "==========================================" -log "Test 4: CPU range affinity (-a 0-2)" -log "==========================================" +test_section "Test 4: CPU range affinity (-a 0-2)" if [ "$num_cpus" -ge 4 ]; then STALLD_LOG4="/tmp/stalld_test_affinity_test4_$$.log"
@@ -176,10 +164,7 @@ fi #============================================================================= # Test 5: Verify stalld actually runs on specified CPU #============================================================================= -log "" -log "==========================================" -log "Test 5: Verify stalld threads run on specified CPU" -log "==========================================" +test_section "Test 5: Verify stalld threads run on specified CPU" if [ "$num_cpus" -ge 2 ]; then test_cpu=1
@@ -212,10 +197,7 @@ fi #============================================================================= # Test 6: Combined with CPU monitoring (-c and -a) #============================================================================= -log "" -log "==========================================" -log "Test 6: Combined affinity and monitoring (-a 0 -c 1)" -log "==========================================" +test_section "Test 6: Combined affinity and monitoring (-a 0 -c 1)" if [ "$num_cpus" -ge 2 ]; then STALLD_LOG6="/tmp/stalld_test_affinity_test6_$$.log"
@@ -248,10 +230,7 @@ fi #============================================================================= # Test 7: Invalid CPU affinity #============================================================================= -log "" -log "==========================================" -log "Test 7: Invalid CPU affinity (-a 999)" -log "==========================================" +test_section "Test 7: Invalid CPU affinity (-a 999)" invalid_cpu=999 INVALID_LOG="/tmp/stalld_test_affinity_invalid_$$.log"
@@ -275,10 +254,7 @@ fi #============================================================================= # Test 8: Verify affinity persists #============================================================================= -log "" -log "==========================================" -log "Test 8: Verify affinity persists over time" -log "==========================================" +test_section "Test 8: Verify affinity persists over time" STALLD_LOG8="/tmp/stalld_test_affinity_test8_$$.log" CLEANUP_FILES+=("${STALLD_LOG8}")
tests/functional/test_backend_selection.sh +5 -8
@@ -57,13 +57,12 @@ test_backend_flag() { } # Test 1: sched_debug backend (full name) -echo "Test 1: Starting stalld with sched_debug backend" +test_section "Test 1: Starting stalld with sched_debug backend" test_backend_flag "sched_debug" "using sched_debug backend" \ "sched_debug backend selected" # Test 2: queue_track backend (if available) -echo "" -echo "Test 2: Check queue_track (BPF) backend" +test_section "Test 2: Check queue_track (BPF) backend" if is_backend_available "queue_track"; then test_backend_flag "queue_track" "using queue_track backend" \ "queue_track backend selected"
@@ -74,15 +73,13 @@ else fi # Test 3: Short name 'S' for sched_debug -echo "" -echo "Test 3: Testing short name 'S' for sched_debug" +test_section "Test 3: Testing short name 'S' for sched_debug" test_backend_flag "S" "using sched_debug backend" \ "Short name 'S' works for sched_debug" # Test 4: STALLD_TEST_BACKEND environment variable -echo "" if [ -n "${STALLD_TEST_BACKEND}" ]; then - echo "Test 4: Testing STALLD_TEST_BACKEND=${STALLD_TEST_BACKEND}" + test_section "Test 4: Testing STALLD_TEST_BACKEND=${STALLD_TEST_BACKEND}" # Normalize short names for expected message BACKEND_NORMALIZED="${STALLD_TEST_BACKEND}" case "${STALLD_TEST_BACKEND}" in
@@ -93,7 +90,7 @@ if [ -n "${STALLD_TEST_BACKEND}" ]; then "using ${BACKEND_NORMALIZED} backend" \ "STALLD_TEST_BACKEND environment variable respected" else - echo "Test 4: Skipping (STALLD_TEST_BACKEND not set)" + test_section "Test 4: Skipping (STALLD_TEST_BACKEND not set)" TEST_PASSED=$((TEST_PASSED + 1)) fi
tests/functional/test_boost_duration.sh +5 -20
@@ -51,10 +51,7 @@ fi #============================================================================= # Test 1: Default duration (should be 3 seconds) #============================================================================= -log "" -log "==========================================" -log "Test 1: Default boost duration (no -d specified)" -log "==========================================" +test_section "Test 1: Default boost duration (no -d specified)" threshold=3 log "Starting stalld with ${threshold}s threshold (default boost duration)"
@@ -80,10 +77,7 @@ stop_stalld #============================================================================= # Test 2: Short duration (1 second) #============================================================================= -log "" -log "==========================================" -log "Test 2: Short boost duration of 1 second" -log "==========================================" +test_section "Test 2: Short boost duration of 1 second" short_duration=1 STALLD_LOG2="/tmp/stalld_test_boost_duration_test2_$$.log"
@@ -111,10 +105,7 @@ stop_stalld #============================================================================= # Test 3: Long duration (10 seconds) #============================================================================= -log "" -log "==========================================" -log "Test 3: Long boost duration of 10 seconds" -log "==========================================" +test_section "Test 3: Long boost duration of 10 seconds" long_duration=10 long_starvation=20
@@ -144,10 +135,7 @@ stop_stalld #============================================================================= # Test 4: Verify task policy is restored after boost duration #============================================================================= -log "" -log "==========================================" -log "Test 4: Verify policy restoration after boost duration" -log "==========================================" +test_section "Test 4: Verify policy restoration after boost duration" threshold=3 duration=2
@@ -176,10 +164,7 @@ stop_stalld #============================================================================= # Test 5: Invalid duration values #============================================================================= -log "" -log "==========================================" -log "Test 5: Invalid duration values" -log "==========================================" +test_section "Test 5: Invalid duration values" # Test with zero duration log "Testing with duration = 0"
tests/functional/test_boost_period.sh +7 -28
@@ -48,10 +48,7 @@ fi #============================================================================= # Test 1: Default period (should be 1,000,000,000 ns = 1 second) #============================================================================= -log "" -log "==========================================" -log "Test 1: Default period (no -p specified)" -log "==========================================" +test_section "Test 1: Default period (no -p specified)" threshold=5 log "Starting stalld with default period"
@@ -84,10 +81,7 @@ stop_stalld #============================================================================= # Test 2: Custom period (500ms = 500,000,000 ns) #============================================================================= -log "" -log "==========================================" -log "Test 2: Custom period of 500,000,000 ns (500ms)" -log "==========================================" +test_section "Test 2: Custom period of 500,000,000 ns (500ms)" custom_period=500000000 rm -f "${STALLD_LOG}"
@@ -113,10 +107,7 @@ stop_stalld #============================================================================= # Test 3: Very short period (100ms = 100,000,000 ns) #============================================================================= -log "" -log "==========================================" -log "Test 3: Short period of 200,000,000 ns (200ms)" -log "==========================================" +test_section "Test 3: Short period of 200,000,000 ns (200ms)" short_period=200000000 rm -f "${STALLD_LOG}"
@@ -142,10 +133,7 @@ stop_stalld #============================================================================= # Test 4: Very long period (10s = 10,000,000,000 ns) #============================================================================= -log "" -log "==========================================" -log "Test 4: Long period of 3,000,000,000 ns (3s)" -log "==========================================" +test_section "Test 4: Long period of 3,000,000,000 ns (3s)" long_period=3000000000 rm -f "${STALLD_LOG}"
@@ -171,10 +159,7 @@ stop_stalld #============================================================================= # Test 5: Invalid period (0) #============================================================================= -log "" -log "==========================================" -log "Test 5: Invalid period value (0)" -log "==========================================" +test_section "Test 5: Invalid period value (0)" INVALID_LOG="/tmp/stalld_test_boost_period_invalid_$$.log" CLEANUP_FILES+=("${INVALID_LOG}")
@@ -197,10 +182,7 @@ fi #============================================================================= # Test 6: Negative period #============================================================================= -log "" -log "==========================================" -log "Test 6: Invalid period value (negative)" -log "==========================================" +test_section "Test 6: Invalid period value (negative)" INVALID_LOG2="/tmp/stalld_test_boost_period_invalid2_$$.log" CLEANUP_FILES+=("${INVALID_LOG2}")
@@ -217,10 +199,7 @@ fi #============================================================================= # Final Summary #============================================================================= -log "" -log "==========================================" -log "Test Summary" -log "==========================================" +test_section "Test Summary" log "Total failures: ${TEST_FAILED}" end_test
tests/functional/test_boost_restoration.sh +6 -24
@@ -50,10 +50,7 @@ CLEANUP_FILES+=("${STALLD_LOG}") #============================================================================= # Test 1: Restore SCHED_FIFO Policy (starvation_gen creates SCHED_FIFO threads) #============================================================================= -log "" -log "==========================================" -log "Test 1: Restore SCHED_FIFO Policy" -log "==========================================" +test_section "Test 1: Restore SCHED_FIFO Policy" threshold=5 boost_duration=3
@@ -159,10 +156,7 @@ stop_stalld #============================================================================= # Test 2: Restore Original RT Policy (SCHED_FIFO) #============================================================================= -log "" -log "==========================================" -log "Test 2: Restore Original SCHED_FIFO Policy" -log "==========================================" +test_section "Test 2: Restore Original SCHED_FIFO Policy" log "Creating a SCHED_FIFO task that gets starved, verify restoration" threshold=5
@@ -273,10 +267,7 @@ stop_stalld #============================================================================= # Test 3: SCHED_OTHER Policy Restoration #============================================================================= -log "" -log "==========================================" -log "Test 3: Restore SCHED_OTHER Policy" -log "==========================================" +test_section "Test 3: Restore SCHED_OTHER Policy" log "Test that SCHED_OTHER tasks are correctly restored after boosting" threshold=5
@@ -304,10 +295,7 @@ stop_stalld #============================================================================= # Test 4: Restoration Timing Verification #============================================================================= -log "" -log "==========================================" -log "Test 4: Restoration Timing Verification" -log "==========================================" +test_section "Test 4: Restoration Timing Verification" threshold=5 boost_duration=4 # 4 second boost
@@ -360,10 +348,7 @@ sleep 1 #============================================================================= # Test 5: Task Exit During Boost #============================================================================= -log "" -log "==========================================" -log "Test 5: Graceful Handling of Task Exit During Boost" -log "==========================================" +test_section "Test 5: Graceful Handling of Task Exit During Boost" threshold=10 boost_duration=5 # Task will exit during boost (after 8s, boost is 5s)
@@ -414,10 +399,7 @@ stop_stalld #============================================================================= # Final Summary #============================================================================= -log "" -log "==========================================" -log "Test Summary" -log "==========================================" +test_section "Test Summary" log "Total failures: ${TEST_FAILED}" end_test
tests/functional/test_boost_runtime.sh +7 -28
@@ -51,10 +51,7 @@ fi #============================================================================= # Test 1: Default runtime (should be 20,000 ns = 20 microseconds) #============================================================================= -log "" -log "==========================================" -log "Test 1: Default runtime (no -r specified)" -log "==========================================" +test_section "Test 1: Default runtime (no -r specified)" threshold=3 log "Starting stalld with ${threshold}s threshold (default boost runtime)"
@@ -80,10 +77,7 @@ stop_stalld #============================================================================= # Test 2: Custom runtime (10,000 ns = 10 microseconds, less than default) #============================================================================= -log "" -log "==========================================" -log "Test 2: Custom runtime of 10,000 ns (10μs)" -log "==========================================" +test_section "Test 2: Custom runtime of 10,000 ns (10μs)" custom_runtime=10000 STALLD_LOG2="/tmp/stalld_test_boost_runtime_test2_$$.log"
@@ -111,10 +105,7 @@ stop_stalld #============================================================================= # Test 3: Larger runtime (100,000 ns = 100 microseconds) #============================================================================= -log "" -log "==========================================" -log "Test 3: Larger runtime of 100,000 ns (100μs)" -log "==========================================" +test_section "Test 3: Larger runtime of 100,000 ns (100μs)" large_runtime=100000 STALLD_LOG3="/tmp/stalld_test_boost_runtime_test3_$$.log"
@@ -142,10 +133,7 @@ stop_stalld #============================================================================= # Test 4: Runtime < period (valid configuration) #============================================================================= -log "" -log "==========================================" -log "Test 4: Runtime < period (valid)" -log "==========================================" +test_section "Test 4: Runtime < period (valid)" # Default period is 1,000,000,000 ns, so runtime of 500,000 ns should be valid valid_runtime=500000
@@ -175,10 +163,7 @@ stop_stalld #============================================================================= # Test 5: Runtime > period (should error or be rejected) #============================================================================= -log "" -log "==========================================" -log "Test 5: Runtime > period (invalid)" -log "==========================================" +test_section "Test 5: Runtime > period (invalid)" invalid_runtime=2000000000 period=1000000000
@@ -204,10 +189,7 @@ fi #============================================================================= # Test 6: Invalid runtime (0) #============================================================================= -log "" -log "==========================================" -log "Test 6: Invalid runtime value (0)" -log "==========================================" +test_section "Test 6: Invalid runtime value (0)" INVALID_LOG2="/tmp/stalld_test_boost_runtime_invalid2_$$.log" CLEANUP_FILES+=("${INVALID_LOG2}")
@@ -225,10 +207,7 @@ fi #============================================================================= # Test 7: Negative runtime #============================================================================= -log "" -log "==========================================" -log "Test 7: Invalid runtime value (negative)" -log "==========================================" +test_section "Test 7: Invalid runtime value (negative)" INVALID_LOG3="/tmp/stalld_test_boost_runtime_invalid3_$$.log" CLEANUP_FILES+=("${INVALID_LOG3}")
tests/functional/test_cpu_selection.sh +6 -12
@@ -35,8 +35,7 @@ fi echo "System has $num_cpus CPUs" # Test 1: Single CPU monitoring -echo "" -echo "Test 1: Single CPU monitoring (-c 0)" +test_section "Test 1: Single CPU monitoring (-c 0)" rm -f "${STALLD_LOG}" start_stalld_with_log "${STALLD_LOG}" -f -v -c 0 -l -t 5
@@ -51,8 +50,7 @@ stop_stalld # Test 2: CPU list (comma-separated) if [ "$num_cpus" -ge 4 ]; then - echo "" - echo "Test 2: CPU list monitoring (-c 0,2)" + test_section "Test 2: CPU list monitoring (-c 0,2)" rm -f "${STALLD_LOG}" start_stalld_with_log "${STALLD_LOG}" -f -v -c 0,2 -l -t 5
@@ -79,8 +77,7 @@ fi # Test 3: CPU range if [ "$num_cpus" -ge 4 ]; then - echo "" - echo "Test 3: CPU range monitoring (-c 0-2)" + test_section "Test 3: CPU range monitoring (-c 0-2)" rm -f "${STALLD_LOG}" start_stalld_with_log "${STALLD_LOG}" -f -v -c 0-2 -l -t 5
@@ -111,8 +108,7 @@ fi # Test 4: Combined format (list and range) if [ "$num_cpus" -ge 6 ]; then - echo "" - echo "Test 4: Combined format (-c 0,2-4)" + test_section "Test 4: Combined format (-c 0,2-4)" rm -f "${STALLD_LOG}" start_stalld_with_log "${STALLD_LOG}" -f -v -c 0,2-4 -l -t 5
@@ -136,8 +132,7 @@ else fi # Test 5: Invalid CPU number (should handle gracefully) -echo "" -echo "Test 5: Invalid CPU number (-c 999)" +test_section "Test 5: Invalid CPU number (-c 999)" invalid_cpu=999 # Create temporary log file for this specific test
@@ -156,8 +151,7 @@ fi # Test 6: Verify non-selected CPUs are NOT monitored if [ "$num_cpus" -ge 2 ]; then - echo "" - echo "Test 6: Verify non-selected CPUs not monitored (-c 0)" + test_section "Test 6: Verify non-selected CPUs not monitored (-c 0)" rm -f "${STALLD_LOG}" start_stalld_with_log "${STALLD_LOG}" -f -v -c 0 -l -t 5
tests/functional/test_deadline_boosting.sh +6 -24
@@ -50,10 +50,7 @@ CLEANUP_FILES+=("${STALLD_LOG}") #============================================================================= # Test 1: Basic DEADLINE Boost Detection #============================================================================= -log "" -log "==========================================" -log "Test 1: Basic DEADLINE Boost Detection" -log "==========================================" +test_section "Test 1: Basic DEADLINE Boost Detection" threshold=5 log "Starting stalld with ${threshold}s threshold (default DEADLINE boosting)"
@@ -98,10 +95,7 @@ stop_stalld #============================================================================= # Test 2: DEADLINE Parameters Verification #============================================================================= -log "" -log "==========================================" -log "Test 2: DEADLINE Parameters Verification" -log "==========================================" +test_section "Test 2: DEADLINE Parameters Verification" threshold=5 # Custom DEADLINE parameters
@@ -163,10 +157,7 @@ stop_stalld #============================================================================= # Test 3: Task Makes Progress During Boost #============================================================================= -log "" -log "==========================================" -log "Test 3: Task Makes Progress During Boost" -log "==========================================" +test_section "Test 3: Task Makes Progress During Boost" threshold=5 boost_duration=5
@@ -234,10 +225,7 @@ stop_stalld #============================================================================= # Test 4: Policy Restoration After Boost #============================================================================= -log "" -log "==========================================" -log "Test 4: Policy Restoration After Boost" -log "==========================================" +test_section "Test 4: Policy Restoration After Boost" threshold=5 boost_duration=3
@@ -315,10 +303,7 @@ stop_stalld #============================================================================= # Test 5: Multiple Simultaneous Boosts #============================================================================= -log "" -log "==========================================" -log "Test 5: Multiple Simultaneous Boosts" -log "==========================================" +test_section "Test 5: Multiple Simultaneous Boosts" if [ ${NUM_CPUS} -lt 2 ]; then log "⚠ SKIP: Need at least 2 CPUs for this test (have ${NUM_CPUS})"
@@ -385,10 +370,7 @@ fi #============================================================================= # Final Summary #============================================================================= -log "" -log "==========================================" -log "Test Summary" -log "==========================================" +test_section "Test Summary" log "Total failures: ${TEST_FAILED}" end_test
tests/functional/test_fifo_boosting.sh +6 -24
@@ -47,10 +47,7 @@ CLEANUP_FILES+=("${STALLD_LOG}") #============================================================================= # Test 1: FIFO Boost with -F Flag #============================================================================= -log "" -log "==========================================" -log "Test 1: FIFO Boost with -F Flag" -log "==========================================" +test_section "Test 1: FIFO Boost with -F Flag" threshold=5 # Create starvation FIRST (before stalld starts)
@@ -88,10 +85,7 @@ stop_stalld #============================================================================= # Test 2: FIFO Priority Verification #============================================================================= -log "" -log "==========================================" -log "Test 2: FIFO Priority Verification" -log "==========================================" +test_section "Test 2: FIFO Priority Verification" threshold=5 rm -f "${STALLD_LOG}"
@@ -144,10 +138,7 @@ stop_stalld #============================================================================= # Test 3: FIFO Emulation Behavior #============================================================================= -log "" -log "==========================================" -log "Test 3: FIFO Emulation Behavior" -log "==========================================" +test_section "Test 3: FIFO Emulation Behavior" log "FIFO emulation cycles: boost→sleep(runtime)→restore→sleep(remainder)" threshold=5
@@ -198,10 +189,7 @@ stop_stalld #============================================================================= # Test 4: FIFO vs DEADLINE Comparison #============================================================================= -log "" -log "==========================================" -log "Test 4: FIFO vs DEADLINE Effectiveness Comparison" -log "==========================================" +test_section "Test 4: FIFO vs DEADLINE Effectiveness Comparison" threshold=5 boost_duration=3
@@ -317,10 +305,7 @@ fi #============================================================================= # Test 5: Single-Threaded Mode Fails with FIFO #============================================================================= -log "" -log "==========================================" -log "Test 5: Single-Threaded Mode with FIFO (Should Fail)" -log "==========================================" +test_section "Test 5: Single-Threaded Mode with FIFO (Should Fail)" log "Attempting to start stalld with -F without -A (single-threaded + FIFO)" STALLD_LOG_FAIL="/tmp/stalld_test_fifo_fail_$$.log"
@@ -342,10 +327,7 @@ fi #============================================================================= # Final Summary #============================================================================= -log "" -log "==========================================" -log "Test Summary" -log "==========================================" +test_section "Test Summary" log "Total failures: ${TEST_FAILED}" end_test
tests/functional/test_fifo_priority_starvation.sh +6 -24
@@ -51,10 +51,7 @@ CLEANUP_FILES+=("${STALLD_LOG}") #============================================================================= # Test 1: Basic FIFO-on-FIFO Starvation Detection #============================================================================= -log "" -log "==========================================" -log "Test 1: Basic FIFO-on-FIFO Starvation Detection" -log "==========================================" +test_section "Test 1: Basic FIFO-on-FIFO Starvation Detection" log "Testing: FIFO:10 blocker starves FIFO:5 blockee" threshold=5
@@ -101,10 +98,7 @@ stop_stalld #============================================================================= # Test 2: Boosting Effectiveness #============================================================================= -log "" -log "==========================================" -log "Test 2: Boosting Allows Progress" -log "==========================================" +test_section "Test 2: Boosting Allows Progress" log "Verify boosting allows FIFO:5 task to make progress despite FIFO:10 blocker" rm -f "${STALLD_LOG}"
@@ -168,10 +162,7 @@ stop_stalld #============================================================================= # Test 3: Starvation Duration Tracking #============================================================================= -log "" -log "==========================================" -log "Test 3: Starvation Duration Tracking" -log "==========================================" +test_section "Test 3: Starvation Duration Tracking" log "Verify duration accumulates correctly (task merging)" rm -f "${STALLD_LOG}"
@@ -232,10 +223,7 @@ stop_stalld #============================================================================= # Test 4: Close Priority Gap #============================================================================= -log "" -log "==========================================" -log "Test 4: Close Priority Gap (FIFO:6 vs FIFO:5)" -log "==========================================" +test_section "Test 4: Close Priority Gap (FIFO:6 vs FIFO:5)" log "Testing edge case with only 1 priority difference" rm -f "${STALLD_LOG}"
@@ -270,10 +258,7 @@ stop_stalld #============================================================================= # Test 5: Correct Task Boosted #============================================================================= -log "" -log "==========================================" -log "Test 5: Verify Correct Task is Boosted" -log "==========================================" +test_section "Test 5: Verify Correct Task is Boosted" log "Ensure stalld boosts the blockee (FIFO:5), not the blocker (FIFO:10)" rm -f "${STALLD_LOG}"
@@ -317,10 +302,7 @@ stop_stalld #============================================================================= # Final Summary #============================================================================= -log "" -log "==========================================" -log "Test Summary" -log "==========================================" +test_section "Test Summary" log "Total failures: ${TEST_FAILED}" if [ -n "${STALLD_TEST_BACKEND}" ] && [ "${STALLD_TEST_BACKEND}" = "queue_track" ]; then
tests/functional/test_force_fifo.sh +6 -24
@@ -44,10 +44,7 @@ fi #============================================================================= # Test 1: Default behavior (should use SCHED_DEADLINE) #============================================================================= -log "" -log "==========================================" -log "Test 1: Default behavior (no -F, should use SCHED_DEADLINE)" -log "==========================================" +test_section "Test 1: Default behavior (no -F, should use SCHED_DEADLINE)" threshold=3 log "Starting stalld with ${threshold}s threshold (default, no -F)"
@@ -82,10 +79,7 @@ stop_stalld #============================================================================= # Test 2: Force FIFO mode (-F) #============================================================================= -log "" -log "==========================================" -log "Test 2: Force FIFO mode (-F)" -log "==========================================" +test_section "Test 2: Force FIFO mode (-F)" # Note: Single-threaded mode only works with SCHED_DEADLINE (dies with FIFO) # So we need to use aggressive mode (-A) when testing FIFO
@@ -123,10 +117,7 @@ stop_stalld #============================================================================= # Test 3: Verify FIFO priority setting #============================================================================= -log "" -log "==========================================" -log "Test 3: Verify FIFO priority is set" -log "==========================================" +test_section "Test 3: Verify FIFO priority is set" STALLD_LOG3="/tmp/stalld_test_force_fifo_test3_$$.log" CLEANUP_FILES+=("${STALLD_LOG3}")
@@ -160,10 +151,7 @@ stop_stalld #============================================================================= # Test 4: Verify FIFO emulation behavior (sleep runtime, restore, sleep remainder) #============================================================================= -log "" -log "==========================================" -log "Test 4: FIFO emulation behavior" -log "==========================================" +test_section "Test 4: FIFO emulation behavior" boost_duration=3 long_starvation=12
@@ -202,10 +190,7 @@ stop_stalld #============================================================================= # Test 5: Single-threaded mode with FIFO (should fail/exit) #============================================================================= -log "" -log "==========================================" -log "Test 5: Single-threaded mode with FIFO (should fail)" -log "==========================================" +test_section "Test 5: Single-threaded mode with FIFO (should fail)" log "Testing single-threaded mode (-O) with -F (should exit)" assert_stalld_rejects "Single-threaded mode rejected FIFO" -f -v -c "${TEST_CPU}" -t ${threshold} -F -O
@@ -213,10 +198,7 @@ assert_stalld_rejects "Single-threaded mode rejected FIFO" -f -v -c "${TEST_CPU} #============================================================================= # Test 6: Compare effectiveness (informational) #============================================================================= -log "" -log "==========================================" -log "Test 6: FIFO vs DEADLINE comparison (informational)" -log "==========================================" +test_section "Test 6: FIFO vs DEADLINE comparison (informational)" comparison_duration=2 comparison_starvation=8
tests/functional/test_foreground.sh +3 -5
@@ -22,7 +22,7 @@ setup_test_environment require_root # Test 1: Without -f flag, stalld should daemonize -echo "Test 1: stalld daemonizes by default" +test_section "Test 1: stalld daemonizes by default" start_stalld -l -t 5 sleep 2
@@ -46,8 +46,7 @@ fi stop_stalld # Test 2: With -f flag, stalld should stay in foreground -echo "" -echo "Test 2: stalld stays in foreground with -f" +test_section "Test 2: stalld stays in foreground with -f" # Start stalld in foreground but in background job start_stalld -f -l -t 5
@@ -70,8 +69,7 @@ fi stop_stalld # Test 3: With -v flag, foreground mode should be implicit -echo "" -echo "Test 3: -v implies foreground mode" +test_section "Test 3: -v implies foreground mode" start_stalld -v -l -t 5 sleep 2
tests/functional/test_idle_detection.sh +6 -24
@@ -75,10 +75,7 @@ log "Reads: /proc/stat for per-CPU idle time" #============================================================================= # Test 1: Idle CPUs Skipped (No Parsing) #============================================================================= -log "" -log "==========================================" -log "Test 1: Idle CPUs Skipped" -log "==========================================" +test_section "Test 1: Idle CPUs Skipped" log "Idle CPUs should be skipped to reduce overhead" threshold=5
@@ -110,10 +107,7 @@ stop_stalld #============================================================================= # Test 2: /proc/stat Parsing #============================================================================= -log "" -log "==========================================" -log "Test 2: /proc/stat Idle Time Parsing" -log "==========================================" +test_section "Test 2: /proc/stat Idle Time Parsing" # Read idle time for test CPU idle_time1=$(get_cpu_idle_time ${TEST_CPU})
@@ -143,10 +137,7 @@ fi #============================================================================= # Test 3: Monitoring Resumes When CPU Becomes Busy #============================================================================= -log "" -log "==========================================" -log "Test 3: Monitoring Resumes for Busy CPUs" -log "==========================================" +test_section "Test 3: Monitoring Resumes for Busy CPUs" threshold=5 rm -f "${STALLD_LOG}"
@@ -181,10 +172,7 @@ stop_stalld #============================================================================= # Test 4: Idle Detection Overhead Reduction #============================================================================= -log "" -log "==========================================" -log "Test 4: Idle Detection Reduces Overhead" -log "==========================================" +test_section "Test 4: Idle Detection Reduces Overhead" log "Comparing overhead with and without idle detection (informational)" # This is informational - we can't easily measure overhead in tests
@@ -203,10 +191,7 @@ log " Function: cpu_had_idle_time() and get_cpu_busy_list()" #============================================================================= # Test 5: Idle Detection with Multiple CPUs #============================================================================= -log "" -log "==========================================" -log "Test 5: Per-CPU Independent Idle Detection" -log "==========================================" +test_section "Test 5: Per-CPU Independent Idle Detection" NUM_CPUS=$(get_num_cpus) if [ ${NUM_CPUS} -lt 2 ]; then
@@ -259,10 +244,7 @@ fi #============================================================================= # Final Summary #============================================================================= -log "" -log "==========================================" -log "Test Summary" -log "==========================================" +test_section "Test Summary" log "Idle detection functions:" log " - cpu_had_idle_time() in stalld.c:226-260" log " - get_cpu_busy_list() in stalld.c:262-308"
tests/functional/test_logging_destinations.sh +4 -7
@@ -28,7 +28,7 @@ has_stalld_log() { } # Test 1: Verbose mode (-v) logs to stdout/stderr -echo "Test 1: Verbose mode (-v) logs to stdout" +test_section "Test 1: Verbose mode (-v) logs to stdout" LOG_FILE="/tmp/stalld_test_verbose_$$.log" CLEANUP_FILES+=("${LOG_FILE}")
@@ -52,8 +52,7 @@ fi stop_stalld # Test 2: Kernel message log (-k) -echo "" -echo "Test 2: Kernel message log (-k)" +test_section "Test 2: Kernel message log (-k)" # Clear dmesg if possible (requires root) if command -v dmesg >/dev/null 2>&1; then
@@ -86,8 +85,7 @@ else fi # Test 3: Syslog (-s, default) -echo "" -echo "Test 3: Syslog (-s, default)" +test_section "Test 3: Syslog (-s, default)" # Check if syslog is available SYSLOG_FILE=""
@@ -144,8 +142,7 @@ else fi # Test 4: Combined logging (-v -k -s) -echo "" -echo "Test 4: Combined logging modes" +test_section "Test 4: Combined logging modes" LOG_FILE="/tmp/stalld_test_combined_$$.log" CLEANUP_FILES+=("${LOG_FILE}")
tests/functional/test_pidfile.sh +7 -26
@@ -34,10 +34,7 @@ CLEANUP_FILES+=("${STALLD_LOG}") #============================================================================= # Test 1: Default pidfile location (no -P specified) #============================================================================= -log "" -log "==========================================" -log "Test 1: Default behavior (no -P specified)" -log "==========================================" +test_section "Test 1: Default behavior (no -P specified)" start_stalld -l -t 5
@@ -71,10 +68,7 @@ stop_stalld #============================================================================= # Test 2: Custom pidfile location #============================================================================= -log "" -log "==========================================" -log "Test 2: Custom pidfile location" -log "==========================================" +test_section "Test 2: Custom pidfile location" custom_pidfile="/tmp/stalld_test_pidfile_custom_$$.pid" CLEANUP_FILES+=("${custom_pidfile}")
@@ -105,8 +99,7 @@ else fi # Test 3: Verify pidfile removed on clean shutdown -log "" -log "Test 3: Verify pidfile removed on clean shutdown" +test_section "Test 3: Verify pidfile removed on clean shutdown" stop_stalld if [ ! -f "${custom_pidfile}" ]; then
@@ -119,10 +112,7 @@ fi #============================================================================= # Test 4: Custom pidfile in /tmp #============================================================================= -log "" -log "==========================================" -log "Test 4: Custom pidfile in /tmp directory" -log "==========================================" +test_section "Test 4: Custom pidfile in /tmp directory" tmp_pidfile="/tmp/stalld_test_tmp_$$.pid" CLEANUP_FILES+=("${tmp_pidfile}")
@@ -153,10 +143,7 @@ stop_stalld #============================================================================= # Test 5: Test with foreground mode #============================================================================= -log "" -log "==========================================" -log "Test 5: Pidfile with foreground mode (-f)" -log "==========================================" +test_section "Test 5: Pidfile with foreground mode (-f)" fg_pidfile="/tmp/stalld_test_pidfile_foreground_$$.pid" CLEANUP_FILES+=("${fg_pidfile}")
@@ -187,10 +174,7 @@ stop_stalld #============================================================================= # Test 6: Invalid pidfile path (permission denied) #============================================================================= -log "" -log "==========================================" -log "Test 6: Invalid pidfile path (permission denied)" -log "==========================================" +test_section "Test 6: Invalid pidfile path (permission denied)" # Use a non-existent parent directory so fopen() fails even as root invalid_pidfile="/nonexistent_${$}/stalld.pid"
@@ -220,10 +204,7 @@ chmod 755 "${test_dir}" #============================================================================= # Test 7: Verify pidfile is readable by other processes #============================================================================= -log "" -log "==========================================" -log "Test 7: Verify pidfile is readable" -log "==========================================" +test_section "Test 7: Verify pidfile is readable" readable_pidfile="/tmp/stalld_test_pidfile_readable_$$.pid" CLEANUP_FILES+=("${readable_pidfile}")
tests/functional/test_runqueue_parsing.sh +10 -40
110 lines not on this page · raw message
tests/functional/test_starvation_detection.sh +7 -28
@@ -57,10 +57,7 @@ CLEANUP_FILES+=("${STALLD_LOG}") #============================================================================= # Test 1: Basic Starvation Detection #============================================================================= -log "" -log "==========================================" -log "Test 1: Basic Starvation Detection" -log "==========================================" +test_section "Test 1: Basic Starvation Detection" threshold=5
@@ -104,10 +101,7 @@ stop_stalld #============================================================================= # Test 2: Context Switch Count Tracking #============================================================================= -log "" -log "==========================================" -log "Test 2: Context Switch Count Tracking" -log "==========================================" +test_section "Test 2: Context Switch Count Tracking" rm -f "${STALLD_LOG}" threshold=5
@@ -161,10 +155,7 @@ stop_stalld #============================================================================= # Test 3: Task Merging (Timestamp Preservation) #============================================================================= -log "" -log "==========================================" -log "Test 3: Task Merging - Timestamp Preservation" -log "==========================================" +test_section "Test 3: Task Merging - Timestamp Preservation" rm -f "${STALLD_LOG}" threshold=3
@@ -224,10 +215,7 @@ wait ${STARVE_PID} 2>/dev/null #============================================================================= # Test 4: Multiple CPUs Detection #============================================================================= -log "" -log "==========================================" -log "Test 4: Multiple CPUs Detection" -log "==========================================" +test_section "Test 4: Multiple CPUs Detection" if [ ${NUM_CPUS} -lt 2 ]; then log "⚠ SKIP: Need at least 2 CPUs for this test (have ${NUM_CPUS})"
@@ -297,10 +285,7 @@ fi #============================================================================= # Test 5: No False Positives (Task Making Progress) #============================================================================= -log "" -log "==========================================" -log "Test 5: No False Positives" -log "==========================================" +test_section "Test 5: No False Positives" rm -f "${STALLD_LOG}" threshold=5
@@ -341,10 +326,7 @@ stop_stalld #============================================================================= # Test 6: Edge Case - Task Exits During Monitoring #============================================================================= -log "" -log "==========================================" -log "Test 6: Task Exits During Monitoring" -log "==========================================" +test_section "Test 6: Task Exits During Monitoring" rm -f "${STALLD_LOG}" threshold=10
@@ -379,10 +361,7 @@ stop_stalld #============================================================================= # Final Summary #============================================================================= -log "" -log "==========================================" -log "Test Summary" -log "==========================================" +test_section "Test Summary" log "Total failures: ${TEST_FAILED}" end_test
tests/functional/test_starvation_threshold.sh +4 -16
@@ -55,10 +55,7 @@ fi #============================================================================= # Test 1: Custom threshold (5 seconds) #============================================================================= -log "" -log "==========================================" -log "Test 1: Custom threshold of 5 seconds" -log "==========================================" +test_section "Test 1: Custom threshold of 5 seconds" threshold=5
@@ -91,10 +88,7 @@ stop_stalld #============================================================================= # Test 2: Verify no detection before threshold #============================================================================= -log "" -log "==========================================" -log "Test 2: No detection before threshold" -log "==========================================" +test_section "Test 2: No detection before threshold" threshold=10 STALLD_LOG2="/tmp/stalld_test_threshold_test2_$$.log"
@@ -135,10 +129,7 @@ stop_stalld #============================================================================= # Test 3: Shorter threshold (3 seconds) #============================================================================= -log "" -log "==========================================" -log "Test 3: Shorter threshold (3 seconds)" -log "==========================================" +test_section "Test 3: Shorter threshold (3 seconds)" threshold=3 STALLD_LOG3="/tmp/stalld_test_threshold_test3_$$.log"
@@ -174,10 +165,7 @@ stop_stalld #============================================================================= # Test 4: Invalid threshold values #============================================================================= -log "" -log "==========================================" -log "Test 4: Invalid threshold values" -log "==========================================" +test_section "Test 4: Invalid threshold values" # Test with zero threshold log "Testing with threshold = 0"
tests/functional/test_task_merging.sh +5 -20
@@ -56,10 +56,7 @@ CLEANUP_FILES+=("${STALLD_LOG}") #============================================================================= # Test 1: Timestamp Preservation for Non-Progressing Tasks #============================================================================= -log "" -log "==========================================" -log "Test 1: Timestamp Preservation Across Cycles" -log "==========================================" +test_section "Test 1: Timestamp Preservation Across Cycles" log "Task merging: same PID + same ctxsw = preserved timestamp" threshold=3
@@ -134,10 +131,7 @@ stop_stalld #============================================================================= # Test 2: Same PID + Same Context Switches = Merged #============================================================================= -log "" -log "==========================================" -log "Test 2: Merge Condition Verification" -log "==========================================" +test_section "Test 2: Merge Condition Verification" log "Merging occurs when: PID matches AND context switches unchanged" threshold=5
@@ -207,10 +201,7 @@ stop_stalld #============================================================================= # Test 3: Task Making Progress (No Merge) #============================================================================= -log "" -log "==========================================" -log "Test 3: No Merge When Task Makes Progress" -log "==========================================" +test_section "Test 3: No Merge When Task Makes Progress" log "When context switches change, timestamp should reset" threshold=5
@@ -262,10 +253,7 @@ stop_stalld #============================================================================= # Test 4: Multiple CPUs with Independent Task Merging #============================================================================= -log "" -log "==========================================" -log "Test 4: Per-CPU Independent Task Merging" -log "==========================================" +test_section "Test 4: Per-CPU Independent Task Merging" NUM_CPUS=$(get_num_cpus) if [ ${NUM_CPUS} -lt 2 ]; then
@@ -345,10 +333,7 @@ fi #============================================================================= # Final Summary #============================================================================= -log "" -log "==========================================" -log "Test Summary" -log "==========================================" +test_section "Test Summary" log "Task merging function: merge_tasks_info() in stalld.c:370-397" log "Merge logic: if (PID == PID && ctxsw == ctxsw) preserve timestamp" log ""
tests/helpers/test_helpers.sh +10 -1
@@ -105,6 +105,15 @@ end_test() { fi } +# Print a section banner for a test sub-section or summary block. +test_section() { + local title="$1" + log "" + log "==========================================" + log "${title}" + log "==========================================" +} + # Record a test pass with a description message. # # Usage: pass "description"
@@ -1129,7 +1138,7 @@ start_starvation_gen() { } # Export functions for use in tests -export -f start_test end_test +export -f start_test end_test test_section export -f pass fail assert_equals assert_contains assert_not_contains export -f assert_file_exists assert_file_not_exists export -f assert_process_running assert_process_not_running
Signature · 1 line
2.54.0
PATCH 1/1 08/33] tests: Introduce assert_stalld_rejects() helper
In reply to Wander Lairson CostaTests that verify stalld rejects invalid arguments repeat the same pattern of invoking stalld under a timeout, capturing the exit code, and comparing it against zero and the timeout exit code. This block appears twelve times across seven test files.
Introduce an assert_stalld_rejects() helper that encapsulates the invocation, exit code check, and temporary log cleanup into a single function call. The helper automatically includes the backend flag set by init_functional_test() so that rejection tests run under the correct backend when the test suite is invoked with an explicit backend selection.
--- tests/functional/test_affinity.sh | 13 +------- tests/functional/test_boost_duration.sh | 25 ++------------ tests/functional/test_boost_period.sh | 23 ++----------- tests/functional/test_boost_runtime.sh | 33 ++----------------- tests/functional/test_cpu_selection.sh | 14 +------- tests/functional/test_pidfile.sh | 17 ++-------- tests/functional/test_starvation_threshold.sh | 25 ++------------ tests/helpers/test_helpers.sh | 21 +++++++++++- 8 files changed, 33 insertions(+), 138 deletions(-)
tests/functional/test_affinity.sh +1 -12
@@ -200,18 +200,7 @@ fi #============================================================================= test_section "Test 7: Invalid CPU affinity (-a 999)" -invalid_cpu=999 -INVALID_LOG="/tmp/stalld_test_affinity_invalid_$$.log" -CLEANUP_FILES+=("${INVALID_LOG}") - -timeout 5 ${TEST_ROOT}/../stalld -f -v ${BACKEND_FLAG} -l -t 5 -a ${invalid_cpu} > "${INVALID_LOG}" 2>&1 -ret=$? - -if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then - pass "Invalid CPU affinity rejected with error" -else - fail "stalld did not reject invalid CPU affinity" -fi +assert_stalld_rejects "Invalid CPU affinity rejected with error" -f -v -l -t 5 -a 999 #============================================================================= # Test 8: Verify affinity persists
tests/functional/test_boost_duration.sh +2 -23
@@ -106,32 +106,11 @@ cleanup_scenario "${STARVE_PID}" #============================================================================= test_section "Test 5: Invalid duration values" -# Test with zero duration log "Testing with duration = 0" -INVALID_LOG="/tmp/stalld_test_boost_duration_invalid_$$.log" -CLEANUP_FILES+=("${INVALID_LOG}") +assert_stalld_rejects "Zero duration rejected with error" -f -v -t ${threshold} -d 0 -timeout 5 ${TEST_ROOT}/../stalld -f -v ${BACKEND_FLAG} -t ${threshold} -d 0 > "${INVALID_LOG}" 2>&1 -ret=$? - -if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then - pass "Zero duration rejected with error" -else - fail "stalld did not reject invalid duration value 0" -fi - -# Test 6: Negative duration log "Testing with duration = -5" -rm -f "${INVALID_LOG}" - -timeout 5 ${TEST_ROOT}/../stalld -f -v ${BACKEND_FLAG} -t ${threshold} -d -5 > "${INVALID_LOG}" 2>&1 -ret=$? - -if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then - pass "Negative duration rejected with error" -else - fail "stalld did not reject invalid negative duration" -fi +assert_stalld_rejects "Negative duration rejected with error" -f -v -t ${threshold} -d -5 log "" log "All boost duration tests completed"
tests/functional/test_boost_period.sh +2 -21
@@ -108,33 +108,14 @@ cleanup_scenario "${STARVE_PID}" #============================================================================= test_section "Test 5: Invalid period value (0)" -INVALID_LOG="/tmp/stalld_test_boost_period_invalid_$$.log" -CLEANUP_FILES+=("${INVALID_LOG}") - -timeout 5 ${TEST_ROOT}/../stalld -f -v ${BACKEND_FLAG} -t $threshold -p 0 > "${INVALID_LOG}" 2>&1 -ret=$? - -if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then - pass "Zero period rejected with error" -else - fail "stalld did not reject invalid period value 0" -fi +assert_stalld_rejects "Zero period rejected with error" -f -v -t $threshold -p 0 #============================================================================= # Test 6: Negative period #============================================================================= test_section "Test 6: Invalid period value (negative)" -rm -f "${INVALID_LOG}" - -timeout 5 ${TEST_ROOT}/../stalld -f -v ${BACKEND_FLAG} -t $threshold -p -1000000 > "${INVALID_LOG}" 2>&1 -ret=$? - -if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then - pass "Negative period rejected with error" -else - fail "stalld did not reject invalid negative period" -fi +assert_stalld_rejects "Negative period rejected with error" -f -v -t $threshold -p -1000000 #============================================================================= # Final Summary
tests/functional/test_boost_runtime.sh +3 -30
@@ -107,52 +107,25 @@ test_section "Test 5: Runtime > period (invalid)" invalid_runtime=2000000000 period=1000000000 -INVALID_LOG="/tmp/stalld_test_boost_runtime_invalid_$$.log" -CLEANUP_FILES+=("${INVALID_LOG}") log "Testing with runtime ${invalid_runtime}ns > period ${period}ns" -timeout 5 ${TEST_ROOT}/../stalld -f -v ${BACKEND_FLAG} -t ${threshold} -r ${invalid_runtime} -p ${period} > "${INVALID_LOG}" 2>&1 -ret=$? - -if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then - pass "Runtime > period rejected with error" -else - fail "stalld did not reject invalid runtime > period" -fi +assert_stalld_rejects "Runtime > period rejected with error" -f -v -t ${threshold} -r ${invalid_runtime} -p ${period} #============================================================================= # Test 6: Invalid runtime (0) #============================================================================= test_section "Test 6: Invalid runtime value (0)" -rm -f "${INVALID_LOG}" - log "Testing with runtime = 0" -timeout 5 ${TEST_ROOT}/../stalld -f -v ${BACKEND_FLAG} -t ${threshold} -r 0 > "${INVALID_LOG}" 2>&1 -ret=$? - -if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then - pass "Zero runtime rejected with error" -else - fail "stalld did not reject invalid runtime value 0" -fi +assert_stalld_rejects "Zero runtime rejected with error" -f -v -t ${threshold} -r 0 #============================================================================= # Test 7: Negative runtime #============================================================================= test_section "Test 7: Invalid runtime value (negative)" -rm -f "${INVALID_LOG}" - log "Testing with runtime = -5000" -timeout 5 ${TEST_ROOT}/../stalld -f -v ${BACKEND_FLAG} -t ${threshold} -r -5000 > "${INVALID_LOG}" 2>&1 -ret=$? - -if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then - pass "Negative runtime rejected with error" -else - fail "stalld did not reject invalid negative runtime" -fi +assert_stalld_rejects "Negative runtime rejected with error" -f -v -t ${threshold} -r -5000 log "" log "All boost runtime tests completed"
tests/functional/test_cpu_selection.sh +1 -13
@@ -135,19 +135,7 @@ fi test_section "Test 5: Invalid CPU number (-c 999)" invalid_cpu=999 -# Create temporary log file for this specific test -INVALID_LOG="/tmp/stalld_invalid_cpu_$$.log" -CLEANUP_FILES+=("${INVALID_LOG}") - -# Run stalld with invalid CPU and capture output -timeout 5 "${TEST_ROOT}/../stalld" -f -v -c $invalid_cpu -l -t 5 > "${INVALID_LOG}" 2>&1 -ret=$? - -if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then - pass "stalld rejected invalid CPU number" -else - fail "stalld did not reject invalid CPU" -fi +assert_stalld_rejects "stalld rejected invalid CPU number" -f -v -c $invalid_cpu -l -t 5 # Test 6: Verify non-selected CPUs are NOT monitored if [ "$num_cpus" -ge 2 ]; then
tests/functional/test_pidfile.sh +2 -15
@@ -151,21 +151,8 @@ stop_stalld #============================================================================= test_section "Test 6: Invalid pidfile path (permission denied)" -# Use a non-existent parent directory so fopen() fails even as root -invalid_pidfile="/nonexistent_${$}/stalld.pid" - -INVALID_LOG="/tmp/stalld_test_pidfile_invalid_$$.log" -CLEANUP_FILES+=("${INVALID_LOG}") - -log "Testing invalid pidfile path: ${invalid_pidfile}" -timeout 5 ${TEST_ROOT}/../stalld -f -v ${BACKEND_FLAG} -l -t 5 --pidfile "${invalid_pidfile}" > "${INVALID_LOG}" 2>&1 -ret=$? - -if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then - pass "Invalid pidfile path rejected with error" -else - fail "stalld did not reject invalid pidfile path" -fi +log "Testing invalid pidfile path" +assert_stalld_rejects "Invalid pidfile path rejected with error" -f -v -l -t 5 --pidfile "/nonexistent_$$/stalld.pid" #============================================================================= # Test 7: Verify pidfile is readable by other processes
tests/functional/test_starvation_threshold.sh +2 -23
@@ -113,32 +113,11 @@ cleanup_scenario "${STARVE_PID}" #============================================================================= test_section "Test 4: Invalid threshold values" -# Test with zero threshold log "Testing with threshold = 0" -INVALID_LOG="/tmp/stalld_test_threshold_invalid_$$.log" -CLEANUP_FILES+=("${INVALID_LOG}") +assert_stalld_rejects "Zero threshold rejected with error" -f -v -t 0 -timeout 5 ${TEST_ROOT}/../stalld -f -v -t 0 > "${INVALID_LOG}" 2>&1 -ret=$? - -if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then - pass "Zero threshold rejected with error" -else - fail "stalld did not reject invalid threshold value 0" -fi - -# Test with negative threshold log "Testing with threshold = -5" -rm -f "${INVALID_LOG}" - -timeout 5 ${TEST_ROOT}/../stalld -f -v -t -5 > "${INVALID_LOG}" 2>&1 -ret=$? - -if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then - pass "Negative threshold rejected with error" -else - fail "stalld did not reject invalid negative threshold" -fi +assert_stalld_rejects "Negative threshold rejected with error" -f -v -t -5 log "" log "All starvation threshold tests completed"
tests/helpers/test_helpers.sh +20 -1
@@ -191,6 +191,25 @@ assert_boost_detected() { fi } +# Assert that stalld rejects invalid arguments and exits non-zero. +# Usage: assert_stalld_rejects <message> [stalld_args...] +assert_stalld_rejects() { + local message=$1 + shift + + local log="/tmp/stalld_reject_$$.log" + timeout 5 ${TEST_ROOT}/../stalld ${BACKEND_FLAG} "$@" > "${log}" 2>&1 + local ret=$? + if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then + pass "${message}" + else + fail "${message}" + log "stalld output:" + cat "${log}" + fi + rm -f "${log}" +} + # Record a test pass with a description message. # # Usage: pass "description"
@@ -1226,7 +1245,7 @@ start_starvation_gen() { } # Export functions for use in tests -export -f start_test end_test test_section cleanup_scenario find_starved_child assert_starvation_detected assert_boost_detected +export -f start_test end_test test_section cleanup_scenario find_starved_child assert_starvation_detected assert_boost_detected assert_stalld_rejects export -f pass fail assert_equals assert_contains assert_not_contains export -f assert_file_exists assert_file_not_exists export -f assert_process_running assert_process_not_running
Signature · 1 line
2.54.0
PATCH 1/1 09/33] tests: Fix boost verification in runtime and duration tests
In reply to Wander Lairson CostaThe boost runtime and duration tests were previously passing the log-only flag (-l) to stalld. This caused the daemon to detect starvation but never actually boost tasks. Furthermore, the tests used assert_starvation_detected, which only verified that starvation was noticed rather than confirming a successful boost. As a result, the tests passed trivially without validating the -r and -d options.
Remove the log-only flag so stalld operates in normal mode and switch the assertions to assert_boost_detected.
--- tests/functional/test_boost_duration.sh | 24 ++++++++++++------------ tests/functional/test_boost_runtime.sh | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-)
tests/functional/test_boost_duration.sh +12 -12
@@ -22,15 +22,15 @@ test_section "Test 1: Default boost duration (no -d specified)" threshold=3 log "Starting stalld with ${threshold}s threshold (default boost duration)" -start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -l +start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} # Create starvation starvation_duration=15 log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} -# Wait for starvation detection -assert_starvation_detected "${STALLD_LOG}" "Starvation detection occurred with default duration" +# Wait for actual boosting +assert_boost_detected "${STALLD_LOG}" "Boost with default duration" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -44,14 +44,14 @@ short_duration=1 rm -f "${STALLD_LOG}" log "Starting stalld with ${threshold}s threshold and ${short_duration}s boost duration" -start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -d ${short_duration} -l +start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -d ${short_duration} # Create starvation log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} -# Wait for starvation detection -assert_starvation_detected "${STALLD_LOG}" "Starvation detection with ${short_duration}s duration" +# Wait for actual boosting +assert_boost_detected "${STALLD_LOG}" "Boost with ${short_duration}s duration" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -67,14 +67,14 @@ threshold=10 rm -f "${STALLD_LOG}" log "Starting stalld with ${threshold}s threshold and ${long_duration}s boost duration" -start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -d ${long_duration} -l +start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -d ${long_duration} # Create starvation log "Creating starvation on CPU ${TEST_CPU} for ${long_starvation}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${long_starvation} -# Wait for starvation detection -assert_starvation_detected "${STALLD_LOG}" "Starvation detection with ${long_duration}s duration" +# Wait for actual boosting +assert_boost_detected "${STALLD_LOG}" "Boost with ${long_duration}s duration" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -89,14 +89,14 @@ duration=2 rm -f "${STALLD_LOG}" log "Starting stalld with ${threshold}s threshold and ${duration}s boost duration" -start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -d ${duration} -l +start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -d ${duration} # Create starvation with a specific task we can track log "Creating starvation on CPU ${TEST_CPU} for 15s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 1 -d 15 -# Wait for starvation detection -assert_starvation_detected "${STALLD_LOG}" "Starvation detection with ${duration}s boost duration" +# Wait for actual boosting +assert_boost_detected "${STALLD_LOG}" "Boost with ${duration}s boost duration" # Cleanup cleanup_scenario "${STARVE_PID}"
tests/functional/test_boost_runtime.sh +12 -12
@@ -22,15 +22,15 @@ test_section "Test 1: Default runtime (no -r specified)" threshold=3 log "Starting stalld with ${threshold}s threshold (default boost runtime)" -start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -l +start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} # Create starvation starvation_duration=10 log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} -# Wait for detection and boosting -assert_starvation_detected "${STALLD_LOG}" "Starvation detection with default runtime" +# Wait for actual boosting +assert_boost_detected "${STALLD_LOG}" "Boost with default runtime" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -44,14 +44,14 @@ custom_runtime=10000 rm -f "${STALLD_LOG}" log "Starting stalld with ${threshold}s threshold and ${custom_runtime}ns runtime" -start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -r ${custom_runtime} -l +start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -r ${custom_runtime} # Create starvation log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} -# Wait for detection and boosting -assert_starvation_detected "${STALLD_LOG}" "Starvation detection with custom runtime ${custom_runtime}ns" +# Wait for actual boosting +assert_boost_detected "${STALLD_LOG}" "Boost with custom runtime ${custom_runtime}ns" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -65,14 +65,14 @@ large_runtime=100000 rm -f "${STALLD_LOG}" log "Starting stalld with ${threshold}s threshold and ${large_runtime}ns runtime" -start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -r ${large_runtime} -l +start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -r ${large_runtime} # Create starvation log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} -# Wait for detection and boosting -assert_starvation_detected "${STALLD_LOG}" "Starvation detection with large runtime ${large_runtime}ns" +# Wait for actual boosting +assert_boost_detected "${STALLD_LOG}" "Boost with large runtime ${large_runtime}ns" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -88,14 +88,14 @@ period=1000000000 rm -f "${STALLD_LOG}" log "Starting stalld with runtime ${valid_runtime}ns < period ${period}ns" -start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -r ${valid_runtime} -p ${period} -l +start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -r ${valid_runtime} -p ${period} # Create starvation log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} -# Wait for detection and boosting -assert_starvation_detected "${STALLD_LOG}" "Starvation detection with runtime < period" +# Wait for actual boosting +assert_boost_detected "${STALLD_LOG}" "Boost with runtime ${valid_runtime}ns < period ${period}ns" # Cleanup cleanup_scenario "${STARVE_PID}"
Signature · 1 line
2.54.0
PATCH 1/1 10/33] tests: Fix subshell swallowing test results
In reply to Wander Lairson CostaTest 5 in the starvation detection suite runs its assertions inside a backgrounded subshell. The pass and fail functions increment the TEST_PASSED and TEST_FAILED counters, but since those variables are copied into the subshell, the increments are discarded when the child exits. The test result is silently lost regardless of outcome.
Remove the subshell wrapper and run the test logic directly in the main shell. Also replace the else branch, which logged informational messages but never called pass or fail, with an actual check that greps for the busy task PID in the starvation log to distinguish false positives from unrelated starvation events.
--- tests/functional/test_starvation_detection.sh | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-)
tests/functional/test_starvation_detection.sh +15 -20
@@ -241,31 +241,26 @@ start_stalld_with_log "${STALLD_LOG}" -f -v -l -t $threshold -c ${TEST_CPU} -a $ # Create a task that gets CPU time but isn't starved # Use a SCHED_OTHER task with lower priority that still gets scheduled log "Creating a busy task that should NOT be starved" -( - taskset -c ${TEST_CPU} bash -c 'for i in {1..100}; do sleep 0.1; done' & - BUSY_PID=$! +taskset -c ${TEST_CPU} bash -c 'for i in {1..100}; do sleep 0.1; done' & +BUSY_PID=$! - # Wait beyond threshold - sleep $((threshold + 3)) +# Wait beyond threshold +sleep $((threshold + 3)) - # Verify this task was NOT reported as starved - # Since it's making progress, stalld shouldn't detect it - if ! grep "starved" "${STALLD_LOG}"; then - pass "No false positive - task making progress not reported as starved" +# Verify this task was NOT reported as starved +# Since it's making progress, stalld shouldn't detect it +if ! grep "starved" "${STALLD_LOG}"; then + pass "No false positive - task making progress not reported as starved" +else + if grep "${BUSY_PID}" "${STALLD_LOG}" | grep -q "starved"; then + fail "False positive - progress-making task ${BUSY_PID} reported as starved" else - # Check if our specific task was reported - log "Log shows starvation, checking if it's our progress-making task..." - log "Log contents:" - cat "${STALLD_LOG}" - # This is not necessarily a failure - there might be other starved tasks - log "⚠ INFO: Starvation detected, but may be from other tasks" + pass "No false positive - starvation detected from other tasks, not ours" fi +fi - kill ${BUSY_PID} 2>/dev/null - wait ${BUSY_PID} 2>/dev/null -) & -SUBSHELL_PID=$! -wait ${SUBSHELL_PID} +kill ${BUSY_PID} 2>/dev/null +wait ${BUSY_PID} 2>/dev/null stop_stalld
Signature · 1 line
2.54.0
PATCH 1/1 11/33] tests: Fix repeated log match finding same line
In reply to Wander Lairson CostaTest 3 in the FIFO priority starvation suite calls wait_for_starvation_detected three times on the same log file to wait for three detection cycles. However, the underlying wait_for_log_message helper uses tail -f -n +1 which reads from the beginning of the file on every invocation, so all three calls instantly match the same first line.
Introduce a wait_for_n_log_matches helper that polls grep -c until the match count reaches the requested threshold, and use it to replace the repeated calls.
--- .../test_fifo_priority_starvation.sh | 10 +++------ tests/helpers/test_helpers.sh | 21 ++++++++++++++++++- 2 files changed, 23 insertions(+), 8 deletions(-)
tests/functional/test_fifo_priority_starvation.sh +3 -7
@@ -139,13 +139,9 @@ start_stalld_with_log "${STALLD_LOG}" -f -v -l -t $threshold -c ${TEST_CPU} -a $ # Wait for multiple detection cycles log "Waiting for first detection cycle..." wait_for_starvation_detected "${STALLD_LOG}" -log "First detection cycle should have occurred" -log "Waiting for second detection cycle..." -wait_for_starvation_detected "${STALLD_LOG}" -log "Second detection cycle should have occurred" -log "Waiting for third detection cycle..." -wait_for_starvation_detected "${STALLD_LOG}" -log "Third detection cycle should have occurred" +log "First detection cycle occurred, waiting for additional cycles..." +wait_for_n_log_matches "starved on CPU" 3 "${STALLD_LOG}" +log "Multiple detection cycles should have occurred" # Check if we see accumulating starvation time in logs # Task merging means the timestamp is preserved, so duration increases
tests/helpers/test_helpers.sh +20 -1
@@ -668,6 +668,25 @@ wait_for_boost_detected() { wait_for_log_message "boosted pid" "${timeout}" "${log_file}" } +# Wait until a log file contains at least N matches of a pattern. +# +# Usage: wait_for_n_log_matches <pattern> <count> <log_file> [timeout] +wait_for_n_log_matches() { + local pattern=$1 + local count=$2 + local log_file=$3 + local timeout=${4:-30} + local end=$((SECONDS + timeout)) + + while [ $SECONDS -lt $end ]; do + local matches + matches=$(grep -c "${pattern}" "${log_file}" 2>/dev/null || true) + [ "${matches:-0}" -ge "${count}" ] && return 0 + sleep 1 + done + return 1 +} + # Get thread scheduling policy get_thread_policy() { local pid=$1
@@ -1250,7 +1269,7 @@ export -f pass fail assert_equals assert_contains assert_not_contains export -f assert_file_exists assert_file_not_exists export -f assert_process_running assert_process_not_running export -f start_stalld stop_stalld kill_existing_stalld cleanup -export -f wait_for_log_message wait_for_stalld_ready wait_for_starvation_detected wait_for_boost_detected +export -f wait_for_log_message wait_for_stalld_ready wait_for_starvation_detected wait_for_boost_detected wait_for_n_log_matches export -f get_thread_policy get_thread_priority export -f create_cpu_load export -f detect_default_backend is_backend_available get_available_backends start_stalld_with_backend
Signature · 1 line
2.54.0
PATCH 1/1 12/33] chore: Remove legacy test infrastructure and stale docs
In reply to Wander Lairson CostaThe legacy test01 wrapper had an undefined STALLD_LOG variable that caused its starvation detection and boost verification checks to silently fail. Rather than fixing the wrapper, remove the entire legacy directory since all its test scenarios are already covered by the modern functional test suite.
Also remove the unit test infrastructure which has been unused since the test suite was created. For a daemon that interacts exclusively with kernel interfaces, unit testing would require impractical mocking while the functional tests validate behavior against real kernels.
As we are deleting out of date files, delete files that were missed in the earlier AI tooling removal commit: the .claude directory with agent definitions, project rules, and context snapshots, along with obsolete test documentation files
that are no longer maintained.
--- .claude/CLAUDE.md | 585 ----------- .claude/agents/agent-prompt-engineer.md | 135 --- .claude/agents/c-expert.md | 53 - .claude/agents/code-reviewer.md | 104 -- .claude/agents/get-agent-hash | 99 -- .claude/agents/git-scm-master.md | 1154 ---------------------- .claude/agents/kernel-hacker.md | 231 ----- .claude/agents/plan-validator.md | 130 --- .claude/agents/project-historian.md | 285 ------ .claude/agents/project-librarian.md | 604 ----------- .claude/agents/project-manager.md | 388 -------- .claude/agents/project-scope-guardian.md | 326 ------ .claude/agents/python-expert.md | 57 -- .claude/agents/test-specialist.md | 656 ------------ .claude/agents/update-agent-hashes | 96 -- .claude/context-snapshot.json | 103 -- .claude/rules | 42 - .gitignore | 5 +- tests/BACKEND_USAGE.md | 269 ----- tests/CONTEXT_SNAPSHOT_2025-10-31.md | 231 ----- tests/Makefile | 29 +- tests/README.md | 29 +- tests/TODO.md | 727 -------------- tests/legacy/README.md | 169 ---- tests/legacy/test01.c | 506 ---------- tests/legacy/test01_wrapper.sh | 161 --- tests/run_tests.sh | 143 +-- 27 files changed, 12 insertions(+), 7305 deletions(-) delete mode 100644 .claude/CLAUDE.md delete mode 100644 .claude/agents/agent-prompt-engineer.md delete mode 100644 .claude/agents/c-expert.md delete mode 100644 .claude/agents/code-reviewer.md delete mode 100755 .claude/agents/get-agent-hash delete mode 100644 .claude/agents/git-scm-master.md delete mode 100644 .claude/agents/kernel-hacker.md delete mode 100644 .claude/agents/plan-validator.md delete mode 100644 .claude/agents/project-historian.md delete mode 100644 .claude/agents/project-librarian.md delete mode 100644 .claude/agents/project-manager.md delete mode 100644 .claude/agents/project-scope-guardian.md delete mode 100644 .claude/agents/python-expert.md delete mode 100644 .claude/agents/test-specialist.md delete mode 100755 .claude/agents/update-agent-hashes delete mode 100644 .claude/context-snapshot.json delete mode 100644 .claude/rules delete mode 100644 tests/BACKEND_USAGE.md delete mode 100644 tests/CONTEXT_SNAPSHOT_2025-10-31.md delete mode 100644 tests/TODO.md delete mode 100644 tests/legacy/README.md delete mode 100644 tests/legacy/test01.c delete mode 100755 tests/legacy/test01_wrapper.sh
.claude/CLAUDE.md deleted +0 -585
@@ -1,585 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Important: Read These Files First - -At the start of every session, **ALWAYS read these files in order**: - -1. **`.claude/rules`** - Critical project-specific rules including: - - Which agents to use for specific tasks (git-scm-master, c-expert, test-specialist, plan-validator) - - Workflow requirements and best practices - - Project conventions and standards - -2. **`.claude/context-snapshot.json`** - Session context and progress tracking: - - Recent work and completed tasks - - Current project state and test coverage - - Implementation details and next steps - - Notes from previous sessions - -## Overview - -`stalld` is a starvation detection and avoidance daemon for Linux -systems. It monitors CPU run queues for threads that are starving -(ready to run but not getting CPU time), and temporarily boosts their -priority using SCHED_DEADLINE (or SCHED_FIFO as fallback) to allow -them to make progress. This prevents indefinite starvation when -high-priority RT tasks monopolize CPUs, at the cost of small latencies -for the application monopolizing the CPU. - -**Primary use case**: DPDK deployments with isolated CPUs running single busy-loop RT tasks, where kernel threads can starve. - -**Not recommended for**: Safety-critical systems (see README.md). - -## Repository Structure - -``` -stalld/ -├── src/ # Main source code (5 C files) -│ ├── stalld.c # Main daemon logic (1,218 LOC), entry point, boosting -│ ├── sched_debug.c # debugfs/procfs backend for parsing /sys/.../debug or /proc sched_debug -│ ├── queue_track.c # eBPF backend for BPF-based task tracking -│ ├── utils.c # Utilities: logging, CPU parsing, argument parsing -│ ├── throttling.c # RT throttling detection and control -│ └── *.h # Headers (stalld.h, sched_debug.h, queue_track.h) -├── bpf/ # eBPF code -│ └── stalld.bpf.c # BPF tracepoint programs for task tracking -├── tests/ # Comprehensive test suite -│ ├── run_tests.sh # Main test runner (auto-discovery, color output) -│ ├── test01.c # Original starvation test (fixed) -│ ├── helpers/ -│ │ ├── test_helpers.sh # Helper library (20+ functions) -│ │ └── starvation_gen.c # Configurable starvation generator -│ ├── functional/ # Functional tests (shell scripts) -│ │ ├── test_foreground.sh -│ │ ├── test_log_only.sh -│ │ └── test_logging_destinations.sh -│ ├── unit/ # Unit tests (C programs) -│ ├── integration/ # Integration tests (shell scripts) -│ ├── fixtures/ # Test data and configurations -│ ├── results/ # Test output logs (gitignored) -│ └── README.md # Test documentation -├── systemd/ # systemd integration (service file, config) -├── man/ # Man page (stalld.8) -├── scripts/ # Helper scripts (throttlectl.sh) -└── Makefile # Build system with arch/kernel detection -``` - -## Source File Guide - -### Core Implementation Files - -**src/stalld.c** (1,218 LOC) - Main daemon -- Entry point: `main()` at line 1121 -- Boosting logic: `boost_with_deadline()`, `boost_with_fifo()` (lines 438-563) -- Threading modes: `single_threaded_main()`, `conservative_main()`, `aggressive_main()` -- Task merging: `merge_tasks_info()` preserves starvation timestamps (lines 370-397) - -**src/utils.c** - Utilities -- Command-line parsing: `parse_args()` -- CPU list parsing and affinity setting -- Logging infrastructure (syslog, kmsg, verbose) -- sched_debug path detection: `find_sched_debug_path()` -- Buffer resizing and memory allocation - -**src/sched_debug.c** - debugfs/procfs backend -- Parses `/sys/kernel/debug/sched/debug` (debugfs) or `/proc/sched_debug` (procfs, older kernels) -- Auto-detects kernel format (3.x, 4.18+, 6.12+) -- Implements `sched_debug_backend` interface defined in stalld.h -- Fallback when eBPF unavailable (i686, powerpc, ppc64le, legacy kernels) - -**src/queue_track.c** - eBPF backend -- Loads and manages BPF programs via skeleton (`stalld.skel.h`) -- Implements `queue_track_backend` interface defined in stalld.h -- Reads task data from BPF maps populated by kernel-side programs -- Default on x86_64, aarch64, s390x with modern kernels - -**src/throttling.c** - RT throttling management -- Checks if RT throttling is disabled (`sched_rt_runtime_us == -1`) -- Disables throttling when needed -- Dies if throttling is enabled (unless running under systemd) - -### eBPF Components - -**bpf/stalld.bpf.c** - Kernel-side BPF programs -- Tracepoints: `sched_wakeup`, `sched_switch`, `sched_migrate_task`, `sched_process_exit` -- Maintains per-CPU task queues in BPF maps -- Tracks task state changes in real-time without polling -- Generated files: `bpf/vmlinux.h` (kernel BTF), `src/stalld.skel.h` (userspace skeleton) - -## Program Flow - -### Startup Sequence (src/stalld.c:main) - -1. Parse command-line arguments (`parse_args()`) -2. Set CPU affinity if configured (`-a` option) -3. Check for DL-server presence (newer kernels have built-in starvation handling) -4. **Verify RT throttling is disabled** (die if enabled, unless systemd manages it) -5. **Detect boost method**: Try SCHED_DEADLINE first, fall back to SCHED_FIFO if unavailable -6. Initialize backend: `queue_track_backend` (eBPF) or `sched_debug_backend` (debugfs/procfs) -7. Allocate per-CPU info structures -8. Setup signal handling -9. Daemonize (unless `-f/--foreground`) -10. Enter main monitoring loop (single/adaptive/aggressive mode) - -### Monitoring Loop (per-CPU or global depending on mode) - -1. **Idle detection** (if enabled): Check `/proc/stat` for idle CPUs, skip if idle -2. **Get task info**: Call backend's `get()` or `get_cpu()` to read task data -3. **Parse tasks**: Call backend's `parse()` to populate `cpu_info` structures -4. **Merge tasks**: Preserve starvation timestamps for tasks making no progress (same context switch count) -5. **Check for starvation**: Identify tasks on runqueue for ≥`starving_threshold` with no context switches -6. **Apply denylist**: Skip tasks matching ignore patterns (`-i` option) -7. **Boost starving tasks**: Apply SCHED_DEADLINE (or FIFO) for `boost_duration` seconds -8. **Restore policy**: Return task to original scheduling policy -9. **Sleep**: Wait `granularity` seconds before next check cycle - -### Threading Modes - -- **Power/Single-threaded** (`-O/--power_mode`): One thread calls `boost_cpu_starving_vector()` to boost all CPUs at once, lower CPU usage, only works with SCHED_DEADLINE (not FIFO) -- **Adaptive** (`-M/--adaptive_mode`, default): Spawns per-CPU threads when tasks approach ½ starvation threshold, threads exit after 10 idle cycles -- **Aggressive** (`-A/--aggressive_mode`): Per-CPU threads from startup, never exit, continuous monitoring, highest precision - -## Command Line Interface - -### Key Options (see man/stalld.8 for complete list) - -**Monitoring:** -- `-c/--cpu <list>`: CPUs to monitor (default: all) -- `-t/--starving_threshold <sec>`: Starvation threshold in seconds (default: 60s) - -**Boosting:** -- `-p/--boost_period <ns>`: SCHED_DEADLINE period (default: 1,000,000,000 ns = 1s) -- `-r/--boost_runtime <ns>`: SCHED_DEADLINE runtime (default: 20,000 ns = 20μs) -- `-d/--boost_duration <sec>`: Boost duration (default: 3s) -- `-F/--force_fifo`: Force SCHED_FIFO instead of SCHED_DEADLINE - -**Threading:** -- `-O/--power_mode`: Power/single-threaded mode (only works with SCHED_DEADLINE) -- `-M/--adaptive_mode`: Adaptive mode (default) -- `-A/--aggressive_mode`: Aggressive mode (per-CPU threads) - -**Filtering:** -- `-i <regex>`: Ignore thread names matching regex (comma-separated) -- `-I <regex>`: Ignore process names matching regex - -**Logging:** -- `-v/--verbose`: Print to stdout -- `-k/--log_kmsg`: Log to kernel buffer (dmesg) -- `-s/--log_syslog`: Log to syslog (default: true) -- `-l/--log_only`: Only log, don't boost (testing mode) - -**Backend:** -- `-b/--backend <name>`: Select backend (sched_debug, queue_track, S, Q) - -**Daemon:** -- `-f/--foreground`: Run in foreground (don't daemonize) -- `-P/--pidfile <path>`: Write PID file -- `-a/--affinity <cpu-list>`: Set stalld affinity to specific CPUs - -Entry point: `main()` in `src/stalld.c:1121` -Argument parsing: `parse_args()` in `src/utils.c` - -## Build Commands - -### Standard Build -```bash -make # Build stalld and tests -make stalld # Build only stalld executable -make static # Build statically linked stalld-static -make tests # Build tests only -``` - -### Architecture-Specific Notes -- The build system auto-detects architecture and kernel version -- eBPF support: Disabled on i686, powerpc, ppc64le, and kernels ≤3.x -- On legacy kernels (≤3.x), build uses `LEGACY=1` and disables BPF - -### Clean and Install -```bash -make clean # Clean all build artifacts -make install # Install to system directories -make uninstall # Remove installed files -``` - -### Development -```bash -make DEBUG=1 # Build with debug symbols (-g3) -make annocheck # Run security analysis on stalld executable -``` - -## Testing - -### Automated Test Suite - -The `tests/` directory contains a comprehensive test suite with automated test runner, helper library, and multiple test categories. - -```bash -# Run all tests -make test -cd tests && ./run_tests.sh - -# Run specific test categories -make test-unit # Unit tests only -make test-functional # Functional tests only -make test-integration # Integration tests only - -# Run individual tests -cd tests && ./run_tests.sh --functional-only -cd tests && functional/test_foreground.sh - -# Matrix testing (test multiple backends/modes) -cd tests && ./run_tests.sh # Default: backend matrix (2× runtime) -cd tests && ./run_tests.sh --full-matrix # Full matrix: backends × modes (6× runtime) -cd tests && ./run_tests.sh --backend-only # Backends only, adaptive mode (2× runtime) -cd tests && ./run_tests.sh --quick # Quick: sched_debug + adaptive (1× runtime) - -# Run tests with specific backend/mode -cd tests && ./run_tests.sh --backend sched_debug # Use debugfs/procfs backend -cd tests && ./run_tests.sh -m power # Use power/single-threaded mode -cd tests && functional/test_log_only.sh -b queue_track -m aggressive # Specific test -``` - -**Test Infrastructure:** -- **run_tests.sh** (~785 lines): Main test orchestrator with auto-discovery, color output, matrix testing (backend × threading mode), per-backend/mode statistics -- **helpers/test_helpers.sh** (~706 lines): Helper library with 20+ functions for assertions, stalld management, backend/mode selection via `parse_test_options()` -- **helpers/starvation_gen.c** (~290 lines): Configurable starvation generator for controlled testing - - Creates SCHED_FIFO blocker at specified priority (default 10, `-p` flag) - - Creates SCHED_FIFO blockees at specified priority (default 1, `-b` flag) - - Enables testing both standard starvation and FIFO-on-FIFO priority starvation scenarios -- **Test organization**: `unit/`, `functional/`, `integration/`, `fixtures/`, `results/` -- **Matrix testing**: Default tests 2 backends (sched_debug, queue_track), optional 3 threading modes (power, adaptive, aggressive) -- **Skip logic**: Power mode automatically skips FIFO tests (incompatible with single-threaded) - -**Backend Selection in Tests:** - -Both the test runner and individual test scripts support runtime backend selection: - -```bash -# Run all tests with specific backend -./run_tests.sh --backend sched_debug # Use debugfs/procfs backend -./run_tests.sh --backend queue_track # Use eBPF backend - -# Run individual test with specific backend -./functional/test_log_only.sh -b sched_debug -./functional/test_log_only.sh -b S # Short form for sched_debug -./functional/test_log_only.sh -b Q # Short form for queue_track - -# Show test-specific help -./functional/test_log_only.sh -h -``` - -Supported backends: -- `sched_debug` or `S`: debugfs/procfs backend (parses /sys/kernel/debug/sched/debug or /proc/sched_debug) -- `queue_track` or `Q`: eBPF backend (uses BPF tracepoints) - -Supported threading modes: -- `power`: Power/single-threaded mode (`-O` flag) - only works with SCHED_DEADLINE -- `adaptive`: Adaptive/conservative mode (`-M` flag) - default -- `aggressive`: Aggressive mode (`-A` flag) - per-CPU threads - -Tests use `parse_test_options()` from `test_helpers.sh` to handle backend and threading mode selection via `-b/--backend` and `-m/--threading-mode` flags. - -**Current Test Coverage:** - -✅ **Phase 1 Complete** (Foundation - 4 tests): -- `test01.c` - Fixed original starvation test (7 critical fixes: error handling, buffer safety, memory cleanup) -- `test_foreground.sh` - Tests `-f` flag prevents daemonization -- `test_log_only.sh` - Tests `-l` flag logs but doesn't boost (supports backend selection) -- `test_logging_destinations.sh` - Tests `-v`, `-k`, `-s` logging options - -✅ **Phase 2 Complete** (Command-Line Options - 9 of 10 tests): -- `test_backend_selection.sh` - Tests `-b` backend selection (argument ordering fix) -- `test_cpu_selection.sh` - Tests `-c` CPU selection -- `test_starvation_threshold.sh` - Tests `-t` threshold option (fixed segfault, documented queue_track limitation) -- `test_boost_period.sh` - Tests `-p` period option (6 tests) -- `test_boost_runtime.sh` - Tests `-r` runtime option (7 tests) -- `test_boost_duration.sh` - Tests `-d` duration option (6 tests) -- `test_affinity.sh` - Tests `-a` affinity option (8 tests) -- `test_pidfile.sh` - Tests `--pidfile` option (7 tests, fixed -P→--pidfile bug) -- `test_boost_restoration.sh` - Verifies policy restoration after boosting (5 tests, 3 pass on sched_debug) -- ⚠️ `test_force_fifo.sh` - SKIPPED (user requested, may return later) - -✅ **Phase 3 Complete** (Core Logic - 7 tests): -- `test_starvation_detection.sh` - Verifies starvation detection (6 tests) -- `test_idle_detection.sh` - Tests `-N` idle detection disable (5 tests) -- `test_task_merging.sh` - Verifies timestamp preservation (4 tests) -- `test_deadline_boosting.sh` - Tests SCHED_DEADLINE boosting (5 tests) -- `test_fifo_boosting.sh` - Tests SCHED_FIFO boosting (5 tests, 3 pass on sched_debug) -- `test_fifo_priority_starvation.sh` - Tests FIFO-on-FIFO priority starvation (5 tests, sched_debug only) -- `test_runqueue_parsing.sh` - Verifies runqueue parsing (5 tests) - -**Known Issues:** -- **queue_track backend limitation**: BPF backend cannot detect SCHED_FIFO tasks waiting on runqueue due to `task_running()` check in `stalld.bpf.c:273` only tracking `__state == TASK_RUNNING`. Tests using `starvation_gen` (SCHED_FIFO workloads) pass on sched_debug but fail on queue_track. -- **Segfault fix**: Fixed critical bug in `merge_tasks_info()` that caused crashes in adaptive/aggressive modes (commit 7af4f55a5765) - -🔄 **Phase 4 Planned** (Advanced Features): -- Threading modes (adaptive vs aggressive) -- Filtering (`-i`, `-I` options) -- Backend comparison tests (eBPF vs debugfs/procfs) -- Integration and stress tests - -**Test Requirements:** -- Root privileges for most tests -- RT throttling disabled: `echo -1 > /proc/sys/kernel/sched_rt_runtime_us` -- stalld built: `make` in project root - -**Helper Functions Available:** -```bash -# Test Options Parsing -parse_test_options "$@" # Parse -b/--backend, -m/--threading-mode, and -h/--help flags - # Sets STALLD_TEST_BACKEND and STALLD_TEST_THREADING_MODE env vars - -# Assertions -assert_equals expected actual "message" -assert_contains haystack needle "message" -assert_file_exists "/path/to/file" -assert_process_running $PID - -# stalld Management -start_stalld [args...] # Start stalld, track PID -stop_stalld # Stop stalld gracefully - -# System Helpers -require_root # Skip test if not root -check_rt_throttling # Check RT throttling status -pick_test_cpu # Pick CPU for testing -wait_for_log_message "pattern" timeout - -# Starvation Generator -../helpers/starvation_gen -c CPU -p blocker_priority -b blockee_priority -n num_threads -d duration -v -# Examples: -# starvation_gen -c 2 -p 80 -n 2 -d 30 # Standard: blocker prio 80, blockees prio 1 -# starvation_gen -c 2 -p 10 -b 5 -n 2 -d 30 # FIFO-on-FIFO: blocker prio 10, blockees prio 5 -``` - -See `tests/README.md` for complete test documentation, writing tests, and troubleshooting. - -### Manual Testing Workflow - -1. Run stalld in foreground with verbose mode: - ```bash - sudo ./stalld -f -v -t 5 # 5 second threshold for faster testing - ``` - -2. In another terminal, create a CPU-intensive RT task to monopolize a CPU - -3. Create a normal task on the same CPU that will starve - -4. Observe stalld detecting and boosting the starving task - -## Development Workflow - -### Debugging - -```bash -make DEBUG=1 # Build with -g3 debug symbols -make clean && make # Full rebuild after changing build options -``` - -**Runtime debugging options:** -- Use `-v` (verbose) to see detailed logging to stdout -- Use `-l` (log-only) to test starvation detection without actually boosting tasks -- Use `-k` to log to kernel buffer (view with `dmesg`) -- Check `/var/log/messages` or `journalctl -u stalld` for syslog output -- Use `-f` to run in foreground (don't daemonize) - -### Code Navigation Tips - -**Starting points for common tasks:** -- Adding new command-line option: `parse_args()` in `src/utils.c` -- Modifying boost behavior: `boost_with_deadline()` and `boost_with_fifo()` in `src/stalld.c:438-563` -- Changing detection logic: `check_starving_tasks()` in `src/stalld.c:616-659` -- Backend implementation: `struct stalld_backend` in `src/stalld.h:79-110` -- eBPF tracepoints: `bpf/stalld.bpf.c` (requires kernel rebuild/reload) - -### Understanding Backend Selection - -**Compile-time default backend** is chosen based on architecture and kernel: - -```c -// src/stalld.c:158-162 -#if USE_BPF - backend = &queue_track_backend; // eBPF backend (default) -#else - backend = &sched_debug_backend; // debugfs/procfs backend (default) -#endif -``` - -`USE_BPF` is set in Makefile based on: -- Architecture (disabled on i686, powerpc, ppc64le) -- Kernel version (disabled on kernels ≤3.x) - -**Runtime backend selection** (via `-b` flag): -```bash -# Force debugfs/procfs backend -sudo ./stalld -b sched_debug -f -v - -# Force eBPF backend -sudo ./stalld -b queue_track -f -v - -# Short forms also supported -sudo ./stalld -b S -f -v # sched_debug -sudo ./stalld -b Q -f -v # queue_track -``` - -If a backend is explicitly requested but unavailable (e.g., eBPF not compiled in, or BPF programs fail to load), stalld will fail to start. - -## Architecture - -### Backend System (src/stalld.h lines 79-110) - -`stalld` uses a **pluggable backend architecture** to collect task information: - -1. **queue_track_backend** (eBPF-based, default on x86_64/aarch64/s390x) - - Uses BPF tracepoints to track task queue state in real-time - - Source: `bpf/stalld.bpf.c` + `src/queue_track.c` - - More efficient, lower overhead - - Tracks: `sched_wakeup`, `sched_switch`, `sched_migrate_task`, `sched_process_exit` - -2. **sched_debug_backend** (debugfs/procfs-based, fallback) - - Parses `/sys/kernel/debug/sched/debug` (debugfs) or `/proc/sched_debug` (procfs, older kernels) - - Source: `src/sched_debug.c` - - Used on i686, powerpc, ppc64le, and legacy kernels (≤3.x) - - Handles multiple kernel sched_debug formats (3.x, 4.18+, 6.12+) - -Backend selection is automatic at compile time (src/stalld.c:158-162) based on architecture and kernel version. - -### Operating Modes (src/stalld.c) - -Three threading modes controlled by `-A` and internal flags: - -1. **Power/Single-threaded mode** (`-O/--power_mode`, `config_single_threaded=1`) - - One thread monitors all CPUs - - Uses `boost_cpu_starving_vector()` to boost all starving tasks at once - - Lower CPU usage, lower precision - - **Only works with SCHED_DEADLINE** (not FIFO) - -2. **Adaptive/Conservative mode** (`-M/--adaptive_mode`, `config_adaptive_multi_threaded=1`, default) - - Starts with single thread - - Spawns per-CPU threads when tasks approach starvation (½ threshold) - - Per-CPU threads exit after 10 idle cycles - -3. **Aggressive mode** (`-A/--aggressive_mode`, `config_aggressive=1`) - - Dedicated thread per monitored CPU from start - - Highest precision, highest CPU usage - - Never exit, continuous monitoring - -### Key Data Structures - -- **`struct task_info`** (src/stalld.h:53-60): Per-task tracking (PID, comm, priority, context switches, starvation timestamp) -- **`struct cpu_info`** (src/stalld.h:65-77): Per-CPU state (running tasks, RT tasks, starving tasks array) -- **`struct stalld_cpu_data`** (src/queue_track.h:19-24): eBPF per-CPU map data -- **`struct queued_task`** (src/queue_track.h:11-17): Task entry in eBPF queue - -### Boosting Logic (src/stalld.c:438-563) - -1. Detect starvation: Task on runqueue for ≥`starving_threshold` seconds with no context switches -2. Save current scheduling policy -3. Boost to SCHED_DEADLINE (runtime/period) or SCHED_FIFO (priority) -4. Sleep for `boost_duration` seconds -5. Restore original policy - -**Important**: FIFO boosting emulates DEADLINE behavior by manually sleeping runtime, restoring policy, sleeping remainder (src/stalld.c:500-526). - -### Task Format Auto-Detection (src/sched_debug.h:43-48) - -The sched_debug backend handles 3 different kernel formats: -- **OLD_TASK_FORMAT**: 3.x kernels (no state column, 'R' prefix for running task) -- **NEW_TASK_FORMAT**: 4.18+ kernels (has 'S' state column) -- **6.12+ format**: Added EEVDF fields (vruntime, eligible, deadline, slice) - -Parser auto-detects format on first read and sets offsets accordingly. - -### eBPF Build Process (Makefile:161-189) - -When `USE_BPF=1`: -1. Generate `bpf/vmlinux.h` from kernel BTF via `bpftool` -2. Compile `bpf/stalld.bpf.c` → `bpf/stalld.bpf.o` using `clang -target bpf` -3. Generate `src/stalld.skel.h` skeleton from `.bpf.o` via `bpftool gen skeleton` -4. Include skeleton in userspace code compilation - -### Idle Detection Optimization (src/stalld.c:226-308) - -When `config_idle_detection=1` (default): -- Parse `/proc/stat` to check CPU idle time before expensive parsing -- Skip parsing for CPUs with increasing idle counter -- Reduces overhead when CPUs aren't busy - -## Configuration Files - -- **systemd/stalld.service**: systemd unit file -- **systemd/stalld.conf**: Configuration options for systemd deployment -- **scripts/throttlectl.sh**: Helper script for RT throttling control - -## RT Throttling - -`stalld` requires RT throttling to be disabled. The daemon handles this automatically unless running under systemd (where systemd should handle it via `CPUQuota=-1`). - -Check: `/proc/sys/kernel/sched_rt_runtime_us` should be `-1`. - -## Important Code Patterns - -### Task Merging (src/stalld.c:370-397) -When re-parsing tasks, `merge_tasks_info()` preserves starvation timestamps for tasks that haven't made progress (same PID, same context switch count). - -### Denylist/Ignore Feature -- `-i` flag: Ignore threads/processes matching regex patterns -- Uses POSIX regex via `regexec()` -- Check both thread name and process group name (src/stalld.c:570-614) - -### Buffer Management -The buffer for sched_debug automatically grows when content increases (src/sched_debug.c:55-58, src/stalld.h:192). - -## Common Gotchas - -1. **Single-threaded mode only works with SCHED_DEADLINE**, not FIFO (dies at src/stalld.c:973) -2. **RT throttling must be off** or stalld exits (src/stalld.c:1154-1161) -3. **sched_debug path varies**: `/sys/kernel/debug/sched/debug` or `/proc/sched_debug` (auto-detected in `utils.c`) -4. **Architecture differences**: eBPF not available on all platforms -5. **Kernel version differences**: Legacy kernels (≤3.x) need special handling - -## Quick Reference - -### Critical Files and Functions - -**Entry points:** -- Main entry: `src/stalld.c:main()` line 1121 -- Boost logic: `src/stalld.c:boost_with_deadline()` line 438 -- Starvation detection: `src/stalld.c:check_starving_tasks()` line 616 -- Backend interface: `src/stalld.h:struct stalld_backend` line 79 - -**Backends:** -- eBPF backend: `src/queue_track.c` + `bpf/stalld.bpf.c` -- debugfs/procfs backend: `src/sched_debug.c` - -**Configuration:** -- Argument parsing: `src/utils.c:parse_args()` -- Defaults in: `src/stalld.c` global variables (lines 49-169) - -### Build Quick Reference - -```bash -make # Build stalld + tests -make DEBUG=1 # Debug build with -g3 -make static # Static binary -make clean # Clean build artifacts -make install # Install to system -``` - -### Key Runtime Requirements - -- **RT throttling must be disabled**: `/proc/sys/kernel/sched_rt_runtime_us == -1` -- **Default starvation threshold**: 60 seconds -- **Default boost**: 20μs runtime / 1s period for 3 seconds -- **Minimum kernel**: 3.10+ (older kernels untested) -- **eBPF requires**: Modern kernel (4.x+), x86_64/aarch64/s390x architecture - -### Debugging Commands - -```bash -sudo ./stalld -f -v -t 5 # Foreground, verbose, 5s threshold -sudo ./stalld -l -v # Log-only mode (no boosting) -dmesg | grep stalld # Check kernel messages (if -k used) -journalctl -u stalld -f # Follow systemd logs -```
.claude/agents/agent-prompt-engineer.md deleted +0 -135
136 lines not on this page · raw message
.claude/agents/c-expert.md deleted +0 -53
53 lines not on this page · raw message
.claude/agents/code-reviewer.md deleted +0 -104
105 lines not on this page · raw message
.claude/agents/get-agent-hash deleted +0 -99
100 lines not on this page · raw message
.claude/agents/git-scm-master.md deleted +0 -1154
1,154 lines not on this page · raw message
.claude/agents/kernel-hacker.md deleted +0 -231
232 lines not on this page · raw message
.claude/agents/plan-validator.md deleted +0 -130
131 lines not on this page · raw message
.claude/agents/project-historian.md deleted +0 -285
286 lines not on this page · raw message
.claude/agents/project-librarian.md deleted +0 -604
218 lines not on this page · raw message
.claude/agents/project-manager.md deleted +0 -388
388 lines not on this page · raw message
.claude/agents/project-scope-guardian.md deleted +0 -326
326 lines not on this page · raw message
.claude/agents/python-expert.md deleted +0 -57
57 lines not on this page · raw message
.claude/agents/test-specialist.md deleted +0 -656
656 lines not on this page · raw message
.claude/agents/update-agent-hashes deleted +0 -96
96 lines not on this page · raw message
.claude/context-snapshot.json deleted +0 -103
103 lines not on this page · raw message
.claude/rules deleted +0 -42
42 lines not on this page · raw message
tests/BACKEND_USAGE.md deleted +0 -269
269 lines not on this page · raw message
tests/CONTEXT_SNAPSHOT_2025-10-31.md deleted +0 -231
231 lines not on this page · raw message
tests/TODO.md deleted +0 -727
727 lines not on this page · raw message
tests/legacy/README.md deleted +0 -169
169 lines not on this page · raw message
tests/legacy/test01.c deleted +0 -506
506 lines not on this page · raw message
tests/legacy/test01_wrapper.sh deleted +0 -161
161 lines not on this page · raw message
tests/run_tests.sh +3 -140
143 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
2.54.0
Part of the diff is not shown here. View the raw message.
PATCH 1/1 13/33] tests: Add assertions to SCHED_OTHER restoration test
In reply to Wander Lairson CostaTest 3 in the boost restoration suite creates a SCHED_OTHER blockee but never verifies that the scheduling policy is actually restored after boosting. It waits for starvation_gen to complete and has a comment about checking the result, but no pass or fail call exists.
Add proper verification by tracking the starved child PID, waiting for the boost to occur and expire, then checking that the policy returns to SCHED_OTHER.
--- tests/functional/test_boost_restoration.sh | 37 +++++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-)
tests/functional/test_boost_restoration.sh +32 -5
@@ -244,12 +244,39 @@ start_stalld_with_log "${STALLD_LOG}" -f -v -t $threshold -c ${TEST_CPU} -a ${ST log "Creating SCHED_OTHER starvation (RT blocker prio 80, SCHED_OTHER blockee)" start_starvation_gen -c ${TEST_CPU} -p 80 -o -n 1 -d 20 -# Wait for starvation_gen to complete -log "Waiting for starvation test to complete..." -wait ${STARVE_PID} 2>/dev/null || true +# Find the starved SCHED_OTHER task +tracked_pid=$(find_starved_child "${STARVE_PID}") + +if [ -n "${tracked_pid}" ]; then + initial_policy=$(get_sched_policy ${tracked_pid}) + log "Tracking task PID ${tracked_pid}, initial policy: ${initial_policy} (expected: 0=SCHED_OTHER)" + + # Wait for boost + log "Waiting for starvation detection and boost..." + if wait_for_boost_detected "${STALLD_LOG}"; then + pass "SCHED_OTHER task boosted" + + # Wait for boost to expire and policy to be restored + sleep $((boost_duration + 1)) -# Check if blockee completed (proves SCHED_OTHER → boost → SCHED_OTHER restoration worked) -# The starvation_gen output will show if blockees completed + if [ -f "/proc/${tracked_pid}/sched" ]; then + final_policy=$(get_sched_policy ${tracked_pid}) + log "Policy after boost: ${final_policy}" + + if [ "$final_policy" = "0" ]; then + pass "Policy restored to SCHED_OTHER (0)" + else + fail "Policy not restored to SCHED_OTHER (got ${final_policy})" + fi + else + log "⚠ INFO: Task exited before restoration check" + fi + else + fail "No boost detected for SCHED_OTHER task" + fi +else + log "⚠ WARNING: Could not find starved task to track" +fi # Cleanup cleanup_scenario "${STARVE_PID}"
Signature · 1 line
2.54.0
PATCH 1/1 15/33] tests: Add idle CPU skipping assertion
In reply to Wander Lairson CostaTest 4 in the idle detection suite was purely informational, printing explanatory text about what idle detection does without any assertions. Replace it with an actual test that starts stalld on an idle CPU and verifies that "skipping" messages appear in the log, confirming the idle detection optimization is working.
--- tests/functional/test_idle_detection.sh | 32 +++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-)
tests/functional/test_idle_detection.sh +17 -15
@@ -140,23 +140,25 @@ fi cleanup_scenario "${STARVE_PID}" #============================================================================= -# Test 4: Idle Detection Overhead Reduction +# Test 4: Idle CPUs Are Skipped #============================================================================= -test_section "Test 4: Idle Detection Reduces Overhead" -log "Comparing overhead with and without idle detection (informational)" - -# This is informational - we can't easily measure overhead in tests -log "With idle detection (default):" -log " - /proc/stat read before parsing" -log " - Idle CPUs skipped (no sched_debug/BPF parsing)" -log " - Reduces CPU usage when system mostly idle" -log "" -log "Without idle detection would:" -log " - Always parse all CPUs" -log " - Higher overhead even when CPUs idle" +test_section "Test 4: Idle CPUs Are Skipped" + +rm -f "${STALLD_LOG}" +threshold=3 + +log "Starting stalld with idle detection on an idle CPU" +start_stalld_with_log "${STALLD_LOG}" -f -v -l -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} + +sleep 3 + +if grep -q "skipping" "${STALLD_LOG}"; then + pass "Idle CPU correctly skipped" +else + fail "No skipping messages for idle CPU" +fi -log "ℹ INFO: Idle detection enabled by default for efficiency" -log " Function: cpu_had_idle_time() and get_cpu_busy_list()" +cleanup_scenario #============================================================================= # Test 5: Idle Detection with Multiple CPUs
Signature · 1 line
2.54.0
PATCH 1/1 14/33] tests: Fix CPU selection grep substring matches
In reply to Wander Lairson CostaThe CPU selection test uses grep patterns like "cpu 1" which can match "cpu 10" or "cpu 12" on systems with more than 9 CPUs. This causes false positives where the test passes even when the wrong CPUs are being monitored.
Introduce a local is_cpu_monitored() helper that greps for the exact stalld log format "adding cpu N$" with an end-of-line anchor, preventing substring matches.
--- tests/functional/test_cpu_selection.sh | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-)
tests/functional/test_cpu_selection.sh +13 -9
@@ -13,6 +13,10 @@ source "${TEST_ROOT}/helpers/test_helpers.sh" # Parse command-line options parse_test_options "$@" || exit $? +is_cpu_monitored() { + grep -q "adding cpu ${1}$" "$STALLD_LOG" +} + start_test "CPU Selection (-c option)" # Setup test environment
@@ -40,7 +44,7 @@ rm -f "${STALLD_LOG}" start_stalld_with_log "${STALLD_LOG}" -f -v -c 0 -l -t 5 # Check that stalld mentions CPU 0 -if grep -q "cpu 0" "$STALLD_LOG"; then +if is_cpu_monitored 0; then pass "stalld monitoring CPU 0" else fail "stalld not monitoring CPU 0"
@@ -57,10 +61,10 @@ if [ "$num_cpus" -ge 4 ]; then # Check for CPU 0 and CPU 2 in output cpu0_found=0 cpu2_found=0 - if grep -q "cpu 0" "$STALLD_LOG"; then + if is_cpu_monitored 0; then cpu0_found=1 fi - if grep -q "cpu 2" "$STALLD_LOG"; then + if is_cpu_monitored 2; then cpu2_found=1 fi
@@ -85,13 +89,13 @@ if [ "$num_cpus" -ge 4 ]; then cpu0_found=0 cpu1_found=0 cpu2_found=0 - if grep -q "cpu 0" "$STALLD_LOG"; then + if is_cpu_monitored 0; then cpu0_found=1 fi - if grep -q "cpu 1" "$STALLD_LOG"; then + if is_cpu_monitored 1; then cpu1_found=1 fi - if grep -q "cpu 2" "$STALLD_LOG"; then + if is_cpu_monitored 2; then cpu2_found=1 fi
@@ -115,7 +119,7 @@ if [ "$num_cpus" -ge 6 ]; then # Should monitor CPUs 0, 2, 3, 4 monitored_cpus=0 for cpu in 0 2 3 4; do - if grep -q "cpu $cpu" "$STALLD_LOG"; then + if is_cpu_monitored ${cpu}; then ((monitored_cpus++)) fi done
@@ -143,8 +147,8 @@ if [ "$num_cpus" -ge 2 ]; then rm -f "${STALLD_LOG}" start_stalld_with_log "${STALLD_LOG}" -f -v -c 0 -l -t 5 - # Check that CPU 1 is NOT mentioned (or mentioned as "not monitoring") - if ! grep -q "cpu 1" "$STALLD_LOG" || grep -q "not monitoring.*cpu 1" "$STALLD_LOG"; then + # Check that CPU 1 is NOT being monitored + if ! is_cpu_monitored 1; then pass "stalld not monitoring non-selected CPU 1" else fail "stalld appears to be monitoring CPU 1 when only CPU 0 selected"
Signature · 1 line
2.54.0
PATCH 1/1 16/33] tests: Remove redundant pkill from cleanup
In reply to Wander Lairson CostaThe cleanup function uses pkill -9 -f starvation_gen to kill all starvation generator processes system-wide. This is redundant because the CLEANUP_PIDS array already tracks every spawned process by PID and terminates them individually.
--- tests/helpers/test_helpers.sh | 4 ---- 1 file changed, 4 deletions(-)
tests/helpers/test_helpers.sh +0 -4
@@ -548,10 +548,6 @@ cleanup() { # Stop stalld stop_stalld - # Kill any starvation generators first (these often have child processes) - # Use pkill which handles process trees better - pkill -9 -f starvation_gen 2>/dev/null || true - # Small delay to let processes terminate sleep 0.2
Signature · 1 line
2.54.0
PATCH 1/1 17/33] tests: Introduce and adopt assert_log_contains() helper
In reply to Wander Lairson CostaMultiple functional tests repeat the pattern of grepping a log file for a specific pattern and calling pass or fail based on the result. This leads to verbose and duplicated boilerplate code across the test suite.
Introduce an assert_log_contains() helper that encapsulates the grep, pass/fail evaluation, and diagnostic output into a single function call. To support various test requirements, the helper includes:
* A --negate flag for asserting the absence of a pattern.
* An --ignore-case flag to easily perform case-insensitive matching, avoiding verbose regex workarounds (like [Ee]rror).
Adopt this new helper across the functional test suite. This refactor simplifies several tests, including replacing nested false-positive checks in test_starvation_detection and cleaning up if/elif/else policy detection blocks in test_force_fifo.
--- tests/functional/test_backend_selection.sh | 9 +--- tests/functional/test_deadline_boosting.sh | 25 ++-------- tests/functional/test_fifo_boosting.sh | 12 +---- .../test_fifo_priority_starvation.sh | 20 ++------ tests/functional/test_force_fifo.sh | 48 ++----------------- tests/functional/test_idle_detection.sh | 6 +-- tests/functional/test_log_only.sh | 8 +--- tests/functional/test_logging_destinations.sh | 4 +- tests/functional/test_starvation_detection.sh | 32 +++---------- tests/functional/test_starvation_threshold.sh | 10 ++-- tests/helpers/test_helpers.sh | 41 +++++++++++++++- 11 files changed, 66 insertions(+), 149 deletions(-)
tests/functional/test_backend_selection.sh +1 -8
@@ -44,14 +44,7 @@ test_backend_flag() { return 1 fi - if grep -q "${expected_msg}" "${log_file}"; then - pass "${description}" - else - fail "Backend message not found (${description})" - echo " Expected: ${expected_msg}" - echo " Log contents:" - cat "${log_file}" - fi + assert_log_contains "${log_file}" "${expected_msg}" "${description}" stop_stalld }
tests/functional/test_deadline_boosting.sh +3 -22
@@ -40,11 +40,7 @@ if wait_for_boost_detected "${STALLD_LOG}"; then pass "Boosting occurred" # Verify SCHED_DEADLINE was used - if grep -q "SCHED_DEADLINE" "${STALLD_LOG}"; then - pass "SCHED_DEADLINE boosting used (default)" - else - fail "SCHED_DEADLINE not mentioned in boost message" - fi + assert_log_contains "${STALLD_LOG}" "SCHED_DEADLINE" "SCHED_DEADLINE boosting used (default)" # Verify boost happened after threshold # (starvation logged, then boosting)
@@ -106,11 +102,7 @@ fi if [ ${boosted_task_found} -eq 0 ]; then log "⚠ INFO: Could not verify DEADLINE policy in /proc (timing issue or boost already expired)" # Still check if boost happened in logs - if grep -q "boosted.*SCHED_DEADLINE" "${STALLD_LOG}"; then - pass "SCHED_DEADLINE boost confirmed in logs" - else - fail "No SCHED_DEADLINE boost detected" - fi + assert_log_contains "${STALLD_LOG}" "boosted.*SCHED_DEADLINE" "SCHED_DEADLINE boost confirmed in logs" fi # Cleanup
@@ -165,11 +157,7 @@ else fi # Verify boost happened -if grep -q "boosted" "${STALLD_LOG}"; then - pass "Boost occurred as expected" -else - fail "No boost detected" -fi +assert_log_contains "${STALLD_LOG}" "boosted" "Boost occurred as expected" # Cleanup cleanup_scenario "${STARVE_PID}"
@@ -286,13 +274,6 @@ else if [ ${boost_count} -ge 2 ]; then pass "Multiple boost events detected (${boost_count})" - # Verify both CPUs mentioned - if grep -q "CPU ${CPU0}" "${STALLD_LOG}" && grep -q "CPU ${CPU1}" "${STALLD_LOG}"; then - pass "Boosts occurred on both CPUs" - else - log "⚠ INFO: Could not verify boosts on both specific CPUs" - fi - # Verify independent boost cycles if [ ${boost_count} -gt 2 ]; then pass "Multiple boost cycles (${boost_count} total), showing independent operation"
tests/functional/test_fifo_boosting.sh +2 -10
@@ -38,11 +38,7 @@ if wait_for_boost_detected "${STALLD_LOG}"; then pass "Boosting occurred with -F flag" # Verify SCHED_FIFO was used - if grep -q "SCHED_FIFO" "${STALLD_LOG}"; then - pass "SCHED_FIFO boosting used (as requested by -F)" - else - fail "SCHED_FIFO not mentioned in boost message" - fi + assert_log_contains "${STALLD_LOG}" "SCHED_FIFO" "SCHED_FIFO boosting used (as requested by -F)" else fail "No boosting detected with -F flag" log "Log contents:"
@@ -88,11 +84,7 @@ fi if [ ${fifo_task_found} -eq 0 ]; then log "⚠ INFO: Could not verify FIFO policy in /proc (timing issue or boost already expired)" # FIFO emulation cycles between FIFO and OTHER, so we may catch it in OTHER state - if grep -q "boosted.*SCHED_FIFO" "${STALLD_LOG}"; then - pass "SCHED_FIFO boost confirmed in logs" - else - fail "No SCHED_FIFO boost detected" - fi + assert_log_contains "${STALLD_LOG}" "boosted.*SCHED_FIFO" "SCHED_FIFO boost confirmed in logs" fi # Cleanup
tests/functional/test_fifo_priority_starvation.sh +3 -17
@@ -44,18 +44,10 @@ if wait_for_starvation_detected "${STALLD_LOG}"; then pass "FIFO-on-FIFO starvation detected" # Verify correct CPU is logged - if grep "starved on CPU ${TEST_CPU}" "${STALLD_LOG}"; then - pass "Correct CPU ID logged (CPU ${TEST_CPU})" - else - fail "Wrong CPU ID in log" - fi + assert_log_contains "${STALLD_LOG}" "starved on CPU ${TEST_CPU}" "Correct CPU ID logged (CPU ${TEST_CPU})" # Verify duration is logged - if grep -E "starved on CPU ${TEST_CPU} for [0-9]+ seconds" "${STALLD_LOG}"; then - pass "Starvation duration logged" - else - fail "Starvation duration not logged" - fi + assert_log_contains "${STALLD_LOG}" "starved on CPU ${TEST_CPU} for [0-9]" "Starvation duration logged" else fail "FIFO-on-FIFO starvation not detected" log "Log contents:"
@@ -106,13 +98,7 @@ log "Context switch delta: ${ctxt_delta}" if [ ${ctxt_delta} -gt 0 ]; then pass "Blockee task made progress (${ctxt_delta} context switches)" else - log "⚠ WARNING: Could not verify progress (timing issue or blockee not found)" - # Check if boosting occurred at least - if grep -q "boosted" "${STALLD_LOG}"; then - log "ℹ INFO: Boosting did occur according to logs" - else - fail "No boosting detected" - fi + assert_log_contains "${STALLD_LOG}" "boosted" "Boosting occurred despite no measurable progress" fi # Cleanup
tests/functional/test_force_fifo.sh +5 -43
66 lines not on this page · raw message
tests/functional/test_idle_detection.sh +1 -5
@@ -152,11 +152,7 @@ start_stalld_with_log "${STALLD_LOG}" -f -v -l -t $threshold -c ${TEST_CPU} -a $ sleep 3 -if grep -q "skipping" "${STALLD_LOG}"; then - pass "Idle CPU correctly skipped" -else - fail "No skipping messages for idle CPU" -fi +assert_log_contains "${STALLD_LOG}" "skipping" "Idle CPU correctly skipped" cleanup_scenario
tests/functional/test_log_only.sh +1 -7
@@ -61,13 +61,7 @@ echo "Waiting for starvation detection..." assert_starvation_detected "${LOG_FILE}" "stalld detected and logged starvation" # Check that stalld did NOT boost (should not see "boosted" message with -l) -if ! grep -q "boosted" "${LOG_FILE}"; then - pass "stalld did not boost in log-only mode" -else - fail "stalld boosted despite -l flag" - echo "Log contents:" - cat "${LOG_FILE}" -fi +assert_log_contains --negate "${LOG_FILE}" "boosted" "stalld did not boost in log-only mode" # Cleanup cleanup_scenario "${STARVGEN_PID}"
tests/functional/test_logging_destinations.sh +1 -3
@@ -41,9 +41,7 @@ if assert_process_running "${STALLD_PID}" "stalld should be running"; then pass "verbose mode produces output" # Should contain initialization messages - if grep -q -E "(stalld|version|monitoring)" "${LOG_FILE}"; then - pass "output contains expected messages" - fi + assert_log_contains "${LOG_FILE}" "stalld\|version\|monitoring" "output contains expected messages" else fail "no output in verbose mode" fi
tests/functional/test_starvation_detection.sh +6 -26
@@ -47,18 +47,10 @@ if wait_for_starvation_detected "${STALLD_LOG}"; then pass "Starvation detected" # Verify correct CPU is logged - if grep "starved on CPU ${TEST_CPU}" "${STALLD_LOG}"; then - pass "Correct CPU ID logged (CPU ${TEST_CPU})" - else - fail "Wrong CPU ID in log" - fi + assert_log_contains "${STALLD_LOG}" "starved on CPU ${TEST_CPU}" "Correct CPU ID logged (CPU ${TEST_CPU})" # Verify duration is logged - if grep -E "starved on CPU ${TEST_CPU} for [0-9]+ seconds" "${STALLD_LOG}"; then - pass "Starvation duration logged" - else - fail "Starvation duration not logged" - fi + assert_log_contains "${STALLD_LOG}" "starved on CPU ${TEST_CPU} for [0-9]" "Starvation duration logged" else fail "Starvation not detected" log "Log contents:"
@@ -249,15 +241,9 @@ sleep $((threshold + 3)) # Verify this task was NOT reported as starved # Since it's making progress, stalld shouldn't detect it -if ! grep "starved" "${STALLD_LOG}"; then - pass "No false positive - task making progress not reported as starved" -else - if grep "${BUSY_PID}" "${STALLD_LOG}" | grep -q "starved"; then - fail "False positive - progress-making task ${BUSY_PID} reported as starved" - else - pass "No false positive - starvation detected from other tasks, not ours" - fi -fi +assert_log_contains --negate "${STALLD_LOG}" \ + "${BUSY_PID}.*starved" \ + "No false positive - progress-making task not reported as starved" kill ${BUSY_PID} 2>/dev/null wait ${BUSY_PID} 2>/dev/null
@@ -289,13 +275,7 @@ else fi # Check for error messages -if grep -iE "error|segfault|crash" "${STALLD_LOG}"; then - fail "Error messages found in log" - log "Errors:" - grep -iE "error|segfault|crash" "${STALLD_LOG}" -else - pass "No error messages in log" -fi +assert_log_contains --negate --ignore-case "${STALLD_LOG}" "error\|segfault\|crash" "No error messages in log" stop_stalld
tests/functional/test_starvation_threshold.sh +3 -7
@@ -70,13 +70,9 @@ wait "${STARVE_PID}" 2>/dev/null || true sleep 2 # Check that starvation_gen was NOT detected (duration less than threshold) -if ! grep -qE "starvation_gen.*starved on CPU ${TEST_CPU}|starved on CPU ${TEST_CPU}.*starvation_gen" "${STALLD_LOG}"; then - pass "No starvation detected for duration less than threshold" -else - fail "Starvation detected before threshold" - log "Found starvation_gen task in logs:" - grep -E "starvation_gen.*starved on CPU|starved on CPU.*starvation_gen" "${STALLD_LOG}" -fi +assert_log_contains --negate "${STALLD_LOG}" \ + "starvation_gen.*starved on CPU ${TEST_CPU}\|starved on CPU ${TEST_CPU}.*starvation_gen" \ + "No starvation detected for duration less than threshold" # Cleanup cleanup_scenario "${STARVE_PID}"
tests/helpers/test_helpers.sh +40 -1
53 lines not on this page · raw message
Signature · 1 line
2.54.0
PATCH 1/1 18/33] tests: Remove weak, redundant, and assertion-free test blocks
In reply to Wander Lairson CostaClean up the functional test suite by removing tests and code blocks that provide no meaningful verification, duplicate existing coverage, or contain no enforceable assertions.
Specific cleanups include:
* Remove tests lacking assertions: Drop blocks that log informational messages but pass on all branches, making them impossible to fail (e.g., in idle detection, FIFO comparison, and boost restoration).
* Consolidate duplicate coverage: Remove tests whose verification is already handled elsewhere, such as:
- Runqueue parsing (covered by test_starvation_detection and test_backend_selection).
- SCHED_FIFO restoration and deadline policy restoration.
- FIFO vs DEADLINE comparison and single-threaded mode rejection (already covered and fixed in test_force_fifo).
- Duplicate task merging tests.
* Remove dead and broken code: Drop the unused get_starved_pid() helper, broken relative paths, unused helper functions in idle detection, and manual task creation scripts that duplicated starvation_gen.
* Refactor redundant checks: Replace informative grep checks that follow wait_for_boost_detected with standard assert_boost_detected calls.
This consolidation reduces test suite execution time, removes buggy legacy code, and ensures all remaining test cases have clear, enforceable pass/fail criteria.
--- tests/functional/test_affinity.sh | 7 - tests/functional/test_boost_period.sh | 13 +- tests/functional/test_boost_restoration.sh | 177 +-------- tests/functional/test_deadline_boosting.sh | 82 +--- tests/functional/test_fifo_boosting.sh | 124 +----- tests/functional/test_force_fifo.sh | 51 +-- tests/functional/test_idle_detection.sh | 111 +----- tests/functional/test_runqueue_parsing.sh | 375 ------------------ tests/functional/test_starvation_detection.sh | 81 +--- tests/functional/test_task_merging.sh | 53 +-- 10 files changed, 29 insertions(+), 1045 deletions(-) delete mode 100755 tests/functional/test_runqueue_parsing.sh
tests/functional/test_affinity.sh +0 -7
@@ -183,13 +183,6 @@ if [ "$num_cpus" -ge 2 ]; then log "⚠ WARNING: stalld affinity ($affinity) doesn't match requested (0)" fi - # Verify it's monitoring CPU 1 by checking logs - if grep -q "cpu 1" "${STALLD_LOG}" || grep -q "monitoring.*1" "${STALLD_LOG}"; then - log "ℹ INFO: stalld monitoring CPU 1 as requested" - else - log "ℹ INFO: CPU 1 monitoring not explicitly confirmed in logs" - fi - stop_stalld else log "⊘ SKIP: Test 6 requires at least 2 CPUs"
tests/functional/test_boost_period.sh +1 -12
@@ -27,18 +27,7 @@ log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} # Wait for starvation detection and boosting -if wait_for_boost_detected "${STALLD_LOG}"; then - pass "Boosting occurred with default period" - - # Try to find period value in logs - if grep -qi "period" "${STALLD_LOG}"; then - log "ℹ INFO: Period information found in logs" - fi -else - fail "No boosting detected" - log "Log contents:" - cat "${STALLD_LOG}" -fi +assert_boost_detected "${STALLD_LOG}" "Boosting occurred with default period" # Cleanup cleanup_scenario "${STARVE_PID}"
tests/functional/test_boost_restoration.sh +4 -173
197 lines not on this page · raw message
tests/functional/test_deadline_boosting.sh +6 -76
103 lines not on this page · raw message
tests/functional/test_fifo_boosting.sh +2 -122
136 lines not on this page · raw message
tests/functional/test_force_fifo.sh +1 -50
63 lines not on this page · raw message
tests/functional/test_idle_detection.sh +6 -105
141 lines not on this page · raw message
tests/functional/test_runqueue_parsing.sh deleted +0 -375
375 lines not on this page · raw message
tests/functional/test_starvation_detection.sh +7 -74
114 lines not on this page · raw message
tests/functional/test_task_merging.sh +2 -51
66 lines not on this page · raw message
Signature · 1 line
2.54.0
PATCH 1/1 19/33] tests: Introduce and adopt assert_success() helper
In reply to Wander Lairson CostaMultiple functional tests repeat a verbose pattern: running a command, checking its exit status, and conditionally calling pass, fail, or printing a warning. These if/else blocks are repeated dozens of times across the test suite. Furthermore, tests that only printed warnings on failure silently masked real test problems.
Introduce an assert_success() helper that encapsulates command execution, exit code evaluation, and standardized pass/fail reporting into a single function call. To support various test requirements, the helper includes a --negate flag for asserting expected command failures. Crucially, this helper unconditionally calls fail() on error, eliminating the weak warning-only pattern.
Adopt this new helper across the functional test suite. This refactor:
* Eliminates over 20 duplicated if/else blocks across the suite.
* Tightens previously weak assertions (e.g., in test_affinity) that only warned on failure to actual test failures.
* Flattens nested conditional logic into clear, sequential assertions (e.g., in test_fifo_priority_starvation).
* Removes redundant checks that were already covered by existing wait_for_boost_detected assertions.
--- tests/functional/test_affinity.sh | 44 +++---------------- tests/functional/test_boost_restoration.sh | 30 +++---------- tests/functional/test_cpu_selection.sh | 24 ++-------- tests/functional/test_deadline_boosting.sh | 14 +----- .../test_fifo_priority_starvation.sh | 44 +++++-------------- tests/functional/test_foreground.sh | 18 ++------ tests/functional/test_logging_destinations.sh | 6 +-- tests/functional/test_pidfile.sh | 24 ++-------- tests/functional/test_starvation_detection.sh | 6 +-- tests/helpers/test_helpers.sh | 34 +++++++++++++- 10 files changed, 71 insertions(+), 173 deletions(-)
tests/functional/test_affinity.sh +7 -37
@@ -60,12 +60,7 @@ sleep 2 default_affinity=$(check_affinity "${STALLD_PID}") log "ℹ INFO: Default affinity: $default_affinity" -# Typically should be all CPUs -if [ -n "$default_affinity" ]; then - pass "stalld has default affinity: $default_affinity" -else - log "⚠ WARNING: Could not determine default affinity" -fi +assert_success "stalld has default affinity" test -n "$default_affinity" stop_stalld
@@ -79,11 +74,7 @@ sleep 2 affinity=$(check_affinity "${STALLD_PID}") -if [ "$affinity" = "0" ]; then - pass "stalld restricted to CPU 0" -else - fail "stalld affinity ($affinity) doesn't match requested (0)" -fi +assert_success "stalld restricted to CPU 0" test "$affinity" = "0" stop_stalld
@@ -99,12 +90,7 @@ if [ "$num_cpus" -ge 4 ]; then affinity=$(check_affinity "${STALLD_PID}") # Accept either "0,2" or "0-2" or "2,0" (different systems may report differently) - if echo "$affinity" | grep -qE '^0,2$|^0-2$|^2,0$'; then - pass "stalld restricted to CPUs 0,2 (affinity: $affinity)" - else - log "⚠ WARNING: stalld affinity ($affinity) may not match requested (0,2) - format may vary" - # Not failing as different systems may report differently - fi + assert_success "stalld restricted to CPUs 0,2" test -n "$(echo "$affinity" | grep -E '^0,2$|^0-2$|^2,0$')" stop_stalld else
@@ -123,11 +109,7 @@ if [ "$num_cpus" -ge 4 ]; then affinity=$(check_affinity "${STALLD_PID}") # Accept various formats: "0-2", "0,1,2", etc. - if echo "$affinity" | grep -qE '0.*1.*2|0-2'; then - pass "stalld restricted to CPU range 0-2 (affinity: $affinity)" - else - log "⚠ WARNING: stalld affinity ($affinity) may not match requested (0-2) - format may vary" - fi + assert_success "stalld restricted to CPU range 0-2" test -n "$(echo "$affinity" | grep -E '0.*1.*2|0-2')" stop_stalld else
@@ -153,11 +135,7 @@ if [ "$num_cpus" -ge 2 ]; then log "ℹ INFO: Found $child_threads threads for stalld" fi - if [ "$affinity" = "$test_cpu" ]; then - pass "stalld process affinity set to CPU $test_cpu" - else - log "⚠ WARNING: stalld affinity ($affinity) doesn't exactly match CPU $test_cpu" - fi + assert_success "stalld process affinity set to CPU $test_cpu" test "$affinity" = "$test_cpu" stop_stalld else
@@ -177,11 +155,7 @@ if [ "$num_cpus" -ge 2 ]; then affinity=$(check_affinity "${STALLD_PID}") - if [ "$affinity" = "0" ]; then - pass "stalld affinity to CPU 0 while monitoring CPU 1" - else - log "⚠ WARNING: stalld affinity ($affinity) doesn't match requested (0)" - fi + assert_success "stalld affinity to CPU 0 while monitoring CPU 1" test "$affinity" = "0" stop_stalld else
@@ -212,11 +186,7 @@ sleep 3 affinity_end=$(check_affinity "${STALLD_PID}") log "ℹ INFO: Affinity after 3s: $affinity_end" -if [ "$affinity_start" = "$affinity_end" ]; then - pass "CPU affinity persisted over time" -else - log "⚠ WARNING: CPU affinity changed (start: $affinity_start, end: $affinity_end)" -fi +assert_success "CPU affinity persisted over time" test "$affinity_start" = "$affinity_end" stop_stalld
tests/functional/test_boost_restoration.sh +5 -25
@@ -46,11 +46,7 @@ if [ -n "${tracked_pid}" ]; then initial_prio=$(get_sched_priority ${tracked_pid}) log "Initial policy: ${initial_policy} (expected: 1=SCHED_FIFO), prio: ${initial_prio}" - if [ "$initial_policy" = "1" ]; then - pass "Initial policy is SCHED_FIFO" - else - log "⚠ WARNING: Initial policy is ${initial_policy}, not SCHED_FIFO (1)" - fi + assert_success "Initial policy is SCHED_FIFO" test "$initial_policy" = "1" # Wait for starvation detection and boosting log "Waiting for starvation detection and boost..."
@@ -61,11 +57,7 @@ if [ -n "${tracked_pid}" ]; then boosted_policy=$(get_sched_policy ${tracked_pid}) log "Policy during boost: ${boosted_policy}" - if [ "$boosted_policy" = "6" ]; then - pass "Task boosted to SCHED_DEADLINE (6)" - else - log "ℹ INFO: Policy is ${boosted_policy} (may be between boost cycles)" - fi + assert_success "Task boosted to SCHED_DEADLINE (6)" test "$boosted_policy" = "6" fi # Wait for boost duration to complete
@@ -102,11 +94,7 @@ if [ -n "${tracked_pid}" ] && [ -f "/proc/${tracked_pid}/sched" ]; then if [ "$final_policy" = "1" ]; then pass "Policy restored to SCHED_FIFO (1)" - if [ "$final_prio" = "$initial_prio" ]; then - pass "Priority restored to ${initial_prio}" - else - log "⚠ INFO: Priority is ${final_prio} (initial was ${initial_prio})" - fi + assert_success "Priority restored to ${initial_prio}" test "$final_prio" = "$initial_prio" else log "ℹ INFO: Final policy is ${final_policy} (task may have exited)" fi
@@ -153,11 +141,7 @@ if [ -n "${tracked_pid}" ]; then final_policy=$(get_sched_policy ${tracked_pid}) log "Policy after boost: ${final_policy}" - if [ "$final_policy" = "0" ]; then - pass "Policy restored to SCHED_OTHER (0)" - else - fail "Policy not restored to SCHED_OTHER (got ${final_policy})" - fi + assert_success "Policy restored to SCHED_OTHER (0)" test "$final_policy" = "0" else log "⚠ INFO: Task exited before restoration check" fi
@@ -199,11 +183,7 @@ if wait_for_boost_detected "${STALLD_LOG}"; then sleep 1 # Verify stalld is still running and didn't crash after task exit - if assert_process_running "${STALLD_PID}" "stalld still running after task exit"; then - pass "stalld handled task exit during boost gracefully" - else - fail "stalld crashed or exited after task died during boost" - fi + assert_success "stalld handled task exit during boost gracefully" kill -0 ${STALLD_PID} else log "⚠ WARNING: No boost detected in this test run"
tests/functional/test_cpu_selection.sh +4 -20
@@ -68,11 +68,7 @@ if [ "$num_cpus" -ge 4 ]; then cpu2_found=1 fi - if [ "$cpu0_found" -eq 1 ] && [ "$cpu2_found" -eq 1 ]; then - pass "stalld monitoring CPUs 0 and 2" - else - fail "stalld not monitoring specified CPUs (0: $cpu0_found, 2: $cpu2_found)" - fi + assert_success "stalld monitoring CPUs 0 and 2" test "$cpu0_found" -eq 1 -a "$cpu2_found" -eq 1 stop_stalld else
@@ -99,11 +95,7 @@ if [ "$num_cpus" -ge 4 ]; then cpu2_found=1 fi - if [ "$cpu0_found" -eq 1 ] && [ "$cpu1_found" -eq 1 ] && [ "$cpu2_found" -eq 1 ]; then - pass "stalld monitoring CPUs 0-2" - else - fail "stalld not monitoring specified CPU range (0: $cpu0_found, 1: $cpu1_found, 2: $cpu2_found)" - fi + assert_success "stalld monitoring CPUs 0-2" test "$cpu0_found" -eq 1 -a "$cpu1_found" -eq 1 -a "$cpu2_found" -eq 1 stop_stalld else
@@ -124,11 +116,7 @@ if [ "$num_cpus" -ge 6 ]; then fi done - if [ "$monitored_cpus" -eq 4 ]; then - pass "stalld monitoring combined CPU specification (0,2-4)" - else - fail "stalld not monitoring all specified CPUs (found $monitored_cpus/4)" - fi + assert_success "stalld monitoring combined CPU specification (0,2-4)" test "$monitored_cpus" -eq 4 stop_stalld else
@@ -148,11 +136,7 @@ if [ "$num_cpus" -ge 2 ]; then start_stalld_with_log "${STALLD_LOG}" -f -v -c 0 -l -t 5 # Check that CPU 1 is NOT being monitored - if ! is_cpu_monitored 1; then - pass "stalld not monitoring non-selected CPU 1" - else - fail "stalld appears to be monitoring CPU 1 when only CPU 0 selected" - fi + assert_success --negate "stalld not monitoring non-selected CPU 1" is_cpu_monitored 1 stop_stalld fi
tests/functional/test_deadline_boosting.sh +1 -13
@@ -41,14 +41,6 @@ if wait_for_boost_detected "${STALLD_LOG}"; then # Verify SCHED_DEADLINE was used assert_log_contains "${STALLD_LOG}" "SCHED_DEADLINE" "SCHED_DEADLINE boosting used (default)" - - # Verify boost happened after threshold - # (starvation logged, then boosting) - if grep -q "starved" "${STALLD_LOG}"; then - pass "Starvation detected before boosting" - else - log "⚠ WARNING: No starvation message before boost" - fi else fail "No boosting detected" log "Log contents:"
@@ -146,11 +138,7 @@ if [ -n "${tracked_pid}" ]; then # Verify task made progress (context switches increased) ctxt_delta=$((ctxt_after - ctxt_before)) - if [ ${ctxt_delta} -gt 0 ]; then - pass "Task made progress during boost (${ctxt_delta} context switches)" - else - fail "No progress during boost (${ctxt_delta} context switches)" - fi + assert_success "Task made progress during boost" test ${ctxt_delta} -gt 0 else log "⚠ WARNING: Could not track starved task PID for progress verification" fi
tests/functional/test_fifo_priority_starvation.sh +12 -32
56 lines not on this page · raw message
tests/functional/test_foreground.sh +3 -15
@@ -35,11 +35,7 @@ if assert_process_running "${STALLD_PID}" "stalld should be running"; then else # On modern systems with session leaders, ppid might not be 1 # Just verify it's not our shell's PID - if [ "${PARENT_PID}" != "$$" ]; then - pass "stalld daemonized (parent is not test shell)" - else - fail "stalld did not daemonize (parent is test shell)" - fi + assert_success "stalld daemonized (parent is not test shell)" test "${PARENT_PID}" != "$$" fi fi
@@ -59,11 +55,7 @@ if assert_process_running "${STALLD_PID}" "stalld should be running with -f"; th # The parent might be the subshell from start_stalld, not directly our shell # So we just verify it's not PID 1 - if [ "${PARENT_PID}" != "1" ]; then - pass "stalld did not daemonize with -f (parent is not init)" - else - fail "stalld daemonized even with -f flag" - fi + assert_success "stalld did not daemonize with -f (parent is not init)" test "${PARENT_PID}" != "1" fi stop_stalld
@@ -77,11 +69,7 @@ sleep 2 if assert_process_running "${STALLD_PID}" "stalld should be running with -v"; then PARENT_PID=$(ps -o ppid= -p ${STALLD_PID} 2>/dev/null | tr -d ' ') - if [ "${PARENT_PID}" != "1" ]; then - pass "-v implies foreground mode" - else - fail "-v should imply foreground mode" - fi + assert_success "-v implies foreground mode" test "${PARENT_PID}" != "1" fi stop_stalld
tests/functional/test_logging_destinations.sh +1 -5
@@ -149,11 +149,7 @@ start_stalld_with_log "${LOG_FILE}" -f -v -k -s -l -t 5 if assert_process_running "${STALLD_PID}" "stalld with combined logging should be running"; then # Verify verbose output - if [ -s "${LOG_FILE}" ]; then - pass "combined logging produces output" - else - fail "no output with combined logging" - fi + assert_success "combined logging produces output" test -s "${LOG_FILE}" fi stop_stalld
tests/functional/test_pidfile.sh +4 -20
@@ -34,11 +34,7 @@ for pidfile in /var/run/stalld.pid /run/stalld.pid; do # Verify PID matches pid_from_file=$(cat "$pidfile") - if [ "$pid_from_file" = "${STALLD_PID}" ]; then - pass "Default pidfile contains correct PID" - else - fail "Default pidfile PID ($pid_from_file) doesn't match stalld PID (${STALLD_PID})" - fi + assert_success "Default pidfile contains correct PID" test "$pid_from_file" = "${STALLD_PID}" break fi done
@@ -70,11 +66,7 @@ if [ -f "${custom_pidfile}" ]; then # Verify content pid_from_file=$(cat "${custom_pidfile}") - if [ "$pid_from_file" = "${STALLD_PID}" ]; then - pass "Custom pidfile contains correct PID ($pid_from_file)" - else - fail "Custom pidfile PID ($pid_from_file) doesn't match stalld PID (${STALLD_PID})" - fi + assert_success "Custom pidfile contains correct PID" test "$pid_from_file" = "${STALLD_PID}" else fail "Custom pidfile not created at ${custom_pidfile}" fi
@@ -107,11 +99,7 @@ if [ -f "${tmp_pidfile}" ]; then pass "Pidfile created in /tmp directory" pid_from_file=$(cat "${tmp_pidfile}") - if [ "$pid_from_file" = "${STALLD_PID}" ]; then - pass "/tmp pidfile contains correct PID" - else - fail "/tmp pidfile has incorrect PID" - fi + assert_success "/tmp pidfile contains correct PID" test "$pid_from_file" = "${STALLD_PID}" else fail "Pidfile not created in /tmp" fi
@@ -135,11 +123,7 @@ if [ -f "${fg_pidfile}" ]; then pass "Pidfile created in foreground mode" pid_from_file=$(cat "${fg_pidfile}") - if [ "$pid_from_file" = "${STALLD_PID}" ]; then - pass "Foreground mode pidfile contains correct PID" - else - fail "Foreground mode pidfile has incorrect PID" - fi + assert_success "Foreground mode pidfile contains correct PID" test "$pid_from_file" = "${STALLD_PID}" else log "⚠ WARNING: Pidfile not created in foreground mode (may be expected)" fi
tests/functional/test_starvation_detection.sh +1 -5
@@ -83,11 +83,7 @@ if [ -n "${tracked_pid}" ]; then log "Context switches after 2s: ${ctxt_after}" ctxt_delta=$((ctxt_after - ctxt_before)) - if [ ${ctxt_delta} -lt 5 ]; then - pass "Context switch count remained low (delta: ${ctxt_delta})" - else - fail "Context switches increased significantly (delta: ${ctxt_delta})" - fi + assert_success "Context switch count remained low" test ${ctxt_delta} -lt 5 else log "⚠ WARNING: Could not find starved task PIDs to verify context switches" fi
tests/helpers/test_helpers.sh +33 -1
@@ -248,6 +248,38 @@ assert_stalld_rejects() { rm -f "${log}" } +# Assert that a command exits successfully (or unsuccessfully with --negate). +# +# Without --negate the assertion passes when the command returns zero. +# With --negate the assertion passes when the command returns non-zero. +# +# Usage: assert_success <message> <command> [args...] +# assert_success --negate <message> <command> [args...] +assert_success() { + local negate=0 + if [ "$1" = "--negate" ]; then + negate=1 + shift + fi + local message=$1 + shift + + "$@" >/dev/null 2>&1 + local success=$(( $? == 0 )) + + if [ $negate -eq 1 ]; then + success=$((1 - success)) + fi + + if [ $success -eq 1 ]; then + pass "${message}" + return 0 + else + fail "${message}" + return 1 + fi +} + # Record a test pass with a description message. # # Usage: pass "description"
@@ -1299,7 +1331,7 @@ start_starvation_gen() { # Export functions for use in tests export -f start_test end_test test_section cleanup_scenario find_starved_child -export -f assert_starvation_detected assert_boost_detected assert_stalld_rejects assert_log_contains +export -f assert_starvation_detected assert_boost_detected assert_stalld_rejects assert_log_contains assert_success export -f pass fail assert_equals assert_contains assert_not_contains export -f assert_file_exists assert_file_not_exists export -f assert_process_running assert_process_not_running
Signature · 1 line
2.54.0
PATCH 1/1 20/33] tests: Replace wait conditionals with asserts
In reply to Wander Lairson CostaSeveral test files currently use custom conditional blocks to wait for starvation or boost detection, manually handling success and failure logging. Replace these verbose conditionals with flat calls to the existing assert_starvation_detected and assert_boost_detected helpers. The previously nested log assertions are subsequently moved to the top level. The assert helpers provide identical behavior by passing on success or failing and dumping the log on error.
Flattening these blocks deduplicates logic already encapsulated in the test helpers and removes unnecessary nesting. This refactoring is a direct prerequisite for introducing fail-fast semantics to the test framework, where calling fail will immediately terminate the test.
--- tests/functional/test_deadline_boosting.sh | 12 ++---------- tests/functional/test_fifo_boosting.sh | 12 ++---------- .../functional/test_fifo_priority_starvation.sh | 16 +++------------- tests/functional/test_starvation_detection.sh | 16 +++------------- 4 files changed, 10 insertions(+), 46 deletions(-)
tests/functional/test_deadline_boosting.sh +2 -10
@@ -36,16 +36,8 @@ start_starvation_gen -c ${TEST_CPU} -p 80 -n 2 -d ${starvation_duration} # Wait for boosting log "Waiting for boost detection..." -if wait_for_boost_detected "${STALLD_LOG}"; then - pass "Boosting occurred" - - # Verify SCHED_DEADLINE was used - assert_log_contains "${STALLD_LOG}" "SCHED_DEADLINE" "SCHED_DEADLINE boosting used (default)" -else - fail "No boosting detected" - log "Log contents:" - cat "${STALLD_LOG}" -fi +assert_boost_detected "${STALLD_LOG}" "Boosting occurred" +assert_log_contains "${STALLD_LOG}" "SCHED_DEADLINE" "SCHED_DEADLINE boosting used (default)" # Cleanup cleanup_scenario "${STARVE_PID}"
tests/functional/test_fifo_boosting.sh +2 -10
@@ -34,16 +34,8 @@ start_stalld_with_log "${STALLD_LOG}" -f -v -g 1 -N -F -A -t $threshold -c ${TES # Wait for boosting log "Waiting for boost detection..." -if wait_for_boost_detected "${STALLD_LOG}"; then - pass "Boosting occurred with -F flag" - - # Verify SCHED_FIFO was used - assert_log_contains "${STALLD_LOG}" "SCHED_FIFO" "SCHED_FIFO boosting used (as requested by -F)" -else - fail "No boosting detected with -F flag" - log "Log contents:" - cat "${STALLD_LOG}" -fi +assert_boost_detected "${STALLD_LOG}" "Boosting occurred with -F flag" +assert_log_contains "${STALLD_LOG}" "SCHED_FIFO" "SCHED_FIFO boosting used (as requested by -F)" # Cleanup cleanup_scenario "${STARVE_PID}"
tests/functional/test_fifo_priority_starvation.sh +3 -13
@@ -40,19 +40,9 @@ start_stalld_with_log "${STALLD_LOG}" -f -v -l -t $threshold -c ${TEST_CPU} -a $ # Wait for starvation detection log "Waiting for starvation detection..." -if wait_for_starvation_detected "${STALLD_LOG}"; then - pass "FIFO-on-FIFO starvation detected" - - # Verify correct CPU is logged - assert_log_contains "${STALLD_LOG}" "starved on CPU ${TEST_CPU}" "Correct CPU ID logged (CPU ${TEST_CPU})" - - # Verify duration is logged - assert_log_contains "${STALLD_LOG}" "starved on CPU ${TEST_CPU} for [0-9]" "Starvation duration logged" -else - fail "FIFO-on-FIFO starvation not detected" - log "Log contents:" - cat "${STALLD_LOG}" -fi +assert_starvation_detected "${STALLD_LOG}" "FIFO-on-FIFO starvation detected" +assert_log_contains "${STALLD_LOG}" "starved on CPU ${TEST_CPU}" "Correct CPU ID logged (CPU ${TEST_CPU})" +assert_log_contains "${STALLD_LOG}" "starved on CPU ${TEST_CPU} for [0-9]" "Starvation duration logged" # Cleanup cleanup_scenario "${STARVE_PID}"
tests/functional/test_starvation_detection.sh +3 -13
@@ -35,19 +35,9 @@ start_stalld_with_log "${STALLD_LOG}" -f -v -N -l -t $threshold -c ${TEST_CPU} - # Wait for starvation detection log "Waiting for starvation detection..." -if wait_for_starvation_detected "${STALLD_LOG}"; then - pass "Starvation detected" - - # Verify correct CPU is logged - assert_log_contains "${STALLD_LOG}" "starved on CPU ${TEST_CPU}" "Correct CPU ID logged (CPU ${TEST_CPU})" - - # Verify duration is logged - assert_log_contains "${STALLD_LOG}" "starved on CPU ${TEST_CPU} for [0-9]" "Starvation duration logged" -else - fail "Starvation not detected" - log "Log contents:" - cat "${STALLD_LOG}" -fi +assert_starvation_detected "${STALLD_LOG}" "Starvation detected" +assert_log_contains "${STALLD_LOG}" "starved on CPU ${TEST_CPU}" "Correct CPU ID logged (CPU ${TEST_CPU})" +assert_log_contains "${STALLD_LOG}" "starved on CPU ${TEST_CPU} for [0-9]" "Starvation duration logged" # Cleanup cleanup_scenario "${STARVE_PID}"
Signature · 1 line
2.54.0
PATCH 1/1 21/33] tests: Remove if-wrappers around assert calls
In reply to Wander Lairson CostaThis is the second commit in a series introducing fail-fast semantics to the test suite. Currently, several test scripts wrap assertion calls in conditional blocks to guard subsequent execution steps.
Since the guarded test logic only makes sense when the initial assertion passes, these conditional wrappers create unnecessary nesting. Once the failure mechanism is updated to be immediately fatal in the next commit, these protective conditional structures will become completely redundant.
Removing the conditional structures and flattening the code flow across the foreground, logging, pidfile, and starvation test scripts improves overall readability and maintainability. Branches handling non-fatal conditions such as skips or informational warnings have been preserved in their original form.
--- tests/functional/test_foreground.sh | 36 +++++++---------- tests/functional/test_logging_destinations.sh | 20 +++------- tests/functional/test_pidfile.sh | 39 +++++-------------- tests/functional/test_starvation_detection.sh | 7 +--- 4 files changed, 29 insertions(+), 73 deletions(-)
tests/functional/test_foreground.sh +13 -23
@@ -26,17 +26,13 @@ test_section "Test 1: stalld daemonizes by default" start_stalld -l -t 5 sleep 2 -# Check if stalld is running -if assert_process_running "${STALLD_PID}" "stalld should be running"; then - # Check parent process - should be init (PID 1) or systemd - PARENT_PID=$(ps -o ppid= -p ${STALLD_PID} 2>/dev/null | tr -d ' ') - if [ "${PARENT_PID}" == "1" ] || [ "${PARENT_PID}" == "2" ]; then - pass "stalld daemonized (parent is init/kthreadd)" - else - # On modern systems with session leaders, ppid might not be 1 - # Just verify it's not our shell's PID - assert_success "stalld daemonized (parent is not test shell)" test "${PARENT_PID}" != "$$" - fi +assert_process_running "${STALLD_PID}" "stalld should be running" + +PARENT_PID=$(ps -o ppid= -p ${STALLD_PID} 2>/dev/null | tr -d ' ') +if [ "${PARENT_PID}" == "1" ] || [ "${PARENT_PID}" == "2" ]; then + pass "stalld daemonized (parent is init/kthreadd)" +else + assert_success "stalld daemonized (parent is not test shell)" test "${PARENT_PID}" != "$$" fi stop_stalld
@@ -48,15 +44,10 @@ test_section "Test 2: stalld stays in foreground with -f" start_stalld -f -l -t 5 sleep 2 -# Check if stalld is running -if assert_process_running "${STALLD_PID}" "stalld should be running with -f"; then - # With -f, it should NOT daemonize, parent should be our shell - PARENT_PID=$(ps -o ppid= -p ${STALLD_PID} 2>/dev/null | tr -d ' ') +assert_process_running "${STALLD_PID}" "stalld should be running with -f" - # The parent might be the subshell from start_stalld, not directly our shell - # So we just verify it's not PID 1 - assert_success "stalld did not daemonize with -f (parent is not init)" test "${PARENT_PID}" != "1" -fi +PARENT_PID=$(ps -o ppid= -p ${STALLD_PID} 2>/dev/null | tr -d ' ') +assert_success "stalld did not daemonize with -f (parent is not init)" test "${PARENT_PID}" != "1" stop_stalld
@@ -66,11 +57,10 @@ test_section "Test 3: -v implies foreground mode" start_stalld -v -l -t 5 sleep 2 -if assert_process_running "${STALLD_PID}" "stalld should be running with -v"; then - PARENT_PID=$(ps -o ppid= -p ${STALLD_PID} 2>/dev/null | tr -d ' ') +assert_process_running "${STALLD_PID}" "stalld should be running with -v" - assert_success "-v implies foreground mode" test "${PARENT_PID}" != "1" -fi +PARENT_PID=$(ps -o ppid= -p ${STALLD_PID} 2>/dev/null | tr -d ' ') +assert_success "-v implies foreground mode" test "${PARENT_PID}" != "1" stop_stalld
tests/functional/test_logging_destinations.sh +5 -15
@@ -35,17 +35,9 @@ CLEANUP_FILES+=("${LOG_FILE}") start_stalld_with_log "${LOG_FILE}" -f -v -l -t 5 -if assert_process_running "${STALLD_PID}" "stalld should be running"; then - # Check that output was written to our log file - if [ -s "${LOG_FILE}" ]; then - pass "verbose mode produces output" - - # Should contain initialization messages - assert_log_contains "${LOG_FILE}" "stalld\|version\|monitoring" "output contains expected messages" - else - fail "no output in verbose mode" - fi -fi +assert_process_running "${STALLD_PID}" "stalld should be running" +assert_success "verbose mode produces output" test -s "${LOG_FILE}" +assert_log_contains "${LOG_FILE}" "stalld\|version\|monitoring" "output contains expected messages" stop_stalld
@@ -147,10 +139,8 @@ CLEANUP_FILES+=("${LOG_FILE}") start_stalld_with_log "${LOG_FILE}" -f -v -k -s -l -t 5 -if assert_process_running "${STALLD_PID}" "stalld with combined logging should be running"; then - # Verify verbose output - assert_success "combined logging produces output" test -s "${LOG_FILE}" -fi +assert_process_running "${STALLD_PID}" "stalld with combined logging should be running" +assert_success "combined logging produces output" test -s "${LOG_FILE}" stop_stalld
tests/functional/test_pidfile.sh +10 -29
@@ -60,16 +60,10 @@ log "Starting stalld with custom pidfile: ${custom_pidfile}" start_stalld -l -t 5 --pidfile "${custom_pidfile}" sleep 2 -# Verify pidfile was created -if [ -f "${custom_pidfile}" ]; then - pass "Custom pidfile created at ${custom_pidfile}" +assert_file_exists "${custom_pidfile}" "Custom pidfile created at ${custom_pidfile}" - # Verify content - pid_from_file=$(cat "${custom_pidfile}") - assert_success "Custom pidfile contains correct PID" test "$pid_from_file" = "${STALLD_PID}" -else - fail "Custom pidfile not created at ${custom_pidfile}" -fi +pid_from_file=$(cat "${custom_pidfile}") +assert_success "Custom pidfile contains correct PID" test "$pid_from_file" = "${STALLD_PID}" # Test 3: Verify pidfile removed on clean shutdown test_section "Test 3: Verify pidfile removed on clean shutdown"
@@ -95,14 +89,10 @@ log "Starting stalld with /tmp pidfile: ${tmp_pidfile}" start_stalld -l -t 5 --pidfile "${tmp_pidfile}" sleep 2 -if [ -f "${tmp_pidfile}" ]; then - pass "Pidfile created in /tmp directory" +assert_file_exists "${tmp_pidfile}" "Pidfile created in /tmp directory" - pid_from_file=$(cat "${tmp_pidfile}") - assert_success "/tmp pidfile contains correct PID" test "$pid_from_file" = "${STALLD_PID}" -else - fail "Pidfile not created in /tmp" -fi +pid_from_file=$(cat "${tmp_pidfile}") +assert_success "/tmp pidfile contains correct PID" test "$pid_from_file" = "${STALLD_PID}" stop_stalld
@@ -151,20 +141,11 @@ log "Starting stalld with readable pidfile: ${readable_pidfile}" start_stalld -l -t 5 --pidfile "${readable_pidfile}" sleep 2 -if [ -f "${readable_pidfile}" ]; then - # Try to read the pidfile as a regular user would - if cat "${readable_pidfile}" > /dev/null 2>&1; then - pass "Pidfile is readable" +assert_file_exists "${readable_pidfile}" "Pidfile created" +assert_success "Pidfile is readable" cat "${readable_pidfile}" - # Check permissions - perms=$(stat -c "%a" "${readable_pidfile}" 2>/dev/null || stat -f "%Lp" "${readable_pidfile}" 2>/dev/null) - log "ℹ INFO: Pidfile permissions: $perms" - else - fail "Pidfile not readable" - fi -else - fail "Pidfile not created" -fi +perms=$(stat -c "%a" "${readable_pidfile}" 2>/dev/null || stat -f "%Lp" "${readable_pidfile}" 2>/dev/null) +log "ℹ INFO: Pidfile permissions: $perms" stop_stalld
tests/functional/test_starvation_detection.sh +1 -6
@@ -186,12 +186,7 @@ start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 3 # Wait for task to exit sleep 5 -# Verify stalld is still running (didn't crash) -if assert_process_running "${STALLD_PID}" "stalld still running after task exit"; then - pass "stalld handled task exit gracefully" -else - fail "stalld crashed or exited unexpectedly" -fi +assert_process_running "${STALLD_PID}" "stalld still running after task exit" # Check for error messages assert_log_contains --negate --ignore-case "${STALLD_LOG}" "error\|segfault\|crash" "No error messages in log"
Signature · 1 line
2.54.0
PATCH 1/1 22/33] tests: Abort immediately on test failure
In reply to Wander Lairson CostaThis is the third commit in a series introducing fail-fast semantics to the test suite. By modifying the fail helper to invoke end_test and exit immediately, tests will now abort on the first failure. This provides faster feedback and produces much shorter, more focused error logs rather than cascading failures.
To support exiting directly from the fail helper, diagnostic logging in seven assert functions is relocated to occur before the failure is triggered. Dead return statements are removed from all twelve assert functions since fail never returns and the return after pass is unnecessary. Additionally, the stalld rejection assertion now registers its temporary file in the cleanup tracking array to ensure trap-based exit handlers correctly reap the resource even when the test aborts prematurely.
--- tests/helpers/test_helpers.sh | 41 ++++++++++------------------------- 1 file changed, 11 insertions(+), 30 deletions(-)
tests/helpers/test_helpers.sh +11 -30
@@ -164,12 +164,10 @@ assert_starvation_detected() { if wait_for_starvation_detected "${log_file}" "${timeout}" "${cpu}"; then pass "${message}" - return 0 else - fail "${message}" log "Log contents:" cat "${log_file}" - return 1 + fail "${message}" fi }
@@ -182,12 +180,10 @@ assert_boost_detected() { if wait_for_boost_detected "${log_file}" "${timeout}"; then pass "${message}" - return 0 else - fail "${message}" log "Log contents:" cat "${log_file}" - return 1 + fail "${message}" fi }
@@ -217,15 +213,13 @@ assert_log_contains() { if [ $found -eq 1 ]; then pass "${message}" - return 0 else - fail "${message}" if [ $negate -eq 1 ]; then log " Pattern '${pattern}' found in ${log_file} but should not be" else log " Pattern '${pattern}' not found in ${log_file}" fi - return 1 + fail "${message}" fi }
@@ -236,14 +230,15 @@ assert_stalld_rejects() { shift local log="/tmp/stalld_reject_$$.log" + CLEANUP_FILES+=("${log}") timeout 5 ${TEST_ROOT}/../stalld ${BACKEND_FLAG} "$@" > "${log}" 2>&1 local ret=$? if [ $ret -ne 0 ] && [ $ret -ne 124 ]; then pass "${message}" else - fail "${message}" log "stalld output:" cat "${log}" + fail "${message}" fi rm -f "${log}" }
@@ -273,10 +268,8 @@ assert_success() { if [ $success -eq 1 ]; then pass "${message}" - return 0 else fail "${message}" - return 1 fi }
@@ -289,13 +282,15 @@ pass() { TEST_PASSED=$((TEST_PASSED + 1)) } -# Record a test failure with a description message. +# Record a test failure and abort the test immediately. # # Usage: fail "description" fail() { local message=${1:-""} log "✗ FAIL: ${message}" TEST_FAILED=$((TEST_FAILED + 1)) + end_test + exit 1 } # Assert functions
@@ -306,12 +301,10 @@ assert_equals() { if [ "${expected}" == "${actual}" ]; then pass "${message}" - return 0 else - fail "${message}" log " Expected: ${expected}" log " Actual: ${actual}" - return 1 + fail "${message}" fi }
@@ -322,11 +315,9 @@ assert_contains() { if echo "${haystack}" | grep -q "${needle}"; then pass "${message}" - return 0 else - fail "${message}" log " String '${needle}' not found" - return 1 + fail "${message}" fi }
@@ -337,11 +328,9 @@ assert_not_contains() { if ! echo "${haystack}" | grep -q "${needle}"; then pass "${message}" - return 0 else - fail "${message}" log " String '${needle}' found but should not be present" - return 1 + fail "${message}" fi }
@@ -351,10 +340,8 @@ assert_file_exists() { if [ -f "${file}" ]; then pass "${message}" - return 0 else fail "${message}" - return 1 fi }
@@ -364,10 +351,8 @@ assert_file_not_exists() { if [ ! -f "${file}" ]; then pass "${message}" - return 0 else fail "${message}" - return 1 fi }
@@ -377,10 +362,8 @@ assert_process_running() { if kill -0 ${pid} 2>/dev/null; then pass "${message}" - return 0 else fail "${message}" - return 1 fi }
@@ -390,10 +373,8 @@ assert_process_not_running() { if ! kill -0 ${pid} 2>/dev/null; then pass "${message}" - return 0 else fail "${message}" - return 1 fi }
Signature · 1 line
2.54.0
PATCH 1/1 23/33] tests: Remove dead code after making fail() fatal
In reply to Wander Lairson CostaThe previous commit making test failures abort immediately rendered several teardown and reporting mechanisms obsolete. Since any failing test now exits the script immediately, end-of-run test summaries reporting the total number of failures will always evaluate to zero during a successful run. These redundant summary banners and failure counts have been removed across all multi-section tests.
This cleanup also drops trailing completion messages and empty log lines that previously cluttered the test output. Unreachable cleanup steps and return statements situated directly after failure calls are now safely discarded. Furthermore, unconditional debug log dumps on success paths have been stripped to keep successful test runs quiet.
Finally, rotting references to specific source code line numbers and redundant backend limitation notes were purged. The relevant backend constraints remain appropriately documented inline where they apply.
--- tests/functional/test_affinity.sh | 3 --- tests/functional/test_backend_selection.sh | 2 -- tests/functional/test_boost_duration.sh | 3 --- tests/functional/test_boost_period.sh | 6 ------ tests/functional/test_boost_restoration.sh | 6 ------ tests/functional/test_boost_runtime.sh | 3 --- tests/functional/test_deadline_boosting.sh | 6 ------ tests/functional/test_fifo_boosting.sh | 6 ------ tests/functional/test_fifo_priority_starvation.sh | 13 ------------- tests/functional/test_force_fifo.sh | 3 --- tests/functional/test_idle_detection.sh | 6 ------ tests/functional/test_log_only.sh | 6 ------ tests/functional/test_pidfile.sh | 3 --- tests/functional/test_starvation_detection.sh | 6 ------ tests/functional/test_starvation_threshold.sh | 3 --- tests/functional/test_task_merging.sh | 9 --------- 16 files changed, 84 deletions(-)
tests/functional/test_affinity.sh +0 -3
@@ -190,7 +190,4 @@ assert_success "CPU affinity persisted over time" test "$affinity_start" = "$aff stop_stalld -log "" -log "All affinity tests completed" - end_test
tests/functional/test_backend_selection.sh +0 -2
@@ -40,8 +40,6 @@ test_backend_flag() { if ! wait_for_stalld_ready "${log_file}" 15; then fail "stalld failed to start (${description})" - stop_stalld - return 1 fi assert_log_contains "${log_file}" "${expected_msg}" "${description}"
tests/functional/test_boost_duration.sh +0 -3
@@ -112,7 +112,4 @@ assert_stalld_rejects "Zero duration rejected with error" -f -v -t ${threshold} log "Testing with duration = -5" assert_stalld_rejects "Negative duration rejected with error" -f -v -t ${threshold} -d -5 -log "" -log "All boost duration tests completed" - end_test
tests/functional/test_boost_period.sh +0 -6
@@ -106,10 +106,4 @@ test_section "Test 6: Invalid period value (negative)" assert_stalld_rejects "Negative period rejected with error" -f -v -t $threshold -p -1000000 -#============================================================================= -# Final Summary -#============================================================================= -test_section "Test Summary" -log "Total failures: ${TEST_FAILED}" - end_test
tests/functional/test_boost_restoration.sh +0 -6
@@ -192,10 +192,4 @@ fi # Cleanup cleanup_scenario "${STARVE_PID}" -#============================================================================= -# Final Summary -#============================================================================= -test_section "Test Summary" -log "Total failures: ${TEST_FAILED}" - end_test
tests/functional/test_boost_runtime.sh +0 -3
@@ -127,7 +127,4 @@ test_section "Test 7: Invalid runtime value (negative)" log "Testing with runtime = -5000" assert_stalld_rejects "Negative runtime rejected with error" -f -v -t ${threshold} -r -5000 -log "" -log "All boost runtime tests completed" - end_test
tests/functional/test_deadline_boosting.sh +0 -6
@@ -197,10 +197,4 @@ else cleanup_scenario "${STARVE_PID0}" "${STARVE_PID1}" fi -#============================================================================= -# Final Summary -#============================================================================= -test_section "Test Summary" -log "Total failures: ${TEST_FAILED}" - end_test
tests/functional/test_fifo_boosting.sh +0 -6
@@ -131,10 +131,4 @@ fi # Cleanup cleanup_scenario "${STARVE_PID}" -#============================================================================= -# Final Summary -#============================================================================= -test_section "Test Summary" -log "Total failures: ${TEST_FAILED}" - end_test
tests/functional/test_fifo_priority_starvation.sh +0 -13
@@ -204,17 +204,4 @@ fi # Cleanup cleanup_scenario "${STARVE_PID}" -#============================================================================= -# Final Summary -#============================================================================= -test_section "Test Summary" -log "Total failures: ${TEST_FAILED}" - -if [ -n "${STALLD_TEST_BACKEND}" ] && [ "${STALLD_TEST_BACKEND}" = "queue_track" ]; then - log "" - log "NOTE: queue_track backend has known limitations with SCHED_FIFO task detection." - log " For reliable FIFO-on-FIFO testing, use the sched_debug backend:" - log " ./test_fifo_priority_starvation.sh -b sched_debug" -fi - end_test
tests/functional/test_force_fifo.sh +0 -3
@@ -112,7 +112,4 @@ test_section "Test 5: Single-threaded mode with FIFO (should fail)" log "Testing single-threaded mode (-O) with -F (should exit)" assert_stalld_rejects "Single-threaded mode rejected FIFO" -f -v -c "${TEST_CPU}" -t ${threshold} -F -O -log "" -log "All force FIFO tests completed" - end_test
tests/functional/test_idle_detection.sh +0 -6
@@ -117,10 +117,4 @@ else cleanup_scenario "${STARVE_PID}" fi -#============================================================================= -# Final Summary -#============================================================================= -test_section "Test Summary" -log "Total failures: ${TEST_FAILED}" - end_test
tests/functional/test_log_only.sh +0 -6
@@ -66,10 +66,4 @@ assert_log_contains --negate "${LOG_FILE}" "boosted" "stalld did not boost in lo # Cleanup cleanup_scenario "${STARVGEN_PID}" -echo "" -echo "Log file contents:" -echo "==================" -cat "${LOG_FILE}" -echo "==================" - end_test
tests/functional/test_pidfile.sh +0 -3
@@ -149,7 +149,4 @@ log "ℹ INFO: Pidfile permissions: $perms" stop_stalld -log "" -log "All pidfile tests completed" - end_test
tests/functional/test_starvation_detection.sh +0 -6
@@ -193,10 +193,4 @@ assert_log_contains --negate --ignore-case "${STALLD_LOG}" "error\|segfault\|cra stop_stalld -#============================================================================= -# Final Summary -#============================================================================= -test_section "Test Summary" -log "Total failures: ${TEST_FAILED}" - end_test
tests/functional/test_starvation_threshold.sh +0 -3
@@ -115,7 +115,4 @@ assert_stalld_rejects "Zero threshold rejected with error" -f -v -t 0 log "Testing with threshold = -5" assert_stalld_rejects "Negative threshold rejected with error" -f -v -t -5 -log "" -log "All starvation threshold tests completed" - end_test
tests/functional/test_task_merging.sh +0 -9
@@ -226,13 +226,4 @@ else cleanup_scenario "${STARVE_PID0}" "${STARVE_PID1}" fi -#============================================================================= -# Final Summary -#============================================================================= -test_section "Test Summary" -log "Task merging function: merge_tasks_info() in stalld.c:370-397" -log "Merge logic: if (PID == PID && ctxsw == ctxsw) preserve timestamp" -log "" -log "Total failures: ${TEST_FAILED}" - end_test
Signature · 1 line
2.54.0
PATCH 1/1 25/33] tests: Extract wait_for_process_exit helper
In reply to Wander Lairson CostaThe SIGTERM-poll-SIGKILL-poll termination sequence is currently duplicated in both stop_stalld() and cleanup() with identical inline polling loops. This refactors the teardown sequence by extracting the polling logic into a new wait_for_process_exit() helper.
The new helper polls process_alive() at 0.1 second intervals, returning 0 on successful exit or 1 upon timeout. Utilizing this helper eliminates the duplicated logic, removes a redundant sleep in cleanup(), and accelerates test teardown by checking process state more frequently than the previous one second intervals, all while maintaining the existing five second total timeout. Furthermore, stop_stalld() is simplified with an early return guard to reduce nesting. The helper is explicitly exported to ensure availability across the test environment.
--- tests/helpers/test_helpers.sh | 85 +++++++++++++++++------------------ 1 file changed, 42 insertions(+), 43 deletions(-)
tests/helpers/test_helpers.sh +42 -43
@@ -360,6 +360,28 @@ process_alive() { kill -0 "$1" 2>/dev/null } +# Poll for a process to exit within a timeout. +# Returns 0 if the process exits, 1 if still alive after timeout. +# +# Usage: wait_for_process_exit <pid> <timeout_seconds> +wait_for_process_exit() { + local pid="$1" + local timeout="$2" + local elapsed=0 + + while process_alive "${pid}" && [ ${elapsed} -lt ${timeout} ]; do + for i in $(seq 1 10); do + sleep 0.1 + if ! process_alive "${pid}"; then + return 0 + fi + done + elapsed=$((elapsed + 1)) + done + + ! process_alive "${pid}" +} + send_signal() { kill -"$1" "$2" 2>/dev/null || true }
@@ -537,32 +559,23 @@ start_stalld() { # SIGKILL if needed. Guarantees the process is dead before # returning so callers do not need post-stop sleeps. stop_stalld() { - if [ -n "${STALLD_PID}" ]; then - if process_alive ${STALLD_PID}; then - # Try graceful shutdown first (SIGTERM) - send_signal TERM ${STALLD_PID} - - # Poll for graceful exit (up to 5 seconds) - local timeout=5 - local elapsed=0 - while process_alive ${STALLD_PID} && [ ${elapsed} -lt ${timeout} ]; do - sleep 1 - elapsed=$((elapsed + 1)) - done + if [ -z "${STALLD_PID}" ]; then + return + fi + + if process_alive ${STALLD_PID}; then + # Try graceful shutdown first (SIGTERM) + send_signal TERM ${STALLD_PID} + # Poll for graceful exit (up to 5 seconds) + if ! wait_for_process_exit ${STALLD_PID} 5; then # Escalate to SIGKILL if still running - if process_alive ${STALLD_PID}; then - send_signal KILL ${STALLD_PID} - # Poll for forced termination (up to 5 seconds) - elapsed=0 - while process_alive ${STALLD_PID} && [ ${elapsed} -lt ${timeout} ]; do - sleep 1 - elapsed=$((elapsed + 1)) - done - fi + send_signal KILL ${STALLD_PID} + # Poll for forced termination (up to 5 seconds) + wait_for_process_exit ${STALLD_PID} 5 fi - STALLD_PID="" fi + STALLD_PID="" } # Kill any existing stalld processes (cleanup from previous runs)
@@ -607,31 +620,17 @@ cleanup() { # Stop stalld stop_stalld - # Small delay to let processes terminate - sleep 0.2 - # Kill any tracked processes # Use SIGKILL (-9) and ignore EPERM errors (process may have different privileges) for pid in "${CLEANUP_PIDS[@]}"; do if [ -n "${pid}" ] && [ "${pid}" -gt 0 ] 2>/dev/null; then # Check if process exists - if process_alive ${pid}; then - send_signal TERM ${pid} - - local timeout=5 - local elapsed=0 - while process_alive ${pid} && [ ${elapsed} -lt ${timeout} ]; do - sleep 1 - elapsed=$((elapsed + 1)) - done - - if process_alive ${pid}; then - send_signal KILL ${pid} - elapsed=0 - while process_alive ${pid} && [ ${elapsed} -lt ${timeout} ]; do - sleep 1 - elapsed=$((elapsed + 1)) - done + if process_alive "${pid}"; then + send_signal TERM "${pid}" + + if ! wait_for_process_exit "${pid}" 5; then + send_signal KILL "${pid}" + wait_for_process_exit "${pid}" 5 fi fi fi
@@ -1323,7 +1322,7 @@ export -f start_test end_test test_section cleanup_scenario find_starved_child export -f assert_starvation_detected assert_boost_detected assert_stalld_rejects assert_log_contains assert_success export -f pass fail assert_equals assert_contains assert_not_contains export -f assert_file_exists assert_file_not_exists -export -f process_alive send_signal assert_process_running assert_process_not_running +export -f process_alive wait_for_process_exit send_signal assert_process_running assert_process_not_running export -f start_stalld stop_stalld kill_existing_stalld cleanup export -f wait_for_log_message wait_for_stalld_ready wait_for_starvation_detected wait_for_boost_detected wait_for_n_log_matches export -f get_thread_policy get_thread_priority
Signature · 1 line
2.54.0
PATCH 1/1 24/33] tests: Introduce and adopt process helpers
In reply to Wander Lairson CostaRaw kill invocations scattered across the test suite obscure the intent of the code. Using kill with signal zero acts as a liveness check rather than signal delivery, while best-effort process cleanup often requires ignoring errors.
This change introduces two dedicated helper functions to make these patterns self-documenting. The process_alive function wraps the zero signal check and preserves the exit code for use in conditionals and loops. The send_signal function wraps standard signal delivery using named signals like TERM or KILL, operating in a fire-and-forget manner where failure is acceptable. These functions are kept separate to distinguish predicates from cleanup actions, and both are exported via export -f for availability in subshells.
Existing scripts including test_helpers.sh, test_boost_restoration.sh, run_tests.sh, and test_starvation_detection.sh are updated to use the new helpers in place of direct kill invocations.
--- tests/functional/test_boost_restoration.sh | 2 +- tests/functional/test_starvation_detection.sh | 2 +- tests/helpers/test_helpers.sh | 60 +++++++++++-------- tests/run_tests.sh | 6 +- 4 files changed, 39 insertions(+), 31 deletions(-)
tests/functional/test_boost_restoration.sh +1 -1
@@ -183,7 +183,7 @@ if wait_for_boost_detected "${STALLD_LOG}"; then sleep 1 # Verify stalld is still running and didn't crash after task exit - assert_success "stalld handled task exit during boost gracefully" kill -0 ${STALLD_PID} + assert_success "stalld handled task exit during boost gracefully" process_alive ${STALLD_PID} else log "⚠ WARNING: No boost detected in this test run"
tests/functional/test_starvation_detection.sh +1 -1
@@ -164,7 +164,7 @@ assert_log_contains --negate "${STALLD_LOG}" \ "${BUSY_PID}.*starved" \ "No false positive - progress-making task not reported as starved" -kill ${BUSY_PID} 2>/dev/null +send_signal TERM ${BUSY_PID} wait ${BUSY_PID} 2>/dev/null stop_stalld
tests/helpers/test_helpers.sh +34 -26
166 lines not on this page · raw message
tests/run_tests.sh +3 -3
@@ -224,7 +224,7 @@ cleanup_runner() { if [ -n "${pids}" ]; then echo -e "${BLUE}Cleaning up remaining stalld processes: ${pids}${NC}" for pid in ${pids}; do - kill -9 ${pid} 2>/dev/null || true + send_signal KILL ${pid} done fi
@@ -250,14 +250,14 @@ kill_existing_stalld_processes() { if [ -n "${pids}" ]; then echo -e "${BLUE}Killing existing stalld processes: ${pids}${NC}" | tee -a "${LOG_FILE}" for pid in ${pids}; do - kill ${pid} 2>/dev/null || true + send_signal TERM ${pid} done sleep 0.5 # Force kill any remaining pids=$(pgrep -x stalld 2>/dev/null) if [ -n "${pids}" ]; then for pid in ${pids}; do - kill -9 ${pid} 2>/dev/null || true + send_signal KILL ${pid} done sleep 0.2 fi
Signature · 1 line
2.54.0
PATCH 1/1 26/33] tests: Reduce default wait timeouts
In reply to Wander Lairson CostaFailing tests currently hang for up to thirty seconds before reporting a failure. Reduce the default wait timeouts in the stalld test helpers to cut this failure reporting time in half. These timeouts only impact the failure path since passing tests utilize event-driven waits that return immediately upon matching a pattern.
Decrease the timeout from thirty to fifteen seconds for the starvation and boost detection waits, their assertion wrappers, and the log match helper. Reduce the stalld ready wait from fifteen to five seconds. The new fifteen second limit provides adequate margin because all callers relying on the default use thresholds of three to five seconds, making detection take approximately nine seconds.
Override the timeout explicitly to twenty seconds for the third test in test_boost_duration.sh. This specific caller uses a threshold of ten, which makes the new fifteen second default too tight.
--- tests/functional/test_boost_duration.sh | 2 +- tests/helpers/test_helpers.sh | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-)
tests/functional/test_boost_duration.sh +1 -1
@@ -74,7 +74,7 @@ log "Creating starvation on CPU ${TEST_CPU} for ${long_starvation}s" start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${long_starvation} # Wait for actual boosting -assert_boost_detected "${STALLD_LOG}" "Boost with ${long_duration}s duration" +assert_boost_detected "${STALLD_LOG}" "Boost with ${long_duration}s duration" 20 # Cleanup cleanup_scenario "${STARVE_PID}"
tests/helpers/test_helpers.sh +6 -6
@@ -159,7 +159,7 @@ find_starved_child() { assert_starvation_detected() { local log_file=$1 local message=${2:-"Starvation detected"} - local timeout=${3:-30} + local timeout=${3:-15} local cpu=${4:-} if wait_for_starvation_detected "${log_file}" "${timeout}" "${cpu}"; then
@@ -176,7 +176,7 @@ assert_starvation_detected() { assert_boost_detected() { local log_file=$1 local message=${2:-"Boost detected"} - local timeout=${3:-30} + local timeout=${3:-15} if wait_for_boost_detected "${log_file}" "${timeout}"; then pass "${message}"
@@ -695,7 +695,7 @@ wait_for_log_message() { # Usage: wait_for_stalld_ready <log_file> [timeout] wait_for_stalld_ready() { local log_file=$1 - local timeout=${2:-15} + local timeout=${2:-5} wait_for_log_message "checking cpu\|waiting tasks\|skipping" "${timeout}" "${log_file}" }
@@ -704,7 +704,7 @@ wait_for_stalld_ready() { # Usage: wait_for_starvation_detected <log_file> [timeout] [cpu] wait_for_starvation_detected() { local log_file=$1 - local timeout=${2:-30} + local timeout=${2:-15} local cpu=${3:-} local pattern="starved on CPU" if [ -n "${cpu}" ]; then
@@ -718,7 +718,7 @@ wait_for_starvation_detected() { # Usage: wait_for_boost_detected <log_file> [timeout] wait_for_boost_detected() { local log_file=$1 - local timeout=${2:-30} + local timeout=${2:-15} wait_for_log_message "boosted pid" "${timeout}" "${log_file}" }
@@ -729,7 +729,7 @@ wait_for_n_log_matches() { local pattern=$1 local count=$2 local log_file=$3 - local timeout=${4:-30} + local timeout=${4:-15} local end=$((SECONDS + timeout)) while [ $SECONDS -lt $end ]; do
Signature · 1 line
2.54.0
PATCH 1/1 27/33] tests: Reduce starvation_gen durations
In reply to Wander Lairson CostaReduce starvation_gen durations across eight functional test files to eliminate unnecessary wait times. The generator runs for its full duration regardless of whether test assertions have already completed, meaning the previous literal values of 20 and 15 seconds wasted up to 10 seconds per test section. Across the full suite, this accumulated into significant idle time.
With a typical threshold of 5 seconds, the generator only needs to run long enough for detection plus a safety margin. Applying a formula of threshold plus boost duration plus a 4-second margin yields an optimal 12 seconds for standard tests. For detection-only tests lacking a boost, a 10-second duration provides ample margin.
Update seven instances of 20-second durations to 12 seconds and nine instances of 15-second durations to 10 seconds, along with two corresponding log messages. Values already optimized at 12 or 3 seconds, as well as variable-based durations computed directly from thresholds, remain unchanged since they already scale proportionally.
--- tests/functional/test_boost_duration.sh | 4 ++-- tests/functional/test_boost_restoration.sh | 4 ++-- tests/functional/test_deadline_boosting.sh | 8 ++++---- tests/functional/test_fifo_boosting.sh | 4 ++-- tests/functional/test_fifo_priority_starvation.sh | 4 ++-- tests/functional/test_log_only.sh | 4 ++-- tests/functional/test_starvation_detection.sh | 2 +- tests/functional/test_task_merging.sh | 6 +++--- 8 files changed, 18 insertions(+), 18 deletions(-)
tests/functional/test_boost_duration.sh +2 -2
@@ -92,8 +92,8 @@ log "Starting stalld with ${threshold}s threshold and ${duration}s boost duratio start_stalld_with_log "${STALLD_LOG}" -f -v -c "${TEST_CPU}" -a ${STALLD_CPU} -t ${threshold} -d ${duration} # Create starvation with a specific task we can track -log "Creating starvation on CPU ${TEST_CPU} for 15s" -start_starvation_gen -c "${TEST_CPU}" -p 80 -n 1 -d 15 +log "Creating starvation on CPU ${TEST_CPU} for 10s" +start_starvation_gen -c "${TEST_CPU}" -p 80 -n 1 -d 10 # Wait for actual boosting assert_boost_detected "${STALLD_LOG}" "Boost with ${duration}s boost duration"
tests/functional/test_boost_restoration.sh +2 -2
@@ -33,7 +33,7 @@ start_stalld_with_log "${STALLD_LOG}" -f -v -t $threshold -c ${TEST_CPU} -a ${ST # Create starvation (starvation_gen creates SCHED_FIFO blocker prio 80, blockee prio 1) log "Creating starvation with SCHED_FIFO tasks (blocker prio 80, blockee prio 1)" -start_starvation_gen -c ${TEST_CPU} -p 80 -b 1 -n 1 -d 20 +start_starvation_gen -c ${TEST_CPU} -p 80 -b 1 -n 1 -d 12 # Find the starved task tracked_pid=$(find_starved_child "${STARVE_PID}")
@@ -120,7 +120,7 @@ start_stalld_with_log "${STALLD_LOG}" -f -v -t $threshold -c ${TEST_CPU} -a ${ST # Use -o flag to create SCHED_OTHER blockees log "Creating SCHED_OTHER starvation (RT blocker prio 80, SCHED_OTHER blockee)" -start_starvation_gen -c ${TEST_CPU} -p 80 -o -n 1 -d 20 +start_starvation_gen -c ${TEST_CPU} -p 80 -o -n 1 -d 12 # Find the starved SCHED_OTHER task tracked_pid=$(find_starved_child "${STARVE_PID}")
tests/functional/test_deadline_boosting.sh +4 -4
@@ -64,7 +64,7 @@ start_stalld_with_log "${STALLD_LOG}" -f -v -g 1 -t $threshold -c ${TEST_CPU} -a # Create starvation log "Creating starvation on CPU ${TEST_CPU}" -start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 15 +start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 10 # Try to find the boosted task PID before it gets boosted tracked_pid=$(find_starved_child "${STARVE_PID}")
@@ -106,7 +106,7 @@ start_stalld_with_log "${STALLD_LOG}" -f -v -g 1 -t $threshold -c ${TEST_CPU} -a # Create starvation log "Creating starvation on CPU ${TEST_CPU}" -start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 20 +start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 12 # Find a starved task before it gets boosted tracked_pid=$(find_starved_child "${STARVE_PID}")
@@ -165,12 +165,12 @@ else # Create starvation on CPU0 log "Creating starvation on CPU ${CPU0}" - start_starvation_gen -c ${CPU0} -p 80 -n 1 -d 15 + start_starvation_gen -c ${CPU0} -p 80 -n 1 -d 10 STARVE_PID0=${STARVE_PID} # Create starvation on CPU1 log "Creating starvation on CPU ${CPU1}" - start_starvation_gen -c ${CPU1} -p 80 -n 1 -d 15 + start_starvation_gen -c ${CPU1} -p 80 -n 1 -d 10 STARVE_PID1=${STARVE_PID} # Wait for boosting on both CPUs
tests/functional/test_fifo_boosting.sh +2 -2
@@ -50,7 +50,7 @@ rm -f "${STALLD_LOG}" # Create starvation FIRST log "Creating starvation on CPU ${TEST_CPU}" -start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 15 +start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 10 tracked_pid=$(find_starved_child "${STARVE_PID}") log "Starting stalld with -F flag (FIFO boosting)"
@@ -103,7 +103,7 @@ rm -f "${STALLD_LOG}" # Create starvation FIRST log "Creating starvation on CPU ${TEST_CPU}" -start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 20 +start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 12 start_stalld_with_log "${STALLD_LOG}" -f -v -g 1 -N -F -A -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} \ -d ${boost_duration} -p ${boost_period} -r ${boost_runtime}
tests/functional/test_fifo_priority_starvation.sh +2 -2
@@ -58,7 +58,7 @@ threshold=5 boost_duration=3 log "Creating FIFO-on-FIFO starvation on CPU ${TEST_CPU}" -start_starvation_gen -c ${TEST_CPU} -p 10 -b 5 -n 1 -d 20 +start_starvation_gen -c ${TEST_CPU} -p 10 -b 5 -n 1 -d 12 # Find the starved task (blockee) PID blockee_pid=$(find_starved_child "${STARVE_PID}")
@@ -180,7 +180,7 @@ rm -f "${STALLD_LOG}" threshold=5 log "Creating FIFO-on-FIFO starvation" -start_starvation_gen -c ${TEST_CPU} -p 10 -b 5 -n 2 -d 20 -v +start_starvation_gen -c ${TEST_CPU} -p 10 -b 5 -n 2 -d 12 -v # Extract blocker and blockee PIDs from starvation_gen output # The output shows "Blocker TID: <pid>" and "Blockee N TID: <pid>"
tests/functional/test_log_only.sh +2 -2
@@ -44,10 +44,10 @@ echo "Stalld will run on CPU ${STALLD_CPU}" LOG_FILE="/tmp/stalld_test_log_only_$$.log" CLEANUP_FILES+=("${LOG_FILE}") -echo "Creating starvation on CPU ${TEST_CPU} (will run for 15 seconds)" +echo "Creating starvation on CPU ${TEST_CPU} (will run for 10 seconds)" # Start starvation generator BEFORE stalld to ensure CPU is busy from the start -start_starvation_gen -c ${TEST_CPU} -p 10 -n 1 -d 15 +start_starvation_gen -c ${TEST_CPU} -p 10 -n 1 -d 10 STARVGEN_PID=${STARVE_PID} # Start stalld in log-only mode with verbose output to capture logs
tests/functional/test_starvation_detection.sh +1 -1
@@ -52,7 +52,7 @@ threshold=5 # Create starvation log "Creating starvation on CPU ${TEST_CPU}" -start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 15 +start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 10 log "Starting stalld with ${threshold}s threshold (log-only mode)" start_stalld_with_log "${STALLD_LOG}" -f -v -N -l -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU}
tests/functional/test_task_merging.sh +3 -3
@@ -101,7 +101,7 @@ start_stalld_with_log "${STALLD_LOG}" -f -v -g 1 -l -t $threshold -c ${TEST_CPU} # Create starvation log "Creating starvation" -start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 20 +start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 12 # Wait for starvation detection wait_for_starvation_detected "${STALLD_LOG}"
@@ -174,11 +174,11 @@ else # Create starvation on both CPUs log "Creating starvation on CPU ${CPU0}" - start_starvation_gen -c ${CPU0} -p 80 -n 1 -d 15 + start_starvation_gen -c ${CPU0} -p 80 -n 1 -d 10 STARVE_PID0=${STARVE_PID} log "Creating starvation on CPU ${CPU1}" - start_starvation_gen -c ${CPU1} -p 80 -n 1 -d 15 + start_starvation_gen -c ${CPU1} -p 80 -n 1 -d 10 STARVE_PID1=${STARVE_PID} # Wait for starvation detection on both CPUs
Signature · 1 line
2.54.0
PATCH 1/1 29/33] tests: Drop redundant sleeps in test_pidfile
In reply to Wander Lairson CostaRemove redundant sleep calls following start_stalld in the pidfile tests. The start_stalld helper already polls for readiness internally before returning. It waits for the pidfile to appear when --pidfile is passed, and uses pgrep retries otherwise. The additional sleeps were dead wait time and unnecessarily slowed down test execution.
--- tests/functional/test_pidfile.sh | 7 ------- 1 file changed, 7 deletions(-)
tests/functional/test_pidfile.sh +0 -7
@@ -22,9 +22,6 @@ test_section "Test 1: Default behavior (no -P specified)" start_stalld -l -t 5 -# Give stalld time to create pidfile -sleep 2 - # Check common default pidfile locations default_found=0 for pidfile in /var/run/stalld.pid /run/stalld.pid; do
@@ -58,7 +55,6 @@ rm -f "${custom_pidfile}" log "Starting stalld with custom pidfile: ${custom_pidfile}" start_stalld -l -t 5 --pidfile "${custom_pidfile}" -sleep 2 assert_file_exists "${custom_pidfile}" "Custom pidfile created at ${custom_pidfile}"
@@ -87,7 +83,6 @@ rm -f "${tmp_pidfile}" log "Starting stalld with /tmp pidfile: ${tmp_pidfile}" start_stalld -l -t 5 --pidfile "${tmp_pidfile}" -sleep 2 assert_file_exists "${tmp_pidfile}" "Pidfile created in /tmp directory"
@@ -107,7 +102,6 @@ rm -f "${fg_pidfile}" log "Starting stalld in foreground mode with pidfile: ${fg_pidfile}" start_stalld -f -v -l -t 5 --pidfile "${fg_pidfile}" -sleep 2 if [ -f "${fg_pidfile}" ]; then pass "Pidfile created in foreground mode"
@@ -139,7 +133,6 @@ rm -f "${readable_pidfile}" log "Starting stalld with readable pidfile: ${readable_pidfile}" start_stalld -l -t 5 --pidfile "${readable_pidfile}" -sleep 2 assert_file_exists "${readable_pidfile}" "Pidfile created" assert_success "Pidfile is readable" cat "${readable_pidfile}"
Signature · 1 line
2.54.0
PATCH 1/1 28/33] tests: Replace init sleeps in test_affinity
In reply to Wander Lairson CostaReplace start_stalld followed by a fixed two-second sleep with start_stalld_with_log in test_affinity.sh. The helper uses event-driven wait_for_stalld_ready internally, returning as soon as the daemon logs its first monitoring message rather than waiting a fixed interval. Since stalld typically initializes in under half a second, this saves approximately nine seconds across the six modified test sections.
--- tests/functional/test_affinity.sh | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)
tests/functional/test_affinity.sh +12 -12
@@ -54,8 +54,8 @@ fi #============================================================================= test_section "Test 1: Default behavior (no affinity restriction)" -start_stalld -f -v -l -t 5 -sleep 2 +rm -f "${STALLD_LOG}" +start_stalld_with_log "${STALLD_LOG}" -f -v -l -t 5 default_affinity=$(check_affinity "${STALLD_PID}") log "ℹ INFO: Default affinity: $default_affinity"
@@ -69,8 +69,8 @@ stop_stalld #============================================================================= test_section "Test 2: Single CPU affinity (-a 0)" -start_stalld -f -v -l -t 5 -a 0 -sleep 2 +rm -f "${STALLD_LOG}" +start_stalld_with_log "${STALLD_LOG}" -f -v -l -t 5 -a 0 affinity=$(check_affinity "${STALLD_PID}")
@@ -84,8 +84,8 @@ stop_stalld test_section "Test 3: Multi-CPU affinity (-a 0,2)" if [ "$num_cpus" -ge 4 ]; then - start_stalld -f -v -l -t 5 -a 0,2 - sleep 2 + rm -f "${STALLD_LOG}" + start_stalld_with_log "${STALLD_LOG}" -f -v -l -t 5 -a 0,2 affinity=$(check_affinity "${STALLD_PID}")
@@ -103,8 +103,8 @@ fi test_section "Test 4: CPU range affinity (-a 0-2)" if [ "$num_cpus" -ge 4 ]; then - start_stalld -f -v -l -t 5 -a 0-2 - sleep 2 + rm -f "${STALLD_LOG}" + start_stalld_with_log "${STALLD_LOG}" -f -v -l -t 5 -a 0-2 affinity=$(check_affinity "${STALLD_PID}")
@@ -123,8 +123,8 @@ test_section "Test 5: Verify stalld threads run on specified CPU" if [ "$num_cpus" -ge 2 ]; then test_cpu=1 - start_stalld -f -v -l -t 5 -a "$test_cpu" - sleep 2 + rm -f "${STALLD_LOG}" + start_stalld_with_log "${STALLD_LOG}" -f -v -l -t 5 -a "$test_cpu" # Check affinity affinity=$(check_affinity "${STALLD_PID}")
@@ -174,8 +174,8 @@ assert_stalld_rejects "Invalid CPU affinity rejected with error" -f -v -l -t 5 - #============================================================================= test_section "Test 8: Verify affinity persists over time" -start_stalld -f -v -l -t 5 -a 0 -sleep 2 +rm -f "${STALLD_LOG}" +start_stalld_with_log "${STALLD_LOG}" -f -v -l -t 5 -a 0 affinity_start=$(check_affinity "${STALLD_PID}") log "ℹ INFO: Initial affinity: $affinity_start"
Signature · 1 line
2.54.0
PATCH 1/1 30/33] tests: Remove redundant sleeps after start_stalld
In reply to Wander Lairson CostaRemove redundant sleep commands from test_foreground.sh and test_logging_destinations.sh. The start_stalld function already polls for readiness before returning, rendering these post-return delays unnecessary.
--- tests/functional/test_foreground.sh | 3 --- tests/functional/test_logging_destinations.sh | 1 - 2 files changed, 4 deletions(-)
tests/functional/test_foreground.sh +0 -3
@@ -24,7 +24,6 @@ require_root # Test 1: Without -f flag, stalld should daemonize test_section "Test 1: stalld daemonizes by default" start_stalld -l -t 5 -sleep 2 assert_process_running "${STALLD_PID}" "stalld should be running"
@@ -42,7 +41,6 @@ test_section "Test 2: stalld stays in foreground with -f" # Start stalld in foreground but in background job start_stalld -f -l -t 5 -sleep 2 assert_process_running "${STALLD_PID}" "stalld should be running with -f"
@@ -55,7 +53,6 @@ stop_stalld test_section "Test 3: -v implies foreground mode" start_stalld -v -l -t 5 -sleep 2 assert_process_running "${STALLD_PID}" "stalld should be running with -v"
tests/functional/test_logging_destinations.sh +0 -1
@@ -49,7 +49,6 @@ if command -v dmesg >/dev/null 2>&1; then DMESG_BEFORE=$(dmesg | wc -l) start_stalld -f -k -l -t 5 - sleep 2 if assert_process_running "${STALLD_PID}" "stalld with -k should be running"; then sleep 1
Signature · 1 line
2.54.0
PATCH 1/1 31/33] tests: Reduce timing and replace sleeps with event waits
In reply to Wander Lairson CostaLower starvation thresholds, reduce starvation_gen durations, and replace fixed sleep delays with process-wait and event-driven synchronization in the functional tests. This reduces test suite runtime without sacrificing reliability.
--- tests/functional/test_deadline_boosting.sh | 2 +- tests/functional/test_fifo_boosting.sh | 2 +- tests/functional/test_fifo_priority_starvation.sh | 5 +++-- tests/functional/test_idle_detection.sh | 2 +- tests/functional/test_starvation_detection.sh | 3 ++- tests/functional/test_starvation_threshold.sh | 5 +---- tests/functional/test_task_merging.sh | 9 ++++----- 7 files changed, 13 insertions(+), 15 deletions(-)
tests/functional/test_deadline_boosting.sh +1 -1
@@ -30,7 +30,7 @@ log "Starting stalld with ${threshold}s threshold (default DEADLINE boosting)" start_stalld_with_log "${STALLD_LOG}" -f -v -g 1 -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} # Create starvation -starvation_duration=$((threshold + 8)) +starvation_duration=$((threshold + 5)) log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c ${TEST_CPU} -p 80 -n 2 -d ${starvation_duration}
tests/functional/test_fifo_boosting.sh +1 -1
@@ -23,7 +23,7 @@ test_section "Test 1: FIFO Boost with -F Flag" threshold=5 # Create starvation FIRST (before stalld starts) -starvation_duration=$((threshold + 8)) +starvation_duration=$((threshold + 5)) log "Creating starvation on CPU ${TEST_CPU} for ${starvation_duration}s" start_starvation_gen -c ${TEST_CPU} -p 80 -n 2 -d ${starvation_duration}
tests/functional/test_fifo_priority_starvation.sh +3 -2
@@ -73,7 +73,8 @@ log "Starting stalld with boosting enabled" start_stalld_with_log "${STALLD_LOG}" -f -v -N -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} -d ${boost_duration} # Wait for detection and boosting -sleep $((threshold + boost_duration + 1)) +wait_for_boost_detected "${STALLD_LOG}" +sleep ${boost_duration} ctxt_after=0 if [ -n "${blockee_pid}" ] && [ -f "/proc/${blockee_pid}/status" ]; then
@@ -104,7 +105,7 @@ rm -f "${STALLD_LOG}" threshold=3 # Create long starvation to trigger multiple detection cycles -starvation_duration=15 +starvation_duration=12 log "Creating long FIFO-on-FIFO starvation for ${starvation_duration}s" start_starvation_gen -c ${TEST_CPU} -p 10 -b 5 -n 2 -d ${starvation_duration}
tests/functional/test_idle_detection.sh +1 -1
@@ -60,7 +60,7 @@ threshold=3 log "Starting stalld with idle detection on an idle CPU" start_stalld_with_log "${STALLD_LOG}" -f -v -l -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} -sleep 3 +wait_for_log_message "skipping" 5 "${STALLD_LOG}" assert_log_contains "${STALLD_LOG}" "skipping" "Idle CPU correctly skipped"
tests/functional/test_starvation_detection.sh +2 -1
@@ -184,7 +184,8 @@ log "Creating short-lived starvation (3s, less than ${threshold}s threshold)" start_starvation_gen -c ${TEST_CPU} -p 80 -n 1 -d 3 # Wait for task to exit -sleep 5 +wait "${STARVE_PID}" 2>/dev/null || true +sleep 1 assert_process_running "${STALLD_PID}" "stalld still running after task exit"
tests/functional/test_starvation_threshold.sh +1 -4
@@ -60,14 +60,11 @@ start_starvation_gen -c "${TEST_CPU}" -p 80 -n 2 -d ${starvation_duration} log "Starting stalld with ${threshold}s threshold" start_stalld_with_log "${STALLD_LOG}" -f -v -N -M -g 1 -c "${TEST_CPU}" -a "${STALLD_CPU}" -t ${threshold} -# Wait for starvation duration + small buffer -sleep 8 - # Wait for starvation generator to fully complete wait "${STARVE_PID}" 2>/dev/null || true # Give stalld time to process and log (if it were to detect) -sleep 2 +sleep 1 # Check that starvation_gen was NOT detected (duration less than threshold) assert_log_contains --negate "${STALLD_LOG}" \
tests/functional/test_task_merging.sh +4 -5
@@ -28,7 +28,7 @@ log "Starting stalld with ${threshold}s threshold (log-only, verbose)" start_stalld_with_log "${STALLD_LOG}" -f -v -g 1 -l -t $threshold -c ${TEST_CPU} -a ${STALLD_CPU} # Create long starvation to span multiple monitoring cycles -starvation_duration=18 +starvation_duration=12 log "Creating starvation for ${starvation_duration}s (multiple detection cycles)" start_starvation_gen -c ${TEST_CPU} -p 80 -n 2 -d ${starvation_duration}
@@ -51,7 +51,7 @@ fi # Wait for second detection cycle log "Waiting for second detection cycle..." -sleep 4 +wait_for_n_log_matches "starved" 2 "${STALLD_LOG}" # Extract second starvation duration second_duration=$(grep "starved.*for [0-9]" "${STALLD_LOG}" | tail -1 | grep -oE "for [0-9]+" | awk '{print $2}')
@@ -72,7 +72,7 @@ fi # Wait for third detection to confirm continued accumulation log "Waiting for third detection cycle..." -sleep 4 +wait_for_n_log_matches "starved" 3 "${STALLD_LOG}" third_duration=$(grep "starved.*for [0-9]" "${STALLD_LOG}" | tail -1 | grep -oE "for [0-9]+" | awk '{print $2}') if [ -z "${third_duration}" ]; then
@@ -182,8 +182,7 @@ else STARVE_PID1=${STARVE_PID} # Wait for starvation detection on both CPUs - wait_for_starvation_detected "${STALLD_LOG}" - sleep 4 + wait_for_n_log_matches "starved" 2 "${STALLD_LOG}" 10 # Check CPU0 starvation accumulation cpu0_detections=$(grep "starved on CPU ${CPU0}" "${STALLD_LOG}" | wc -l)
Signature · 1 line
2.54.0
PATCH 1/1 32/33] tests: Fix async-signal-unsafe handler
In reply to Wander Lairson CostaRemove the printf call from the starvation_gen signal handler. Calling printf from a signal handler is undefined behavior under POSIX because it is not async-signal-safe. If the signal arrives while another thread holds the internal stdout lock, the process deadlocks. The handler now only sets the volatile running flag.
--- tests/helpers/starvation_gen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tests/helpers/starvation_gen.c +1 -1
@@ -102,7 +102,7 @@ void *blockee_thread(void *arg) { }void signal_handler (int sig) { -printf ("\nReceived signal %d, exiting...\n" , sig); + (void )sig; running =0 ; }
Signature · 1 line
2.54.0
PATCH 1/1 33/33] bpf: Replace linear task scan with hash map
In reply to Wander Lairson CostaThe BPF queue_track backend previously utilized an O(2048) linear array scan within find_queued_task() on every sched_wakeup event. This design introduced a ~2us latency penalty, which is highly problematic for Telco workloads where it consumed 20% of the stringent 10us real-time latency budget.
Resolve this bottleneck by replacing the per-CPU array with a new BPF hash map, stalld_task_map, keyed by a composite of CPU and PID to enable O(1) task lookups. Consequently, the stalld_cpu_data structure shrinks from approximately 65KB down to 12 bytes. The enqueue_task, dequeue_task, and update_or_add_task routines are rewritten to utilize standard BPF map helpers. In userspace, queue_track.c now iterates through the hash map using bpf_map_get_next_key while applying CPU filtering.
Initialize the legacy pointer in task_running() to avoid use of uninitialized variable.
Verification on a real-time system confirms that thread latency dropped from 5-6us down to 3us. This improvement completely eliminates the overhead, returning performance to the baseline established when stalld is not running.
--- bpf/stalld.bpf.c | 206 ++++++++++++++++------------------------------ src/queue_track.c | 95 +++++++++++---------- src/queue_track.h | 107 ++---------------------- 3 files changed, 131 insertions(+), 277 deletions(-)
bpf/stalld.bpf.c +73 -133
@@ -33,6 +33,14 @@ struct { __type(value, struct stalld_cpu_data); } stalld_per_cpu_data SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_HASH); +/* resized at load time to MAX_QUEUE_TASK * nr_cpus */ + __uint(max_entries, MAX_QUEUE_TASK); + __type(key, struct task_map_key); + __type(value, struct queued_task); +} stalld_task_map SEC(".maps"); +#if DEBUG_STALLD #define log (msg, ...) bpf_printk("%s: " msg, __func__, ##__VA_ARGS__)#else
@@ -82,15 +90,6 @@ struct task_struct___legacy {* task_is_rt - Check if a task is a real-time task. * @p: A pointer to the kernel's `task_struct` for the task. * -* This function determines if a task belongs to a real-time (RT) scheduling -* class based on its priority. In the Linux kernel, static priorities from -* 0 to 99 are reserved for RT tasks (SCHED_FIFO and SCHED_RR), while -* priorities from 100 to 139 are used for normal tasks (SCHED_NORMAL, -* SCHED_BATCH, etc.). -* -* This check is essential for `stalld` to distinguish between high-priority -* RT tasks that have strict scheduling deadlines and normal tasks. -* * Return: `true` if the task has a real-time priority (0-99), * `false` otherwise. */
@@ -103,13 +102,6 @@ static inline bool task_is_rt(const struct task_struct *p)* task_cpu - Get the CPU number that a task is currently running on. * @p: A pointer to the kernel's `task_struct` for the task. * -* This function retrieves the CPU identifier where the task is currently -* scheduled. -* -* The CPU number is crucial for `stalld` to associate a task with the -* correct per-CPU data map, ensuring that task tracking and starvation -* analysis are performed in the right context. -* * Return: The integer ID of the CPU the task is running on. */ static inline int task_cpu (const struct task_struct *p)
@@ -126,23 +118,6 @@ static inline int task_cpu(const struct task_struct *p)* compute_ctxswc - Compute the total context switch count for a task. * @p: A pointer to the `task_struct` (process descriptor) of the task. * -* This function calculates the total number of context switches a task -* has undergone by summing its voluntary and involuntary context switch -* counts. -* -* The `nvcsw` (number of voluntary context switches) increments when a task -* explicitly yields the CPU (e.g., waiting for I/O, sleeping, or blocking -* on a lock). -* -* The `nivcsw` (number of involuntary context switches) increments when a task -* is preempted by the scheduler (e.g., its timeslice expires, or a higher -* priority task becomes runnable). -* -* The sum of these two counters provides a comprehensive measure of how many -* times the task has been context-switched in and out of the CPU. This value -* is crucial for tools like `stalld` to detect if a task has made progress -* (i.e., has run at least once) since a previous observation. -* * Return: The total context switch count (nvcsw + nivcsw) for the given task. */ static inline long compute_ctxswc (const struct task_struct *p)
@@ -152,7 +127,8 @@ static inline long compute_ctxswc(const struct task_struct *p)static inline unsigned int task_running (const struct task_struct *p) { -const struct task_struct___legacy *lp; +const struct task_struct___legacy *lp = (const void *) p; +const unsigned int state =bpf_core_field_exists (p->__state) ?BPF_CORE_READ (p, __state) :BPF_CORE_READ (lp, state);
@@ -178,52 +154,52 @@ static struct stalld_cpu_data *get_cpu_data(int cpu)return NULL ; } -static int enqueue_task (const struct task_struct *p,struct stalld_cpu_data *cpu_data) +static int enqueue_task (const struct task_struct *p,int cpu) { -struct queued_task *task; -const long pid = p->pid; - -for_each_task_entry (cpu_data, task) { -if (task->pid ==0 || task->pid == pid) { - task->ctxswc =compute_ctxswc (p); - task->prio = p->prio; - task->is_rt =task_is_rt (p); - task->tgid = p->tgid; - -/* -* User reads pid to know that there is no data here. -* Update it last. -*/ -barrier (); - task->pid = pid; -log_task (p); -return 0 ; - } - } +struct task_map_key key; +struct queued_task task; -log_task_error (p); +/* +* pid 0 (idle/swapper) must not enter the hash map: userspace +* uses pid 0 as "no task" in cpu_starving_vector, so a real +* entry with pid 0 would be silently skipped. +*/ +if (!p->pid) +return 0 ; + + key.cpu = cpu; + key.pid = p->pid; + task.pid = p->pid; + task.tgid = p->tgid; + task.is_rt =task_is_rt (p); + task.prio = p->prio; + task.ctxswc =compute_ctxswc (p); + +if (bpf_map_update_elem (&stalld_task_map, &key, &task,BPF_ANY ) <0 ) { +log_task_error (p); +return -1 ; + } + +log_task (p);return 0 ; }/** * dequeue_task - Removes a task from a CPU's queue. -* @p: Pointer to the task_struct of the task to remove. -* @cpu_data: Pointer to the per-CPU data structure. -* -* This function finds and removes a task from the specified CPU's run queue. -* It updates the appropriate counter (RT or non-RT) for the queued tasks. +* @p: Pointer to the task_struct of the task to remove. +* @cpu: The CPU number to dequeue from. * * Return: 1 if the task was found and removed, 0 otherwise. */ -static int dequeue_task (const struct task_struct *p,struct stalld_cpu_data *cpu_data) +static int dequeue_task (const struct task_struct *p,int cpu) { -struct queued_task *task; -long pid = p->pid; +struct task_map_key key; - task =find_queued_task (cpu_data, pid); -if (task) { - task->pid =0 ; + key.cpu = cpu; + key.pid = p->pid; + +if (bpf_map_delete_elem (&stalld_task_map, &key) ==0 ) {log_task (p);return 1 ; }
@@ -233,85 +209,55 @@ static int dequeue_task(const struct task_struct *p, struct stalld_cpu_data *cpu }/* -* update_or_add_task - Manages a task's lifecycle within a per-CPU tracking queue. -* -* This function handles the logic for managing individual task entries within -* stalld's BPF program. It dynamically adds, updates, or removes a task from a -* specific CPU's tracking array based on its current state. This ensures the -* array provides an accurate, real-time view of tasks on the run queue. -* -* The function's logic is organized into three primary scenarios: -* 1. Update: If a task is already tracked and is still in the TASK_RUNNING -* state, its dynamic properties (context switch count, priority) are -* refreshed. -* 2. Remove: If a tracked task is no longer in the TASK_RUNNING state -* (e.g., it has gone to sleep or terminated), it is removed from the queue -* by invalidating its entry (setting pid to 0). -* 3. Add: If a new, previously unseen task is encountered and is in the -* TASK_RUNNING state, it is added to the first available empty slot in -* the queue. +* update_or_add_task - Manages a task's lifecycle within the hash map. * -* Parameters: -* cpu_data: A pointer to the `stalld_cpu_data` structure for the target CPU. -* p: A pointer to the kernel's `task_struct` for the task to be processed. +* Three scenarios: +* 1. Update: task exists and is TASK_RUNNING -> refresh fields in-place. +* 2. Remove: task exists but not TASK_RUNNING -> delete from map. +* 3. Add: task not found and is TASK_RUNNING -> insert into map. */ -static void update_or_add_task (struct stalld_cpu_data *cpu_data, -const struct task_struct *p) +static void update_or_add_task (const struct task_struct *p,int cpu) { +struct task_map_key key;struct queued_task *task_entry; -/* Try to find the task first */ - task_entry =find_queued_task (cpu_data, p->pid); + key.cpu = cpu; + key.pid = p->pid; + + task_entry =bpf_map_lookup_elem (&stalld_task_map, &key);if (task_entry) {if (task_running (p)) { -/* Task found: Update its dynamic fields */ task_entry->ctxswc =compute_ctxswc (p); task_entry->prio = p->prio; task_entry->is_rt =task_is_rt (p); }else { -/* Task is not running. Remove it. */ log_task_prefix ("dequeue " , p); - task_entry->pid =0 ; +bpf_map_delete_elem (&stalld_task_map, &key); }return ; } -/* -* If we reach here, the task was NOT found, so it's new. -* Check if the new task is in the `TASK_RUNNING` state before adding to queue. -*/ if (!task_running (p))return ; -/* -* Task not found and is running: find an empty slot to add it -* We iterate through all slots to find the first empty one. -*/ -enqueue_task (p, cpu_data); +enqueue_task (p, cpu); }/** * __sched_wakeup - Common handler for task wakeup tracepoints. * @ctx: A pointer to the tracepoint context. * -* This function serves as the common implementation for handling both -* `sched_wakeup` and `sched_wakeup_new` tracepoints. It extracts the -* task_struct from the context, determines its target CPU, and if that -* CPU is being monitored, enqueues the task for tracking. -* -* This centralized approach avoids code duplication and provides a -* single point of logic for task wakeup events. -* * Return: Always returns 0. */ static int __sched_wakeup (u64 *ctx) {const struct task_struct *p = (void *) ctx[0 ]; -struct stalld_cpu_data *cpu_data =get_cpu_data (task_cpu (p)); +int cpu =task_cpu (p); +struct stalld_cpu_data *cpu_data =get_cpu_data (cpu);if (cpu_data) -update_or_add_task (cpu_data, p); +update_or_add_task (p, cpu);return 0 ; }
@@ -332,10 +278,11 @@ SEC("tp_btf/sched_process_exit")int handle__sched_process_exit (u64 *ctx) {const struct task_struct *p = (void *) ctx[0 ]; -struct stalld_cpu_data *cpu_data =get_cpu_data (task_cpu (p)); +int cpu =task_cpu (p); +struct stalld_cpu_data *cpu_data =get_cpu_data (cpu);if (cpu_data) -dequeue_task (p, cpu_data); +dequeue_task (p, cpu);return 0 ; }
@@ -343,7 +290,8 @@ int handle__sched_process_exit(u64 *ctx) SEC("tp_btf/sched_switch")int handle__sched_switch (u64 *ctx) { -struct stalld_cpu_data *cpu_data =get_cpu_data (bpf_get_smp_processor_id ()); +int cpu =bpf_get_smp_processor_id (); +struct stalld_cpu_data *cpu_data =get_cpu_data (cpu);const struct task_struct *prev = (void *) ctx[1 ];const struct task_struct *next = (void *) ctx[2 ];
@@ -353,9 +301,8 @@ int handle__sched_switch(u64 *ctx) cpu_data->nr_rt_running =task_is_rt (next); -// update the context switch count of the tasks -update_or_add_task (cpu_data, next); -update_or_add_task (cpu_data, prev); +update_or_add_task (next, cpu); +update_or_add_task (prev, cpu);return 0 ; }
@@ -373,17 +320,15 @@ int handle__sched_migrate_task(u64 *ctx)/* * Dequeue the task from its original CPU and re-enqueue it on the * destination CPU. This ensures its run queue state is tracked -* correctly across migrations. If the task was not found on the -* original CPU, there is no need to enqueue it on the new one, as -* it was not being monitored. +* correctly across migrations. */ if (cpu_data) {log ("task=%s(%ld) orig=%d dest=%d" , p->comm, p->tgid, orig_cpu, dest_cpu); -if (dequeue_task (p, cpu_data)) { +if (dequeue_task (p, orig_cpu)) { cpu_data =get_cpu_data (dest_cpu);if (cpu_data) -enqueue_task (p, cpu_data); +enqueue_task (p, dest_cpu); } }
@@ -393,29 +338,24 @@ int handle__sched_migrate_task(u64 *ctx)/** * iter_task - BPF iterator program for task enumeration * @ctx: Iterator context containing the current task -* -* This BPF iterator program walks through all tasks in the system and -* provides visibility into their scheduling state. It's useful for getting -* a system-wide snapshot of task states, complementing the event-driven -* tracepoint programs that track dynamic task state changes. */ SEC("iter/task")int iter_task (struct bpf_iter__task *ctx) {const struct task_struct *p = ctx->task; -struct stalld_cpu_data *cpu_data; +int cpu;if (!p)return 0 ; - cpu_data =get_cpu_data (task_cpu (p)); -if (!cpu_data) + cpu =task_cpu (p); +if (!get_cpu_data (cpu))return 0 ;log_task (p);if (task_running (p)) -enqueue_task (p, cpu_data); +enqueue_task (p, cpu);return 0 ; }
src/queue_track.c +53 -42
184 lines not on this page · raw message
src/queue_track.h +5 -102
121 lines not on this page · raw message
Signature · 1 line
2.54.0