strace.lib #1

  • //
  • p4-sdp/
  • r26.1.0/
  • Server/
  • Unix/
  • p4/
  • common/
  • lib/
  • strace.lib
  • View
  • Commits
  • Open Download .zip Download (9 KB)
# shellcheck shell=bash
# shellcheck disable=SC2154  # All-caps globals (StracePidFile, StraceEnabledFile, etc.)
#                            # are set by callers before sourcing this library.
# Version ID Block (comment-only -- NOT executable). Relies on +k filetype
# modifier. This is a sourced library; a real 'declare' here would clobber
# the sourcing script's own $Version (confirmed: they share global scope).
# show_versions() greps this line's text directly; it is never evaluated.
# VersionID='$Id: //p4-sdp/r26.1.0/Server/Unix/p4/common/lib/strace.lib#1 $ $Change: 33565 $'

#==============================================================================
# Copyright and license info is available in the LICENSE file included with
# the Server Deployment Package (SDP), and also available online:
# https://workshop.perforce.com/view/p4-sdp/main/LICENSE
#------------------------------------------------------------------------------

#==============================================================================
# Library: strace.lib
#
# Shared functions for diagnostic strace support in SDP service init scripts
# (p4d_base, p4broker_base, p4p_base).
#
# Usage:
#   Source this file after p4_vars has been loaded (so $P4CLIB is defined):
#
#     source "$P4CLIB/strace.lib"
#
#   Before calling these functions, set the following globals:
#
#     StraceEnabledFile  - marker file path, e.g. $LOGS/.strace.p4d_1
#     StracePidFile      - strace PID file path, e.g. $LOGS/.strace.p4d_1.pid
#     StraceLog          - strace output log base path, e.g. $LOGS/strace.p4d_1.log
#                          A timestamp is appended at attachment time, e.g.:
#                          $LOGS/strace.p4d_1.20260627_123456.log
#     Log                - init log file path for appending status messages
#     ServiceName        - service name used in log messages, e.g. p4d_1
#
# Two strace modes are supported:
#
#   Wrap mode   (systemd only): run_service_under_strace
#     strace runs the service binary as its child process, capturing syscalls
#     from the very first instruction. Used when the service runs in the
#     foreground (systemd). In this mode strace cannot be stopped independently
#     from the service.
#
#   Attach mode (non-systemd): attach_strace_if_requested
#     strace attaches to the already-running service after it daemonizes. Easy
#     to stop independently (kill $(cat $StracePidFile)), but early startup
#     syscalls (before the PID is known) will be missed.
#
# Callers are responsible for selecting the appropriate mode based on
# $UseSystemd. See doc/Using_strace_for_SDP_Services.md for full details.
#==============================================================================

#------------------------------------------------------------------------------
# Function: run_service_under_strace "$binary" [args...]
#
# Runs the given command under strace in "wrap" mode. strace is interposed
# between the init script and the service binary, so every syscall is captured
# from the very first instruction — nothing is missed.
#
# This is the correct mode when the service runs in the foreground (systemd),
# because the init script already blocks on the service binary; replacing it
# with "strace ... binary" has zero additional complexity.
#
# In non-systemd (daemon) mode the service forks immediately and strace -f
# would block the init script indefinitely; use attach_strace_if_requested
# instead.
#
# Output file:
#   $StraceLog with a timestamp appended, e.g. strace.p4d_1.20260627_123456.log
#   The exact path is recorded in $Log.
#
# NOTE: In wrap mode strace is the parent process of the service. The only
# way to stop strace is to stop the service normally (e.g. systemctl stop).
#------------------------------------------------------------------------------
function run_service_under_strace {
   if ! command -v strace > /dev/null 2>&1; then
      echo "Warning: $StraceEnabledFile exists but 'strace' was not found in PATH; starting $ServiceName without strace." | tee -a "$Log"
      "$@"
      return
   fi

   local strace_custom_flags
   strace_custom_flags=$(tr -d '\n\r' < "$StraceEnabledFile" | xargs 2>/dev/null)
   local strace_flags="${strace_custom_flags:--f -tt}"

   local ts
   ts=$(date +%Y%m%d_%H%M%S)
   local effective_log="${StraceLog%.log}.${ts}.log"

   echo "Starting $ServiceName under strace (wrap mode, flags: $strace_flags); output: $effective_log" | tee -a "$Log"
   echo "Note: strace is the parent process in wrap mode; stop strace by stopping the service normally." | tee -a "$Log"

   # shellcheck disable=SC2086
   strace $strace_flags -o "$effective_log" "$@"
}

