In the process of software development, debugging can be said to be a basic skill. The English word for debugging is debug. As the name implies, it means to remove bugs. As the saying goes, programming is the process of creating bugs, so the importance of debug is beyond doubt. If you can master debugging skills, you can quickly locate bugs in the code. It is important to understand that reading code does not necessarily mean writing code, and writing code does not necessarily mean debugging code. In order to write bug-free code, we must master some basic debugging skills. \

To do a good job, he must sharpen his tools. Whether your development tool is IntelliJ IDEA or Eclipse, the debugger is standard. When encountering a program with a problem, the proper use of the debugger trace and breakpoint skills can quickly locate the cause of the problem. Log is not a debugging tool. Do not use system.out.println as a debugging tool in the development environment. Master the debugging skills of the debugger is the right way.

First, actual combat IDEA debugging skills

If you’re a Java developer, you probably haven’t heard of IntelliJ IDEA. Like most Java developers, I started with Eclipse, but since I switched to IDEA, I can’t live without it. Completely become a big fan of IDEA (sorry, a wave of ads..) .

I have to say that JetBrains, a software company from The Czech Republic, is really a sincere enterprise. All its products are excellent. In addition to IDEA, there are WebStorm, PhpStorm, PyCharm, etc., all of which have similar styles.

Open the debugging panel of IDEA, as shown in the figure below, which can be roughly divided into five parts:

  • Single-step tracking
  • Breakpoint management
  • Evaluation expression
  • Stacks and threads
  • Variable to observe

1.1 Single step tracking

When it comes to debugging, it is estimated that many people’s first reaction is to trace the application step by step. In fact, IDEA provides many shortcut keys to help us trace the application.

  • Show Execution Point

Navigate Back; Navigate Back; Navigate Back; Navigate Back; Navigate Back; Navigate Back; It is relatively easy to use this technique to quickly locate the line of code that the debugger is currently executing.

  • Step Over

This is the most basic single step command, executing one line of code at a time, and skipping it if it has a method, literally one step at a time.

  • Step In / Force Step In

Step Over skips the execution of the method. You can observe the return value of the method, but if you need to Step inside the method to observe the execution details of the method, you need to use Step In. In addition, the Step In command skips the JDK’s built-in system methods. To track the execution details of system methods, use the Force Step In command.

Which system methods to ignore when Stepping can be configured in the configuration items Settings -> Build, Execution, Deployment -> Debugger -> Stepping of IDEA, as shown in the figure below.

  • Step Out

When you use the Step In command to trace the inside of a method and find that you don’t want to call the method anymore, you can simply finish the method and stop on the line next to the method. This is the Step Out command.

  • Drop to Frame

This is a debugger killer. During single-step debugging, if the execution of the key code is not seen due to careless single-step, for example, to locate the value of an intermediate variable, it would be nice to go back to that key code and execute it again. Drop to Frame provides this capability. It can go back to the method call (unlike Step Out, which goes back to the next line of the method call), allowing us to debug the method again, this time without being careless.

As the name suggests, Drop to frame removes the top stack frame (the currently executing method) and sends the program back to the previous stack frame (the parent method). As you can imagine, this only restores local variables in the stack. If there are operations on global variables in the method, there is no way to do it again.

  • Run to Cursor / Force Run to Cursor

These two commands are very useful when we need temporary breakpoints. For example, we already know which line of code we want to analyze, but we do not need to set a lot of unnecessary breakpoints. We can use this command to execute a row directly, and even ignore all breakpoints to reach the place we want to analyze.

1.2 Breakpoint Management

Breakpoints are one of the basic functions of the debugger. They allow the program to pause where it is needed to help analyze the process of the program. Breakpoint management in IDEA is shown in the figure below. Using the breakpoint technique properly can quickly stop the program where we want to stop it:

Breakpoints can be divided into two types: line breakpoints, which pause at a specific line of code, and global breakpoints, which pause when a condition is met, not limited to a fixed line, such as stopping the program when an exception occurs.

1.2.1 line breakpoints

  • Suspend (All / Thread)
  • Condition

Conditional breakpoints. This is a skill that should be learned by every developer who uses a debugger. When traversing a large List or Map, like a thousand Person objects, you can’t call every one of them. You might just want to break the conditional breakpoint when Person. name = ‘Zhangsan’, as shown below:

  • Log message to console
  • Evaluate and log

Some people may be surprised to see the Suspend option above, but I’m setting a breakpoint just to stop the program. Why do you need this option? Isn’t it a bit redundant? Do you want to make a breakpoint but not stop the program?

I was wondering about this until I discovered the Evaluate and log technique, until I suddenly realized how useful Suspend Off + Evaluate and log really was. As mentioned earlier, don’t use system.out.println as a debugging tool, because you can use this technique to print all the information you want without modifying your source code.

  • Remove once hit

One-time breakpoint. Run to Cursor, described above, is an example of a one-time breakpoint.

  • Instance filters
  • Class filters
  • Pass count

I don’t use a lot of these, but it’s probably a very useful trick to write down. In IDEA every object has an Instance ID. Instance filters are used to break when the Instance of the code at the breakpoint matches the specified ID. Pass count pauses at the number of times the breakpoint has been executed.

1.2.2 Global Breakpoints

  • Exception breakpoints
  • Method breakpoints
  • Field watchpoints

Personally, I feel that these techniques are not very common, so if you are interested in them, please try them on yourself.

1.3 Evaluation expression

Next to a bunch of buttons for single step tracking is an unobtrusive button called “evaluation Expression.” It is useful for debugging, you can view the value of a variable, you can evaluate the value of an expression, and you can even evaluate your own code, which corresponds to the following two different modes:

  • Expression Mode
  • Code Fragment Mode

These modes are similar to Expression View and Display View in Eclipse. In Display View, you can also write a code to execute it, which is really powerful, but note that you can only write code snippets, not custom methods, as shown in the following image:

1.4 Stacks and threads

One view can see all the current threads, and the other view can see the current function stack. In the Thread view, you can perform Thread dump to analyze what each Thread is currently doing. Stack view can switch the stack frame, combined with the variable observation area on the right, you can easily view the local variables and parameters in each function.

  • Thread view
  • The stack view

1.5 Observation of variables

Variables and observations can be displayed together or separately (as shown below). I prefer to display them separately so that local variables, parameters, and static variables are displayed in the variables area and the expression to be observed is displayed in the observations area.

The observation area is similar to the Expression mode in the evaluation Expression. You can add the Expression to be observed and see the value of the Expression in real time during debugging. The contents of the variable area are relatively fixed, and the values change as the left stack frame adjusts. You can also change the value of the variable.

Second, use JDB command line debugging

I believe many people have heard of GDB, which can be said to be the originator of debugging, in the past when learning C/C++, is to use it to debug programs. Like GDB, JDB is a command-line version of the debugger for debugging Java programs. And JDB doesn’t need to be installed and downloaded, it comes with the JDK (in the JDK bin directory, not in the JRE).

Each research a new technology, I always look for the command line version of the tool can replace, operating under the command line gives a person a kind of steadfast feeling, each command, each parameter, there are clear, this compared with a graphical interface tools, can learn a deeper knowledge, rather than the technical details hidden in the graphical interface, You can see that every parameter on the command line, every configuration, is a learning point.

2.1 Basic JDB Commands

To debug the Java program in JDB, use the JDB Test command to load the program.

After running the JDB Test command, the program does not run at this point, but sits there waiting for further commands. At this point we can figure out where to set a breakpoint, such as at main(), and then run the program using the run command:

> stop in test. main Delaying the breakpoint test. main. Will be set after the class is loaded. > run Run Test set uncaptured java.lang.Throwable set delayed uncaptured java.lang.Throwable > VM started: Set delayed breakpoint: test.mainCopy the code

This breakpoint is called a “delayed breakpoint” and is not set until the program is actually running, when the JVM starts. In addition to the stop in class. Method command, you can also set breakpoints using the stop at Class:LineNumber command.

main[1] stop at Test:25
Copy the code

In JDB, there are no conditional breakpoints or Instance filters that are supported in IDEA. At the breakpoint, you can use the list command to view the code near the breakpoint, step, print or dump to print the values of variables or expressions, locals to view all the variables in the current method, and cont to continue executing the code.

There are a few other commands that I won’t cover, but you can use help to see a list of all the commands, or refer to the official JDB documentation.

2.2 Explore the class file structure

When debugging a Java program in the JDB, it is possible that the source file and the class file are not together. In this case, you need to specify the source location:

# jdb -sourcepath path/to/source Test
Copy the code

If you do not specify the source location, the list command will prompt you that the source file is not found. If there is no source file, then the JDB is completely blind. We know that Java code runs in the JVM as bytecode when it is executed, so we can guess that there must be some information associated with the source code in the class file, similar to the OBJ file in C/C++. How else could the list command show which line of code is currently executing? We can use the open source Jclasslib software to view the contents of the class file. A standard class file contains the following information:

  • The basic information
  • Constant pool
  • interface
  • attribute
  • The parent class
  • field
  • methods
    • Code attributes
      • The line number attribute
      • Local variable scale

One of the most important parts is the Code attribute, as shown in the figure below. Below the Code attribute is the LineNumberTable line number attribute, which the debugger uses to correlate bytecodes with source Code. For class files, see:

