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

от автора


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 в реальной жизни не удастся. Будьте бдительны, берите сторону монеты непосредственно умом.
Да прибудет с вами удача )

Благодарю за внимание!

ссылка на оригинал статьи http://habrahabr.ru/post/227639/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *