Как настроить дебаггер java

Отладка Java-приложений из командной строки

Некоторые ошибки трудно воспроизвести на вашем персональном компьютере, но их легко воспроизвести на производственных или тестовых машинах. Это обычная ситуация, с которой часто сталкиваются профессиональные Java-разработчики. Для отладки таких проблем OpenJDK предоставляет два инструмента: remote debugging и jdb . Эта статья посвящена jdb .

Для приложений Java типичными производственными и тестовыми машинами являются серверы Linux без графического интерфейса, поэтому доступны только инструменты командной строки. И мы не можем использовать профессиональные IDE, такие как IntelliJ IDEA, Eclipse или Apache NetBeans IDE.

В таких сценариях мы можем использовать jdb . jdb — это отладчик командной строки, входящий в состав OpenJDK.

Это перевод руководства для начинающих. Очевидно эксперты все это знают и им не стоит тратит время на его чтение.

Отладка Java с помощью утилиты «jdb»

jdb расположена в каталоге jdk/bin. Она использует интерфейс отладки Java (JDI) для запуска и подключения к целевой JVM. Интерфейс отладки Java (JDI) предоставляет интерфейс языка программирования Java для отладки приложений на языке программирования Java. JDI является частью архитектуры отладчика платформы Java (Java Platform Debugger Architecture).

В этом разделе мы рассмотрим, как подключить jdb к java-приложению и начать отладку и мониторинг.

Команда jdb

Формат команды jdb:

Пример Java приложения для отладки

Ниже приведен пример Java класса, который мы собираемся отладить и попытаться понять различные доступные функции. Важно скомпилировать этот класс с параметром «-g» (javac -g Test.java), который генерирует всю отладочную информацию, включая локальные переменные. По умолчанию генерируется только информация о номере строки и исходном файле.

Подключение jdb к java-приложению

Приведенная ниже команда — это наиболее распространенный способ запуска приложения с отладчиком jdb. Здесь мы не передаем никаких параметров jdb, мы передали только имя класса, который не требует никаких аргументов:

Таким образом, мы запускаем выполнение основного класса «Test» аналогично тому, как мы запускаем выполнение основного класс в среде IDE. jdb останавливает JVM перед выполнением первой инструкции этого класса.

Другой способ использовать команду jdb для подключения ее к уже запущенной JVM. Синтаксис для запуска JVM с портом отладчика:

Чтобы подключить jdb к этому удаленному jvm, используйте синтаксис ниже:

В этой статье мы не будем подробно рассматривать удаленную отладку.

Отладка и мониторинг

Ниже приводится команда для присоединения jdb к Java-программе Test:

Установите точку останова в строке 5, используя команду «stop», как показано ниже:

Запустите выполнение основного класса приложения с помощью команды «run»:

Выполнить текущую строку с помощью команды «step»:

Выполнить текущую строку с помощью команды «step»:

Печать локальной переменной i с помощью команды «print»:

Печать всех локальных переменных в текущем фрейме стека с использованием команды «locals»:

Выгрузите стек потока, используя команду «where»:

Выдать список потоков в запущенном приложении, используя команду «threads»:

Продолжить выполнение с точки останова, используя команду cont :

Все доступные команды jdb можно получить используя команду «help»:

Поддерживается весьма стандартный синтаксис команд.

Команды запуска могут быть помещены в файлы «jdb.ini» или «.jdbrc» в каталогах user.home или user.dir.

Заключение

OpenJDK предоставляет множество замечательных инструментов для устранения неполадок и диагностики Java-приложений. Эти инструменты помогут вам быстро исправить проблемы в рабочем приложении. jdb может быть большим подспорьем, когда нет другого способа, кроме отладки приложения, а ваша любимая IDE недоступна. Знание таких функций поможет вам улучшить задание Java.

Чтобы помочь вам, автор статьи написал электронную книгу 5 steps to Best Java Jobs. Вы можете загрузить это пошаговое руководство бесплатно!

Источник

Программный дебаг Java-приложений посредством JDI

Введение

В процессе отладки приложений работающих на JVM посредством дебаггера в Eclipse меня всегда впечатляло то, сколько доступа можно получить к данным приложения — потокам, значениям переменных и т.п. И в то же время периодически возникало желание «заскриптовать» некоторые действия или получить больше контроля над ними.

Читайте также:  Свекровь настроила всех родственников против меня

Например, иногда для того чтоб «мониторить» состояние какой-то переменной, меняющейся в цикле, я использовал условный брейкпойнт, условием к которому был код вроде «System.out.println(theVariable); return false». Этот хак позволял получить лог значений переменной практически не прерывая работы приложения (она, естественно, всё-таки прерывалась на время выполнения кода условия, но не более). Плюс, нередко при просмотре каких-нибудь данных через вид Display порядком раздражало то, что результат евалюейшна кода в Display введённого добавлялся тут же после него.

В общем хотелось получить возможность делать всё то же самое например через Bean Shell или Groovy Shell, что в принципе аналогично программному дебагу. По логике это не должно было быть сложно — ведь делает же это как-то сам Eclipse, верно?

Проведя некоторый рисёрч я смог получить доступ к отладочной информации JVM программно, и спешу поделится примером.

О JPDA и JDI

Для отладки JVM придуманы специальные стандарты, собранные вместе под «зонтичным» термином JPDA — Java Platform Debugger Architecture. В них входят JVMTI — нативный интерфейс для отладки приложений в JVM посредством вызова сишных функций, JDWP — протокол передачи данных между дебаггером и JVM, приложения в которой отлаживают и т.д.

Всё это выглядело не особо релевантно. Но сверх этого всего в JPDA входит некий JDI — Java Debug Interface. Это Java API для отладки JVM приложений — то, что доктор прописал. Официальная страница о JPDA подтвердила наличие reference имплементации JDI от Sun/Oracle. Значит, оставалось только начать ней пользоватся

Пример

В качестве proof of concept я решил попробовать запустить два Groovy Shell-а — один в отладочном режиме в качестве «подопытного», второй в качестве отладчика. В подопытном шелле была задана строчная переменная, значение которой требовалось получить из шелла-«отладчика».

Подопытный был запущен с следующими парметрами:
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=7896
Т.е. JVM была запущена в режиме удалённой отладки через TCP/IP, и ожидала соединения от отладчика на порту 7896.

Также в подопытном Groovy Shell была выполнена следующая команда:

Соответственно значение “Some special value” должно было быть получено в отладчике.

Т.к. это не просто значение поля какого-нибудь объекта, для того чтоб его получить надо было немного знать внутренности Groovy Shell (или как минимум подглядывать в исходники), но тем интереснее и реалистичнее мне показалась задача.

Далее дело было за «отладчиком»:

Рассмотрим всё пошагово:

Соединение с JVM

При помощи JDI соединяемся с JVM которую задумали отлаживать (хост == локалхост т.к. я всё делал на одной машине, но сработает аналогично и с удалённой; порт же тот, что был выставлен в debug-параметрах «подопытной» JVM).
JDI позволяет присоединятся к JVM как через сокеты, так и напрямую к локальному процессу. Поэтому VirtualMachineManager возвращает больше одного AttachingConnector-а. Мы выбираем нужный коннектор по имени транспорта («dt_socket»)

Получение стактрейса потока main

Полученный интерфейс к удалённой JVM позволяет посмотреть запущенные в ней потоки, приостановить их и т.п. Но для того, чтоб иметь возможность делать вызовы методов в удалённой JVM нам нужен поток в ней, который был бы остановлен именно брейкпойнтом. О чём собственно гласит следующий пункт JDI javadoc:
«Method invocation can occur only if the specified thread has been suspended by an event which occurred in that thread. Method invocation is not supported when the target VM has been suspended through VirtualMachine.suspend() or when the specified thread is suspended through ThreadReference.suspend().»

Для установки брейкпойнта я пошёл несколько специфическим путём — не заглядывать в сорцы Groovy Shell а просто посмотреть что сейчас происходит в JVM и выставить брейкпойнт прямо в том, что происходит.

В потоках подопытной JVM был обнаружен поток main, и в его стактрейс я и заглянул. Поток был предварительно остановлен — чтоб стактрейс оставался актуален во время последующих манипуляций.

Читайте также:  Против меня настроили всех моих знакомых

В результате получил такое:

Установка брейкпойнта

Итак, мы имеем стактрейс остановленного потока main. API JDI возвращает для потоков так называемые StackFrame, из которых можно получить их Location. Собственно сей Location и требуется для установки брейкпойнта.
Не долго думая, локейшн я взял из «jline.ConsoleReader$readLine.call», и в него установил брейкпойнт, после чего запустил поток main работать дальше:

Теперь брейкпойнт установлен. Переключившись в подопытный Groovy Shell и нажав ввод я увидел что он действительно остановился. У нас есть остановка потока на брейкпойнте — всё готово к вмешательству в работу подопытной JVM.

Получение ссылки на объект Groovy Shell

API JDI позволяет из StackFrame получать видимые в них переменные. Чтоб получить значение переменной из контекста Groovy Shell надо было сначала вытянуть ссылку на сам шелл. Но где он?

Подсматриваем все видимые переменные во всех стек фреймах:

Обнаружился стек фрейм в объекте «org.codehaus.groovy.tools.shell.Main» с видимой переменной shell:
«48: org.codehaus.groovy.tools.shell.Main:131 in thread instance of java.lang.Thread(name=’main’, >

Получение искомого значения из Groovy Shell

У shell.Main имеется поле interpreter. Зная немного внутренности Groovy Shell я заранее знал что переменные GroovyShell контекста хранятся в объекте типа groovy.lang.Binding, который можно получить вызвав getContext() у Interpreter (вызов метода необходим т.к. соответствующего поля со ссылкой на groovy.lang.Binding в Interpreter нет).

Из Binding значение переменной можно получить вызовом метода getVariable(String varName).

Последняя строка скрипта вернула нам ожидаемое значение «Some special value» — всё работает!

Последний штрих

Шутки ради я решил еще и поменять значение этой переменной из отладчика — для этого достаточно было вызвать у Binding метод setVariable(String varName, Object varValue). Что может быть проще?

Чтоб убедится что всё сработало я также задизейблил брейкпойнт и запустил обратно приостановленный ранее по брейкпойнту поток main.

Переключившись в последний раз в подопытный Groovy Shell я проверил значиение переменной myVar, и оно оказалось равно «Surprise!».

Выводы

Быть Java программистом это счастье, ибо Sun подарил нам мощные инструменты — а значит большие возможности (-:
А если еще дописать к Groovy удобные врапперы (метаклассы) для JDI можно сделать программную отладку из Groovy Shell вполне приятной. К сожалению пока-что она выглядит где-то так же, как, например, доступ к полям и методам через reflection API.

Источник

Tutorial: Debug your first Java application

You have created and run your Java application. Let’s imagine you have discovered that it functions not the way you expected. For example, it returns a wrong value or crashes with an exception. Seems like you have errors in your code and it’s time to debug it.

What Is Debugging?

Broadly, debugging is the process of detecting and correcting errors in a program.

There are different kinds of errors, which you are going to deal with. Some of them are easy to catch, like syntax errors, because they are taken care of by the compiler. Another easy case is when the error can be quickly identified by looking at the stack trace, which helps you figure out where the error occurred.

However, there are errors which can be very tricky and take really long to find and fix. For example, a subtle logic error, which happened early in the program may not manifest itself until very late, and sometimes it is a real challenge to sort things out.

This is where the debugger is useful. The debugger is a powerful tool, which lets you find bugs a lot faster by providing an insight into the internal operations of a program. This is possible by pausing the execution and analyzing the state of the program by thorough examination of variables and how they are changed line by line. While debugging, you are in full control of the things. In this manual we are covering a basic debugging scenario to get you started.

Examine the code

Let’s try a simple debugging case. Imagine we have the following application:

Читайте также:  Опель омега люк не работает

The program is supposed to calculate the average of all values passed as command-line arguments.

It compiles and runs without issues, however the result is not what one would expect. For instance, when we pass 1 2 3 as the input, the result is 6.0 .

First of all, you need to think about where the suspected error might be coming from. We can assume the problem is not in the print statements. Most likely, unexpected results are coming from our findAverage method. In order to find the cause, let’s examine its behavior at runtime.

Set breakpoints

To examine how the program operates at runtime, we need to suspend its execution before the suspected piece of code. This is done by setting breakpoints. Breakpoints indicate the lines of code where the program will be suspended for you to examine its state.

Click the gutter at the line where the findAverage method is called.

Run the program in debug mode

Now let’s start the program in debug mode.

Since we are going to pass arguments for running and debugging the program, make sure the run/debug configuration has these arguments in place.

From the main menu, select Run | Edit Configurations .

Enter arguments in the Program arguments field.

Click the Run button near the main method. From the menu, select Debug .

Analyze the program state

After the debugger session has started, the program runs normally until a breakpoint is hit. When this happens, the line where the program paused gets highlighted and the Debug tool window appears.

The highlighted line has not been executed yet. The program now waits for further instructions from you. The suspended state lets you examine variables, which hold the state of the program.

As the findAverage method has not been called yet, all its local variables like result are not yet in scope, however, we can examine the contents of the args array ( args is in scope for the main method). The contents of args are displayed inline where args is used:

You can also get information about all variables that are currently in scope in the Variables panel.

Step through the program

Now that we are comfortable with the Debug tool window, it’s time to step into the findAverage method and find out what is happening inside it.

To step into a method, click the Step Into button or press F7 .

Another line gets highlighted in the editor because we advanced the execution point one step forward.

Continue stepping with Step Over F8 . Notice how it is different from Step Into – it also advances the execution one step forward, but it doesn’t visit any methods like Integer.parseInt() along the way.

Let’s keep stepping and see how the local variable result is declared and how it is changed with each iteration of the loop.

Right now the variable s contains the value «3» . It is going to be converted to an Integer and be added to result , which currently has the value of 3.0 . No errors so far. The sum is calculated correctly.

Two more steps take us to the return statement and we see where the omission was. We forgot to divide the sum by the number of values. This was the cause of incorrect method return.

Let’s correct the error.

return result / input.length;

Stop and rerun the debugger session

In order to check that the program works fine, let’s stop the debugger session and rerun the program.

Click the Stop button or press Ctrl+F2 .

Click the Run button near the main method. From the menu, select Run .

Verify that the program works correctly now.

Источник

Оцените статью