Skip to content

fix(tracer): fix BSD-incompatible sed injection at remaining jvm/native call sites#2047

Open
carlos-alm wants to merge 1 commit into
fix/issue-1911-stale-doc-comment-roleclassificationnodefrom
fix/issue-1913-tracer-sed-injection-uses-bsd-incompatible
Open

fix(tracer): fix BSD-incompatible sed injection at remaining jvm/native call sites#2047
carlos-alm wants to merge 1 commit into
fix/issue-1911-stale-doc-comment-roleclassificationnodefrom
fix/issue-1913-tracer-sed-injection-uses-bsd-incompatible

Conversation

@carlos-alm

Copy link
Copy Markdown
Contributor

Summary

  • jvm-tracer.sh (java/kotlin/scala/groovy) and native-tracer.sh (csharp/swift/dart/zig) used the GNU-only single-line a\text/i\text sed form at 8 more call sites beyond the one rust site Rust dynamic tracer fixture has no main.rs — trace_rust always fails before producing edges #1759 already fixed — BSD sed (macOS) rejects it outright, or when the failure is swallowed by 2>/dev/null || true, silently skips the injection entirely.
  • Since the exact same broken shape repeats across all 8 sites, extracted three shared helpers into tracer-common.shsedi_insert_before, sedi_insert_before_end, sedi_append_unless — that build the portable multi-line form internally, so future injection sites can't reintroduce the GNU-only shortcut. This is the approach the issue itself suggested ("consider adding a small helper... so future injection sites can't reintroduce the GNU-only shortcut").
  • Fixing the syntax alone surfaced a second, previously-masked bug at four of the same sites (jvm-tracer.sh java/scala/groovy dump, native-tracer.sh csharp dump): \s is a GNU-only regex escape that BSD sed's regex engine (BRE or ERE) does not treat as whitespace, so /^\s*\}/ never matched an indented closing brace on macOS — the dump() call would still never have been injected even after the a\/i\ fix, just without the syntax error. Replaced with the portable [[:space:]] POSIX bracket expression already used correctly elsewhere in the same file.

Closes #1913

Test plan

  • New tests/benchmarks/resolution/tracer/tracer-common.test.ts (5 tests) exercises all three helpers directly against synthetic files mirroring each of the 8 original sites' shapes, plus a regression guard that greps jvm-tracer.sh/native-tracer.sh/go-tracer.sh for any remaining single-line a\/i\ form
  • tests/benchmarks/resolution/tracer/tracer-validation.test.ts — 42/42 passed
  • Verified all 8 original call sites directly with the sed helpers in isolation (mirroring the issue's own no-toolchain verification method) — every one now injects correctly on this BSD sed (macOS)
  • Full end-to-end swift tracer run (only real matching toolchain available locally) — 100% same-file recall, covering both the func main branch (previously dormant/broken) and the top-level fallback branch
  • Manually reconstructed the java fixture's full injection pipeline (javac itself isn't available in this sandbox — no real JDK installed) and confirmed byte-correct CallTracer.traceCall()/CallTracer.dump() placement
  • npm run lint — clean; shellcheck on all touched .sh files — no new findings
  • bash -n clean on tracer-common.sh, jvm-tracer.sh, native-tracer.sh

Filed two out-of-scope findings surfaced while getting these tracers to actually run on BSD sed for the first time:

…ve call sites

jvm-tracer.sh (java/kotlin/scala/groovy) and native-tracer.sh
(csharp/swift/dart/zig) used the GNU-only single-line `a\text`/`i\text` sed
form at 8 more call sites beyond the one #1759 already fixed for rust — BSD
sed (macOS) requires the text on the line after the backslash and otherwise
errors or, when the error is swallowed by `2>/dev/null || true`, silently
skips the injection entirely.

Mirrors #1759's fix, but since the broken shape repeats identically across
all 8 sites, extracts three shared helpers into tracer-common.sh
(sedi_insert_before, sedi_insert_before_end, sedi_append_unless) that build
the portable multi-line form internally, so future injection sites can't
reintroduce the GNU-only shortcut — the approach the issue itself suggested.

Fixing the syntax alone surfaced a second, previously-masked bug at four of
the same sites (jvm-tracer.sh java/scala/groovy dump, native-tracer.sh
csharp dump): `\s` is a GNU-only regex escape that BSD sed's regex engine
does not treat as whitespace, so `/^\s*\}/` never matched an indented
closing brace on macOS — the dump() call would still never have been
injected even after the a\/i\ fix. Replaced with the portable
`[[:space:]]` POSIX bracket expression already used correctly elsewhere in
the same patterns.
@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Replaces eight BSD-incompatible GNU-only a\text/i\text sed injection sites across jvm-tracer.sh and native-tracer.sh by extracting three portable helpers (sedi_insert_before, sedi_insert_before_end, sedi_append_unless) into tracer-common.sh, and fixes a secondary \s-on-BSD bug at four of those same sites by replacing it with the POSIX [[:space:]] bracket expression.

  • tracer-common.sh gains three new helper functions that build the portable multi-line i\<newline>text form internally, so future injection sites cannot accidentally reintroduce the GNU-only shortcut.
  • jvm-tracer.sh (java/kotlin/scala/groovy) and native-tracer.sh (csharp/swift/dart/zig) now delegate all sed insertion to the new helpers; \s patterns replaced with [[:space:]].
  • A new tracer-common.test.ts directly exercises all three helpers and adds a regression guard that greps the tracer scripts for any surviving single-line a\/i\ occurrences.