Ginobefunny.com/post/deep_i…

Off-topic: How to debug without source code?

Without the source code, it is possible to use step in the JDB, but there is no way to show the code currently running, which is almost blind tuning. Bytecode Debugger (JBCD) ¶ Bytecode Debugger (JBCD) ¶ Bytecode Debugger (JBCD) ¶

Reverseengineering.stackexchange.com/questions/7…

Third, about remote debugging

We are getting closer and closer to the truth about the Java debugger through studying JDB, but there is one final step left. Let’s take a look at how Java programs are debugged in IDEA. If you are curious, you may have discovered the following secrets while debugging in IDEA:

Or when debugging Tomcat, there is a similar set of parameters that seem like a magic spell:

Once you understand the parameters, you break the Spell of the Java debugger and realize what the Java debugger really is:

"C: \ Program Files \ Java \ jdk1.8.0 _111 \ bin \ Java" -agentlib:jdwp=transport=dt_socket,address=127.0. 01.:20060,suspend=y,server=n Foo
Connected to the target VM, address: '127.0.0.1:20060', transport: 'socket'
Copy the code

There are two key points:

  • Java programs run with-agentlibThe -agentlib argument is not the same as the -JavaAgent argument. This library should be a native program written in C/C++ (JNI), similar to JDWP here. On Windows it corresponds to a JDMP. DLL library file and on Linux it corresponds to a JDMP. so file. So what does this JDWP library file actually do? And what does the string of arguments after it mean?
  • The Connected to the target VM indicates that the debugger is Connected to a network address. The Connected to the target VM indicates that the debugger is Connected to a network address. So what exactly is this address? The IP address is 127.0.0.1 for local debugging. Is it also supported for remote debugging?

In the Run/Debug Configuration page of IDEA, you can also add a remote debugging, as shown in the following interface, you can find the above spell parameters appear again:

With these questions in mind, let’s take a look at the basics of the Java debugger before we actually start remote debugging.

Java debugging principle and JPDA introduction

In the world of martial arts, the world martial arts can be divided into two kinds: one pays attention to the novelty of moves, can take the enemy by surprise, good at using the characteristics of weapons and their own sophisticated techniques to attack the enemy unprepared; Another pay attention to internal work mind method, even the most common moves, combined with their own deep internal forces, when the move can also have the potential of thunder. In fact, in the world of technology, martial arts can also be divided into two types: skills and principles.

All the above mentioned, whether you have mastered all the debugging techniques of IDEA, or have memorized all the commands of JDB, are just changes in the way. Let’s look at the internal workings of the debugger.

4.1 JPDA

We know that Java programs are running on the JVM. To debug a Java program, we actually need to ask the JVM for the current state of the running state, and issue certain instructions to the JVM, or receive callback from the JVM. In order to debug Java programs, the JVM provides a set of tools and interfaces for debugging, which together we call JPDA (Java Platform Debugger Architecture).

This system provides developers with a set of APIS for debugging Java programs, is a set of interfaces and protocols for developing Java debugging tools. Essentially, it’s a gateway, a set of tools, to the virtual machine, to see how it works.

JPDA consists of three relatively independent levels and defines how they interact. The three layers from bottom to top are Java Virtual Machine Tool Interface (JVMTI), Java Debug Line Protocol (JDWP), and Java Debug Interface (JDI), as shown below (image from IBM developerWorks) :

These three modules break down the debugging process into several natural concepts: the debugger and the debuggee, and the communicator between them. The debugger runs on the Java VIRTUAL machine we want to debug. It can monitor the information of the current VIRTUAL machine through the standard interface JVMTI. The debugger defines debugging interfaces that can be used by users. Through these interfaces, users can send debugging commands to the vm to be debugged, and the debugger receives and displays the debugging results.

Between debugger and debugger, debugging commands and debugging results are transmitted through JDWP communication protocol. All commands are encapsulated into JDWP command packages and sent to the debugger through the transport layer. After receiving the JDWP command packages, the debugger parses the commands and converts them into JVMTI calls to run on the debugger. Similarly, the results of a JVMTI run are formatted as JDWP packets, sent to the debugger and returned to the JDI call. Debugger developers get data and issue instructions through JDI.

For details, please refer to

www.ibm.com/developerwo…

4.2 Connectors & Transport

So far, we’ve seen that JDWP is a communication protocol between the debugger and the program being debugged. JDWP: JDWP =transport=dt_socket,address=127.0.0.1:20060,suspend=y,server=n

In fact, this place on the JDMP. DLL library file JDI, JDWP, JVMTI three parts series into a whole, it can not only call the local JVMTI provided by the debugging ability, but also realize the JDWP communication protocol to meet the communication between JVMTI and JDI.