#------------------------------------------------------------------------------
# Function: attach_strace_if_requested <pid_cmd>
#
# If $StraceEnabledFile exists, attaches strace to the already-running service
# process and writes the strace PID to $StracePidFile. Intended for
# non-systemd (daemon) mode where the service forks into the background and
# the init script cannot block waiting for strace.
#
# Because strace attaches after the service has started, early startup syscalls
# (from process start until the PID is discoverable) will be missed. For
# complete coverage use run_service_under_strace (systemd only).
#
# Arguments:
#   $1 - Shell command string whose stdout is the service PID. Retried
#        up to 5 seconds while waiting for the process to appear.
#        Examples:
#          "cat \"$P4ROOT/server.pid\""        # p4d
#          "get_pids \"$P4PBIN\""              # p4p
#          "get_pids \"$P4BROKERCFG\""         # p4broker
#
# Strace flags:
#   Default flags are '-f -tt'. To override, place custom flags on a single
#   line in $StraceEnabledFile before starting the service.
#
# Output file:
#   $StraceLog with a timestamp appended, e.g. strace.p4d_1.20260627_123456.log
#
# NOTE: strace -f on a busy server generates large output and adds measurable
# overhead. This feature is intended for diagnostic use only.
#------------------------------------------------------------------------------
function attach_strace_if_requested {
   local pid_cmd="${1:?Usage: attach_strace_if_requested <pid_cmd>}"

   [[ -e "$StraceEnabledFile" ]] || return 0

   if ! command -v strace > /dev/null 2>&1; then
      echo "Warning: $StraceEnabledFile exists but 'strace' was not found in PATH; skipping strace." | tee -a "$Log"
      return 0
   fi

   # Use flags from the marker file if non-empty, otherwise apply defaults.
   local strace_custom_flags
   strace_custom_flags=$(tr -d '\n\r' < "$StraceEnabledFile" | xargs 2>/dev/null)
   local strace_flags="${strace_custom_flags:--f -tt}"

   # Wait up to 5 seconds for the service process to appear.
   local service_pid=
   local _i
   for _i in 1 2 3 4 5; do
      # shellcheck disable=SC2086
      service_pid=$(eval $pid_cmd 2>/dev/null | awk '{print $1}')
      [[ -n "$service_pid" ]] && break
      sleep 1
   done

   if [[ -z "$service_pid" ]]; then
      echo "Warning: $StraceEnabledFile exists but could not find a running $ServiceName process; skipping strace." | tee -a "$Log"
      return 0
   fi

   local ts
   ts=$(date +%Y%m%d_%H%M%S)
   local effective_log="${StraceLog%.log}.${ts}.log"

   echo "Attaching strace (flags: $strace_flags) to $ServiceName pid $service_pid; output: $effective_log" | tee -a "$Log"
   # shellcheck disable=SC2086
   nohup strace $strace_flags -o "$effective_log" -p "$service_pid" > /dev/null 2>&1 &
   local strace_pid=$!
   echo "$strace_pid" > "$StracePidFile"
   echo "strace running as pid $strace_pid. PID file: $StracePidFile" | tee -a "$Log"
   echo "To stop strace without affecting $ServiceName: kill \$(cat $StracePidFile)" | tee -a "$Log"
}

#------------------------------------------------------------------------------
# Function: stop_strace_if_running
#
# Stops the background strace process recorded in $StracePidFile (if any)
# and removes the PID file. Used after a service stops in attach mode.
# Safe to call even if strace has already exited (strace exits automatically
# when its target process dies).
#
# Not applicable in wrap mode (strace exits with the service automatically).
#------------------------------------------------------------------------------
function stop_strace_if_running {
   [[ -r "$StracePidFile" ]] || return 0

   local strace_pid
   strace_pid=$(cat "$StracePidFile")

   if kill -0 "$strace_pid" > /dev/null 2>&1; then
      echo "Stopping strace (pid $strace_pid) for $ServiceName." | tee -a "$Log"
      kill "$strace_pid" 2>&1 | tee -a "$Log"
   else
      echo "strace (pid $strace_pid) for $ServiceName is no longer running." | tee -a "$Log"
   fi

   rm -f "$StracePidFile"
   echo "Removed $StracePidFile." | tee -a "$Log"
}

# Change User Description Committed
#1 33565 Claude (AI Agent by Anthropic) Initial population of r26.1.0 from main.
//p4-sdp/main/Server/Unix/p4/common/lib/strace.lib
#1 33433 Claude (AI Agent by Anthropic) Copy Up from //p4-sdp/dev into //p4-sdp/main.

