Отладка Java приложения, которое нельзя остановить. Ловим экзотику выполнения самыми доступными средствами — BTrace подход

  • Tutorial

Java приложения — значит в современном Java мире возможность встретить такое процентов на 90%, а то и больше (рассматриваем самые распространённые окружения, HotSpot based JVM версии от 1.6)
которое нельзя остановить — приложение работает, и перезапускать его по тем или иным причинам категорически нельзя
экзотика — нечто такое этакое, что не каждый день в голову взбредёт поймать (определённая последовательность вызова методов, диковинные комбинации значений параметров, ...)
доступными средствами — бесплатно, работоспособно, эффективно, легко, просто и т.д и т.п. В данной статье рассмотрен замечательный инструмент BTrace kenai.com/projects/btrace

И само собой в код Java приложения заранее ничего специально не добавлено касательно средств дебага…



Данная статья по сути является продолжением поста «Отладка Java приложения, когда оно совсем не ждёт — добро пожаловать в InTrace подход» habrahabr.ru/post/219661, в котором было показано как вклиниться в уже запущенное приложение и собрать достаточно подробный трейс выполнения. Что есть весьма полезное мастерство, но в реальной жизни, иногда, бывают случаи, когда проскакивает непонятное поведение с вероятностью 1 на 1 000, а то и хуже, и попробуй пади это найди в тоннах трейсов.
Поэтому берём для примера простенькую программу (файл excitement/Coin.java) и будем собирать «экзотику» на лету.

package excitement;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Random;

public class Coin {
  public void head() {
  }

  public void tail() {
  }

  public static void main(String... args) throws Exception {
    System.out.println("Нажмите любой Enter для продолжения...");
    new BufferedReader(new InputStreamReader(System.in)).readLine();

    Random rand = new Random();

    for (int count = 0; count < 1000; ++count) {
      if (rand.nextInt(2) > 0) {
        new Coin().head();
      } else {
        new Coin().tail();
      }
    }
    System.out.println("Вот и всё!");
  }

}



Скомпилируем
javac excitement/Coin.java


И запустим
javac excitement.Coin


Проще ведь некуда, правда? )

За экзотику я возьму волнующий вопрос: «Сколько же раз подряд максимально выпадут орёл и решка, ну а также вообще сколько раз они просто выпадут?» Такой себе тест rand.nextInt(2). Каковы прогнозы? Ставки принимаются…

Получить ответ поможет весьма известный и, к ко всему прочему, просто великолепный инструмент BTrace kenai.com/projects/btrace, неоднократно упоминаемый на хабре в коментах, но к сожалению ни разу доселе не описанный в постах.

Для его запуска стоит рассмотреть пару способов:
1) любителям командной строки — консольная утилита скачиваемая с kenai.com/projects/btrace/downloads/directory/releases/release-1.2.4 (последняя доступная версия)
и запускаемая как
btrace <PID> TracingScript.java

где
PID — это идентификатор процесса (получаемый, к примеру, через jps)
TracingScript.java — трассирующий скрипт, с коим более плотное знакомство будет чуть далее

2) любителям окошек предлагается использовать плагин в VisualVM visualvm.java.net/download.html. Для чего заходим в Tools->Plugins->Available Plugins кликаем BTrace Workbench и давим «Install», внимательно читаем лицензию (хотя кто их читает), ладно, так и быть, без малейших колебаний соглашаемся в этом и последующих окнах на всё при всё. И теперь, после успешной установки, в контекстном меню интересующего процесса, в VisualVM появился новый пункт «Trace Application...»



BTrace делает свою работу полагаясь на алгоритм описанный в очень Java подобном скрипте (также можно пользовать D-scriptы). Очень подобном — поскольку это как бы и самая что ни наесть Java, но всё же из-за того, что BTrace не изменяет выполнение трассируемой программы (я имею в виду всёже старается не модифицировать её поведение, только получать информацию о выполнении максимально следуя формату «read-only»), приходиться забыть про множество вещей джавы (начиная с создания новых объектов и заканчивая ещё много чем, см. kenai.com/projects/btrace/pages/UserGuide BTrace Restrictions) и использовать средства предоставляемые непосредственно BTrace.