To fully understand this string of parameters, you need to learn two more concepts: Connectors and Transport.

There are five common connectors, which refer to the way JDWP establishes connections:

  • Socket-attaching connector
  • Shared-memory attaching connector
  • Socket-listening connector
  • Shared-memory listening connector
  • Command-line launching connector

The attaching connector and listening connector is the difference between the debugger as a server, or be debugged as a server.

Transport refers to the JDWP method of communication. Once a connection is established between the debugger and the program being debugged, they need to communicate with each other. There are currently two methods of communication: sockets and shared-memory (Shared memory, only for Windows).

4.3 Actual combat remote debugging

Java debuggers and debugators run on a C/S architecture, with one end being started as a server and the other connected as a client. If the debugger is running as a server, the following command line arguments must be added:

# java -agentlib:jdwp=transport=dt_socket,server=y,address=5005 Test
# java -agentlib:jdwp=transport=dt_shmem,server=y,address=javadebug Test
Copy the code

Socket requires a port number that the debugger uses to connect to it. Shared memory requires a connection name, not a port number.

After the program is up and running, you can attach the debugger to the program using JDB’s -attach parameter:

# jdb -attach 5005
# jdb -attach javadebug
Copy the code

IOException: shmemBase_attach failed: Java.io.IOException: shmemBase_attach failed: java.io. The system cannot find The file Specified The system cannot find The file Specified because JDB-Attach used The system default transfer to establish The connection, and The default transfer on Windows is shared memory. To connect to a socket on Windows, use the jdb-connect command. The parameters of the command are not well written.

# jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=5005
# jdb -connect com.sun.jdi.SharedMemoryAttach:name=javadebug
Copy the code

If, conversely, you want the debugger to run as a server, execute the following command:

# jdb -listen javadebug
Copy the code

The Java program then connects to the debugger with the following arguments:

# java -agentlib:jdwp=transport=dt_shmem,address=javadebug, suspend=y Test
# java -agentlib:jdwp=transport=dt_socket,address=127.0. 01.:5005,suspend=y,server=n
Copy the code

Finally, we go back to look at the string of magic parameters printed by IDEA. We can guess boldly that IDEA starts the debugger in the form of a server and listens on port 20060. Then Java programs connect to this port through socket communication. And suspend the JVM for debugging.

"C: \ Program Files \ Java \ jdk1.8.0 _111 \ bin \ Java" -agentlib:jdwp=transport=dt_socket,address=127.0. 01.:20060,suspend=y,server=n Foo
Connected to the target VM, address: '127.0.0.1:20060', transport: 'socket'
Copy the code

If you are doing remote debugging under IDEA, you can refer to another debugging related topic on IBM developerWorks: Remotely debugging Java applications using Eclipse.

conclusion

This article first introduces some common debugging skills of IDEA, and then through the use of JDB for Java program debugging, learning the common commands of JDB, finally through remote debugging leads to the topic of debugger principle, to JPDA, JVMTI, JDWP, JDI and other concepts have a preliminary understanding. From moves to heart, from skills to principles, gradually unveiled the mystery of the Java debugger.

For developers, if only know the tricks, only some strange skills, so he just use tools more handy, it is difficult to get qualitative breakthroughs in technology; And if only understand the mind, only immersed in basic principles and theories, then he can only do a high-eyed and low-handed academic school, empty full of great truth but useless. We should be both inside and outside repair, the moves and the heart method together, in order to achieve mastery.

Finally, the topic of debugging has to be added: debugging programs is a time-consuming and laborious process, and when debugging is needed to locate problems, the logic and clarity of the code is problematic, the best code does not need debugging. So less debugging, more unit testing, more refactoring, and clearer code is the best way to program. More IDEA using skills, Java bosom friend public account inside reply “IDEA aggregation”, send you a complete tutorial

About the uncertainty effect of debuggers

In quantum physics, there is a noun called uncertainty principle, also known as uncertainty principle, which states that the position and momentum of a particle cannot be determined at the same time. The smaller the uncertainty of the position, the greater the uncertainty of the momentum, and vice versa.

The white point is, if you want to measure the position of the particle accurately, you can’t measure the momentum of the particle accurately; If you want to measure the momentum of a particle very accurately, then the position of the particle cannot be determined; It is the measurement itself that causes the system to be affected.

Applying this phenomenon to the debugger world has a similar effect. Because of the interference of the debugger itself, the program is not the same as before. So the question is, can you really trust the results when you run them under the debugger?

Here’s an interesting example I came up with. Suppose we set a breakpoint at line 4, what would be the output?

From: www.aneasystone.com/