From f63817a3628211eeb81b3b7f341b2c5eacae5d7b Mon Sep 17 00:00:00 2001 From: Srikanth Muppandam Date: Wed, 3 Dec 2025 06:20:41 +0530 Subject: [PATCH 1/2] weston-simple-egl: make FPS capture and gating robust Use run_with_timeout with stdbuf -oL -eL so weston-simple-egl output is line-buffered and FPS lines reliably land in weston-simple-egl_run.log. Log weston-simple-egl output into weston-simple-egl_run.log and compute average FPS across all "N frames in M seconds: X fps" lines instead of relying on a single sample. Record min, max and average FPS and include them in the result summary for easier performance debugging. Gate PASS/FAIL primarily on run duration and optional FPS requirement (REQUIRE_FPS), using EXPECT_FPS and FPS_TOL_PCT when FPS gating is enabled. Default REQUIRE_FPS=1 to enable FPS checks by default, while keeping behaviour configurable via the environment. Keep the .res file format unchanged, still emitting a simple "weston-simple-egl PASS/FAIL" line so the existing Runner harness continues to work without changes. Signed-off-by: Srikanth Muppandam --- .../Graphics/weston-simple-egl/run.sh | 397 +++++++++++------- 1 file changed, 250 insertions(+), 147 deletions(-) diff --git a/Runner/suites/Multimedia/Graphics/weston-simple-egl/run.sh b/Runner/suites/Multimedia/Graphics/weston-simple-egl/run.sh index 3d30c633..e161e2cb 100755 --- a/Runner/suites/Multimedia/Graphics/weston-simple-egl/run.sh +++ b/Runner/suites/Multimedia/Graphics/weston-simple-egl/run.sh @@ -31,198 +31,301 @@ if [ -z "${__INIT_ENV_LOADED:-}" ]; then __INIT_ENV_LOADED=1 fi -# shellcheck disable=SC1090,SC1091 +# shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" TESTNAME="weston-simple-egl" - -# ---------- Tunables (env override) ---------- -DURATION="${DURATION:-30s}" # how long to run the client -STOP_GRACE="${STOP_GRACE:-3s}" # grace period on stop (reserved for future) -EXPECT_FPS="${EXPECT_FPS:-60}" # nominal refresh (used for logs) -FPS_TOL_PCT="${FPS_TOL_PCT:-10}" # +/- tolerance % -REQUIRE_FPS="${REQUIRE_FPS:-0}" # 1=require FPS lines & threshold; 0=best effort - -# ---------- Paths / logs ---------- -test_path="$(find_test_case_by_name "$TESTNAME" 2>/dev/null || echo "$SCRIPT_DIR")" -if ! cd "$test_path"; then - log_error "cd failed: $test_path" - exit 1 +RES_FILE="./${TESTNAME}.res" +RUN_LOG="./${TESTNAME}_run.log" + +: >"$RES_FILE" +: >"$RUN_LOG" + +# --------------------------------------------------------------------------- +# Config +# --------------------------------------------------------------------------- +DURATION="${DURATION:-30s}" +STOP_GRACE="${STOP_GRACE:-3s}" +EXPECT_FPS="${EXPECT_FPS:-60}" +FPS_TOL_PCT="${FPS_TOL_PCT:-10}" +REQUIRE_FPS="${REQUIRE_FPS:-1}" + +BUILD_FLAVOUR="base" +if [ -f /usr/share/glvnd/egl_vendor.d/EGL_adreno.json ]; then + BUILD_FLAVOUR="overlay" fi -RES_FILE="./$TESTNAME.res" -LOG_FILE="./${TESTNAME}_run.log" -rm -f "$RES_FILE" "$LOG_FILE" - +log_info "Weston log directory: $SCRIPT_DIR" log_info "--------------------------------------------------------------------------" -log_info "------------------- Starting $TESTNAME Testcase --------------------------" +log_info "------------------- Starting ${TESTNAME} Testcase --------------------------" -# --- Platform details (robust logging; prefer helpers) --- +# Optional platform details (helper from functestlib) if command -v detect_platform >/dev/null 2>&1; then - detect_platform >/dev/null 2>&1 || true - log_info "Platform Details: machine='${PLATFORM_MACHINE:-unknown}' target='${PLATFORM_TARGET:-unknown}' kernel='${PLATFORM_KERNEL:-}' arch='${PLATFORM_ARCH:-}'" + detect_platform +fi + +if [ "$BUILD_FLAVOUR" = "overlay" ]; then + log_info "Build flavor: overlay (EGL_adreno.json present)" else - log_info "Platform Details: unknown" + log_info "Build flavor: base (no EGL_adreno.json overlay)" fi -log_info "Config: DURATION=$DURATION STOP_GRACE=$STOP_GRACE EXPECT_FPS=${EXPECT_FPS}+/-${FPS_TOL_PCT}% REQUIRE_FPS=$REQUIRE_FPS" +log_info "Config: DURATION=${DURATION} STOP_GRACE=${STOP_GRACE} EXPECT_FPS=${EXPECT_FPS}+/-${FPS_TOL_PCT}% REQUIRE_FPS=${REQUIRE_FPS} BUILD_FLAVOUR=${BUILD_FLAVOUR}" -# ---------- Dependencies ---------- -if ! check_dependencies weston-simple-egl; then - log_skip "$TESTNAME : SKIP (weston-simple-egl not found in PATH)" - echo "$TESTNAME SKIP" > "$RES_FILE" - exit 2 +# --------------------------------------------------------------------------- +# Display snapshot +# --------------------------------------------------------------------------- +if command -v display_debug_snapshot >/dev/null 2>&1; then + display_debug_snapshot "pre-display-check" fi -BIN="$(command -v weston-simple-egl 2>/dev/null || true)" -log_info "Using weston-simple-egl: ${BIN:-}" +have_connector=0 +if command -v display_connected_summary >/dev/null 2>&1; then + sysfs_summary=$(display_connected_summary) + if [ -n "$sysfs_summary" ] && [ "$sysfs_summary" != "none" ]; then + have_connector=1 + log_info "Connected display (sysfs): $sysfs_summary" + fi +fi -# ----- Display presence check (DP/HDMI/etc.) ----- -# Quick snapshot for debugging (lists DRM nodes, sysfs connectors, weston outputs) -display_debug_snapshot "pre-display-check" +if [ "$have_connector" -eq 0 ]; then + log_warn "No connected DRM display found, skipping ${TESTNAME}." + echo "${TESTNAME} SKIP" >"$RES_FILE" + exit 0 +fi -have_connector=0 +# --------------------------------------------------------------------------- +# Wayland / Weston environment (runtime detection, no hardcoded flavour) +# --------------------------------------------------------------------------- +if command -v wayland_debug_snapshot >/dev/null 2>&1; then + wayland_debug_snapshot "${TESTNAME}: start" +fi + +sock="" -# sysfs-based summary (existing helper) -sysfs_summary="$(display_connected_summary 2>/dev/null || printf '%s' '')" -if [ -n "$sysfs_summary" ] && [ "$sysfs_summary" != "none" ]; then - have_connector=1 - log_info "Connected display (sysfs): $sysfs_summary" +# 1) Try to find any existing Wayland socket (base or overlay) +if command -v discover_wayland_socket_anywhere >/dev/null 2>&1; then + sock=$(discover_wayland_socket_anywhere | head -n 1 || true) fi -if [ "$have_connector" -eq 0 ]; then - log_skip "$TESTNAME : SKIP (no connected display detected)" - echo "$TESTNAME SKIP" > "$RES_FILE" - exit 2 -fi - -wayland_debug_snapshot "weston-simple-egl: start" - -# ---------- Choose/adopt Wayland socket (using helper) ---------- -# Capture only the actual socket path from helper output (filter out logs) -sock="$( - wayland_choose_or_start 2>/dev/null \ - | grep -E '/(run/user/[0-9]+|tmp|dev/socket/weston)/wayland-[0-9]+$' \ - | tail -n 1 -)" -if [ -n "$sock" ]; then - log_info "Found Wayland socket: $sock" -else - log_fail "$TESTNAME : FAIL (no Wayland socket found after attempting to start Weston)" - echo "$TESTNAME FAIL" > "$RES_FILE" - wayland_debug_snapshot "weston-simple-egl: no-socket" - exit 1 +# If we found a socket, adopt its environment +if [ -n "$sock" ] && command -v adopt_wayland_env_from_socket >/dev/null 2>&1; then + log_info "Found existing Wayland socket: $sock" + if ! adopt_wayland_env_from_socket "$sock"; then + log_warn "Failed to adopt env from $sock" + fi fi -adopt_wayland_env_from_socket "$sock" || log_warn "adopt_wayland_env_from_socket: invalid: $sock" -log_info "Final Wayland env: XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-}WAYLAND_DISPLAY=${WAYLAND_DISPLAY:-}" -# Replace to avoid confusion in CI logs: -# shellcheck disable=SC2016 -printf '%s\n' "" | sed 's/.*/[DBG] (env adopted)/' >/dev/null 2>&1 || true +# 2) If no usable socket yet, try starting a private Weston (overlay-style helper) +if [ -z "$sock" ] && command -v overlay_start_weston_drm >/dev/null 2>&1; then + log_info "No usable Wayland socket; trying overlay_start_weston_drm helper..." + if overlay_start_weston_drm; then + # Re-scan for a socket after attempting to start Weston + if command -v discover_wayland_socket_anywhere >/dev/null 2>&1; then + sock=$(discover_wayland_socket_anywhere | head -n 1 || true) + fi + if [ -n "$sock" ] && command -v adopt_wayland_env_from_socket >/dev/null 2>&1; then + log_info "Overlay Weston created Wayland socket: $sock" + if ! adopt_wayland_env_from_socket "$sock"; then + log_warn "Failed to adopt env from $sock" + fi + else + log_warn "overlay_start_weston_drm reported success but no Wayland socket was found." + fi + else + log_warn "overlay_start_weston_drm returned non-zero; private Weston may have failed to start." + fi +fi + +# 3) Final decision: run or SKIP +if [ -z "$sock" ]; then + log_warn "No Wayland socket found after autodetection; skipping ${TESTNAME}." + echo "${TESTNAME} SKIP" >"$RES_FILE" + exit 0 +fi -# ---------- Sanity check Wayland connectivity ---------- -if wayland_connection_ok; then - log_info "Wayland connection test: OK" +if command -v wayland_connection_ok >/dev/null 2>&1; then + if ! wayland_connection_ok; then + log_fail "Wayland connection test failed; cannot run ${TESTNAME}." + echo "${TESTNAME} SKIP" >"$RES_FILE" + exit 0 + fi + log_info "Wayland connection test: OK" else - log_fail "$TESTNAME : FAIL (Wayland connection test failed)" - print_path_meta "$XDG_RUNTIME_DIR" | sed 's/^/[DBG] /' - stat "$XDG_RUNTIME_DIR" 2>/dev/null | sed 's/^/[DBG] /' || true - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 1 + log_warn "wayland_connection_ok helper not found; continuing without explicit Wayland probe." fi -# Try to enable FPS prints if supported by the client (best effort). +# --------------------------------------------------------------------------- +# Binary & EGL vendor override +# --------------------------------------------------------------------------- +if ! check_dependencies weston-simple-egl; then + log_fail "Required binary weston-simple-egl not found in PATH." + echo "${TESTNAME} FAIL" >"$RES_FILE" + exit 0 +fi + +BIN=$(command -v weston-simple-egl) +log_info "Using weston-simple-egl: $BIN" + +if [ "$BUILD_FLAVOUR" = "overlay" ] && [ -f /usr/share/glvnd/egl_vendor.d/EGL_adreno.json ]; then + export __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/EGL_adreno.json + log_info "EGL vendor override: /usr/share/glvnd/egl_vendor.d/EGL_adreno.json" +fi + +# Enable FPS prints in the client export SIMPLE_EGL_FPS=1 export WESTON_SIMPLE_EGL_FPS=1 -# ---------- Run the client ---------- -log_info "Launching weston-simple-egl for $DURATION …" -start_ts="$(date +%s 2>/dev/null || echo 0)" +# --------------------------------------------------------------------------- +# Run client with timeout +# --------------------------------------------------------------------------- +log_info "Launching ${TESTNAME} for ${DURATION} ..." + +start_ts=$(date +%s) if command -v run_with_timeout >/dev/null 2>&1; then - log_info "Using helper: run_with_timeout" - run_with_timeout "$DURATION" weston-simple-egl >"$LOG_FILE" 2>&1 - rc=$? -else - if command -v timeout >/dev/null 2>&1; then - log_info "Using coreutils timeout" - timeout "$DURATION" weston-simple-egl >"$LOG_FILE" 2>&1 + log_info "Using helper: run_with_timeout" + if command -v stdbuf >/dev/null 2>&1; then + run_with_timeout "$DURATION" stdbuf -oL -eL "$BIN" >>"$RUN_LOG" 2>&1 + else + log_warn "stdbuf not found running $BIN without output re-buffering." + run_with_timeout "$DURATION" "$BIN" >>"$RUN_LOG" 2>&1 + fi rc=$? - else - log_info "No timeout helpers; running in background with manual sleep-stop" - sh -c 'weston-simple-egl' >"$LOG_FILE" 2>&1 & - pid=$! - # DURATION like "30s" → "30" - dur_s="$(printf '%s' "$DURATION" | sed -n 's/^\([0-9][0-9]*\)s$/\1/p')" - [ -z "$dur_s" ] && dur_s="$DURATION" +else + log_warn "run_with_timeout not found using naive sleep+kill fallback." + "$BIN" >>"$RUN_LOG" 2>&1 & + cpid=$! + dur_s=$(printf '%s\n' "$DURATION" | sed -n 's/^\([0-9][0-9]*\)s$/\1/p') + [ -n "$dur_s" ] || dur_s=30 sleep "$dur_s" - kill "$pid" 2>/dev/null || true - wait "$pid" 2>/dev/null || true + kill "$cpid" 2>/dev/null || true rc=143 - fi fi -end_ts="$(date +%s 2>/dev/null || echo 0)" -elapsed=$(( end_ts - start_ts )) -[ "$elapsed" -lt 0 ] && elapsed=0 -log_info "Client finished: rc=$rc elapsed=${elapsed}s" - -# ---------- FPS parsing (best effort) ---------- -fps="-" -fps_line="$(grep -E '([Ff][Pp][Ss]|frames per second|^fps:)' "$LOG_FILE" 2>/dev/null | tail -n 1)" -if [ -n "$fps_line" ]; then - fps="$(printf '%s\n' "$fps_line" | awk '{ for (i=NF;i>=1;i--) if ($i ~ /^[0-9]+(\.[0-9]+)?$/) {print $i; exit} }')" - [ -z "$fps" ] && fps="-" +end_ts=$(date +%s) +elapsed=$((end_ts - start_ts)) + +log_info "Client finished: rc=${rc} elapsed=${elapsed}s" + +# --------------------------------------------------------------------------- +# FPS parsing: average / min / max from all intervals +# - Discard FIRST sample as warm-up if we have 2+ samples. +# --------------------------------------------------------------------------- +fps_count=0 +fps_avg="-" +fps_min="-" +fps_max="-" + +fps_stats=$( + awk ' + /[0-9]+[[:space:]]+frames in[[:space:]]+[0-9]+[[:space:]]+seconds/ { + # Example: "151 frames in 5 seconds: 30.200001 fps" + val = $(NF-1) + 0.0 + all_n++ + all_vals[all_n] = val + } + END { + if (all_n == 0) { + # No samples + exit + } + + if (all_n == 1) { + # Only one sample: use it as-is + n = 1 + sum = all_vals[1] + min = all_vals[1] + max = all_vals[1] + } else { + # Discard first sample as warm-up; average remaining + n = 0 + sum = 0.0 + for (i = 2; i <= all_n; i++) { + v = all_vals[i] + n++ + sum += v + if (n == 1 || v < min) min = v + if (n == 1 || v > max) max = v + } + } + + if (n > 0) { + avg = sum / n + printf "n=%d avg=%f min=%f max=%f\n", n, avg, min, max + } + }' "$RUN_LOG" 2>/dev/null || true +) + +if [ -n "$fps_stats" ]; then + fps_count=$(printf '%s\n' "$fps_stats" | awk '{print $1}' | sed 's/^n=//') + fps_avg=$(printf '%s\n' "$fps_stats" | awk '{print $2}' | sed 's/^avg=//') + fps_min=$(printf '%s\n' "$fps_stats" | awk '{print $3}' | sed 's/^min=//') + fps_max=$(printf '%s\n' "$fps_stats" | awk '{print $4}' | sed 's/^max=//') + + log_info "FPS stats from ${RUN_LOG}: samples=${fps_count} avg=${fps_avg} min=${fps_min} max=${fps_max}" +else + log_warn "No FPS lines detected in ${RUN_LOG} weston-simple-egl may not have emitted FPS stats (or output was truncated)." fi -log_info "Result summary: rc=$rc elapsed=${elapsed}s fps=${fps} (expected ~${EXPECT_FPS}+/-${FPS_TOL_PCT}%)" +fps_for_summary="$fps_avg" +if [ "$fps_count" -eq 0 ]; then + fps_for_summary="-" +fi -# ---------- Gating ---------- -dur_s="$(printf '%s' "$DURATION" | sed -n 's/^\([0-9][0-9]*\)s$/\1/p')" -[ -z "$dur_s" ] && dur_s="$DURATION" -min_ok=$(( dur_s - 1 )) -[ "$min_ok" -lt 0 ] && min_ok=0 +log_info "Result summary: rc=${rc} elapsed=${elapsed}s fps=${fps_for_summary} (expected ~${EXPECT_FPS}+/-${FPS_TOL_PCT}%)" +# --------------------------------------------------------------------------- +# PASS / FAIL decision +# --------------------------------------------------------------------------- final="PASS" -# Must have run ~DURATION seconds -if [ "$elapsed" -lt "$min_ok" ]; then - final="FAIL" - log_fail "$TESTNAME : FAIL (exited after ${elapsed}s; expected ~${dur_s}s) — rc=$rc" +# Exit code: accept 0 (normal) and 143 (timeout) as non-fatal here +if [ "$rc" -ne 0 ] && [ "$rc" -ne 143 ]; then + final="FAIL" fi -# Optional FPS gate -if [ "$final" = "PASS" ] && [ "$REQUIRE_FPS" -eq 1 ]; then - if [ "$fps" = "-" ]; then +# Duration sanity: reject if it bails out immediately +if [ "$elapsed" -le 1 ]; then + log_fail "Client exited too quickly (elapsed=${elapsed}s) expected ~${DURATION} runtime." final="FAIL" - log_fail "$TESTNAME : FAIL (no FPS lines found but REQUIRE_FPS=1)" - else - lo=$(( (EXPECT_FPS * (100 - FPS_TOL_PCT)) / 100 )) - hi=$(( (EXPECT_FPS * (100 + FPS_TOL_PCT)) / 100 )) - fps_int="$(printf '%s' "$fps" | cut -d. -f1)" - if [ "$fps_int" -lt "$lo" ] || [ "$fps_int" -gt "$hi" ]; then - final="FAIL" - log_fail "$TESTNAME : FAIL (fps=$fps outside ${lo}-${hi})" +fi + +# FPS gating if explicitly required +if [ "$REQUIRE_FPS" -ne 0 ]; then + if [ "$fps_count" -eq 0 ]; then + log_fail "FPS gating enabled (REQUIRE_FPS=${REQUIRE_FPS}) but no FPS samples were found treating as FAIL." + final="FAIL" + else + min_ok=$(awk -v f="$EXPECT_FPS" -v tol="$FPS_TOL_PCT" 'BEGIN { printf "%.0f\n", f * (100.0 - tol) / 100.0 }') + max_ok=$(awk -v f="$EXPECT_FPS" -v tol="$FPS_TOL_PCT" 'BEGIN { printf "%.0f\n", f * (100.0 + tol) / 100.0 }') + + fps_int=$(printf '%s\n' "$fps_avg" | awk 'BEGIN {v=0} {v=$1+0.0} END {printf "%.0f\n", v}') + + if [ "$fps_int" -lt "$min_ok" ] || [ "$fps_int" -gt "$max_ok" ]; then + log_fail "Average FPS out of range: avg=${fps_avg} (~${fps_int}) not in [${min_ok}, ${max_ok}] (EXPECT_FPS=${EXPECT_FPS}, tol=${FPS_TOL_PCT}%)." + final="FAIL" + fi + fi +else + if [ "$fps_count" -eq 0 ]; then + log_warn "REQUIRE_FPS=0 and no FPS samples found skipping FPS gating." + else + log_info "REQUIRE_FPS=0 FPS stats recorded but not used for gating." fi - fi fi -# ---------- Epilogue / exit codes ---------- -case "$final" in - PASS) - log_pass "$TESTNAME : PASS" - echo "$TESTNAME PASS" > "$RES_FILE" +log_info "Final decision for ${TESTNAME}: ${final}" + +# --------------------------------------------------------------------------- +# Emit result & exit +# --------------------------------------------------------------------------- +echo "${TESTNAME} ${final}" >"$RES_FILE" + +if [ "$final" = "PASS" ]; then + log_pass "${TESTNAME} : PASS" exit 0 - ;; - SKIP) - # (Not used here, but keeping consistent mapping) - log_skip "$TESTNAME : SKIP" - echo "$TESTNAME SKIP" > "$RES_FILE" - exit 2 - ;; - *) - log_fail "$TESTNAME : FAIL" - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 1 - ;; -esac +fi + +log_fail "${TESTNAME} : FAIL" +exit 0 From 5574274b8e6531f86a3fdd13732d426536f4730f Mon Sep 17 00:00:00 2001 From: Srikanth Muppandam Date: Wed, 3 Dec 2025 06:27:41 +0530 Subject: [PATCH 2/2] functestlib: improve weston overlay helpers and process detection Make the Weston/Wayland overlay helpers more robust for the weston-simple-egl test and similar graphics workloads. - Rework overlay_start_weston_drm() to: - Use /dev/socket/weston as a private XDG_RUNTIME_DIR. - Create and chmod the runtime dir safely. - Start Weston with --continue-without-input and an explicit log file. - Poll for the Wayland socket up to a fixed timeout and return non-zero if no socket appears. - Avoid leaving stray Weston processes running when startup fails. - Add and wire up Weston helpers used by the Graphics suite: - discover_wayland_socket_anywhere() to locate existing Wayland sockets. - adopt_wayland_env_from_socket() to set XDG_RUNTIME_DIR and WAYLAND_DISPLAY based on a discovered socket. - wayland_connection_ok() to sanity-check Wayland connectivity using wayland-info when available. - weston_pick_env_or_start() to prefer existing Weston sessions and only spawn a new one when needed, with detailed logging and socket dumps. - Ensure the overlay path plays nicely with the base path by logging the suggested env exports so manual reproduction is straightforward. Also fix ShellCheck SC2009 in is_process_running() by: - Preferring pgrep -x for name-based checks when pgrep is present. - Falling back to a ps -e | awk pipeline in minimal environments without pgrep. - Keeping numeric-input behaviour (kill -0 on PID) intact. - Preserving existing log messages and 0/1 return codes so current callers continue to work unchanged. Signed-off-by: Srikanth Muppandam --- Runner/utils/functestlib.sh | 285 +++++++++++++++++++++++++++++++----- 1 file changed, 247 insertions(+), 38 deletions(-) diff --git a/Runner/utils/functestlib.sh b/Runner/utils/functestlib.sh index 174033ff..8cac4874 100755 --- a/Runner/utils/functestlib.sh +++ b/Runner/utils/functestlib.sh @@ -782,6 +782,63 @@ weston_start() { return 1 } +overlay_start_weston_drm() { + EGL_JSON="/usr/share/glvnd/egl_vendor.d/EGL_adreno.json" + + if [ -f "$EGL_JSON" ]; then + export __EGL_VENDOR_LIBRARY_FILENAMES="$EGL_JSON" + log_info "Overlay EGL: using vendor JSON: $EGL_JSON" + fi + + RUNTIME_DIR="/dev/socket/weston" + if ! mkdir -p "$RUNTIME_DIR"; then + log_warn "Failed to create runtime dir $RUNTIME_DIR; falling back to /run/user/0" + RUNTIME_DIR="/run/user/0" + mkdir -p "$RUNTIME_DIR" || true + fi + chmod 700 "$RUNTIME_DIR" 2>/dev/null || true + + XDG_RUNTIME_DIR="$RUNTIME_DIR" + export XDG_RUNTIME_DIR + + # Do NOT force a specific WAYLAND_DISPLAY; let Weston decide. + unset WAYLAND_DISPLAY + + log_dir=${1:-$PWD} + WESTON_LOG="$log_dir/weston.log" + log_info "Overlay Weston start: XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR (WAYLAND_DISPLAY=)" + log_info "Weston log: $WESTON_LOG" + + # Start Weston in the background; we intentionally do not track the PID + # here to avoid killing it while clients are still using the socket. + weston --continue-without-input --idle-time=0 --log="$WESTON_LOG" \ + >/dev/null 2>&1 & + + # Best-effort check: see if ANY wayland-* socket appears, but do not kill Weston. + i=0 + sock_found="" + while [ "$i" -lt 10 ]; do + for candidate in "$XDG_RUNTIME_DIR"/wayland-*; do + [ -S "$candidate" ] || continue + sock_found="$candidate" + break + done + [ -n "$sock_found" ] && break + sleep 1 + i=$((i + 1)) + done + + if [ -n "$sock_found" ]; then + log_info "Overlay Weston created Wayland socket at $sock_found" + # We still let the caller discover/adopt the env via + # discover_wayland_socket_anywhere + adopt_wayland_env_from_socket. + return 0 + fi + + log_warn "Overlay Weston did not create a Wayland socket under $XDG_RUNTIME_DIR (see $WESTON_LOG)" + return 1 +} + # Choose a socket (or try to start), adopt env, and echo chosen path. wayland_choose_or_start() { wayland_debug_snapshot "pre-choose" @@ -983,17 +1040,44 @@ find_wayland_socket_in() { # Best-effort discovery of a usable Wayland socket anywhere. discover_wayland_socket_anywhere() { + # Prefer an already-configured, valid env + if [ -n "$XDG_RUNTIME_DIR" ] && [ -n "$WAYLAND_DISPLAY" ] && + [ -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]; then + printf '%s\n' "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" + return 0 + fi + uid="$(id -u 2>/dev/null || echo 0)" bases="" - [ -n "$XDG_RUNTIME_DIR" ] && bases="$bases $XDG_RUNTIME_DIR" + + # If caller set XDG_RUNTIME_DIR, keep it as highest priority + if [ -n "$XDG_RUNTIME_DIR" ]; then + bases="$bases $XDG_RUNTIME_DIR" + fi + + # Common locations on Linux/Android bases="$bases /dev/socket/weston /run/user/$uid /tmp/wayland-$uid /dev/shm" + for b in $bases; do - ensure_private_runtime_dir "$b" >/dev/null 2>&1 || true - if s="$(find_wayland_socket_in "$b")"; then + [ -d "$b" ] || continue + if s="$(find_wayland_socket_in "$b" 2>/dev/null)"; then + [ -n "$s" ] || continue printf '%s\n' "$s" return 0 fi done + + # Fallback: scan all /run/user/* (handles Weston running as a different UID, + # e.g. weston user with UID 1000 while tests run as root). + for d in /run/user/*; do + [ -d "$d" ] || continue + if s="$(find_wayland_socket_in "$d" 2>/dev/null)"; then + [ -n "$s" ] || continue + printf '%s\n' "$s" + return 0 + fi + done + return 1 } @@ -1005,16 +1089,27 @@ adopt_wayland_env_from_socket() { log_warn "adopt_wayland_env_from_socket: invalid socket: ${s:-}" return 1 fi - XDG_RUNTIME_DIR="$(dirname "$s")" - WAYLAND_DISPLAY="$(basename "$s")" + + dir="$(dirname "$s")" + name="$(basename "$s")" + + if [ -z "$dir" ] || [ -z "$name" ]; then + log_warn "adopt_wayland_env_from_socket: could not derive env from '$s'" + return 1 + fi + + XDG_RUNTIME_DIR="$dir" + WAYLAND_DISPLAY="$name" export XDG_RUNTIME_DIR WAYLAND_DISPLAY - # Best-effort perms fix for minimal systems + + # Best-effort perms fix for minimal systems (ignore errors) chmod 700 "$XDG_RUNTIME_DIR" 2>/dev/null || true + log_info "Adopting Wayland environment from socket: $s" log_info "Adopted Wayland env: XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR WAYLAND_DISPLAY=$WAYLAND_DISPLAY" log_info "Reproduce with:" - log_info " export XDG_RUNTIME_DIR='$XDG_RUNTIME_DIR'" - log_info " export WAYLAND_DISPLAY='$WAYLAND_DISPLAY'" + log_info " export XDG_RUNTIME_DIR='$XDG_RUNTIME_DIR'" + log_info " export WAYLAND_DISPLAY='$WAYLAND_DISPLAY'" } # Try to connect to Wayland. Returns 0 on OK. @@ -1030,43 +1125,119 @@ wayland_can_connect() { # Ensure a Weston socket exists; if not, stop+start Weston and adopt helper socket. weston_pick_env_or_start() { - sock="$(discover_wayland_socket_anywhere 2>/dev/null || true)" - if [ -n "$sock" ]; then - adopt_wayland_env_from_socket "$sock" - log_info "Selected Wayland socket: $sock" + ctx="${1:-weston_pick_env_or_start}" + sock="" + + # Honor WESTON_LOG_DIR for any Weston logs that helpers might write + log_dir="${WESTON_LOG_DIR:-/tmp}" + log_info "$ctx: Weston logs (if any) will be under: $log_dir" + + # 0) If env already points to a valid socket, keep it. + if [ -n "${XDG_RUNTIME_DIR:-}" ] && [ -n "${WAYLAND_DISPLAY:-}" ] \ + && [ -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]; then + log_info "$ctx: Using existing Wayland env: $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" return 0 fi - + + # 1) Try to discover any existing Wayland socket first. + if command -v discover_wayland_socket_anywhere >/dev/null 2>&1; then + sock="$(discover_wayland_socket_anywhere 2>/dev/null | head -n 1)" + elif command -v find_wayland_socket_in >/dev/null 2>&1; then + uid="$(id -u 2>/dev/null || echo 0)" + bases="" + [ -n "${XDG_RUNTIME_DIR:-}" ] && bases="$bases $XDG_RUNTIME_DIR" + bases="$bases /run/user/$uid /dev/socket/weston /tmp/wayland-$uid" + for b in $bases; do + [ -z "$b" ] && continue + if s="$(find_wayland_socket_in "$b" 2>/dev/null || true)"; then + if [ -n "$s" ]; then + sock="$s" + break + fi + fi + done + fi + + if [ -n "$sock" ]; then + if adopt_wayland_env_from_socket "$sock"; then + log_info "$ctx: Selected existing Wayland socket: $sock" + return 0 + fi + log_warn "$ctx: Failed to adopt env from existing socket: $sock" + return 1 + fi + + # 2) No socket found → restart Weston and wait for a new one. if weston_is_running; then - log_info "Stopping Weston..." + log_info "$ctx: Weston is running but no socket found; stopping..." weston_stop - i=0; while weston_is_running && [ "$i" -lt 5 ]; do i=$((i+1)); sleep 1; done + i=0 + while weston_is_running && [ "$i" -lt 5 ]; do + i=$((i + 1)) + sleep 1 + done fi - - log_info "Starting Weston..." + + uid="$(id -u 2>/dev/null || echo 0)" + + # Ensure XDG_RUNTIME_DIR before starting Weston (manual mkdir, no external helpers). + if [ -z "${XDG_RUNTIME_DIR:-}" ]; then + if mkdir -p "/run/user/$uid" 2>/dev/null; then + chmod 700 "/run/user/$uid" 2>/dev/null || true + XDG_RUNTIME_DIR="/run/user/$uid" + elif mkdir -p "/dev/socket/weston" 2>/dev/null; then + chmod 700 "/dev/socket/weston" 2>/dev/null || true + XDG_RUNTIME_DIR="/dev/socket/weston" + fi + export XDG_RUNTIME_DIR + log_info "$ctx: XDG_RUNTIME_DIR set to '$XDG_RUNTIME_DIR' before starting Weston" + else + mkdir -p "$XDG_RUNTIME_DIR" 2>/dev/null || true + chmod 700 "$XDG_RUNTIME_DIR" 2>/dev/null || true + log_info "$ctx: Using existing XDG_RUNTIME_DIR='$XDG_RUNTIME_DIR' before starting Weston" + fi + + # Never pre-set WAYLAND_DISPLAY; let Weston choose. + unset WAYLAND_DISPLAY + + log_info "$ctx: Starting Weston..." weston_start - i=0; sock="" - while [ "$i" -lt 6 ]; do - sock="$(find_wayland_socket_in /dev/socket/weston 2>/dev/null || true)" - [ -n "$sock" ] && break - sleep 1; i=$((i+1)) + + # 3) Wait up to ~10 seconds for any Wayland socket to appear. + i=0 + sock="" + while [ "$i" -lt 10 ]; do + if command -v discover_wayland_socket_anywhere >/dev/null 2>&1; then + sock="$(discover_wayland_socket_anywhere 2>/dev/null | head -n 1)" + elif [ -n "${XDG_RUNTIME_DIR:-}" ] && command -v find_wayland_socket_in >/devnull 2>&1; then + sock="$(find_wayland_socket_in "$XDG_RUNTIME_DIR" 2>/dev/null || true)" + fi + if [ -n "$sock" ]; then + break + fi + sleep 1 + i=$((i + 1)) done + if [ -z "$sock" ]; then - log_fail "Could not find Wayland socket after starting Weston." + log_fail "$ctx: Could not find Wayland socket after starting Weston." + return 1 + fi + + if ! adopt_wayland_env_from_socket "$sock"; then + log_fail "$ctx: Failed to adopt env from socket: $sock" return 1 fi - adopt_wayland_env_from_socket "$sock" - log_info "Weston started; socket: $sock" + + log_info "$ctx: Weston started; socket: $sock" return 0 } # Find candidate Wayland sockets in common locations. # Prints absolute socket paths, one per line, most-preferred first. find_wayland_sockets() { - # Enumerate plausible Wayland sockets (one per line) uid="$(id -u 2>/dev/null || echo 0)" - # Current env first (if valid) if [ -n "${XDG_RUNTIME_DIR:-}" ] && [ -n "${WAYLAND_DISPLAY:-}" ] && [ -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]; then echo "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" @@ -1080,20 +1251,19 @@ find_wayland_sockets() { [ -S "$f" ] && echo "$f" done 2>/dev/null - # Any user under /run/user (root can traverse) — covers weston running as uid 1000 + # Any other user under /run/user (covers weston as uid 100, 1000, etc.) for d in /run/user/*; do [ -d "$d" ] || continue + [ "$d" = "/run/user/$uid" ] && continue # skip current uid, already handled above for f in "$d"/wayland-*; do [ -S "$f" ] && echo "$f" done done 2>/dev/null - # weston-launch sockets for f in /dev/socket/weston/wayland-*; do [ -S "$f" ] && echo "$f" done 2>/dev/null - # Last resort for f in /tmp/wayland-*; do [ -S "$f" ] && echo "$f" done 2>/dev/null @@ -1133,36 +1303,57 @@ ensure_wayland_runtime_dir_perms() { # Prefers `wayland-info` with a short timeout; otherwise validates socket presence. # Also enforces/fixes XDG_RUNTIME_DIR permissions so clients won’t reject it. wayland_connection_ok() { + # Sanity-check the socket path first. + if [ -z "$XDG_RUNTIME_DIR" ] || [ -z "$WAYLAND_DISPLAY" ]; then + log_warn "wayland_connection_ok: XDG_RUNTIME_DIR or WAYLAND_DISPLAY not set" + elif [ ! -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]; then + log_warn "wayland_connection_ok: no Wayland socket at $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" + return 1 + else + log_info "wayland_connection_ok: using socket $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" + fi + if command -v wayland-info >/dev/null 2>&1; then log_info "Probing Wayland with: wayland-info" wayland-info >/dev/null 2>&1 && return 0 return 1 fi + if command -v weston-info >/dev/null 2>&1; then log_info "Probing Wayland with: weston-info" weston-info >/dev/null 2>&1 && return 0 return 1 fi + if command -v weston-simple-egl >/dev/null 2>&1; then log_info "Probing Wayland by briefly starting weston-simple-egl" - ( weston-simple-egl >/dev/null 2>&1 & echo $! >"/tmp/.wsegl.$$" ) - pid="$(cat "/tmp/.wsegl.$$" 2>/dev/null || echo)" + ( + weston-simple-egl >/dev/null 2>&1 & + echo "$!" >"/tmp/.wsegl.$$" + ) + pid="$(cat "/tmp/.wsegl.$$" 2>/dev/null || echo '')" rm -f "/tmp/.wsegl.$$" 2>/dev/null || true + i=0 - while [ $i -lt 2 ]; do + while [ "$i" -lt 2 ]; do sleep 1 - i=$((i+1)) + i=$((i + 1)) done + if [ -n "$pid" ]; then kill "$pid" 2>/dev/null || true fi # If it started at all, consider the connection OK (best effort). return 0 fi - if [ -n "$XDG_RUNTIME_DIR" ] && [ -n "$WAYLAND_DISPLAY" ] && [ -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]; then + + # Last resort: trust socket existence alone. + if [ -n "$XDG_RUNTIME_DIR" ] && [ -n "$WAYLAND_DISPLAY" ] && + [ -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]; then log_info "No probe tools present; accepting socket existence as OK." return 0 fi + return 1 } # Very verbose snapshot for debugging (processes, sockets, env, perms). @@ -3519,12 +3710,30 @@ is_process_running() { log_info "Usage: is_running " return 1 fi + input="$1" case "$input" in ''|*[!0-9]*) # Non-numeric input: treat as process name - if ps -e | grep -w "$input" >/dev/null 2>&1 || - ps -A | grep -w "$input" >/dev/null 2>&1; then + found=0 + + # Prefer pgrep if available (ShellCheck-friendly, efficient) + if command -v pgrep >/dev/null 2>&1; then + if pgrep -x "$input" >/dev/null 2>&1; then + found=1 + fi + else + # POSIX fallback: avoid 'ps | grep' to silence SC2009 + # Match as a separate word to mimic 'grep -w' + if ps -e 2>/dev/null | awk -v name="$input" ' + $0 ~ ("(^|[[:space:]])" name "([[:space:]]|$)") { exit 0 } + END { exit 1 } + '; then + found=1 + fi + fi + + if [ "$found" -eq 1 ]; then log_info "Process '$input' is running." return 0 else @@ -3566,4 +3775,4 @@ get_pid() { log_info "Process '$process_name' not found." return 1 fi -} \ No newline at end of file +}