Debugging a Java application when it is not waiting at all - welcome to the InTrace approach

Have you ever studied step by step the execution of some Java code that you did not bother to equip with logging tools or other monitoring mechanisms?
We complicate the task by the fact that we will not recompile the code under study, and indeed restart the existing process (the case when incomprehensible behavior was even floating when restarted). The Java machine, of course, is launched with the most common options (without parameters for the debugger attachment or some other bells and whistles).
And so I want to understand what is happening.

That is what we will do.

For an illustrative example, let's take a simple code, some semblance of the implementation of the good old well-known fairy tale “Turnip”. The purpose of the demonstration of the code below is not to show the beauty of organizing classes or methods, let alone naming (here, rather, disgrace), but simply to make a series of calls after calls.

     1: package skazka; 
     2: 
     3: import java.io.BufferedReader; 
     4: import java.io.InputStreamReader; 
     5: 
     6: public class Repka { 
     7:   public static void main(String[] args) throws Exception { 
     8:     new BufferedReader(new InputStreamReader(System.in)).readLine(); 
     9: 
    10:     try { 
    11:       new Dedka().tyanem("репка", 200); 
    12:     } catch (Exception ex) { 
    13:       processError(ex); 
    14:     } 
    15:     
    16:   } 
    17: 
    18:   public static boolean processError(Exception ex) { 
    19:     // ... 
    20:     return true; 
    21:   } 
    22: } 
    23: 
    24: class Dedka { 
    25:   void tyanem(String target, int weight) { 
    26:     new Babka().tyanem(target, weight - 15); 
    27:   } 
    28: } 
    29: 
    30: class Babka { 
    31:   void tyanem(String target, int weight) { 
    32:     new Vorona().tyanem(target, weight - 10); 
    33:   } 
    34: } 
    35: 
    36: class Vorona { 
    37:   void tyanem(String target, int weight) { 
    38:     if (!target.equals("сыр")) { 
    39:       throw new RuntimeException("Ворона ждёт от бога сыра"); 
    40:     } 
    41: 
    42:     // ... 
    43:   } 
    44: } 
    


Compile

javac skazka/Repka.java

And run

java skazka.Repka


Now you need to somehow "wedge" into an already running process and adapt the code to your needs. The Java agent created by Martin Robertson (for which many, many sincere thanks), called InTrace mchr3k.github.io/org.intrace , is perfectly suited for this , allowing it to be embedded into the executable code on the fly, providing it with tracing elements.

The InTrace agent is placed in an already running Java process in a couple of simple ways (to choose from):
1) programmatically
public static void loadAgent(String pid, String agentFilePath) throws Exception {
    VirtualMachine vm = VirtualMachine.attach(pid);
    vm.loadAgent(agentFilePath, "");
    vm.detach();
}


Having slipped the Java agent intrace-agent.jar process from github.com/mchr3k/org.intrace/tree/master/binaries/jars/latest_development

2), taking VisualVM (downloading, if there is no visualvm.java.net from here ) and patching it ( Tools-> Plugins) InTrace plugin org-intrace-visualvm.nbm taken from
github.com/mchr3k/org.intrace/tree/master/binaries/jars/latest_development The

trouble is that org-intrace-visualvm.nbm is not the best fresh files, so find where VisualVM stores plugins and replace the corresponding dzharniki with more recent ones from github.com/mchr3k/org.intrace/tree/master/binaries/jars/latest_development
In my case (Linux OS), I should replace
intrace-agent.jar on intrace-agent.jar and intrace-client-gui-linux.jar on intrace-ui.jar (see the screenshot below, the plugin tells the way)

Now it remains to run VisualVM, call the context menu for skazka.Repka and select “InTrace Application ...”, after which we see the


“Load InTrace Agent” button and the console will report on the InTrace implementation with a text similar to



The implementation stage has been successfully completed: the InTrace agent has successfully settled in the Java machine, ready to transform the byte code, supplying it with trace commands , and report the results.

We proceed to the stage of monitoring the result, for which we launch the agent management and analysis of results (a separate application).

Again, there are two simple options
1) we implemented the agent in the first “software way" then we launch
java -jar intrace-ui.jar  

taken here
github.com/mchr3k/org.intrace/tree/master/binaries/jars/latest_development
2) in case of using VisualVM, click the “Launch InTrace Client” button, and, in principle, the exact same file will be launched as in the first example (since we have patched a new intrace-ui.jar)



We drive in the address, configure the parameters of the trace (I turned on Entry / Exit + Branch + Args), press the button “Classes ...” and specify skazka there (this will force tracing to be added to the classes from the skazka package).
We press “Enter” in the console with the running “Turnip” (do not forget that this is what she expects to continue) and in the end we will get the program execution chain on the client in the form of output:

