Skip to content

Commit 7e5f7f5

Browse files
Use JSON pipeline timing data
Replace the sourced timing.env handoff with a pipeline_timing.json data file generated by collect_timing.sh and parsed by result.sh with jq. Harden timing parsing tests and document the required BASE_PATH setting in the production Gunicorn examples. Signed-off-by: Yoshifumi Nakamura <nakamura@riken.jp>
1 parent 7782f67 commit 7e5f7f5

6 files changed

Lines changed: 104 additions & 18 deletions

File tree

.github/workflows/result-server-tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ on:
55
paths:
66
- "result_server/**"
77
- "scripts/bk_functions.sh"
8+
- "scripts/collect_timing.sh"
89
- "scripts/result.sh"
910
- "scripts/result_server/**"
1011
- "scripts/estimation/**"
@@ -32,6 +33,7 @@ on:
3233
paths:
3334
- "result_server/**"
3435
- "scripts/bk_functions.sh"
36+
- "scripts/collect_timing.sh"
3537
- "scripts/result.sh"
3638
- "scripts/result_server/**"
3739
- "scripts/estimation/**"

docs/deploy/hardening-guide.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ Example options:
6868
```text
6969
WorkingDirectory=<deploy-root>
7070
Environment=PYTHONPATH=<deploy-root>/benchkit/result_server
71+
Environment=BASE_PATH=<result-data-root>
7172
ExecStart=<venv>/bin/gunicorn \
7273
-w 2 \
7374
-b 127.0.0.1:8800 \
@@ -79,6 +80,7 @@ ExecStart=<venv>/bin/gunicorn \
7980
An equivalent direct import form is:
8081

8182
```text
83+
BASE_PATH=<result-data-root> \
8284
gunicorn --chdir <deploy-root>/benchkit/result_server \
8385
-w 2 \
8486
-b 127.0.0.1:8800 \
@@ -93,6 +95,11 @@ while the existing `from routes.*` and `from utils.*` imports are resolved by
9395
putting `benchkit/result_server` on `PYTHONPATH` or making it the working
9496
directory.
9597

98+
`BASE_PATH` is required because the application validates the result-data root
99+
when `app.py` is imported. Point it at the directory that contains the portal's
100+
received results, estimated results, profiler archives, and related runtime
101+
data.
102+
96103
For deployments that want a separate audit file in addition to stderr capture,
97104
set:
98105

result_server/tests/test_script_source_info_security.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ def test_result_script_does_not_source_source_info_env():
1515
assert "jq -n" in result_script
1616

1717

18+
def test_result_script_does_not_source_timing_env():
19+
result_script = (REPO_ROOT / "scripts" / "result.sh").read_text(encoding="utf-8")
20+
21+
assert ". results/timing.env" not in result_script
22+
assert "source results/timing.env" not in result_script
23+
assert "results/pipeline_timing.json" in result_script
24+
25+
1826
def test_bk_fetch_source_writes_encoded_source_info_values():
1927
bk_functions = (REPO_ROOT / "scripts" / "bk_functions.sh").read_text(encoding="utf-8")
2028

scripts/collect_timing.sh

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,58 @@
11
#!/bin/bash
22
# collect_timing.sh - Collect timing information from timestamp files
3-
# Reads build/run/queue timestamp files and generates results/timing.env
3+
# Reads build/run/queue timestamp files and generates results/pipeline_timing.json
44

55
BUILD_TIME=0
66
QUEUE_TIME=0
77
RUN_TIME=0
88

9+
timestamp_value() {
10+
local path="$1"
11+
local value=""
12+
13+
if [ -f "$path" ]; then
14+
value=$(cat "$path")
15+
fi
16+
17+
case "$value" in
18+
''|*[!0-9]*)
19+
printf ''
20+
;;
21+
*)
22+
printf '%s' "$value"
23+
;;
24+
esac
25+
}
26+
927
# Build time = build_end - build_start
1028
if [ -f results/build_start ] && [ -f results/build_end ]; then
11-
bs=$(cat results/build_start)
12-
be=$(cat results/build_end)
13-
BUILD_TIME=$((be - bs))
29+
bs=$(timestamp_value results/build_start)
30+
be=$(timestamp_value results/build_end)
31+
if [ -n "$bs" ] && [ -n "$be" ] && [ "$be" -ge "$bs" ]; then
32+
BUILD_TIME=$((be - bs))
33+
fi
1434
fi
1535

1636
# Run time = run_end - run_start
1737
if [ -f results/run_start ] && [ -f results/run_end ]; then
18-
rs=$(cat results/run_start)
19-
re=$(cat results/run_end)
20-
RUN_TIME=$((re - rs))
38+
rs=$(timestamp_value results/run_start)
39+
re=$(timestamp_value results/run_end)
40+
if [ -n "$rs" ] && [ -n "$re" ] && [ "$re" -ge "$rs" ]; then
41+
RUN_TIME=$((re - rs))
42+
fi
2143
fi
2244

2345
# Queue time: not measurable with current Jacamar/pjsub architecture
2446
# (before_script/script all run inside the batch job, so queue_submit
2547
# is recorded after the job has already started)
2648
QUEUE_TIME=0
2749

