From 4b51edd68874210b603886172a5f22345beb4bbe Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 7 Sep 2026 06:23:34 +0000 Subject: [PATCH 1/3] =?UTF-8?q?Un=20paquet=20de=20d=C3=A9monstration=20:?= =?UTF-8?q?=20l'outil=20ET=20l'application=20qu'il=20observe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le kit hors ligne porte l'outil vers une application qu'on a déjà. Celui-ci porte une application AUSSI — sample-app.jar, ses dépendances et ses sources — pour qu'on puisse rejouer la campagne publiée avec un JDK et rien d'autre, sans construire le dépôt et sans réseau : l'édition complète dépose ses cinq composants dans ~/.runtime-xray au premier lancement. Deux décisions ont été prises en l'exécutant, pas en le lisant, et les deux auraient livré un paquet qui démontre le contraire de ce pour quoi il existe. --classes ne nomme que sample-app.jar. Ajouter libs/commons-lang3.jar — que l'application atteint vraiment, et que la démo publiée passait vraiment — met ses 231 classes dans la couverture, dont aucune n'a sa source dans le paquet. Le rapport s'ouvrait sur « 231 classes mesurées sur 258 n'ont pas leur source » : exactement le mode de défaillance que cet outil existe pour expliquer, sur le premier écran d'une démonstration. --attach-after se calcule sur la charge. Les valeurs se capturent en s'attachant à la JVM vivante, et les 8 s par défaut ne conviennent qu'à la charge complète. Raccourcir la campagne — ce que le README propose — faisait finir l'application avant l'attachement, et un tiers de ce que montre le rapport disparaissait avec un avertissement que personne n'aurait relié au ITERATIONS= qu'il venait de taper. Le README dit aussi ce qu'on ne verra pas sous Windows, et que c'est la plateforme et non le paquet : async-profiler n'y publie aucun binaire. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01J956wjynbd7fkZx4HpjHzP --- CLAUDE.md | 18 ++++ README.md | 20 +++++ bin/demo-kit.sh | 234 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 272 insertions(+) create mode 100755 bin/demo-kit.sh diff --git a/CLAUDE.md b/CLAUDE.md index c04d980..a7da284 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -97,6 +97,24 @@ suffice to get out of it, and it must stay so. `bin/offline-kit.sh` assembles the equivalent package to carry, fingerprints included. +`bin/demo-kit.sh` assembles a different one, and the difference is the point: the offline kit +carries the tool towards an application one already has, the demo kit carries **an +application too** — `sample-app.jar`, its dependencies and its sources — so somebody can +replay the published campaign with a JDK and nothing else. Two decisions in it were made by +running it rather than by reading it, and both would have shipped a package that demonstrates +the opposite of what it is for: + +- **`--classes` names `sample-app.jar` alone.** Adding `libs/commons-lang3.jar`, which the + application really does reach and which the published demo really did pass, puts its 231 + classes into the coverage — none with its source in the package. The report then opened on + *"231 measured classes out of 258 have no source"*: the exact failure mode this tool exists + to explain, on the first screen of a demonstration. +- **`--attach-after` is computed from the workload.** Values are captured by attaching to the + live JVM, and the default 8 s suits the full run only. Shortening the campaign — which the + README offers — made the application finish before the attachment, and a third of what the + report shows disappeared with a warning nobody would connect to the `ITERATIONS=` they had + just typed. + ## When the report does not show what was expected This is the tool's most costly failure mode, because it is **silent**: an empty code panel diff --git a/README.md b/README.md index 093dec3..b9b9094 100644 --- a/README.md +++ b/README.md @@ -270,6 +270,26 @@ async-profiler and Arthas (Apache-2.0) — that is what sets them apart from the which redistributes nothing. See [THIRD-PARTY.md](THIRD-PARTY.md). They are not published on Maven Central: the 170 KB jar stays the normal artefact. +#### The demonstration package, to try the tool without building it + +[`bin/demo-kit.sh`](bin/demo-kit.sh) assembles a package that carries the tool **and the +application it observes**, so the campaign published above can be replayed on any machine +with a JDK and nothing else: + +```bash +bin/demo-kit.sh # → target/runtime-xray-demo.zip (~18 MB) + +# then, wherever it is unzipped +./demo.sh # Linux / macOS demo.cmd on Windows +``` + +It holds the complete edition, `sample-app.jar` with its two dependencies, the sample's +sources — without them the report opens on "Source unavailable", which is the failure the +tool exists to explain rather than to demonstrate — and the three runs of the published +report, with their own root methods and filters. Under Windows the call tree is absent, and +that is the platform and not the package: async-profiler publishes no binary for it, and the +report now says so where the tree would be. + #### The kit, if one wants to see what is being carried [`bin/offline-kit.sh`](bin/offline-kit.sh) assembles the package to carry — the jar, the three diff --git a/bin/demo-kit.sh b/bin/demo-kit.sh new file mode 100755 index 0000000..0fc2ccf --- /dev/null +++ b/bin/demo-kit.sh @@ -0,0 +1,234 @@ +#!/usr/bin/env bash +# Assembles the package that reproduces the published demonstration, locally, on a machine +# that has neither this repository nor a network. +# +# The offline kit next door answers a different question: it carries the tool towards an +# application one already has. This one carries an application TOO — the one the demo is +# made of — so that somebody can run the campaign, open the report, and compare it with +# https://beennnn.github.io/runtime-xray/multi/ without having anything to build. +# +# The three runs are the demo's own: same names, same root methods, same filters. Only the +# iteration counts are overridable, because they decide how long one waits and nothing else. +# +# bin/demo-kit.sh +set -uo pipefail + +REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +KIT="$REPO_DIR/target/demo-kit/runtime-xray-demo" +ZIP="$REPO_DIR/target/runtime-xray-demo.zip" +VERSION="$(grep -m1 '' "$REPO_DIR/pom.xml" | sed 's/.*\(.*\)<\/version>.*/\1/')" + +echo "Demo kit — runtime-xray $VERSION" + +# The COMPLETE edition, and not the ordinary jar: the point of this package is that one +# unzips it and it runs. The ordinary jar would go looking for its three components on a +# Maven repository at the first launch, which is exactly the machine one does not have. +(cd "$REPO_DIR" && mvn -q -Pcomplet -DskipTests package) \ + || { echo " ❌ building the complete edition" >&2; exit 1; } + +APP_JAR="$REPO_DIR/sample-app/target/sample-app.jar" +TOOL_JAR="$REPO_DIR/orchestrator/target/runtime-xray-complet.jar" +for f in "$APP_JAR" "$TOOL_JAR"; do + [ -f "$f" ] || { echo " ❌ missing: $f" >&2; exit 1; } +done + +rm -rf "$REPO_DIR/target/demo-kit" +mkdir -p "$KIT/libs" "$KIT/src" + +cp "$TOOL_JAR" "$KIT/runtime-xray.jar" +cp "$APP_JAR" "$KIT/sample-app.jar" +# sample-app.jar names them in its manifest's Class-Path: without them it does not start. +cp "$REPO_DIR"/sample-app/target/libs/*.jar "$KIT/libs/" +# The sources are what the code panel displays. Without them the report opens, measures +# everything, and shows "Source unavailable" on every class — which is the very failure +# this tool exists to explain, and a poor thing to demonstrate. +cp -r "$REPO_DIR/sample-app/src/main/java/." "$KIT/src/" + +printf ' %-28s%s\n' "runtime-xray.jar" "$(( $(wc -c < "$KIT/runtime-xray.jar") / 1024 / 1024 )) MB, components embedded" +printf ' %-28s%s\n' "sample-app.jar + libs/" "the observed application" +printf ' %-28s%s\n' "src/" "$(find "$KIT/src" -name '*.java' | wc -l | tr -d ' ') source files" + +# ------------------------------------------------------------------ the campaign +# +# Written twice rather than once in a portable form: the two shells share no syntax worth +# the trouble, and a reader on either system must be able to read what is about to run. + +cat > "$KIT/demo.sh" <<'EOF' +#!/usr/bin/env bash +# Reproduces the published demonstration: three runs of the same application, observed +# differently, accumulated into one report. +# +# ./demo.sh the three runs, some two minutes +# ITERATIONS=4000000 ./demo.sh shorter, same shape +# OUT=elsewhere ./demo.sh +set -euo pipefail +cd "$(dirname "$0")" + +OUT="${OUT:-report}" +ITERATIONS="${ITERATIONS:-24000000}" +SHORT=$(( ITERATIONS / 3 )) + +# The application's own classes, and them alone. Adding libs/commons-lang3.jar here — which +# the application does reach — puts its 231 classes into the coverage, none of which has its +# source in this package: the report then opens on "Source unavailable" 231 times over, +# which is the very failure this tool exists to explain and a poor thing to demonstrate. +COMMON=(--sources src --classes sample-app.jar --out "$OUT") + +# Values are captured by attaching to the live JVM: attach after it has ended and there is +# nothing to inspect. The default of 8 s suits the full workload; a shortened run needs a +# shortened delay, or one silently loses a third of what the report shows. +attach_for(){ local s=$(( $1 / 3000000 )); [ "$s" -lt 2 ] && s=2; [ "$s" -gt 8 ] && s=8; echo "$s"; } + +run(){ # name root method filter iterations + echo + echo "▶ $1" + java -jar runtime-xray.jar \ + --java "java -jar sample-app.jar --iterations $4" \ + --attach-after "$(attach_for "$4")" \ + --name "$1" --root "$2" --filter "$3" "${COMMON[@]}" +} + +run "Scenario 1" "lab.sample.RoutePlanner::travelTimeMinutes" "lab/sample/*" "$SHORT" +run "Scenario 2" "lab.sample.terrain.Terrain::slowdownFactor" "lab/sample/terrain/*" "$ITERATIONS" +run "Full scenario" "lab.sample.RoutePlanner::travelTimeMinutes" "lab/sample/*" "$ITERATIONS" + +echo +echo "Report: $OUT/index.html" +echo "Compare with https://beennnn.github.io/runtime-xray/multi/" +EOF +chmod +x "$KIT/demo.sh" + +cat > "$KIT/demo.cmd" <<'EOF' +@echo off +rem Reproduces the published demonstration: three runs of the same application, observed +rem differently, accumulated into one report. +rem +rem demo.cmd the three runs +rem set ITERATIONS=4000000 & demo.cmd +setlocal +cd /d "%~dp0" + +if "%OUT%"=="" set OUT=report +if "%ITERATIONS%"=="" set ITERATIONS=24000000 +set /a SHORT=%ITERATIONS%/3 + +rem The application's own classes and them alone: adding libs\commons-lang3.jar would put +rem its 231 source-less classes into the coverage of a package meant to demonstrate. +set COMMON=--sources src --classes sample-app.jar --out "%OUT%" + +rem Values are captured by attaching to the live JVM. The default delay of 8 s suits the +rem full workload; a shortened run needs a shortened delay or the values are lost. +set /a ATTACH=%ITERATIONS%/3000000 +if %ATTACH% LSS 2 set ATTACH=2 +if %ATTACH% GTR 8 set ATTACH=8 +set /a ATTACH_SHORT=%SHORT%/3000000 +if %ATTACH_SHORT% LSS 2 set ATTACH_SHORT=2 +if %ATTACH_SHORT% GTR 8 set ATTACH_SHORT=8 + +echo. +echo ^> Scenario 1 +java -jar runtime-xray.jar --java "java -jar sample-app.jar --iterations %SHORT%" ^ + --attach-after %ATTACH_SHORT% ^ + --name "Scenario 1" --root "lab.sample.RoutePlanner::travelTimeMinutes" ^ + --filter "lab/sample/*" %COMMON% || exit /b 1 + +echo. +echo ^> Scenario 2 +java -jar runtime-xray.jar --java "java -jar sample-app.jar --iterations %ITERATIONS%" ^ + --attach-after %ATTACH% ^ + --name "Scenario 2" --root "lab.sample.terrain.Terrain::slowdownFactor" ^ + --filter "lab/sample/terrain/*" %COMMON% || exit /b 1 + +echo. +echo ^> Full scenario +java -jar runtime-xray.jar --java "java -jar sample-app.jar --iterations %ITERATIONS%" ^ + --attach-after %ATTACH% ^ + --name "Full scenario" --root "lab.sample.RoutePlanner::travelTimeMinutes" ^ + --filter "lab/sample/*" %COMMON% || exit /b 1 + +echo. +echo Report: %OUT%\index.html +echo Compare with https://beennnn.github.io/runtime-xray/multi/ +EOF + +# ---------------------------------------------------------------- instructions + +cat > "$KIT/README.txt" < SHA256 +EOF + +(cd "$KIT" && find . -type f ! -name SHA256SUMS.txt -print0 | sort -z \ + | xargs -0 sha256sum > SHA256SUMS.txt) + +# ------------------------------------------------------------------- archive + +rm -f "$ZIP" +if command -v zip >/dev/null 2>&1; then + (cd "$REPO_DIR/target/demo-kit" && zip -qr "$ZIP" runtime-xray-demo) +else + (cd "$REPO_DIR/target/demo-kit" && jar --create --file "$ZIP" --no-manifest runtime-xray-demo) +fi +[ -s "$ZIP" ] || { echo " ❌ archive not produced" >&2; exit 1; } + +echo +echo " ✅ $ZIP ($(( $(wc -c < "$ZIP") / 1024 / 1024 )) MB)" From 01f5beecdbdd5205972bb0d1e98c14b953e40bd5 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 7 Sep 2026 06:28:08 +0000 Subject: [PATCH 2/3] =?UTF-8?q?Dimensionner=20chaque=20run=20sur=20sa=20ra?= =?UTF-8?q?cine=20:=20la=20d=C3=A9mo=20publi=C3=A9e=20en=20tue=20un?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mesuré en exécutant le paquet, pas en le lisant. Les trois runs observés de la même façon : RoutePlanner::travelTimeMinutes 8 M itérations → 31 s RoutePlanner::travelTimeMinutes 24 M itérations → 206 s Terrain::slowdownFactor 24 M itérations → coupé à 600 s Terrain::slowdownFactor est appelée plusieurs fois par itinéraire là où l'autre l'est une seule fois, et capturer les valeurs veut dire tracer CHAQUE invocation. Donner le même nombre d'itérations aux trois runs — ce que fait la démonstration publiée — rend le deuxième dix fois plus long que les autres, et ici l'outil a dû l'arrêter : « stopped after 600 s (safety limit) ». Un paquet de démonstration dont le run du milieu se fait tuer démontre le garde-fou. Chaque run est donc dimensionné pour sa propre racine, à partir d'un WORKLOAD unique : /4 pour le premier, /8 pour celui qui trace la méthode chaude, entier pour le dernier. La forme de la campagne ne bouge pas — trois exécutions, trois racines, trois filtres — seul le nombre d'échantillons derrière les pourcentages. Le même test a par ailleurs prouvé le chemin embarqué, jamais exercé ici puisque le dépôt Maven local le précède : avec ~/.runtime-xray vide et MAVEN_REPO_LOCAL sur un répertoire vide, les cinq composants sortent du jar complet. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01J956wjynbd7fkZx4HpjHzP --- bin/demo-kit.sh | 61 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/bin/demo-kit.sh b/bin/demo-kit.sh index 0fc2ccf..d322960 100755 --- a/bin/demo-kit.sh +++ b/bin/demo-kit.sh @@ -58,15 +58,28 @@ cat > "$KIT/demo.sh" <<'EOF' # Reproduces the published demonstration: three runs of the same application, observed # differently, accumulated into one report. # -# ./demo.sh the three runs, some two minutes -# ITERATIONS=4000000 ./demo.sh shorter, same shape +# ./demo.sh the three runs, some two minutes +# WORKLOAD=2000000 ./demo.sh shorter, on a slower machine # OUT=elsewhere ./demo.sh set -euo pipefail cd "$(dirname "$0")" OUT="${OUT:-report}" -ITERATIONS="${ITERATIONS:-24000000}" -SHORT=$(( ITERATIONS / 3 )) + +# The three workloads are NOT the same, and not because the scenarios differ: because +# capturing values means tracing every invocation of the root method, and the three roots +# are not called at the same rate. Measured here, all three observed the same way: +# +# RoutePlanner::travelTimeMinutes 8 M iterations → 31 s +# RoutePlanner::travelTimeMinutes 24 M iterations → 206 s +# Terrain::slowdownFactor 24 M iterations → cut off at the 600 s guard rail +# +# Terrain::slowdownFactor is called several times per itinerary where the other is called +# once. Giving the three runs the same number of iterations — which is what the published +# demonstration does — makes the second one the longest by far, and on this machine made +# the tool stop it: a demonstration package whose middle run gets killed demonstrates the +# guard rail. Each run is therefore sized for its own root. +WORKLOAD="${WORKLOAD:-8000000}" # The application's own classes, and them alone. Adding libs/commons-lang3.jar here — which # the application does reach — puts its 231 classes into the coverage, none of which has its @@ -88,9 +101,9 @@ run(){ # name root method filter iterations --name "$1" --root "$2" --filter "$3" "${COMMON[@]}" } -run "Scenario 1" "lab.sample.RoutePlanner::travelTimeMinutes" "lab/sample/*" "$SHORT" -run "Scenario 2" "lab.sample.terrain.Terrain::slowdownFactor" "lab/sample/terrain/*" "$ITERATIONS" -run "Full scenario" "lab.sample.RoutePlanner::travelTimeMinutes" "lab/sample/*" "$ITERATIONS" +run "Scenario 1" "lab.sample.RoutePlanner::travelTimeMinutes" "lab/sample/*" "$(( WORKLOAD / 4 ))" +run "Scenario 2" "lab.sample.terrain.Terrain::slowdownFactor" "lab/sample/terrain/*" "$(( WORKLOAD / 8 ))" +run "Full scenario" "lab.sample.RoutePlanner::travelTimeMinutes" "lab/sample/*" "$WORKLOAD" echo echo "Report: $OUT/index.html" @@ -109,8 +122,12 @@ setlocal cd /d "%~dp0" if "%OUT%"=="" set OUT=report -if "%ITERATIONS%"=="" set ITERATIONS=24000000 -set /a SHORT=%ITERATIONS%/3 + +rem The three workloads differ because the three root methods are not called at the same +rem rate, and capturing values means tracing every invocation. See demo.sh for the figures. +if "%WORKLOAD%"=="" set WORKLOAD=8000000 +set /a W1=%WORKLOAD%/4 +set /a W2=%WORKLOAD%/8 rem The application's own classes and them alone: adding libs\commons-lang3.jar would put rem its 231 source-less classes into the coverage of a package meant to demonstrate. @@ -118,30 +135,27 @@ set COMMON=--sources src --classes sample-app.jar --out "%OUT%" rem Values are captured by attaching to the live JVM. The default delay of 8 s suits the rem full workload; a shortened run needs a shortened delay or the values are lost. -set /a ATTACH=%ITERATIONS%/3000000 +set /a ATTACH=%WORKLOAD%/3000000 if %ATTACH% LSS 2 set ATTACH=2 if %ATTACH% GTR 8 set ATTACH=8 -set /a ATTACH_SHORT=%SHORT%/3000000 -if %ATTACH_SHORT% LSS 2 set ATTACH_SHORT=2 -if %ATTACH_SHORT% GTR 8 set ATTACH_SHORT=8 echo. echo ^> Scenario 1 -java -jar runtime-xray.jar --java "java -jar sample-app.jar --iterations %SHORT%" ^ - --attach-after %ATTACH_SHORT% ^ +java -jar runtime-xray.jar --java "java -jar sample-app.jar --iterations %W1%" ^ + --attach-after 2 ^ --name "Scenario 1" --root "lab.sample.RoutePlanner::travelTimeMinutes" ^ --filter "lab/sample/*" %COMMON% || exit /b 1 echo. echo ^> Scenario 2 -java -jar runtime-xray.jar --java "java -jar sample-app.jar --iterations %ITERATIONS%" ^ - --attach-after %ATTACH% ^ +java -jar runtime-xray.jar --java "java -jar sample-app.jar --iterations %W2%" ^ + --attach-after 2 ^ --name "Scenario 2" --root "lab.sample.terrain.Terrain::slowdownFactor" ^ --filter "lab/sample/terrain/*" %COMMON% || exit /b 1 echo. echo ^> Full scenario -java -jar runtime-xray.jar --java "java -jar sample-app.jar --iterations %ITERATIONS%" ^ +java -jar runtime-xray.jar --java "java -jar sample-app.jar --iterations %WORKLOAD%" ^ --attach-after %ATTACH% ^ --name "Full scenario" --root "lab.sample.RoutePlanner::travelTimeMinutes" ^ --filter "lab/sample/*" %COMMON% || exit /b 1 @@ -185,9 +199,14 @@ The three runs They accumulate in the same output directory: the report shows the campaign, and the coverage can be united across the runs one ticks. - The three runs take some two and a half minutes in all — the workload is what makes - the profile dense enough to read. ITERATIONS=8000000 ./demo.sh shortens them; the - shape does not change, only the number of stack samples behind the percentages. + The three runs take some two minutes in all, and they carry three different + workloads on purpose: capturing values means tracing every invocation of the root + method, and Terrain::slowdownFactor is called several times per itinerary where + RoutePlanner::travelTimeMinutes is called once. Giving all three the same number of + iterations makes the second one ten times the longest. + + WORKLOAD=2000000 ./demo.sh shortens all three, for a slower machine. The shape does + not change, only the number of stack samples behind the percentages. What you will NOT see under Windows The call tree, and it is not a defect of the package: time is sampled by From 11b0301a9d409c21e390b23bc2fb9d4af9c1fc2a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 7 Sep 2026 06:31:08 +0000 Subject: [PATCH 3/3] Calibrer la racine chaude au lieu de l'extrapoler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le correctif précédent dimensionnait le run du milieu à WORKLOAD/8 pour ne plus franchir le garde-fou. Il ne le franchissait plus, et ne récoltait plus que 64 échantillons : un arbre qui ne montre rien, ce qui remplace un défaut par un autre. Les points mesurés sur Terrain::slowdownFactor, tous observés pareil : 1 M → 4 s, 64 mesures 4 M → 24 s, 279 mesures 2 M → 5 s, 116 mesures 24 M → coupé à 600 s Au-delà de 4 M, allonger le run n'achète presque plus d'échantillons : le temps part dans le traçage, qui n'est pas dans lab/sample/terrain/* et ne compte donc pas. C'est précisément ce que ce run est là pour montrer — ce que fait un filtre étroit — donc le prolonger n'aurait rien démontré de plus. D'où la moitié du WORKLOAD pour les deux premiers runs et le WORKLOAD entier pour le dernier : moins de deux minutes en tout. La démo publiée donne 24 M aux trois et paie onze minutes, sur la machine où elle a été enregistrée ; le README dit comment s'en rapprocher pour qui veut attendre. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01J956wjynbd7fkZx4HpjHzP --- bin/demo-kit.sh | 61 ++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/bin/demo-kit.sh b/bin/demo-kit.sh index d322960..ac05bcb 100755 --- a/bin/demo-kit.sh +++ b/bin/demo-kit.sh @@ -66,20 +66,27 @@ cd "$(dirname "$0")" OUT="${OUT:-report}" -# The three workloads are NOT the same, and not because the scenarios differ: because -# capturing values means tracing every invocation of the root method, and the three roots -# are not called at the same rate. Measured here, all three observed the same way: +# The workload is not the same for the three runs, and the reason is not the scenario: +# capturing values means tracing EVERY invocation of the root method, and the two roots are +# not called at the same rate. Measured here, all runs observed identically: # -# RoutePlanner::travelTimeMinutes 8 M iterations → 31 s -# RoutePlanner::travelTimeMinutes 24 M iterations → 206 s -# Terrain::slowdownFactor 24 M iterations → cut off at the 600 s guard rail +# RoutePlanner::travelTimeMinutes 2 M → 5 s Terrain::slowdownFactor 1 M → 4 s +# RoutePlanner::travelTimeMinutes 8 M → 37 s Terrain::slowdownFactor 2 M → 5 s +# RoutePlanner::travelTimeMinutes 24 M → 206 s Terrain::slowdownFactor 4 M → 24 s +# Terrain::slowdownFactor 24 M → cut +# off at the 600 s guard rail # -# Terrain::slowdownFactor is called several times per itinerary where the other is called -# once. Giving the three runs the same number of iterations — which is what the published -# demonstration does — makes the second one the longest by far, and on this machine made -# the tool stop it: a demonstration package whose middle run gets killed demonstrates the -# guard rail. Each run is therefore sized for its own root. +# The published demonstration gives all three runs 24 M, and pays 11 minutes for it — on the +# machine it was recorded on, where the middle run took 498 s. On a slower one that run +# crosses the guard rail and the tool stops it: a demonstration package whose middle run +# gets killed demonstrates the guard rail. So the full scenario carries the workload and the +# two others carry half of it. +# +# Note what the figures also say: past 4 M, the terrain run buys almost no extra sample. The +# time goes into the tracing, which is not in "lab/sample/terrain/*" and therefore does not +# count — a narrow filter is exactly what that run is there to show. WORKLOAD="${WORKLOAD:-8000000}" +HALF=$(( WORKLOAD / 2 )) # The application's own classes, and them alone. Adding libs/commons-lang3.jar here — which # the application does reach — puts its 231 classes into the coverage, none of which has its @@ -101,8 +108,8 @@ run(){ # name root method filter iterations --name "$1" --root "$2" --filter "$3" "${COMMON[@]}" } -run "Scenario 1" "lab.sample.RoutePlanner::travelTimeMinutes" "lab/sample/*" "$(( WORKLOAD / 4 ))" -run "Scenario 2" "lab.sample.terrain.Terrain::slowdownFactor" "lab/sample/terrain/*" "$(( WORKLOAD / 8 ))" +run "Scenario 1" "lab.sample.RoutePlanner::travelTimeMinutes" "lab/sample/*" "$HALF" +run "Scenario 2" "lab.sample.terrain.Terrain::slowdownFactor" "lab/sample/terrain/*" "$HALF" run "Full scenario" "lab.sample.RoutePlanner::travelTimeMinutes" "lab/sample/*" "$WORKLOAD" echo @@ -123,11 +130,11 @@ cd /d "%~dp0" if "%OUT%"=="" set OUT=report -rem The three workloads differ because the three root methods are not called at the same -rem rate, and capturing values means tracing every invocation. See demo.sh for the figures. +rem The first two runs carry half the workload: capturing values means tracing every +rem invocation of the root method, and Terrain::slowdownFactor is called far more often +rem than RoutePlanner::travelTimeMinutes. See demo.sh for the measured figures. if "%WORKLOAD%"=="" set WORKLOAD=8000000 -set /a W1=%WORKLOAD%/4 -set /a W2=%WORKLOAD%/8 +set /a HALF=%WORKLOAD%/2 rem The application's own classes and them alone: adding libs\commons-lang3.jar would put rem its 231 source-less classes into the coverage of a package meant to demonstrate. @@ -141,14 +148,14 @@ if %ATTACH% GTR 8 set ATTACH=8 echo. echo ^> Scenario 1 -java -jar runtime-xray.jar --java "java -jar sample-app.jar --iterations %W1%" ^ +java -jar runtime-xray.jar --java "java -jar sample-app.jar --iterations %HALF%" ^ --attach-after 2 ^ --name "Scenario 1" --root "lab.sample.RoutePlanner::travelTimeMinutes" ^ --filter "lab/sample/*" %COMMON% || exit /b 1 echo. echo ^> Scenario 2 -java -jar runtime-xray.jar --java "java -jar sample-app.jar --iterations %W2%" ^ +java -jar runtime-xray.jar --java "java -jar sample-app.jar --iterations %HALF%" ^ --attach-after 2 ^ --name "Scenario 2" --root "lab.sample.terrain.Terrain::slowdownFactor" ^ --filter "lab/sample/terrain/*" %COMMON% || exit /b 1 @@ -199,14 +206,16 @@ The three runs They accumulate in the same output directory: the report shows the campaign, and the coverage can be united across the runs one ticks. - The three runs take some two minutes in all, and they carry three different - workloads on purpose: capturing values means tracing every invocation of the root - method, and Terrain::slowdownFactor is called several times per itinerary where - RoutePlanner::travelTimeMinutes is called once. Giving all three the same number of - iterations makes the second one ten times the longest. + The three runs take under two minutes in all. The published report is denser — it + gives all three runs three times this workload and pays eleven minutes for it, on the + machine it was recorded on. On a slower one its middle run crosses the tool's 600 s + guard rail and gets stopped: capturing values means tracing every invocation of the + root method, and Terrain::slowdownFactor is called far more often than + RoutePlanner::travelTimeMinutes. - WORKLOAD=2000000 ./demo.sh shortens all three, for a slower machine. The shape does - not change, only the number of stack samples behind the percentages. + WORKLOAD=16000000 ./demo.sh gets closer to the published density and takes some four + minutes; WORKLOAD=2000000 shortens it for a slower machine. Only the number of stack + samples behind the percentages changes. What you will NOT see under Windows The call tree, and it is not a defect of the package: time is sampled by