Skip to content

Commit 5c817d7

Browse files
committed
chore(ci): add container multiple-trace diagnostics
1 parent 0254c79 commit 5c817d7

3 files changed

Lines changed: 249 additions & 0 deletions

File tree

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
name: Container Multiple Trace Diagnose
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
branches: [ main ]
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
host-to-private-multiple-trace-diagnose:
13+
name: Container Diagnose (host-to-private multiple-trace-targets)
14+
runs-on: ubuntu-22.04
15+
timeout-minutes: 45
16+
env:
17+
LLVM_SYS_181_PREFIX: /usr/lib/llvm-18
18+
FIXTURE_BINARY: ghostscope/tests/fixtures/sample_program/sample_program_o2
19+
E2E_CONTAINER_IMAGE: ghcr.io/swananan/ghostscope-e2e-runtime@sha256:d5df1b977c38f7a51bbf28b878f2246705a05b83ac6df7cb6be8f8a4de4105f4
20+
E2E_CHILD_CONTAINER_IMAGE: ghcr.io/swananan/ghostscope-e2e-runtime@sha256:d5df1b977c38f7a51bbf28b878f2246705a05b83ac6df7cb6be8f8a4de4105f4
21+
E2E_SANDBOX_SESSION: container-mtt-diagnose-${{ github.run_id }}-${{ github.run_attempt }}
22+
E2E_GHOSTSCOPE_SANDBOX: host
23+
E2E_TARGET_SANDBOX: docker-private
24+
E2E_TARGET_MODE: same
25+
E2E_GHOSTSCOPE_LOG_LEVEL: trace
26+
E2E_GHOSTSCOPE_ENABLE_LOGGING: 1
27+
E2E_GHOSTSCOPE_LOG_CONSOLE: 1
28+
RUST_BACKTRACE: full
29+
RUST_LOG: trace
30+
31+
steps:
32+
- uses: actions/checkout@v4
33+
with:
34+
submodules: recursive
35+
36+
- name: Install LLVM 18 and Polly
37+
run: |
38+
wget https://apt.llvm.org/llvm.sh
39+
chmod +x llvm.sh
40+
sudo ./llvm.sh 18
41+
sudo apt-get install -y llvm-18-dev libpolly-18-dev
42+
43+
- name: Install Rust toolchain
44+
uses: dtolnay/rust-toolchain@1.88.0
45+
46+
- name: Cache cargo registry
47+
uses: actions/cache@v4
48+
with:
49+
path: ~/.cargo/registry
50+
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
51+
restore-keys: |
52+
${{ runner.os }}-cargo-registry-
53+
54+
- name: Cache cargo index
55+
uses: actions/cache@v4
56+
with:
57+
path: ~/.cargo/git
58+
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
59+
restore-keys: |
60+
${{ runner.os }}-cargo-index-
61+
62+
- name: Cache cargo build
63+
uses: actions/cache@v4
64+
with:
65+
path: target
66+
key: ${{ runner.os }}-${{ github.job }}-target-${{ hashFiles('**/Cargo.lock') }}
67+
restore-keys: |
68+
${{ runner.os }}-${{ github.job }}-target-
69+
70+
- name: Build Diagnostic Tools And Fixture
71+
shell: bash
72+
run: |
73+
set -euxo pipefail
74+
cargo build -p dwarf-tool
75+
make -C ghostscope/tests/fixtures/sample_program clean sample_program_o2
76+
77+
- name: Dump DWARF And Disassembly Diagnostics
78+
shell: bash
79+
run: |
80+
set -euxo pipefail
81+
mkdir -p /tmp/mtt-diagnose
82+
fixture="$GITHUB_WORKSPACE/$FIXTURE_BINARY"
83+
84+
./target/debug/dwarf-tool -t "$fixture" function calculate_something --quiet --json \
85+
| tee /tmp/mtt-diagnose/function-calculate_something.json
86+
./target/debug/dwarf-tool -t "$fixture" source-line sample_program.c:16 --quiet --json \
87+
| tee /tmp/mtt-diagnose/source-line-sample_program.c-16.json
88+
89+
dwarfdump_bin="$(command -v llvm-dwarfdump-18 || command -v llvm-dwarfdump)"
90+
"$dwarfdump_bin" --name=calculate_something "$fixture" \
91+
| tee /tmp/mtt-diagnose/llvm-dwarfdump-name-calculate_something.txt
92+
93+
readelf --debug-dump=info --wide "$fixture" \
94+
| grep -n -E -C 6 'calculate_something|DW_TAG_subprogram|DW_TAG_inlined_subroutine|DW_AT_entry_pc|DW_AT_ranges|DW_AT_abstract_origin' \
95+
| tee /tmp/mtt-diagnose/readelf-debug-info-snippet.txt || true
96+
97+
readelf --debug-dump=loc --wide "$fixture" \
98+
> /tmp/mtt-diagnose/readelf-debug-loc.txt
99+
100+
objdump -d --no-show-raw-insn "$fixture" \
101+
| tee /tmp/mtt-diagnose/objdump-disassembly.txt >/dev/null
102+
103+
python - <<'PY'
104+
import json
105+
import os
106+
import pathlib
107+
import subprocess
108+
from json import JSONDecodeError, JSONDecoder
109+
110+
out_dir = pathlib.Path("/tmp/mtt-diagnose")
111+
fixture = pathlib.Path(os.environ["GITHUB_WORKSPACE"]) / os.environ["FIXTURE_BINARY"]
112+
113+
def write_parse_debug(path: pathlib.Path, raw: str, error: Exception):
114+
stem = path.name
115+
(out_dir / f"{stem}.parse-error.txt").write_text(f"{error}\n")
116+
(out_dir / f"{stem}.head.txt").write_text(raw[:4096])
117+
(out_dir / f"{stem}.head.hex.txt").write_text(
118+
raw[:1024].encode("utf-8", errors="replace").hex() + "\n"
119+
)
120+
121+
def load_json_payload(path: pathlib.Path):
122+
raw = path.read_text()
123+
decoder = JSONDecoder()
124+
errors = []
125+
126+
for start, ch in enumerate(raw):
127+
if ch != "{":
128+
continue
129+
try:
130+
data, _ = decoder.raw_decode(raw[start:])
131+
return data
132+
except JSONDecodeError as err:
133+
errors.append(f"offset {start}: {err}")
134+
135+
error = RuntimeError(
136+
f"Unable to decode JSON from {path}. Attempts: "
137+
+ ("; ".join(errors[:8]) if errors else "no opening brace found")
138+
)
139+
write_parse_debug(path, raw, error)
140+
raise error
141+
142+
def collect_addresses(path: pathlib.Path):
143+
data = load_json_payload(path)
144+
addrs = set()
145+
146+
for module in data.get("modules", []):
147+
for entry in module.get("addresses", []):
148+
addr_text = entry.get("address")
149+
if addr_text:
150+
addrs.add(int(addr_text, 16))
151+
152+
for entry in data.get("addresses", []):
153+
addr_text = entry.get("address")
154+
if addr_text:
155+
addrs.add(int(addr_text, 16))
156+
157+
return sorted(addrs)
158+
159+
addresses = sorted(
160+
set(collect_addresses(out_dir / "function-calculate_something.json"))
161+
| set(collect_addresses(out_dir / "source-line-sample_program.c-16.json"))
162+
)
163+
(out_dir / "resolved-addresses.txt").write_text(
164+
"".join(f"0x{addr:x}\n" for addr in addresses)
165+
)
166+
167+
for addr in addresses:
168+
start = max(0, addr - 16)
169+
stop = addr + 32
170+
disasm = subprocess.check_output(
171+
[
172+
"objdump",
173+
"-d",
174+
"--start-address",
175+
hex(start),
176+
"--stop-address",
177+
hex(stop),
178+
str(fixture),
179+
],
180+
text=True,
181+
)
182+
(out_dir / f"objdump-window-0x{addr:x}.txt").write_text(disasm)
183+
PY
184+
185+
- name: Run Single Container Multiple Trace Test
186+
shell: bash
187+
run: |
188+
set -euxo pipefail
189+
sudo -E env \
190+
E2E_CONTAINER_IMAGE="$E2E_CONTAINER_IMAGE" \
191+
E2E_CHILD_CONTAINER_IMAGE="$E2E_CHILD_CONTAINER_IMAGE" \
192+
E2E_SANDBOX_SESSION="$E2E_SANDBOX_SESSION" \
193+
E2E_GHOSTSCOPE_SANDBOX="$E2E_GHOSTSCOPE_SANDBOX" \
194+
E2E_TARGET_SANDBOX="$E2E_TARGET_SANDBOX" \
195+
E2E_TARGET_MODE="$E2E_TARGET_MODE" \
196+
E2E_GHOSTSCOPE_LOG_LEVEL="$E2E_GHOSTSCOPE_LOG_LEVEL" \
197+
E2E_GHOSTSCOPE_ENABLE_LOGGING="$E2E_GHOSTSCOPE_ENABLE_LOGGING" \
198+
E2E_GHOSTSCOPE_LOG_CONSOLE="$E2E_GHOSTSCOPE_LOG_CONSOLE" \
199+
RUST_BACKTRACE="$RUST_BACKTRACE" \
200+
RUST_LOG="$RUST_LOG" \
201+
"$(which cargo)" test -p ghostscope --test script_execution test_multiple_trace_targets -- --nocapture \
202+
2>&1 | tee /tmp/mtt-diagnose/test-multiple-trace-targets.log
203+
204+
- name: Collect Runtime Logs
205+
if: always()
206+
shell: bash
207+
run: |
208+
set -euxo pipefail
209+
mkdir -p /tmp/mtt-diagnose
210+
if [ -f ghostscope.log ]; then
211+
cp ghostscope.log /tmp/mtt-diagnose/ghostscope.log
212+
fi
213+
if [ -f g.log ]; then
214+
cp g.log /tmp/mtt-diagnose/g.log
215+
fi
216+
find /tmp/mtt-diagnose -maxdepth 1 -type f | sort > /tmp/mtt-diagnose/manifest.txt
217+
218+
- name: Upload Diagnostic Artifacts
219+
if: always()
220+
uses: actions/upload-artifact@v4
221+
with:
222+
name: container-mtt-diagnose-${{ github.run_id }}-${{ github.run_attempt }}
223+
path: /tmp/mtt-diagnose
224+
225+
- name: Cleanup Session Sandboxes
226+
if: always()
227+
run: |
228+
ids=$(docker ps -aq --filter "label=ghostscope.session=$E2E_SANDBOX_SESSION")
229+
if [ -n "$ids" ]; then
230+
docker rm -f $ids
231+
fi

ghostscope-compiler/src/script/compiler.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,14 @@ impl<'a> AstCompiler<'a> {
347347
module_addresses.len(),
348348
index
349349
);
350+
let resolved_addresses: Vec<String> = module_addresses
351+
.iter()
352+
.map(|addr| format!("{}@0x{:x}", addr.module_path.display(), addr.address))
353+
.collect();
354+
debug!(
355+
"Resolved {}:{} for trace point {} to addresses: {:?}",
356+
file_path, line_number, index, resolved_addresses
357+
);
350358

351359
// Validate optional single-index selection (1-based)
352360
if let Some(idx) = self.compile_options.selected_index {

ghostscope-dwarf/src/analyzer.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,16 @@ impl DwarfAnalyzer {
704704
line_number,
705705
self.modules.len()
706706
);
707+
let addresses: Vec<String> = results
708+
.iter()
709+
.map(|addr| format!("{}@0x{:x}", addr.module_path.display(), addr.address))
710+
.collect();
711+
tracing::debug!(
712+
"Source line '{}:{}' resolved to addresses: {:?}",
713+
file_path,
714+
line_number,
715+
addresses
716+
);
707717
}
708718

709719
results.sort_by(|a, b| {

0 commit comments

Comments
 (0)