А теперь скрипт (файл TracingScript.java)

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace // скриптом выступает джава класс
public class TracingScript {
@Property // возможно смотреть значения "на лету" через MBean JMX (jconsole, VisualVM, ...)
private static long tailCount;
@Property(name="Total head count is") // другое имя для JMX
private static long headCount;
@Property
private static long maxHeadSequence = 1;
@Property
private static long maxTailSequence = 1;
@Property
private static long sequence = 1;
@Property
private static long prevId = -1;

@OnMethod(clazz = "excitement.Coin",  // вклиниваемся в метод в пакете  excitement класса Coin
         method = "head", // c именем head
         location = @Location(Kind.RETURN)) // при возврате из него
 public static void onHead() {
    ++headCount;
    sequence = prevId == 0 ? sequence + 1 : 1;
    if (sequence > maxHeadSequence) maxHeadSequence = sequence;
    prevId = 0;
 }

@OnMethod(clazz = "excitement.Coin", 
         method = "tail", 
         location = @Location(Kind.RETURN))
 public static void onTail() {
    ++tailCount;
    sequence = prevId == 1 ? sequence + 1 : 1;
    if (sequence > maxTailSequence) maxTailSequence = sequence;
    prevId = 1;
 }

@OnExit // вызывается при завершении программы
public static void onexit(int code) {
        println(strcat("total heads:", str(headCount))); // из-за ограничения на создание объектов  наблюдаются свои примочки по работе со строками
        println(strcat("total tails:", str(tailCount)));
        println(strcat("max tail sequence:", str(maxTailSequence)));
        println(strcat("max head sequence:", str(maxHeadSequence)));
    }
}


В конце концов, запускаем этот скрипт, жмём «Enter» в ожидающей бросания монет программе и получаем (у кого как, а у меня вышло так):

total heads:531
total tails:469
max tail sequence:9
max head sequence:8

В целом орёл и решка случилось выпадали по 8 и 9 раз подряд (хотя у меня за несколько запусков бывало и 10-11 раз). Желающим предлагается самостоятельно проверить насколько полученное совпадает с результатами формул теории вероятностей (дабы не заехать тут в сложную тему касательно способов генерации таких простых случайных чисел).

Подводя итоги:
BTrace изрядно мощный инструмент, позволяющий на лету трассировать весьма и весьма диковенные особенности выполнения. В данной статье затронута лишь вершина айсберга его шикарных возможностей (при желании хоть бери да пиши книгу), материал приводится с целью преподать самые азы и как можно более заинтересовать. Кого зацепило, смотрите более подробно тут kenai.com/projects/btrace/pages/UserGuide, прежде всего, обратите внимание на количество аннотаций и длинный список очень-очень жизненно полезных примеров. Но всё же не забывайте — всё происходит на свой страх и риск, ибо применяемая BTracе для достижения цели (вклинивания) трансформация Java классов, всегда может сыграть злую шутку.

И напоследок, про монеты (физика да и только) — зачастую монета не идеально сбалансирована (обычно орёл чуть тяжелее решки), так что подбросить монету и получить 50/50 в реальной жизни не удастся. Будьте бдительны, берите сторону монеты непосредственно умом.
Да прибудет с вами удача )

Благодарю за внимание!
Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 22

    +4
    А какой overhead при его использовании?
      +1
      Обычно — минимален:
      — при подцеплении происходит трансформация классов выполняющейся программы (дабы понаставить хуки)
      — дальше уже зависит от того, что навесили на выполняющуюся программу и как именно смотрится результат (сеть, JMX, ...)

      stackoverflow.com/questions/6831224/what-is-the-overhead-of-btrace

        +1
        при подцеплении происходит трансформация классов выполняющейся программы (дабы понаставить хуки)

        А при отцеплении всё взад вертается? Или так и остаётся. Что будет если несколько раз подцепляться, трансформация будет выполняться каждый раз?
        –3
        Хуки же ставятся наверняка рефлекшином. Откуда информация, что минимален. Можно бэнчмарки увидеть?
        Мне кажется, что эту вещь ну никак нельзя использовать в продакшине. Максимум на тестовых серверах.
        • UFO just landed and posted this here
            0
            Хочу jmh бенчмарк. Если система критичная к перформансу это важно. (да, java не real-time, и да, лучше использовать в таких системах c++)
            • UFO just landed and posted this here
            +1
            «Когда кажется, надо смотреть в сорцы» © мой коллега :)
            Нет там Reflection в рантайме. Динамическая генерация байткода + Instrumentation.retransformClasses
              0
              Если бы там был Reflection, то требование «не создавать объекты» было бы ну очень трудно выполнить. :-)
        0
        А приложения которые выполняются внутри Tomcat можно отлаживать?
        0
        Интересная тулза, но все же удобнее держать jvm в дебаге, оверхед для большинства задач будет незаметен.
          –1
          А если у вас сервер за 10тыс км и кучей фаирволов?
          • UFO just landed and posted this here
              0
              Тут недопонимание вижу.

              Пошло сравнение BTrace с методом «держать jvm в дебаге» для разбора проблем.
              Но так как BTrace так же не помощник если сервера не доступны — см. «10тыс км и кучей фаирволов», то не понятно высказанное возражение.

              PS: BTrace взял на вооружение. Статья — хороший кик старт.
              • UFO just landed and posted this here
                  0
                  Извините, я вас не понимаю. Ветка обсуждения не про это.
                0
                Да я уже привык, неадекватов тут увы очень много.
            +1
            BTrace это не заменитель «классического» дебагера, это всего лишь одно из хороших средств отладки, с ориетацией на задачи связанные с мониторингом приложения в реальном времени опираясь на скрипты
            • UFO just landed and posted this here

              Only users with full accounts can post comments. Log in, please.