Confidence Score: 4/5

Safe to merge; the shell changes mechanically replace known-broken GNU-only sed forms with the portable multi-line equivalent and the fix is verified by new tests that run on the host's own sed.

The helper implementations are correct and the per-language call sites map cleanly onto the original logic. The only notable gap is a misleading docstring on sedi_insert_before that says "first" when the function inserts before every matching line — a future caller relying on first-only semantics would get silent double-injection. The regression guard's intentional omission of tracer-common.sh also leaves a small, undocumented surface. Neither affects current behaviour.

tracer-common.sh (docstring on sedi_insert_before), tracer-common.test.ts (undocumented exclusion of tracer-common.sh from the regression guard)

Important Files Changed

Filename Overview
tests/benchmarks/resolution/tracer/tracer-common.sh Introduces three portable sed helpers; the sedi_insert_before docstring incorrectly says "first line" when the helper inserts before every matching line.
tests/benchmarks/resolution/tracer/jvm-tracer.sh All eight GNU-only a\text/i\text sites replaced with helper calls; \s replaced with [[:space:]] at java/scala/groovy dump sites; behaviour is equivalent on both GNU and BSD sed.
tests/benchmarks/resolution/tracer/native-tracer.sh csharp, swift, dart, and zig dump-injection sites ported to portable helpers; no logic changes beyond the sed syntax and \s[[:space:]] fix.
tests/benchmarks/resolution/tracer/tracer-common.test.ts Five new tests cover all three helpers and add a regression guard for the GNU-only form; guard intentionally omits tracer-common.sh to avoid false-positives from the i\\ in the helpers themselves, creating a small unguarded surface.

Fix All in Claude Code

Reviews (1): Last reviewed commit: "fix(tracer): fix BSD-incompatible sed in..." | Re-trigger Greptile

Comment on lines +26 to +27
# Insert TEXT on the line immediately before the first line matching PATTERN.
sedi_insert_before() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The docstring says "first line matching PATTERN" but the generated sed command ${pattern} i\ applies to every line that matches the pattern, not only the first. The current sole call-site happens to target a unique string (/CallTracer.Dump/), so the mismatch is harmless today — but a future caller that expects first-only semantics would silently get multiple insertions. The comment should say "every" (or restrict the implementation to a 0,/pat/-scoped address for truly first-only behaviour).

Suggested change
# Insert TEXT on the line immediately before the first line matching PATTERN.
sedi_insert_before() {
# Insert TEXT on the line immediately before every line matching PATTERN.
sedi_insert_before() {

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

Comment on lines +139 to +159
const guardedFiles = ['jvm-tracer.sh', 'native-tracer.sh', 'go-tracer.sh'];

for (const name of guardedFiles) {
const code = fs
.readFileSync(path.join(TRACER_DIR, name), 'utf8')
.split('\n')
// Drop comment lines so prose that *describes* the banned form (as
// this very fix's changelog comments do) isn't mistaken for it.
.filter((line) => !line.trim().startsWith('#'))
.join('\n');
// \S excludes the newline that must immediately follow a portable
// "i\" / "a\" — so this only matches the banned single-line form
// where real text follows the backslash on the same line.
const offender = code.match(/[ai]\\\S/);
expect(
offender,
`${name} contains a GNU-only single-line a\\/i\\ form: ${offender}`,
).toBeNull();
}
});
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Regression guard omits tracer-common.sh itself

The guard intentionally skips tracer-common.sh to avoid false-positives from the i\\ in the helpers' own portable implementation (the JS regex /[ai]\\\S/ would match the second backslash in i\\<newline>text). That's the right trade-off, but it means a new injection helper mistakenly written with the GNU-only form directly inside tracer-common.sh would go undetected. A comment in the test recording why the file is excluded would make this intentional gap visible to the next contributor.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Codegraph Impact Analysis

7 functions changed6 callers affected across 2 files

  • trace_dotnet in tests/benchmarks/resolution/tracer/native-tracer.sh:471 (1 transitive callers)
  • trace_swift in tests/benchmarks/resolution/tracer/native-tracer.sh:634 (1 transitive callers)
  • trace_dart in tests/benchmarks/resolution/tracer/native-tracer.sh:720 (1 transitive callers)
  • trace_zig in tests/benchmarks/resolution/tracer/native-tracer.sh:823 (1 transitive callers)
  • sedi_insert_before in tests/benchmarks/resolution/tracer/tracer-common.sh:27 (2 transitive callers)
  • sedi_insert_before_end in tests/benchmarks/resolution/tracer/tracer-common.sh:39 (6 transitive callers)
  • sedi_append_unless in tests/benchmarks/resolution/tracer/tracer-common.sh:51 (1 transitive callers)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant