Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-09 10:47:24

0001 #!/bin/bash
0002 # segfault-es-replay.sh — one Event Service unit's simulation request run
0003 # again, inside the campaign image on npps0 (swf-epicprod
0004 # docs/SEGFAULT_DIAGNOSIS.md, Event Service units, Reproduction).
0005 #
0006 # Shipped and started by scripts/segfault-es-reproduce.py, never run on the
0007 # monitor host. The working directory holds what the doer shipped:
0008 #   payload/                the payload of the job's version
0009 #   environment-replay.sh   the task's payload environment, registration and
0010 #                           copies off, the resident simulation switched on
0011 #   replay.env              REPLAY_FILE (EVGEN/<file>), REPLAY_EXT,
0012 #                           REPLAY_EVENTS, REPLAY_CHUNK, REPLAY_SKIP,
0013 #                           REPLAY_LABEL, REPLAY_SEED, REPLAY_TIMEOUT_S
0014 #
0015 # 1. run.sh runs the unit as a slot runs its first unit (es/es_slot.py,
0016 #    run_unit): the unit's skip, chunk and label, the slot's resident
0017 #    simulation socket, state and reply, and EPICPROD_UNIT_TEMPLATE, so
0018 #    run.sh writes the request's npsim arguments beside the template. The
0019 #    request the resident receives is the job's own: input, first event,
0020 #    count and seed.
0021 # 2. A resident death is DD4hep's "Handle signal: N" in the state
0022 #    directory's resident.log. Then the same arguments run once more as a
0023 #    cold npsim under gdb, over the request's events cut from the source
0024 #    (es/evgen_ranges.py), with the request seed and no per-event seed,
0025 #    which is how the resident seeds a request.
0026 # 3. out/result.json, written last, is the outcome the doer collects:
0027 #    crashed (with the signal), completed, or inconclusive (with the reason).
0028 set -u
0029 HERE=$(pwd)
0030 source ./replay.env
0031 mkdir -p run state out tmp gdb
0032 # run.sh sources environment*.sh from its working directory.
0033 cp environment-replay.sh run/
0034 SOCK=/tmp/esreplay-$$.sock
0035 export EPICPROD_SKIP_EVENTS="${REPLAY_SKIP}" EPICPROD_CHUNK_LABEL="${REPLAY_LABEL}"
0036 export EPICPROD_SIM_SOCKET="${SOCK}" EPICPROD_SIM_STATE="${HERE}/state"
0037 export EPICPROD_SIM_REPLY="${HERE}/out/simulation.json"
0038 export EPICPROD_UNIT_TEMPLATE="${HERE}/out/unit-template.json"
0039 export EPICPROD_CALL_HOME=0 EPICPROD_NTHREADS=1 REGISTRATION_STAGGER_MAX_S=0
0040 # Scratch in the work tree: the container binds the host /tmp, which on
0041 # npps0 is the small root volume (es_slot.py, TMPDIR).
0042 export TMPDIR="${HERE}/tmp"
0043 export PAYLOAD_STAGES_LOG="${HERE}/out/stages.log" PAYLOAD_REPORT="${HERE}/out/payload-report.json"
0044 export PAYLOAD_JOB_REPORT="${HERE}/out/jobReport.json"
0045 
0046 started=$(date +%s)
0047 echo "replay: run.sh ${REPLAY_FILE} ${REPLAY_EXT} ${REPLAY_EVENTS} ${REPLAY_CHUNK}, skip ${REPLAY_SKIP}, label ${REPLAY_LABEL:-none}"
0048 (cd run && timeout "${REPLAY_TIMEOUT_S}" "${HERE}/payload/run.sh" \
0049    "${REPLAY_FILE}" "${REPLAY_EXT}" "${REPLAY_EVENTS}" "${REPLAY_CHUNK}") > out/payload.log 2>&1
0050 rc=$?
0051 ended=$(date +%s)
0052 echo "replay: run.sh exited ${rc} after $((ended - started)) s"
0053 
0054 # A unit's exit leaves its resident up (resident_sim.py, request).
0055 (cd payload/es && python3 -c 'import sys; from resident_sim import exchange; exchange(sys.argv[1], {"command": "stop"}, 10)' \
0056    "${SOCK}") > /dev/null 2>&1 || true
0057 pkill -f -- "--socket ${SOCK}" 2>/dev/null || true
0058 
0059 # The crash is the first crash-class signal DD4hep's handler reports (6, 7,
0060 # 8, 11); a SIGTERM after it is the request timeout ending a resident that
0061 # stalled after its signal, and a SIGTERM alone is no crash (41494, 2026-10-29:
0062 # SIGABRT, a stall, then SIGTERM at 1,800 s).
0063 signal=$(grep -oE 'Handle signal: (6|7|8|11)\b' state/resident.log 2>/dev/null | head -1 | grep -oE '[0-9]+$')
0064 gdb_rc=""
0065 if [ -n "${signal}" ] && [ -s out/unit-template.json.npsim.json ]; then
0066   # The request's npsim arguments as a cold run: the source's events cut to
0067   # a file of their own (skip 0), the request seed, no per-event seed.
0068   if python3 - out/unit-template.json.npsim.json gdb > gdb/args.log 2>&1 <<'EOF'
0069 import json, os, sys
0070 argv, d = json.load(open(sys.argv[1])), sys.argv[2]
0071 out, source, i = [], '', 0
0072 while i < len(argv):
0073     flag = argv[i]
0074     if flag == '--random.enableEventSeed':
0075         i += 1
0076         continue
0077     if flag in ('--skipNEvents', '--inputFiles', '--outputFile'):
0078         value = argv[i + 1]
0079         if flag == '--inputFiles':
0080             source = value
0081             value = os.path.abspath(os.path.join(d, 'range.hepmc3'))
0082         elif flag == '--skipNEvents':
0083             value = '0'
0084         else:
0085             value = os.path.abspath(os.path.join(d, 'out.edm4hep.root'))
0086         out += [flag, value]
0087         i += 2
0088         continue
0089     out.append(flag)
0090     i += 1
0091 seed = argv[argv.index('--random.seed') + 1] if '--random.seed' in argv else ''
0092 if seed != os.environ.get('REPLAY_SEED'):
0093     sys.exit(f'request seed {seed} is not the recorded {os.environ.get("REPLAY_SEED")}')
0094 open(os.path.join(d, 'source'), 'w').write(source)
0095 open(os.path.join(d, 'args'), 'w').write('\0'.join(out))
0096 print('cold npsim arguments:', ' '.join(out))
0097 EOF
0098   then
0099     python3 payload/es/evgen_ranges.py --input "$(cat gdb/source)" --first-event "${REPLAY_SKIP}" \
0100       --events "${REPLAY_EVENTS}" --output gdb/range.hepmc3 > gdb/cut.log 2>&1
0101     cut_rc=$?
0102     if [ "${cut_rc}" -eq 0 ]; then
0103       mapfile -d '' args < gdb/args
0104       npsim_py="$(dirname "$(readlink -f "$(command -v npsim)")")/npsim.py"
0105       (cd run && timeout "${REPLAY_TIMEOUT_S}" gdb -batch -ex run -ex bt -ex 'info registers rip' \
0106          --args python3 "${npsim_py}" "${args[@]}") > out/gdb.log 2>&1
0107       gdb_rc=$?
0108     else
0109       echo "replay: the range cut failed (exit ${cut_rc}); see gdb/cut.log"
0110     fi
0111   fi
0112 fi
0113 
0114 python3 - "${rc}" "${signal}" "${started}" "${ended}" "${gdb_rc}" > out/result.json.tmp <<'EOF'
0115 import json, os, socket, sys
0116 rc, signal, started, ended, gdb_rc = sys.argv[1:6]
0117 def read(path):
0118     try:
0119         with open(path) as f:
0120             return f.read()
0121     except OSError:
0122         return ''
0123 reply = {}
0124 try:
0125     reply = json.loads(read('out/simulation.json') or '{}')
0126 except ValueError:
0127     pass
0128 stages = read('out/stages.log').strip().splitlines()
0129 gdb = read('out/gdb.log')
0130 stopped = next((l.split('Program received signal', 1)[1].strip().rstrip('.')
0131                 for l in gdb.splitlines() if 'Program received signal' in l), '')
0132 resident = read('state/resident.log')
0133 stalled = signal and 'Handle signal: 15' in resident
0134 if signal:
0135     outcome, reason = 'crashed', f'resident npsim died on signal {signal}' + (
0136         ', stalled after it and was killed at the request timeout' if stalled else '')
0137 elif reply and not reply.get('error'):
0138     outcome, reason = 'completed', ''
0139 else:
0140     outcome = 'inconclusive'
0141     reason = (f'run.sh exited {rc}' + (' (timed out)' if rc == '124' else '')
0142               + (' after the resident was killed at the request timeout with no crash signal'
0143                  if 'Handle signal: 15' in resident else ' with no simulation reply and no resident death')
0144               + (f'; last stage: {stages[-1]}' if stages else ''))
0145 json.dump({'outcome': outcome, 'reason': reason, 'signal': int(signal) if signal else None,
0146            'payload_exit': int(rc), 'started_at': int(started), 'ended_at': int(ended),
0147            'host': socket.gethostname().split('.')[0],
0148            'simulation_reply': {k: reply.get(k) for k in ('events', 'seed', 'first_event',
0149                                                           'seed_mode', 'wall_s', 'error') if k in reply},
0150            'gdb': {'ran': bool(gdb), 'rc': int(gdb_rc) if gdb_rc else None, 'stopped_on': stopped},
0151            'stages': stages[-8:]}, sys.stdout, indent=1)
0152 EOF
0153 mv -f out/result.json.tmp out/result.json
0154 echo "replay: result written"