Skip to content

Commit 21523a3

Browse files
authored
Add unwinding validation and reporting (#274)
1 parent f3ea224 commit 21523a3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+4052
-2500
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
You are "Gradle Log Analyst".
2+
3+
Goal:
4+
- Parse one or more Gradle log files and output exactly two artifacts:
5+
1) build/reports/claude/gradle-summary.md (human summary, <150 lines)
6+
2) build/reports/claude/gradle-summary.json (structured data)
7+
8+
Rules:
9+
- NEVER paste full logs or long snippets in chat. Always write to files.
10+
- Chat output must be a 3–6 line status with the two relative file paths only.
11+
- If a log path is not provided, auto-pick the most recent file under build/logs/*.log.
12+
- Prefer grep/awk/sed or a tiny Python script; keep it cross-platform.
13+
14+
Extract at minimum:
15+
- Final status (SUCCESS/FAILED) and total time
16+
- Failing tasks and their exception headlines
17+
- Test summary per task (passed/failed/skipped), top failing tests
18+
- Warnings (deprecations), configuration cache notes, cache misses
19+
- Slowest tasks (e.g., top 10 by duration)
20+
- Dependency/network issues (timeouts, 401/403, artifact not found)
21+
22+
Emit JSON with keys:
23+
{ status, totalTime, failedTasks[], warnings[], tests{total,failed,skipped,modules[]}, slowTasks[], depIssues[], actions[] }
24+
25+
Graceful Degradation:
26+
- If log is malformed/empty, write a short summary explaining why and exit successfully.

.claude/agents/patch-analyst.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
You are "Patch Analyst".
2+
3+
## Purpose
4+
Generate *universal patching entries* for a specific file by comparing:
5+
- **Upstream:** ddprof-lib/build/async-profiler/src/<FILE>
6+
- **Local:** ddprof-lib/src/main/cpp/<FILE>
7+
8+
The patching format and rules are defined in **gradle/patching.gradle**. Read that file to understand the expected data model, field names, and constraints. Then emit patch entries that conform exactly to that spec.
9+
10+
## Inputs
11+
- Primary input: a **filename** (e.g., `stackFrame.h`), sometimes mentioned only in natural language (e.g., “use `stackFrame.h` from upstream”).
12+
- Optional: explicit upstream/local paths (if provided, prefer those).
13+
14+
## Output (files, not chat)
15+
Write **both** of these artifacts:
16+
1. `build/reports/claude/patches/<FILE>.patch.json` — machine-readable entries per your universal patching format.
17+
2. `build/reports/claude/patches/<FILE>.patch.md` — brief human summary of the changes and how they map to the universal patch entries.
18+
19+
**Chat output rule:** respond with **only** a 3–6 line status containing the filename, detected changes count, and the two relative output paths. Do **not** paste long diffs or large blobs into chat.
20+
21+
## Required Tools
22+
- Read / Write files
23+
- Bash: grep, awk, sed, diff or git
24+
- (Optional) python3 for robust parsing if needed
25+
26+
## Canonical Paths
27+
- Upstream file: `ddprof-lib/build/async-profiler/src/<FILE>`
28+
- Local file: `ddprof-lib/src/main/cpp/<FILE>`
29+
30+
If `<FILE>` is not found at those exact locations, search within the respective roots for a case-sensitive match. If multiple matches exist, select the exact basename equality first; otherwise fail with a short note in the `.md` report.
31+
32+
## Diff Policy (very important)
33+
**Do not consider:**
34+
- Newline differences (CRLF vs LF).
35+
- Copyright/license/header boilerplate differences.
36+
37+
**Implementation hints (use any equivalent cross-platform approach):**
38+
- Normalize newlines to LF on the fly (e.g., `sed 's/\r$//'`).
39+
- Strip copyright/license/SPDX lines before diffing:
40+
- remove lines matching (case-insensitive):
41+
- `^//.*copyright`
42+
- `^\\*.*copyright`
43+
- `^/\\*.*copyright`
44+
- `spdx-license-identifier`
45+
- `apache license` | `mit license` | `all rights reserved`
46+
- Perform a whitespace-insensitive, blank-line-insensitive diff:
47+
- Prefer `git diff --no-index -w --ignore-blank-lines --ignore-space-at-eol --unified=0 <up> <local>`
48+
- Or `diff -u -w -B <up> <local>`
49+
50+
## Patch Entry Generation
51+
1. **Read** `gradle/patching.gradle` and extract the **universal patching schema**:
52+
- field names (e.g., operation type, target file, selectors/range, replacement payload, pre/post conditions, version guards, id/slug, etc.)
53+
- any ordering/atomicity rules
54+
- how to represent insert/replace/delete and multi-hunk patches
55+
- how to encode context (before/after lines) or anchors
56+
2. **Map each diff hunk** to a conforming patch entry:
57+
- Prefer *anchor-based* or *range-based* selectors as defined by the config.
58+
- Include minimal stable context that will survive formatting (ignore pure whitespace).
59+
- Coalesce adjacent hunks where allowed by the spec.
60+
- Add a meaningful `id`/`label` per entry (e.g., `<FILE>:include-guard-fix`, `<FILE>:struct-field-sync`).
61+
3. **Version/Guarding**:
62+
- If the config supports *guards* (e.g., “only apply if upstream pattern X exists and local pattern Y exists”), populate them.
63+
- If the config supports a *dry-run/apply* mode, set `apply=false` by default unless instructed otherwise.
64+
4. **Safety**:
65+
- Never write outside `build/reports/claude/patches/`.
66+
- Only modify the 'gradle/patching.gradle' file.
67+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
description: Run a Gradle task, capture console to a timestamped log, then delegate parsing to the sub-agent and reply briefly.
3+
usage: "/build-and-summarize <gradle-args...>"
4+
---
5+
6+
**Task:** Build with Gradle (plain console, info level), capture output to `build/logs/`, then have `gradle-log-analyst` parse the log and write:
7+
- `build/reports/claude/gradle-summary.md`
8+
- `build/reports/claude/gradle-summary.json`
9+
10+
Make sure to use the JAVA_HOME environment variable is set appropriately.
11+
12+
```bash
13+
set -euo pipefail
14+
mkdir -p build/logs build/reports/claude
15+
STAMP="$(date +%Y%m%d-%H%M%S)"
16+
17+
# Default to 'build' if no args were given
18+
ARGS=("$@")
19+
if [ "${#ARGS[@]}" -eq 0 ]; then
20+
ARGS=(build)
21+
fi
22+
23+
# Make a filename-friendly label (first arg only)
24+
LABEL="$(echo "${ARGS[0]}" | tr '/:' '__')"
25+
LOG="build/logs/${STAMP}-${LABEL}.log"
26+
27+
echo "Running: ./gradlew ${ARGS[*]} -i --console=plain"
28+
# Capture both stdout and stderr to the log while streaming to terminal
29+
(./gradlew "${ARGS[@]}" -i --console=plain 2>&1 | tee "$LOG") || true
30+
31+
# Delegate parsing to the sub-agent
32+
echo "Delegating to gradle-logs-analyst agent..."
33+
claude "Act as the gradle-logs-analyst agent to parse the build log at: $LOG. Generate the required gradle summary artifacts as specified in the gradle-logs-analyst agent definition."
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
---
2+
description: Compare upstream vs local for a given filename and generate universal patch entries via the Patch Analyst agent.
3+
usage: "/compare-and-patch <filename>"
4+
---
5+
6+
**Task:** Resolve the upstream and local paths for the provided `<filename>`,
7+
then delegate to the `patch-analyst` sub-agent to read `gradle/patching.gradle`,
8+
compute the diff (ignoring newline and copyright-only changes),
9+
and write two artifacts:
10+
- `build/reports/claude/patches/<FILE>.patch.json`
11+
- `build/reports/claude/patches/<FILE>.patch.md`
12+
13+
```bash
14+
set -euo pipefail
15+
16+
if [ $# -lt 1 ]; then
17+
echo "Usage: /compare-and-patch <filename>"
18+
exit 2
19+
fi
20+
21+
FILE="$1"
22+
UP_ROOT="ddprof-lib/build/async-profiler/src"
23+
LOCAL_ROOT="ddprof-lib/src/main/cpp"
24+
25+
mkdir -p build/reports/claude/patches
26+
27+
# Resolve canonical upstream and local paths
28+
UP_CANON="$UP_ROOT/$FILE"
29+
LOCAL_CANON="$LOCAL_ROOT/$FILE"
30+
31+
# If direct paths don't exist, try to find by basename match inside each root
32+
if [ ! -f "$UP_CANON" ]; then
33+
FOUND_UP=$(find "$UP_ROOT" -type f -name "$(basename "$FILE")" -maxdepth 1 2>/dev/null | head -n1 || true)
34+
if [ -n "$FOUND_UP" ]; then UP_CANON="$FOUND_UP"; fi
35+
fi
36+
37+
if [ ! -f "$LOCAL_CANON" ]; then
38+
FOUND_LOCAL=$(find "$LOCAL_ROOT" -type f -name "$(basename "$FILE")" -maxdepth 1 2>/dev/null | head -n1 || true)
39+
if [ -n "$FOUND_LOCAL" ]; then LOCAL_CANON="$FOUND_LOCAL"; fi
40+
fi
41+
42+
# Minimal existence check—agent will handle edge cases and write a status
43+
if [ ! -f "$UP_CANON" ]; then
44+
echo "Upstream file not found under $UP_ROOT for $FILE"
45+
fi
46+
if [ ! -f "$LOCAL_CANON" ]; then
47+
echo "Local file not found under $LOCAL_ROOT for $FILE"
48+
fi
49+
50+
echo "Resolved:"
51+
echo " upstream: $UP_CANON"
52+
echo " local: $LOCAL_CANON"
53+
54+
# NOTE: Do not compute the diff here; let the agent do normalization and policy (ignore EOL/copyright).
55+
# Delegate to the patch-analyst agent with resolved paths
56+
57+
echo "Delegating to patch-analyst agent..."
58+
claude "Act as the patch-analyst agent to analyze $FILE. Use upstream file: $UP_CANON and local file: $LOCAL_CANON. Generate the required patch analysis artifacts as specified in the patch-analyst agent definition."

.claude/settings.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Read",
5+
"Write",
6+
"Bash(grep:*)",
7+
"Bash(awk:*)",
8+
"Bash(sed:*)",
9+
"Bash(python3:*)",
10+
"Bash(./gradlew:*)",
11+
"Bash(sh:*)",
12+
"Bash(ls:*)",
13+
"Bash/date:*",
14+
"Bash/mkdir:*",
15+
"Bash/tee:*"
16+
],
17+
"ask": [],
18+
"deny": [
19+
"Bash(sudo:*)",
20+
"Bash(rm:*)"
21+
]
22+
},
23+
"hooks": {
24+
"SubagentStop": [
25+
{
26+
"matcher": "gradle-log-analyst",
27+
"hooks": [
28+
"echo '[gradle-log-analyst] Wrote build/reports/claude/gradle-summary.{md,json}' >&2"
29+
]
30+
}
31+
]
32+
}
33+
}

.github/scripts/prepare_reports.sh

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
#!/usr/bin/env bash
22

33
set -e
4-
mkdir -p reports
5-
cp /tmp/hs_err* reports/ || true
6-
cp ddprof-test/javacore*.txt reports/ || true
7-
cp ddprof-test/build/hs_err* reports/ || true
8-
cp -r ddprof-lib/build/tmp reports/native_build || true
9-
cp -r ddprof-test/build/reports/tests reports/tests || true
10-
cp -r /tmp/recordings reports/recordings || true
11-
find ddprof-lib/build -name 'libjavaProfiler.*' -exec cp {} reports/ \; || true
4+
mkdir -p test-reports
5+
mkdir -p unwinding-reports
6+
cp /tmp/hs_err* test-reports/ || true
7+
cp ddprof-test/javacore*.txt test-reports/ || true
8+
cp ddprof-test/build/hs_err* test-reports/ || true
9+
cp -r ddprof-lib/build/tmp test-reports/native_build || true
10+
cp -r ddprof-test/build/reports/tests test-reports/tests || true
11+
cp -r /tmp/recordings test-reports/recordings || true
12+
find ddprof-lib/build -name 'libjavaProfiler.*' -exec cp {} test-reports/ \; || true
13+
14+
cp -r ddprof-test/build/reports/unwinding-summary.md unwinding-reports/ || true
15+
cp -r /tmp/unwinding-recordings/* unwinding-reports/ || true

.github/scripts/test_alpine_aarch64.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,7 @@ JAVA_VERSION=$("${JAVA_TEST_HOME}/bin/java" -version 2>&1 | awk -F '"' '/version
3030
export JAVA_VERSION
3131

3232
apk update && apk add curl moreutils wget hexdump linux-headers bash make g++ clang git cppcheck jq cmake gtest-dev gmock tar binutils >/dev/null
33+
# Install debug symbols for musl libc
34+
apk add musl-dbg
3335

3436
./gradlew -PCI -PkeepJFRs :ddprof-test:test${CONFIG} --no-daemon --parallel --build-cache --no-watch-fs
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#! /bin/sh
2+
3+
set -e
4+
set +x
5+
6+
export KEEP_JFRS=true
7+
export TEST_COMMIT="${1}"
8+
export TEST_CONFIGURATION="${2}"
9+
export LIBRARY="musl"
10+
export CONFIG="${3}"
11+
export JAVA_HOME="${4}"
12+
export JAVA_TEST_HOME="${5}"
13+
14+
export PATH="${JAVA_HOME}/bin":${PATH}
15+
16+
# due to env hell in GHA containers, we need to re-do the logic from Extract Versions here
17+
JAVA_VERSION=$("${JAVA_TEST_HOME}/bin/java" -version 2>&1 | awk -F '"' '/version/ {
18+
split($2, v, "[._]");
19+
if (v[2] == "") {
20+
# Version is like "24": assume it is major only and add .0.0
21+
printf "%s.0.0\n", v[1]
22+
} else if (v[1] == "1") {
23+
# Java 8 or older: Format is "1.major.minor_update"
24+
printf "%s.%s.%s\n", v[2], v[3], v[4]
25+
} else {
26+
# Java 9 or newer: Format is "major.minor.patch"
27+
printf "%s.%s.%s\n", v[1], v[2], v[3]
28+
}
29+
}')
30+
export JAVA_VERSION
31+
32+
apk update && apk add curl moreutils wget hexdump linux-headers bash make g++ clang git cppcheck jq cmake gtest-dev gmock tar binutils >/dev/null
33+
# Install debug symbols for musl libc
34+
apk add musl-dbg
35+
36+
./gradlew -PCI :ddprof-test:unwindingReport --no-daemon --parallel --build-cache --no-watch-fs

0 commit comments

Comments
 (0)