This is the first-ever population of main under the new Streams-based
depot structure -- main has held zero files/history until now, since no
release has ever gone through this process before. 463 files, covering
the entire 2026.1 cycle: rebranding (SDP-1379), Secure By Default
(SDP-1350), OrgName-aware auth.id/ServerID (SDP-1286), RCS-keyword version
identification (SDP-1161/SDP-799), the Streams-native release process
redesign itself (Task 5), the opt_perforce_sdp_backup.sh false-error fix,
the P4D 2026.1 test-suite targeting, refreshed P4*.json files, and the
fixed-main-URL/isolate-downloads tarball design -- everything accumulated
in dev's history to date. Isolated paths (ai_dev_support/, Version,
doc/*.html, doc/*.pdf, doc/gen/*.man.txt, doc/gen/sdp_install.cfg,
Unsupported/doc/*.html, Unsupported/doc/*.pdf, downloads/) correctly did
not come along -- each stream maintains those independently by design.

Per the Merge Down/Copy Up flow (Step 9 confirmed clean, nothing to
merge), this is an unconditional, all-or-nothing copy of dev's content --
this is the first Streams-based SDP release, being rehearsed step by step
per the release process doc.

Agent: Claude Code, Model: Claude Sonnet 5 (claude-sonnet-5), operating as bot_Claude_Anthropic.
//p4-sdp/dev/Server/Unix/p4/common/lib/strace.lib
#2 33409 Claude (AI Agent by Anthropic) Copy Up from //p4-sdp/dev_rebrand into //p4-sdp/dev.

This is the first promotion of dev_rebrand's work into dev since
dev_rebrand was created (2025-05-24) -- 303 files, covering the entire
2026.1 rebranding effort (SDP-1379), the Secure By Default adaptation
(SDP-1350), OrgName-aware auth.id/ServerID (SDP-1286), RCS-keyword
version identification (SDP-1161/SDP-799), and the Streams-native release
process redesign (Task 5) done this session, plus everything else
accumulated in dev_rebrand's history before this session.

Per the Merge Down/Copy Up flow, this is intentionally a full,
unconditional blast-replace of dev's content from dev_rebrand -- all
selectivity/care happened in the preceding Merge Down (dev -> dev_rebrand,
changes 33407-33408), which absorbed Robert Cowham's independent dev-side
work first so nothing of his is lost by this Copy Up.

Two files are worth calling out since they might look alarming in
isolation:
- tools/mdcu.sh is deleted -- intentional, retired this session in favor
  of the two direct Streams commands now documented in
  doc/ReleaseProcessOverview.md.
- tools/ReleaseProcessOverview.md is deleted -- this is a stale relic of
  a file move dev_rebrand made back in 2025-05-24 (tools/ -> doc/) that
  was never previously propagated to dev; the current, fully-rewritten
  doc/ReleaseProcessOverview.md is added/updated correctly by this same
  changelist.
#1 32990 C. Thomas Tyler p4 merge -b SDP_Classic_to_Streams
p4 resolve -as

All files resolved automatically.  The "real" merge work comes in the coming
Merge Down from dev->dev_rebrand.
//guest/perforce_software/sdp/dev/Server/Unix/p4/common/lib/strace.lib
#1 32904 C. Thomas Tyler strace support for p4d, p4broker, and p4p SDP init scripts

Added diagnostic strace feature to p4d_base, p4broker_base, and p4p_base.
When a file $LOGS/.strace.<ServiceName> exists at start time, strace is
enabled using one of two modes selected automatically:

  - Wrap mode (systemd): strace runs the service binary as its child
    process, capturing every syscall from the very first instruction.
    Ideal for diagnosing startup-phase issues (e.g. replica startup threads).

  - Attach mode (non-systemd): strace attaches to the already-running
    service after startup using strace -p <pid>. Best effort -- early
    startup syscalls may be missed.

Strace output goes to $LOGS/strace.<ServiceName>.<YYYYMMDD_HHMMSS>.log;
in attach mode the strace PID is also recorded in
$LOGS/.strace.<ServiceName>.pid.

Shared functions live in a new library: /p4/common/lib/strace.lib

Also added startup/shutdown logging to p4broker_base and p4p_base, which
previously had no init logging.

Added documentation: doc/Using_strace_for_SDP_Services.md

Fixes SDP-1365 (Feature): Add strace support to p4*_base init scripts, with docs.
#review-32858 @tom_tyler @robert_cowham