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