TGViewer
Верхняя полка📝 Верхняя полка📝 @stopshelf · 360 subscribers
Post #464 382
Отлов спорадичных багов с помощью BTrace 👣

Один из забористых багов, с которым мне довелось "развлекаться" в преддверии отпуска, сочетал в себе много интересного:
— воспроизводился только на удалённом сервере в контуре партнёра;
— требовал одновременных действий минимум 2-х человек;
— не имёл чёткого набора шагов, а только некий паттерн;
— из-за этого требовал по несколько попыток, чтобы проявиться.

Как не трудно догадаться, там была замешана гонка потоков. Она сильно осложняла диагностику, и даже удалённое подключение отладчиком через SSH-туннель не давало результата — как только один или несколько потоков оказывались под отладкой, они переставали "шалить" и укладывались в положенные временные рамки. Словом, типичный гейзенбаг — существует только пока за ним никто не наблюдает 👻

Втыкать логирование в разные места кода и таким образом постепенно локализовывать проблему было можно, но уж очень долго и утомительно — нужно было бесконечно готовить патчи, выносить их на этот (довольно нагруженный) сервер и перезапускать его, ожидая полного прогрева. Я даже попробовал, но уже на второй итерации мне это разонравилось🤢

И тогда мне вспомнился BTrace — диагностический инструмент под зонтиком OpenJDK, позволяющий внедрять в байт-код работающего приложения различные инъекции (прямо на #Java) для доставания почти любых данных из полей и локальных переменных, в том числе аргументов методов. Из-за того, что такими инъекциями можно наломать дров, в BTrace придуман специальный DSL, сильно ограничивающий допустимые действия (например, нельзя вызывать почти никакие методы прикладного кода), но позволяющий безопасно "снюхивать" данные из многих точек приложения 👃

Основной фичей BTrace в этот раз для меня стало отслеживание зависимых вызовов — это когда для заданного метода агент реагирует не на любые вызовы, а только на те, которые произошли из тела другого (тоже заданного) метода. В тексте BTrace-скрипта выглядит это примерно так (сокращённо):

    @OnMethod(clazz = "SpreadsheetSession"
, method = "handleCellValue" // метод-инициатор вызова
, location = @Location(value = Kind.CALL
, clazz = "SpreadsheetSession"
, method = "calculateCellsDependentOnSingle")) // целевой (проблемный) метод
public static void handleCellValue(
@TargetMethodOrField String method,
@ProbeMethodName String probeMethod,
AnyType cellAbsRef,
boolean hasManualInput) {
// ...
String spreadsheet = str(Reflective.get("entity", cellAbsRef)); // извлечение данных через DSL
// вывод извлеченных данных (тоже на DSL)
println(method + " from " + probeMethod + ": " + spreadsheet + ":" + column + row + ", manualInput: " + hasManualInput);
}

Это похоже на зависимые точки останова в отладчике, только никакого останова не происходит, а собранные данные выводятся в консоль, из которой запущен BTrace 🖥

Конечно, первое применение BTrace требует сильно больше времени, чем повтыкать System.out.println() или подцепиться отладчиком. Но зато потом, когда механизм освоен и отлажен, итерации диагностики вида "написал-применил-посмотрел-повторил" укорачиваются до нескольких минут и позволяют эффективно локализовывать даже такие упоротые изощрённые проблемы, как эта 🧶

Надеюсь, вам никогда не придётся отлаживать такие баги. Но если вдруг, имейте в виду, что BTrace может в этом пригодиться ✍️

—
P.S. Любопытно, что 10 лет назад, когда я не знал о BTrace (хотя он уже в каком-то виде существовал), я создал собственный инструмент jMint со схожей идеей: внедрять в работающее приложение кусочки кода на Java (дроплеты), чтобы менять его поведение с целью тестирования и/или отладки. Основное отличие jMint — в отсутствии "безопасного" DSL. Причина здесь не только в слабоумии и отваге автора, но и в стремлении дать возможность заглушать нежелательное поведение или временно менять интересующее. Поэтому применение инструмента было строго ограничено тестовым контуром. Несмотря на опасности и ограничения, он неплохо прижился в моей прошлой команде и какое-то время продолжал применяться после моего ухода. Подробнее о нём можно узнать здесь 📖
—
GitHub GitHub - btraceio/btrace: Production-safe dynamic tracing and diagnostics for Java applications—attach to live JVMs with no restart… Production-safe dynamic tracing and diagnostics for Java applications—attach to live JVMs with no restart or recompilation. - btraceio/btrace
  • 🔥 12
  • 👍 4
  • ❤ 3
More from @stopshelf
  1. Oct 2, 2026Post #485
  2. Sep 18, 2026#инструменты для презентаций на Markdown Сегодня нужно было по-быстрому сделать слайды для…
  3. Sep 14, 2026Post #483
  4. Aug 31, 2026Игровой портал в прошлое 🔮 Года полтора назад я захотел показать ребёнку игру моего детст…
  5. Aug 29, 2026Post #481
  6. Aug 28, 2026Post #480
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →