|
| 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 |
0 commit comments