28-
echo "BUILD_TIME=$BUILD_TIME" > results/timing.env
29-
echo "QUEUE_TIME=$QUEUE_TIME" >> results/timing.env
30-
echo "RUN_TIME=$RUN_TIME" >> results/timing.env
50+
cat > results/pipeline_timing.json <<EOF
51+
{
52+
"build_time": $BUILD_TIME,
53+
"queue_time": $QUEUE_TIME,
54+
"run_time": $RUN_TIME
55+
}
56+
EOF
3157

3258
echo "Timing collected: build=${BUILD_TIME}s queue=${QUEUE_TIME}s run=${RUN_TIME}s"

scripts/result.sh

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,16 +158,24 @@ write_result_json() {
158158
local idx="$1"
159159
local fom_breakdown_block=""
160160

161-
# Build pipeline_timing block if timing.env exists (only for first result to avoid duplication)
161+
# Build pipeline_timing block if pipeline_timing.json exists (only for first result to avoid duplication).
162+
# Treat the file as data; never source generated timing files as shell.
162163
local timing_block=""
163-
if [ "$idx" = "0" ] && [ -f results/timing.env ]; then
164-
source results/timing.env
164+
if [ "$idx" = "0" ] && [ -f results/pipeline_timing.json ]; then
165+
local pipeline_timing_json
166+
pipeline_timing_json=$(jq -c '
167+
def num: if type == "number" then . else (tonumber? // 0) end;
168+
{
169+
build_time: ((.build_time // 0) | num),
170+
queue_time: ((.queue_time // 0) | num),
171+
run_time: ((.run_time // 0) | num)
172+
}
173+
' results/pipeline_timing.json 2>/dev/null || true)
174+
if [ -z "$pipeline_timing_json" ] || [ "$pipeline_timing_json" = "null" ]; then
175+
pipeline_timing_json='{"build_time":0,"queue_time":0,"run_time":0}'
176+
fi
165177
timing_block=",
166-
\"pipeline_timing\": {
167-
\"build_time\": ${BUILD_TIME:-0},
168-
\"queue_time\": ${QUEUE_TIME:-0},
169-
\"run_time\": ${RUN_TIME:-0}
170-
}"
178+
\"pipeline_timing\": $pipeline_timing_json"
171179
fi
172180

173181
# Build execution_mode block

scripts/tests/test_result_profile_data.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,20 @@ cat > "${TMP_DIR}/results/result" <<'EOF'
1818
FOM:9.9999999999999995e-07 FOM_version:test Exp:CASE0 node_count:1 numproc_node:1 nthreads:2
1919
EOF
2020

21+
cat > "${TMP_DIR}/results/pipeline_timing.json" <<'EOF'
22+
{
23+
"build_time": "12",
24+
"queue_time": 0,
25+
"run_time": 34
26+
}
27+
EOF
28+
29+
cat > "${TMP_DIR}/results/timing.env" <<'EOF'
30+
BUILD_TIME=$(touch timing_env_was_sourced)
31+
QUEUE_TIME=999
32+
RUN_TIME=999
33+
EOF
34+
2135
cat > "${TMP_DIR}/bk_profiler_artifact/meta.json" <<'EOF'
2236
{
2337
"tool": "fapp",
@@ -84,9 +98,13 @@ jq -e '
8498
.profile_data.level == "single" and
8599
.profile_data.report_format == "text" and
86100
.profile_data.run_count == 1 and
101+
.pipeline_timing.build_time == 12 and
102+
.pipeline_timing.queue_time == 0 and
103+
.pipeline_timing.run_time == 34 and
87104
(.profile_data.events | index("pa1") != null) and
88105
(.profile_data.report_kinds | index("summary_text") != null)
89106
' "${RESULT_JSON}" >/dev/null
107+
test ! -f "${TMP_DIR}/timing_env_was_sourced"
90108

91109
NCU_RESULT_JSON="${TMP_DIR}/ncu/results/result0.json"
92110
test -f "${NCU_RESULT_JSON}"
@@ -100,4 +118,21 @@ jq -e '
100118
(.profile_data.report_kinds | index("ncu_report") != null)
101119
' "${NCU_RESULT_JSON}" >/dev/null
102120

121+
TIMING_TMP="${TMP_DIR}/timing"
122+
mkdir -p "${TIMING_TMP}/results"
123+
printf '100\n' > "${TIMING_TMP}/results/build_start"
124+
printf '115\n' > "${TIMING_TMP}/results/build_end"
125+
printf 'bad\n' > "${TIMING_TMP}/results/run_start"
126+
printf '200\n' > "${TIMING_TMP}/results/run_end"
127+
pushd "${TIMING_TMP}" >/dev/null
128+
bash "${REPO_DIR}/scripts/collect_timing.sh" >/dev/null
129+
popd >/dev/null
130+
test -f "${TIMING_TMP}/results/pipeline_timing.json"
131+
test ! -f "${TIMING_TMP}/results/timing.env"
132+
jq -e '
133+
.build_time == 15 and
134+
.queue_time == 0 and
135+
.run_time == 0
136+
' "${TIMING_TMP}/results/pipeline_timing.json" >/dev/null
137+
103138
echo "result profile_data test passed"

0 commit comments

Comments
 (0)