...

среда, 9 июля 2014 г.

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



Java приложения — значит в современном Java мире возможность встретить такое процентов на 90%, а то и больше (рассматриваем самые распространённые окружения, HotSpot based JVM версии от 1.6)

которое нельзя остановить — приложение работает, и перезапускать его по тем или иным причинам категорически нельзя

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

доступными средсвами — бесплатно, работоспособно, эффективно, легко, просто и т.д и т.п. В данной статье рассмотрен замечательный инструмент BTrace http://ift.tt/pjZBKg

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


Данная статья по сути является продолжением поста «Отладка Java приложения, когда оно совсем не ждёт — добро пожаловать в InTrace подход» http://ift.tt/1vZwcRo, в котором было показано как вклиниться в уже запущенное приложение и собрать достаточно подробный трейс выполнения. Что есть весьма полезное мастерство, но в реальной жизни, иногда, бывают случаи, когда проскакивает непонятное поведение с вероятностью 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 http://ift.tt/pjZBKg, неоднократно упоминаемый на хабре в коментах, но к сожалению ни разу доселе не описанный в постах.


Для его запуска стоит рассмотреть пару способов:

1) любителям командной строки — консольная утилита скачиваемая с http://ift.tt/VIrjBd (последняя доступная версия)

и запускаемая как



btrace <PID> TracingScript.java




где

PID — это идентификатор процесса (получаемый, к примеру, через jps)

TracingScript.java — трассирующий скрипт, с коим более плотное знакомство будет чуть далее

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



BTrace делает свою работу полагаясь на алгоритм описанный в очень Java подобном скрипте (также можно пользовать D-scriptы). Очень подобном — поскольку это как бы и самая что ни наесть Java, но всё же из-за того, что BTrace не изменяет выполнение трассируемой программы (я имею в виду всёже старается не модифицировать её поведение, только получать информацию о выполнении максимально следуя формату «read-only»), приходиться забыть про множество вещей джавы (начиная с создания новых объектов и заканчивая ещё много чем, см. http://ift.tt/p7IziO 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 изрядно мощный инструмент, позволяющий на лету трассировать весьма и весьма диковенные особенности выполнения. В данной статье затронута лишь вершина айсберга его шикарных возможностей (при желании хоть бери да пиши книгу), материал приводится с целью преподать самые азы и как можно более заинтересовать. Кого зацепило, смотрите более подробно тут http://ift.tt/p7IziO, прежде всего, обратите внимание на количество аннотаций и длинный список очень-очень жизненно полезных примеров. Но всё же не забывайте — всё происходит на свой страх и риск, ибо применяемая BTracе для достижения цели (вклинивания) трансформация Java классов, всегда может сыграть злую шутку.


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

Да прибудет с вами удача )


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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий