Некоторые ошибки трудно воспроизвести на вашем персональном компьютере, но их легко воспроизвести на производственных или тестовых машинах. Это обычная ситуация, с которой часто сталкиваются профессиональные 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:
jdb [options] [classname] [arguments]
options: This represents the jdb command-line options (e.g. attach, launch).
classname: This represents the name of the main class to debug.
arguments: This represents the arguments that are passed to the main() method of the class.
Пример Java приложения для отладки
Ниже приведен пример Java класса, который мы собираемся отладить и попытаться понять различные доступные функции. Важно скомпилировать этот класс с параметром «-g» (javac -g Test.java), который генерирует всю отладочную информацию, включая локальные переменные. По умолчанию генерируется только информация о номере строки и исходном файле.
public class Test
{
public static void main(String[] args)
{
System.out.println("First Line of main function");
System.out.println("Second Line of main function");
System.out.println("Third Line of main function");
int i=0;
System.out.println("i: " + i);
i = 2;
System.out.println("i: " + i);
while(true)
{
}
}
}
Подключение jdb к java-приложению
Приведенная ниже команда - это наиболее распространенный способ запуска приложения с отладчиком jdb. Здесь мы не передаем никаких параметров jdb, мы передали только имя класса, который не требует никаких аргументов:
jdb Test
Таким образом, мы запускаем выполнение основного класса «Test» аналогично тому, как мы запускаем выполнение основного класс в среде IDE. jdb останавливает JVM перед выполнением первой инструкции этого класса.
Другой способ использовать команду jdb для подключения ее к уже запущенной JVM. Синтаксис для запуска JVM с портом отладчика:
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 Test
Чтобы подключить jdb к этому удаленному jvm, используйте синтаксис ниже:
jdb -attach 5005
В этой статье мы не будем подробно рассматривать удаленную отладку.
Отладка и мониторинг
Ниже приводится команда для присоединения jdb к Java-программе Test:
/jdk/bin/jdb Test
Initializing jdb ...
>
Установите точку останова в строке 5, используя команду «stop», как показано ниже:
> stop at Test:5
Deferring breakpoint Test:5.
It will be set after the class is loaded.
>
Запустите выполнение основного класса приложения с помощью команды "run":
> run
run Test
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
>
VM Started: Set deferred breakpoint Test:5
Breakpoint hit: "thread=main", Test.main(), line=5 bci=0
5 System.out.println("First Line of main function");
Выполнить текущую строку с помощью команды «step»:
main[1] step
> First Line of main function
Step completed: "thread=main", Test.main(), line=6 bci=8
6 System.out.println("Second Line of main function");
Выполнить текущую строку с помощью команды «step»:
main[1] step
> Second Line of main function
Step completed: "thread=main", Test.main(), line=7 bci=16
7 System.out.println("Third Line of main function");
Печать локальной переменной i с помощью команды "print":
main[1] print i
i = 0
Печать всех локальных переменных в текущем фрейме стека с использованием команды "locals":
main[1] locals
Method arguments:
args = instance of java.lang.String[0] (id=841)
Local variables:
i = 0
Выгрузите стек потока, используя команду "where":
main[1] where
[1] Test.main (Test.java:10)
Выдать список потоков в запущенном приложении, используя команду "threads":
main[1] threads
Group system:
(java.lang.ref.Reference$ReferenceHandler)804 Reference Handler running
(java.lang.ref.Finalizer$FinalizerThread)805 Finalizer cond. waiting
(java.lang.Thread)806 Signal Dispatcher running
(java.lang.Thread)803 Notification Thread running
Group main:
(java.lang.Thread)1 main running
Group InnocuousThreadGroup:
(jdk.internal.misc.InnocuousThread)807 Common-Cleaner cond. waiting
Продолжить выполнение с точки останова, используя команду cont
:
main[1] cont
> i: 0
i: 2
Все доступные команды jdb можно получить используя команду "help":
connectors -- list available connectors and transports in this VM
run [class [args]] -- start execution of application's main class
threads [threadgroup] -- list threads
thread -- set default thread
suspend [thread id(s)] -- suspend threads (default: all)
resume [thread id(s)] -- resume threads (default: all)
where [ | all] -- dump a thread's stack
wherei [ | all]-- dump a thread's stack, with pc info
up [n frames] -- move up a thread's stack
down [n frames] -- move down a thread's stack
kill -- kill a thread with the given exception object
interrupt -- interrupt a thread
print -- print value of expression
dump -- print all object information
eval -- evaluate expression (same as print)
set = -- assign new value to field/variable/array element
locals -- print all local variables in current stack frame
classes -- list currently known classes
class -- show details of named class
methods -- list a class's methods
fields -- list a class's fields
threadgroups -- list threadgroups
threadgroup -- set current threadgroup
stop [go|thread] []
-- set a breakpoint
-- if no options are given, the current list of breakpoints is printed
-- if "go" is specified, immediately resume after stopping
-- if "thread" is specified, only suspend the thread we stop in
-- if neither "go" nor "thread" are specified, suspend all threads
-- if an integer is specified, only stop in the specified thread
-- "at" and "in" have the same meaning
-- can either be a line number or a method:
-- :
-- .[(argument_type,...)]
clear .[(argument_type,...)]
-- clear a breakpoint in a method
clear : -- clear a breakpoint at a line
clear -- list breakpoints
catch [uncaught|caught|all] |
-- break when specified exception occurs
ignore [uncaught|caught|all] |
-- cancel 'catch' for the specified exception
watch [access|all] .
-- watch access/modifications to a field
unwatch [access|all] .
-- discontinue watching access/modifications to a field
trace [go] methods [thread]
-- trace method entries and exits.
-- All threads are suspended unless 'go' is specified
trace [go] method exit | exits [thread]
-- trace the current method's exit, or all methods' exits
-- All threads are suspended unless 'go' is specified
untrace [methods] -- stop tracing method entrys and/or exits
step -- execute current line
step up -- execute until the current method returns to its caller
stepi -- execute current instruction
next -- step one line (step OVER calls)
cont -- continue execution from breakpoint
list [line number|method] -- print source code
use (or sourcepath) [source file path]
-- display or change the source path
exclude [, ... | "none"]
-- do not report step or method events for specified classes
classpath -- print classpath info from target VM
monitor -- execute command each time the program stops
monitor -- list monitors
unmonitor -- delete a monitor
read -- read and execute a command file
lock -- print lock info for an object
threadlocks [thread id] -- print lock info for a thread
pop -- pop the stack through and including the current frame
reenter -- same as pop, but current frame is reentered
redefine
-- redefine the code for a class
disablegc -- prevent garbage collection of an object
enablegc -- permit garbage collection of an object
!! -- repeat last command
-- repeat command n times
# -- discard (no-op)
help (or ?) -- list commands
dbgtrace [flag] -- same as dbgtrace command line option
version -- print version information
exit (or quit) -- exit debugger
: a full class name with package qualifiers
: a class name with a leading or trailing wildcard ('*')
: thread number as reported in the 'threads' command
: a Java(TM) Programming Language expression.
Поддерживается весьма стандартный синтаксис команд.
Команды запуска могут быть помещены в файлы "jdb.ini" или ".jdbrc" в каталогах user.home или user.dir.
Выход из jdb:
quit
Заключение
OpenJDK предоставляет множество замечательных инструментов для устранения неполадок и диагностики Java-приложений. Эти инструменты помогут вам быстро исправить проблемы в рабочем приложении. jdb может быть большим подспорьем, когда нет другого способа, кроме отладки приложения, а ваша любимая IDE недоступна. Знание таких функций поможет вам улучшить задание Java.
Чтобы помочь вам, автор статьи написал электронную книгу 5 steps to Best Java Jobs. Вы можете загрузить это пошаговое руководство бесплатно!
sshikov
Автор преувеличивает. Почти всегда можно подключиться удаленно. Исключения в моей практике выглядят например так: есть кластер, ну скажем Hadoop, и ваше приложение запускается на неизвестном заранее узле. Вопрос тут не в том, чтобы подключиться из IDEA, а скорее в том, чтобы понять, куда именно из узлов, и какие порты там не заняты. Ну т.е. это скорее неудобно, а не невозможно — причем если вы решите через jdb это проделать, то у вас ровно такие же проблемы будут.
Hivemaster
Почти никогда нельзя, если в компании есть ИБ.
sshikov
При чем тут ИБ? У нас ИБ не запрещает доступ к прому (кому положено, т.е. 3 линии поддержки, в которую входят и разработчики тоже). И так было во всех компаниях, где я работал. А тут речь даже не идет о пром средах, а скорее о линукс машинах, где IDE не стоит. И тут ИБ вообще никаким боком не мешает.
sergey-gornostaev
Например в банках мешает. Ни у кого из разработчиков доступа к проду нет. Можно под залог жизни матери получить доступ к ИФТ, но для этого выдадут специальную машину, на которой можно запустить только ssh-клиент и подключить с него только к конкретному адресу и порту, да и то только в рабочее время и только через VPN с двухфакторной авторизацией по сертификату и OTP.
sshikov
А можно спросить, в скольких банках вы работали? Я с 2005 года поработал примерно на десяток-другой крупнейших банков РФ — и везде у меня был доступ к проду. И сейчас есть — и это тоже банк. И даже из дома. Совершенно не понимаю таких обобщений.
И вообще не понимаю, при чем тут пром? Если у вас нет доступа на хост — то запустить там jdb вам точно не дадут, как и подключиться отладчиком. То есть на то о чем автор пишет, это не влияет.
sergey-gornostaev
В Сбере так. Мои бывшие коллеги в Альфе и ВТБ ровно в тех же условиях.
sshikov
В Сбере НЕ так. Потому что я в Сбере работаю сейчас. И у нас доступ к прому есть по факту у всех разработчиков, QA, аналитиков — т.е. у всех, кому нужно.
sergey-gornostaev
Какой проект, если не секрет?
sshikov
В личку напишу. В смысле — я охотно верю, что где-то доступа нет. Просто не стоит это обобщать. У меня были проекты, где права были вплоть до рутовых. И конечно эти гайки постепенно закручиваются везде, по понятным причинам.
Hivemaster
Так вы же первый и обобщили. Я работаю в ритейле, а не в банке, но у меня тоже доступа к проду нет.
sshikov
Я не обобщал. Вы испытываете какие-то сложности с пониманием слов «почти всегда»?
Hivemaster
Испытываю сложность с пониманием почему ваше «почти всегда» — это не обобщение, а моё «почти никогда» — это обобщение?
sshikov
Так я и не говорил, что вы обощали. Я просто озвучил свой опыт работы в банках (ну т.е. я верю, что ИБ может запретить, в этом сомневаться глупо). И еще — автор не говорит только о пром средах, если вы перечитаете — он говорит о средах без UI (и даже явно пишет — пром и тестовые).
Ну т.е. подразумевается, что доступ к среде у вас есть, но UI нет. Вот это на мой взгляд и неправда. Если у вас есть доступ к среде — что вам помешает идею запустить удаленно? У меня всю мою практику работы на компании все тестовые машины были без мониторов, и без графического интерфейса — и это никогда не мешало запустить там скажем firefox, или идею, и отладчиком подключиться. Если вы можете зайти по ssh — почему вы не можете графические приложения запускать, какая тут связь? X server скажем кто-то отменил, который на машине клиента запускается?
А если вам ИБ запрещает доступ к пром среде — так авторская идея использовать там инструмент командной строки тоже выглядит глупо, потому что в пром среде вам не разрешат перезапустить приложение с включенной отладкой. Более того, скорее всего это вовсе даже не ИБ сделает, а просто поддержка.
YuryB
Ну значит ты везунчик без ИБ или сидишь на микропроекте. К примеру на моём где инфраструктура вся в США даже просто удалённого доступа для простых разрабов нет, чтобы запустить из консоли дебаггер, прямой коннект запрещён не то, что к базе, даже к некоторым серверам. хочешь что-то посмотреть - дуй в удалённый рабочий стол - citrix, от туда кстати и запускают шел для конекта (кому согласовали). со своей тачки у тебя есть прямой доступ только к стейджингу, бамбу, стэшу и спланку, и, кстати, я думаю, что только так и правильно. При этом я не могу сказать, что у нас прям супер продвинутый или защищённый заказчик, скорее люди просто выполняют рекомендации по защите инфраструктуры. Майкрософтовские креды кстати тоже смогли везде интегрировать, куда бы ты не захотел залогиниться - везде будут именно твои стандартные креды. Ну а если у вас не так, значит у вас дыра и проходной двор
sshikov
>или сидишь на микропроекте
Ну да, микропроект — в отделе примерно 750 человек, и еще так было в десяти предыдущих банках :) Если бы вы читали предыдущие комменты, вы бы увидели, что это не одиночный случай.
Нет, я просто сотрудник банка, и ИБ в том числе понимает, что для работы доступ нужен. Это кстати не значит, что он есть вообще у всех — а именно у тех, кому нужно по работе.
Hivemaster
Наши безопасники считают, что код должен быть всесторонне проверен разработчиками и QA на своих стендах прежде, чем попадёт в прод. Если надо отлаживать на проде, то разработчики и/или QA облажались. Кроме того, они считают, что чем меньше людей имеет доступ к проду, тем проще предотвратить или хотя бы обнаружить утечку персональных данных клиентов.
sshikov
>Наши безопасники считают, что код должен быть всесторонне проверен разработчиками и QA на своих стендах прежде, чем попадёт в прод.
Это вполне логично. Но не универсально на 100%.
>Если надо отлаживать на проде, то разработчики и/или QA облажались.
Или безопасники ошиблись в своих оценках. Например, в наших условиях нет возможности сделать тестовые стенды, хотя бы немного похожие на пром — ну или это очень дорого обойдется (даже не считая того, что большую часть времени они будут простаивать). А если даже это и сделать — то для поддержания их в актуальном состоянии придется удвоить размеры команд. Так что некоторые проблемы у нас воспроизводятся строго на проме, потому что только там есть правильные версии всех зависимостей.
>Кроме того, они считают, что чем меньше людей имеет доступ к проду, тем проще предотвратить или хотя бы обнаружить утечку персональных данных клиентов.
Ну и это логично. Поэтому доступ к прому не с рабочих машин, а с виртуалок, где все действия логируются и т.п. Причем я бы сказал, что это тоже всегда так было на практике — т.е. я могу вспомнить такое примерно в 2009.
YuryB
"Так что некоторые проблемы у нас воспроизводятся строго на проме, потому что только там есть правильные версии всех зависимостей."
ух, по-моему вы сами себя нарочно закапываете, учитывая сколько людей работает я вот никак не поверю, что никого нельзя загрузить в свободное время в конце спринта на приведение инфраструктуры в порядок. хотя вспоминаю как сам короткое время работал над российским банковским проектом и там тоже никто не парился насчёт мок сервисов например. трудозотраты небольшие, а страдали из-за их отсутствия все.
sshikov
Вы представляете себе интеграционный проект, в котором задействованы примерно пара сотен систем (у каждой из которых свои особенности)? Полноценный стенд для интеграции — это по факту N стендов, по одному от каждой интегрируемой системы. Я не могу повторить их стенд сам — для этого нужны специалисты от той системы, с которой мы интегрируемся. Загрузить в свободное время в конце спринта — это соседний отдел, который нам никак не подчиняется, между прочим. В каждом отдельном случае — еще один.
Я даже не могу поставить такую же версию Оракла — потому что текущая поддерживаемая например 12g, а на проме у коллег стоит 11g, и мне ее просто негде взять — так же, как и линуксы идентичной версии, «как на проме». В итоге тестового стенда Оракла 11g нет и не будет.
Я уже не говорю про то, что стенды разработки по железу примерно на порядок меньше промышленных. Вы предлагаете рядом поставить второй идентичный набор стоек? А платить кто будет, вы?
YuryB
платить будет банк, у него денег много :) всё что вы описали это по большому счёту технический долг, особенно про софт. если им не заниматься, то конечно придёте в ситуацию когда без дебагера в продакшен никак и не понятно как выруливать из всего этого.
YuryB
значит в вашем банке огромная дыра в информационной безопасности, вот и всё :) ну и безопасники ваши оказались слишком нежные, раз их получилось прогнуть на такое. есть кстати ещё разные тулы для понимания что как работает в связке, под аббревиатурой APM, у нас к примеру используется dynatrace, хотя опять, доступ по итогу дали только девопсам. в принципе за всё время работы я не могу вспомнить случая, когда мы бы не смогли по коду понять что не так в коде, достаточно логирования sql запросов, чтобы понять какие данные пришли, дальше глянуть по коду что получилось по итогу и почему. в принципе конечно было бы проще сразу дебагером тыкнуть на стейджинг дате и том, что репортали тестеры, но по факту пошевелив мозгами всегда понимали в чём дело ну за день точно. в крайнем случаи просто делали временные комиты в которых было логирование подозрительных участков.