[21:52:39.861]:[1]:skazka.Dedka:: {:24
[21:52:39.862]:[1]:skazka.Dedka:: }:24
[21:52:39.862]:[1]:skazka.Dedka:tyanem: {:26
[21:52:39.865]:[1]:skazka.Dedka:tyanem: Arg (target): репка
[21:52:39.866]:[1]:skazka.Dedka:tyanem: Arg (weight): 200
[21:52:39.868]:[1]:skazka.Babka:: {:30
[21:52:39.869]:[1]:skazka.Babka:: }:30
[21:52:39.871]:[1]:skazka.Babka:tyanem: {:32
[21:52:39.871]:[1]:skazka.Babka:tyanem: Arg (target): репка
[21:52:39.871]:[1]:skazka.Babka:tyanem: Arg (weight): 185
[21:52:39.878]:[1]:skazka.Vorona:: {:36
[21:52:39.878]:[1]:skazka.Vorona:: }:36
[21:52:39.878]:[1]:skazka.Vorona:tyanem: {:38
[21:52:39.879]:[1]:skazka.Vorona:tyanem: Arg (target): репка
[21:52:39.879]:[1]:skazka.Vorona:tyanem: Arg (weight): 175
[21:52:39.879]:[1]:skazka.Vorona:tyanem: /:39
[21:52:39.885]:[1]:skazka.Vorona:tyanem: Throw:39: java.lang.RuntimeException: Ворона ждёт от бога сыра
	at skazka.Vorona.tyanem(Repka.java:39)
	at skazka.Babka.tyanem(Repka.java:32)
	at skazka.Dedka.tyanem(Repka.java:26)
	at skazka.Repka.main(Repka.java:11)
[21:52:39.885]:[1]:skazka.Vorona:tyanem: }:39
[21:52:39.886]:[1]:skazka.Repka:main: /:13
[21:52:39.890]:[1]:skazka.Repka:processError: {:20
[21:52:39.890]:[1]:skazka.Repka:processError: Arg (ex): java.lang.RuntimeException: Ворона ждёт от бога сыра
[21:52:39.891]:[1]:skazka.Repka:processError: Return: 1
[21:52:39.891]:[1]:skazka.Repka:processError: }:20
[21:52:39.891]:[1]:skazka.Repka:main: }:16


Each output line is described in the following format
[текущее время]:[идентификатор потока]:пакет:метод:дополнительная информация

where:
current time - the
thread identifier speaks for itself - the unique identifier of the stream, different streams - different numbers (1, 2, 3, 4, ...)
package - namespace
method - name of the method in which
additional information is executed - depends on the situation.

Supported traces:
- input / output of the method
[21:52:39.861]:[1]:skazka.Dedka:: {:24
[21:52:39.862]:[1]:skazka.Dedka:: }:24

or
[21:52:39.890]:[1]:skazka.Repka:processError: {:20


- branches
[21:52:39.879]:[1]:skazka.Vorona:tyanem: /:39

- arguments (display of objects goes through toString ())
[21:52:39.865]:[1]:skazka.Dedka:tyanem: Arg (target): репка
[21:52:39.866]:[1]:skazka.Dedka:tyanem: Arg (weight): 200

Return Values
[21:38:31.444]:[1]:skazka.Repka:processError: Return: 1

- exceptions
[21:52:39.885]:[1]:skazka.Vorona:tyanem: Throw:39: java.lang.RuntimeException: Ворона ждёт от бога сыра
	at skazka.Vorona.tyanem(Repka.java:39)
	at skazka.Babka.tyanem(Repka.java:32)
	at skazka.Dedka.tyanem(Repka.java:26)
	at skazka.Repka.main(Repka.java:11)

In general, all that is needed to successfully understand the progress of the program.

Practice analyzing the output using the above example (something that may be immediately intuitive is not entirely clear, but if you figure it out quickly, you get used to it).

A vivid example of what the code turns into (how traces are arranged) can be found on the author’s page mchr3k.github.io/org.intrace/howintraceworks.html I

also advise you to immediately pay attention to the very useful “Filter ...” button, which helps to save from unnecessary garbage

Existing integration with Eclipse allows you to run a Java application with the InTrace agent already implemented, configure tracing parameters and analyze the result of work. See mchr3k.github.io/org.intrace/eclipse.html for more details .

TOTAL

Advantages:
1) Tracking program execution on the fly:
- method calls, arguments passed, branches, generated exceptions, ...
- without recompilation and even without restarting a running program
2) OpenSource code is available
3) Easy to configure and use
4) cross-platform solution (Windows, Linux)
5) the ability to integrate with popular development tools (Eclipse, VisualVM)

Disadvantages:
1) Change the bytecode on the fly:
- suffer from speed performance (productivity)
- the introduction of an agent "mutate" the current process, and he has Never attempt to be as before (twice not enter the same river)
- modifications of the byte code go “as is” and naturally “at your own peril and risk”
2) There is no mass support - only one developer, but is compensated by the small size of the project and the availability of source codes. I am glad that the last update in the repository was a couple of weeks ago from the publication of this post.
3) The product is well tested on Java 1.6, but not tested on Java 1.7 (and there are problems with it, see the bug github.com/mchr3k/org.intrace/issues/28 ). As far as I understand, the author uses only 1.6, wrote “for himself” and not at all for commerce, so who needs newer, do not be too lazy to take and screw

In conclusion: InTrace is an agent that definitely deserves acquaintance at least, and, perhaps, will become an indispensable tool for you, revealing Java application debugging from a different angle, and, undoubtedly, it will help out more than once.

Taking into account the comments and additions, I seek to unleash the power of InTrace (as well as a number of other interesting agents) in the JAVA hub and I will be very grateful for the help.

Thank you for your attention and be sure to share with those who may be interested!

Also popular now: