Sunday, March 17, 2019

JDB | Using Java Command Line Debugging Tool


Hello Friends, Welcome to my blog. Today in this blog we will be learning one of the tools provided by Java for the application debugging & inspection known as JDB. It is the command line tool which allows debugging local or remote java application. many developments tools like Eclipse, NetBeans uses JDB internally to debug remote or local the application.

  • JDB is a java command line debugging tool
  • It can debug local as well as the remote java application
  • It ships by default with JDK in the bin directory
  • You can use this tool if the debugging is enabled on a remote java application server, but the debugging port is not opened over internet/intranet due to security constraints and you have ssh access to the server or the jump host.
  • This tool has a rich set of commands which is enough to troubleshoot your problem.

So let's get our hand dirty and connect to already running application on the local server. I have added the Java debugging parameter before running my application. My spring boot application is running on localhost:8080 and debug port is 8001.

Since my application is local, am using the only port to connect. if you have a remote application, you can provide IP: Port from the jump box/bastion server. 

Connect Command
[root@localhost anilsable]# jdb -attach 8001
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
Initializing jdb ...
> 


Help Command, You will get Complete List of Supportable operations in JDB
> help
** command list **
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
.
.
.
.
Startup commands can be placed in either "jdb.ini" or ".jdbrc"
in user.home or user.dir




List all threads in the system
> threads
Group system:
  (java.lang.ref.Reference$ReferenceHandler)0x153                        Reference Handler                                    cond. waiting
  (java.lang.ref.Finalizer$FinalizerThread)0x152                         Finalizer                                            cond. waiting
  (java.lang.Thread)0x151                                                Signal Dispatcher                                    running
Group main:
  (java.lang.Thread)0x1236                                               ContainerBackgroundProcessor[StandardEngine[Tomcat]] sleeping
  (org.springframework.boot.web.embedded.tomcat.TomcatWebServer$1)0x1241 container-0                                          sleeping
  (org.apache.tomcat.util.net.NioBlockingSelector$BlockPoller)0x13e7     NioBlockingSelector.BlockPoller-1                    running
  (org.apache.tomcat.util.threads.TaskThread)0x13f1                      http-nio-8080-exec-1                                 cond. waiting
  (org.apache.tomcat.util.threads.TaskThread)0x13f8                      http-nio-8080-exec-2                                 cond. waiting
  (org.apache.tomcat.util.threads.TaskThread)0x13f7                      http-nio-8080-exec-3                                 cond. waiting
  (org.apache.tomcat.util.threads.TaskThread)0x13f6                      http-nio-8080-exec-4                                 cond. waiting
  (org.apache.tomcat.util.threads.TaskThread)0x13f5                      http-nio-8080-exec-5                                 cond. waiting
  (org.apache.tomcat.util.threads.TaskThread)0x13f4                      http-nio-8080-exec-6                                 cond. waiting
  (org.apache.tomcat.util.threads.TaskThread)0x13f3                      http-nio-8080-exec-7                                 cond. waiting
  (org.apache.tomcat.util.threads.TaskThread)0x13f2                      http-nio-8080-exec-8                                 cond. waiting
  (org.apache.tomcat.util.threads.TaskThread)0x13fb                      http-nio-8080-exec-9                                 cond. waiting
  (org.apache.tomcat.util.threads.TaskThread)0x13fc                      http-nio-8080-exec-10                                cond. waiting
  (java.lang.Thread)0x13fd                                               http-nio-8080-ClientPoller-0                         running
  (java.lang.Thread)0x13ff                                               http-nio-8080-Acceptor-0                             running
  (java.lang.Thread)0x1402                                               http-nio-8080-AsyncTimeout                           sleeping
  (java.lang.Thread)0x141b                                               DestroyJavaVM                                        running
> 


Adding Breakpoint to the method
> stop at org.javahotfix.threading.controller.SomeController:21
Set breakpoint org.javahotfix.threading.controller.SomeController:21
>


Executing URL which will hit above the breakpoint
curl -G http://localhost:8080/somecontroller?operationCode=A


You will be notified with information, whenever the breakpoint got hit.
You can notice here, the previously prompt sign was > and now it is replaced with the thread name when the breakpoint got hits. This means now whatever operation you are going to perform that will be on executing on that particular thread and execution flow.
> 
Breakpoint hit: "thread=http-nio-8080-exec-1", org.javahotfix.threading.controller.SomeController.performOperation(), line=21 bci=0

http-nio-8080-exec-1[1] 



Printing Objects, Local Variables
You can print the instance variable, objects, local objects, local variable using dump and print command. You can print all local variable using locals command.
http-nio-8080-exec-1[1] dump requestCode
 requestCode = "A"
http-nio-8080-exec-1[1] 


http-nio-8080-exec-1[1] print requestCode
 requestCode = "A"
http-nio-8080-exec-1[1] 



http-nio-8080-exec-1[1] locals
Method arguments:
requestCode = "A"
Local variables:
http-nio-8080-exec-1[1] 



Know the location where are you currently
This command will print the stack trace of the current thread. using this you can identify where are you right now.
http-nio-8080-exec-1[1] where
  [1] org.javahotfix.threading.controller.SomeController.performOperation (SomeController.java:21)
  [2] sun.reflect.NativeMethodAccessorImpl.invoke0 (native method)
  [3] sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
  [4] sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
  [5] java.lang.reflect.Method.invoke (Method.java:498)
  [6] org.springframework.web.method.support.InvocableHandlerMethod.doInvoke (InvocableHandlerMethod.java:209)
  [7] org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest (InvocableHandlerMethod.java:136)
  [8] org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle (ServletInvocableHandlerMethod.java:102)
  [9] org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod (RequestMappingHandlerAdapter.java:891)
  [10] org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal (RequestMappingHandlerAdapter.java:797)
  [11] org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle (AbstractHandlerMethodAdapter.java:87)

  [47] org.apache.coyote.http11.Http11Processor.service (Http11Processor.java:800)
  [48] org.apache.coyote.AbstractProcessorLight.process (AbstractProcessorLight.java:66)
  [49] org.apache.coyote.AbstractProtocol$ConnectionHandler.process (AbstractProtocol.java:806)
  [50] org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun (NioEndpoint.java:1,498)
  [51] org.apache.tomcat.util.net.SocketProcessorBase.run (SocketProcessorBase.java:49)
  [52] java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1,142)
  [53] java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:617)
  [54] org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run (TaskThread.java:61)
  [55] java.lang.Thread.run (Thread.java:745)



Expression Evaluation
Using the eval function, you can evaluate all the expression inside the context of the thread.
http-nio-8080-exec-1[1] eval requestCode == "A"    
 requestCode == "A" = false
http-nio-8080-exec-1[1] eval requestCode.equals("A");
 requestCode.equals("A"); = true
http-nio-8080-exec-1[1] 



Assigning Values
You can use the set command to assign new values to the variable while runtime.
http-nio-8080-exec-1[1] dump requestCode
 requestCode = "A"
http-nio-8080-exec-1[1] set requestCode = "B"   
 requestCode = "B" = "B"
http-nio-8080-exec-1[1] dump requestCode
 requestCode = "B"
http-nio-8080-exec-1[1] 



Execute the current line and move to the next line
This function will execute the current line and move the cursor to next line, if your current line has another function, call, it will go inside the function call. if you want to step over the line you can use next command.
http-nio-8080-exec-1[1] step
Step completed: "thread=http-nio-8080-exec-1", org.javahotfix.threading.controller.SomeController.performOperation(), line=24 bci=2

http-nio-8080-exec-1[1] http-nio-8080-exec-1[1] locals
Method arguments:
requestCode = "B"
Local variables:
timeDuration = 0
http-nio-8080-exec-1[1]


http-nio-8080-exec-1[1] step
> 
Step completed: "thread=http-nio-8080-exec-1", org.javahotfix.threading.controller.SomeController.getSomeServiceBean(), line=54 bci=0

http-nio-8080-exec-1[1] locals
No local variables
http-nio-8080-exec-1[1] 



Step Up and move to the caller of the function
This command will step up the execution to 1 level up so as it will reach to the caller of the function. 
http-nio-8080-exec-1[1] step up
> 
Step completed: "thread=http-nio-8080-exec-1", org.javahotfix.threading.controller.SomeController.performOperation(), line=30 bci=158

http-nio-8080-exec-1[1] 



Continue the execution by release hold thread by the debugger
This command will release the thread holder by the debugger, and continue its remaining execution.
http-nio-8080-exec-1[1] cont
> 



Clear will be used to release the breakpoint
This command will list all the breakpoints marked when executed without parameters. You can pass breakpoint location as a parameter to this command, this will clear the breakpoint being triggered in subsequent flows.
> clear
Breakpoints set:
 breakpoint org.javahotfix.threading.controller.SomeController:21
> clear org.javahotfix.threading.controller.SomeController:21
Removed: breakpoint org.javahotfix.threading.controller.SomeController:21
> 



Exit to the debugger console
This command will exit the debugger console, and detach the debugger to the remote application. 
> exit
[root@localhost anilsable]# 



So that's all for this tutorials friends, I hope you like these tutorials. Please provide your feedback, suggestion & queries in below comment box.

Till then goodbye
Happy Coding & have a wonderful day



3 comments:

  1. Hello, i read you post and successfully debugged my application on command line.
    Thank you for this post. It's awesome!

    ReplyDelete
  2. Hello, I've read this post, and solved my problem. Thank you very much.
    Did you have some detail bog to show how to load source java file when run 'list' in jdb?

    ReplyDelete
    Replies
    1. You need to have source jar on your classpath. that will resolve your problem.

      Delete