Posted by simonis
on January 24, 2008 at 4:33 PM PST
Here comes the second part of "HotSpot development on Linux with
NetBeans". While the first part focused on building and running
the different flavors (opt/debug, client/server JIT compiler, template/C++
interpreter) of the HotSpot VM on Linux/x86, this second part concludes
with a short evaluation of NetBeans 6.0 as an development environment for
Here comes the second part of "HotSpot development on Linux with NetBeans". While the first part focused on building and running the different flavors (opt/debug, client/server JIT compiler, template/C++ interpreter) of the HotSpot VM on Linux/x86, this second part concludes with a short evaluation of NetBeans 6.0 as an development environment for HotSpot hacking.
Developing with NetBeans
Now that we've successfully built the OpenJDK and know how we can efficiently build and run the HotSpot, we are ready to start using NetBeans (NB for short) for HotSpot development. Be sure to use at least version 6.0 if you want to do C/C++ development because starting with 6.0, NetBeans comes with builtin and highly improved support for C/C++ projects that supersedes the old "cpp" and "cpplite" modules which formed the NetBeans C/C++ Developer Pack (CND) in the past. There's now a special C/C++ Bundle available for download, that's only 11mb in size and contains all the needed plugins for C/C++ development.
After installation, be sure you install the Mercurial plugin by selecting Tools ->Plugins ->Available Plugins ->Mercurial. You need to have Mercurial installed and available in your PATH for the Mercurial plugin to work (see Prerequisites - Mercurial ). If your Mercurial executable isn't installed in a standard path or if you want to use a special Mercurial version with the NB Mercurial plugin you can configure this under Tools ->Options ->Versioning ->Mercurial ->Mercurial Executable Path.
You should note that if you are using NetBeans remotely (starting it on a remote host and setting the DISPLAY variable to your local machine), you'll probably want to set "-Dsun.java2d.pmoffscreen=false " in the NetBeans configuration file /etc/netbeans.conf. Without this setting, NB was so slow for me that it was effectively unusable (e.g. opening the "File" menu took about 20 seconds - every time!).
Cloning with NetBeans
The nice thing about using NB for HotSpot development is that it has builtin Mercurial support, so we don't need to use additional command line tools for version control. Although we could start right away with the available HotSpot sources from the previously cloned OpenJDK (see Cloning the OpenJDK sources ) and have Mercurial support for it, we will instead clone a brand new HotSpot repository from within NB to work with.
To achieve this we select Versioning ->Mercurial ->Clone Other.... In the appearing "Clone External Repository" wizard, we enter http://hg.openjdk.java.net/jdk7/jdk7/hotspot as the "Repository URL" and a local "Parent Directory" and "Clone Name" (usually hotspot) for the new clone (don't forget to adjust the proxy settings if necessary!). After clicking "Finish", NB will clone the HotSpot sources to /. For the rest of this blog I'll assume that the HotSpot sources have been cloned to /OpenJDK/jdk7/hotspot. Here's the content of the Mercurial Output window after a successful clone:
adding file changes
2895 files updated, 0 files merged, 0 files removed, 0 files unresolved
The number of output lines is greater than 500; see message log for complete output
INFO Clone From: http://hg.openjdk.java.net/jdk7/jdk7/hotspot
INFO To: /OpenJDK/jdk7/hotspot
INFO: End of Clone
Now that we have cloned a fresh repository, we should not forget to patch the sources with the patch file that has been previously described in order to work around some build problems.
Creating a HotSpot project
With the newly cloned HotSpot sources, we are ready to create a NetBeans project for HotSpot. We more or less follow the description of "Creating a C/C++ Project From Existing Code " in the NB Quick Start tutorial, but as you'll see, there are quite some intricate issues you need to consider here, in order to get a working HotSpot project.
First we start the "New Project" wizard by going to File ->New Project. In the "New Project" wizard we select "C/C++ Project From Existing Code" as project type. In the next step, we select "Using an existing makefile" and enter /OpenJDK/jdk7/hotspot/make/Makefile as the project makefile. In the following "Build Actions" step, we have to specify the project's "Working Directory". The presetting /OpenJDK/jdk7/hotspot/make (i.e. the directory that contains the makefile we've just specified in the previous step) will not work here because of a NB bug (see Issue 125214 ). We have to use the HotSpot root directory (i.e. /OpenJDK/jdk7/hotspot in our case) in order to work around this problem.
Next we have to define the right "Build Command" which is a little bit tricky, because we've just selected the HotSpot root directory as our working directory. So the first thing we have to do in the "Build Command", will be to change into the make/ directory. As real build command we can use a command line similar to the ones we used in the Building the HotSpot section. And although the build output will be captured in the NB "Build Output Window", it may be still a good idea to additionally store it in a file. So here's a first version of the "Build Command":
cd make && LANG=C ALT_BOOTDIR=/share/software/jse/1.6.0/ ALT_OUTPUTDIR=../../hotspot_c2_debug \
make jvmg 2>&1 | tee ../../hotspot_c2_debug.log
But wait, there's another problem in NB with VERY long lines in the "Build Output Window" (see Issue 124796 ): if a line exceeds 32Kb, the output will stop and the build will freeze! Unfortunately, the HotSpot Makefile indeed produces a VERY HUGE line: the command line which is created by the makefiles to compile the Java sources of the Serviceability Agent may exceed 100Kb (depending on the absolute base path of your HotSpot sources). With the additional filter part which is needed to strip this huge line from the output, our build command looks as follows:
cd make && LANG=C ALT_BOOTDIR=/share/software/jse/1.6.0/ ALT_OUTPUTDIR=../../hotspot_c2_debug \
make jvmg 2>&1 | grep -v "javac \-source 1\.4" | tee ../../hotspot_c2_debug.log
The clean command is easier - we just have to call the "clean" target. Nevertheless we have to specify ALT_OUTPUTDIR= on the command line such that make knows which directory we want to clean:
cd make && ALT_OUTPUTDIR=../../hotspot_c2_debug make clean
We leave the "Build Result" field empty for now, because the directories which will contain the build results don't exist until now anyway and NB will complain about this fact. Later on, after we have build the project for the first time, it will be possible to adjust this setting easily.
The next step in the "New Project" wizard requires the specification of the folders which contain files that should be added to the project. Again, the "Working Directory" specified in a previous step is the predefined default setting for this entry. Because we have already set the "Working Directory" to point to the HotSpot root directory, this presetting is ok for now. Later on we can (and we will) add additional source folders for the files which were automatically generated by the HotSpot make to the project. We can also restrict the "File types" to ".c .cpp .h .hpp .ad", because these file types are probably the only ones we will be interested in (.ad is the extension for so called "Architecture Description " files which are not real C/C++ files, but contain C/C++ code which is used to automatically generate some other source files, so it is probably a good idea to have them in the project).
We will skip the "Code Assistance Configuration" for now and come back later to it. In the final project configuration step we'll have to choose a name for our project (i.e. hotspot_NB) and a directory (the "Project Location") where NetBeans will save its project folder. Because I don't like to clutter my version controlled directories with external files, I usually place the project directory parallel to the project's root directory (i.e. into /OpenJDK/jdk7 in this example). But that's up to the user. It may be for example a good idea (once the things have settled down) to place a generic NetBeans project directory (you can find such pre-configured projects for Solaris here ) inside the HotSpot make directory. In such a case, NB could automatically detect and open the project during the cloning process (this is an option which can be activated with a check box in the Mercurial Cloning Wizard).
After we have created the new project, we can open the context menu of the project in the "Projects" view and select the "Build" action. This will start the HotSpot build and log all the build output to the "Build Output Window". The build command that was specified in the previous step will only build the server version of the HotSpot VM. If we want to build different versions of the VM (with C1 or C2 JIT compiler, with template or C++ interpreter, debug, optimized or product versions) we can create different project configurations for each version by selecting Set Configuration ->Manage Configurations... from the project's context menu. In the appearing "Project Properties" window, we press the Manage Configurations... button and get the "Configurations" window. Here we can rename our current configuration to "C2 debug" (because it builds a debug version of the HotSpot with the C2 server compiler) and make a copy of "C2 debug" which we rename to "C1 debug" (this will be the configuration for building a debug VM with the C1 client compiler). After we return to the "Project Properties" window by pressing OK, we can select the "C1 debug" configuration and adapt the build and clean commands accordingly (originally they are just copies of the corresponding commands from the "C2 debug" configuration). For building the "C1 debug" configuration, we could set them as follows:
cd make && LANG=C ALT_BOOTDIR=/share/software/jse/1.6.0/ ALT_OUTPUTDIR=../../hotspot_c1_debug \
make jvmg1 2>&1 | grep -v "javac \-source 1\.4" | tee ../../hotspot_c1_debug.log
cd make && ALT_OUTPUTDIR=../../hotspot_c1_debug make clean
With the knowledge from the Building the HotSpot section, it should be easy to create all the required, additional configurations accordingly. Notice however, that it may be a good idea to first complete the Code Assistance configuration described in the next section before cloning a configuration. This will save you a lot of time because most of the Code Assistance settings can be shared across configurations.
NetBeans Code Assistance
After we have successfully built the HotSpot project for the first time, it is necessary to refine the project settings. This is because the HotSpot is a quite unusual project in the sense that it has a different source structure than most open source C/C++ projects and therefore doesn't fit right away into a NetBeans project.
As you probably realized already, the HotSpot source tree contains sources for different operating systems and processor architectures. These different files often contain classes and functions with the same names for different platforms. This isn't a problem during the build because the build process figures out the current platform and only considers the needed files. But it considerably confuses the NB Code Assistance to have more than one class or method definition with the same name. Therefore, my advice is to remove the unused platform files from the project. This has the additional benefit of speeding up the parsing of a project at startup, because the project will contain considerably fewer files. If we build on Linux/x86, the following files and directories can be safely removed from the project (right click the corresponding file/directory items in the "Project View" and choose "Remove"):
In the past, the JDK contained the two distinct subdirectories i486 and amd64 for 32-bit and 64-bit x86 architectures respectively. Now, these two architectures have been merged into the single subdirectory x86. However, because the x86 subdirectory still contains files which are relevant for only one of the two architectures, we can also remove the following files from the project on a 32-bit Linux system:
Another peculiarity of HotSpot is the fact that it creates source files during the build process. These files are placed into the sub-folder __compiler<1|2>/generated of the specified output folder (i.e. /OpenJDK/jdk7/hotspot_c2_debug/linux_i486_compiler2/generated in our example). Among others, these are mainly the files generated by the ADL compiler from the .ad file in the __compiler<1|2>/generated/adfiles subdirectory and the include files generated by MakeDeps from the "includeDB" files in the __compiler<1|2>/generated/incls subdirectory. (Notice that the "includeDB" technique is another undocumented, HotSpot specific trickery that's beyond the scope of this introduction. You can find a minimalistic introduction at the top of hotspot/src/share/vm/includeDB_core )
In the next step, we will therefore add the to two subdirectories generated/adfiles/ and generated/incls/ from within the output folder to the sources of our project by selecting Add Existing Items from Folders... from the project's context menu. Don't forget to use .c .cpp .h .hpp .incl as "File types" because the include files generated by MakeDeps all have .incl suffixes. We'll also have to tell NetBeans to treat files with a .incl suffix like usual C/C++ header files by going to Tools ->Options ->Advanced Options ->IDE Configuration ->System ->Object Types ->C and C++ Header Data Objects and add incl as extension in the Extension and MIME Types field.
Once we have added all the additional files to our project, we can start configuring the NetBeans Code Assistance. There are two main points that have to be done here: first we have to specify the directories where the Code Assistance parser should look for include files and second we have to define the preprocessor directives which should be considered during code parsing. For the first step we have to go to Project Properties ->Code Assistance ->C++ Compiler ->Include Directories. Unfortunately it is only possible to add one directory at a time from the file selection box and there are quite some directories we have to add:
After we've added the required include directories, we have to tell the Code Assistance parser which preprocessor definitions it should use when parsing the project. For the time being, I just took a look at the compilation command line of some arbitrary HotSpot files, copied all the definitions they contained and inserted them into the Preprocessor Definitions field:
LINUX _GNU_SOURCE IA32 ASSERT DEBUG COMPILER2 COMPILER1 _REENTRANT VM_LITTLE_ENDIAN GAMMA
Because I was not sure if Code Assistance would parse .h as C or as C++ files, I doubled the values for Include Directories and Preprocessor Definitions in the C Compiler section. If you prefer typing instead of clicking, you can also edit the project's configurations file /nbproject/configurations.xml by hand and simply duplicate the entries inside the tag for the tag. As you can see, the enclosing tag contains all the relevant configuration settings for a project:
<conf name="C2_debug" type="0">
<buildCommand>cd make &&
make jvmg 2>&1 |
grep -v "javac \-source 1\.4" | tee ../../hotspot_c2_debug.log
<cleanCommand>cd make &&
ALT_OUTPUTDIR=../../hotspot_c2_debug make clean
<preprocessor>LINUX _GNU_SOURCE IA32 ASSERT DEBUG COMPILER2
COMPILER1 _REENTRANT VM_LITTLE_ENDIAN GAMMA
<preprocessor>LINUX _GNU_SOURCE IA32 ASSERT DEBUG COMPILER2
COMPILER1 _REENTRANT VM_LITTLE_ENDIAN GAMMA
Of course, manual changes to the project's configurations file should only be done if a project isn't active in a running NetBeans session! Notice that the parsing of a project like HotSpot may take a considerable amount of time - on my machine about 60 seconds. The progress of this operation is indicated by a small progress bar in the lower right corner of NB. Clicking on it will open a small window which logs the files that are currently processed by the Code Assistance parser. NetBeans parses a project every time the project is opened. Although this may take some time, I think this was the right decision, because this way there's no database file on disk that can get corrupted. If you should encounter problems with Code Assistance (see below), you'll just have to close and reopen the accountable project, to hopefully get it working again.
During my first configuration attempts, I sometimes got null pointer exceptions during code parsing (see issue 125611 ) and once Code Configuration didn't complete at all. However, once I figured out and set up the right Code Assistance settings (especially the right include paths and preprocessor directives), Code Assistance ran quite smooth. Especially the possibility of defining different project configurations with different preprocessor directives (opt vs. debug, template vs. C++ interpreter) and include paths and easily switching between these configurations while the source information is always accurate, is quite nice.
Debugging with NetBeans
After I had finally finished the "Code Assistance" configuration I was keen on trying the debugging support in NetBeans. I first went to the Project Properties ->Make and changed the Build Results entry to point to the gamma executable in the output directory (../hotspot_c2_debug/linux_i486_compiler2/jvmg/gamma in this example). Notice that this will always be a relative path with respect to your project directory if you choose the executable with the file selection box. The only way to enter an absolute path name here is by entering it manually into the text field (you'll have to do this if you choose to use another Run Directory than the default project directory in the next step).
In the Running category of the Project Properties, I entered "-XX:+TraceBytecodes -XX:StopInterpreterAt=1 -version" for the Arguments and added JAVA_HOME and LD_LIBRARY_PATH to the Environment with the values as described in Running the HotSpot . If you don't set the Run Directory, the NB project directory will be used as a default. If you choose another Run Directory you need to set Build Results in the Make category such that it contains the absolute path to the executable, in order to run it successfully.
With these settings, it was possible to run the HotSpot by executing the Run Main Project action. This will open a fresh xterm window, dump the executed bytecodes (because of the -XX:+TraceBytecodes option) and finally print out the version of the VM and the Java Runtime Environment:
 virtual void java.io.FileOutputStream.write(jobject, jint, jint)
 385353 0 fast_aload_0
 385354 1 aload_1
 385355 2 iload_2
 385356 3 iload_3
 385357 4 invokespecial 5376 <writeBytes> <([BII)V>
OpenJDK Runtime Environment (build 1.7.0-internal-debug-dXXXXXX_04_jan_2008_11_27-b00)
 virtual void java.io.FileOutputStream.write(jobject, jint, jint)
 390343 0 fast_aload_0
 390344 1 aload_1
 390345 2 iload_2
 390346 3 iload_3
 390347 4 invokespecial 5376 <writeBytes> <([BII)V>
OpenJDK Server VM (build 12.0-b01-internal-jvmg, mixed mode) 390348 7 return
392702 bytecodes executed in 229.6s (0.002MHz)
As you can see, printing the OpenJDK version requires the execution of nearly 400.000 bytecodes, so think twice before before you heedlessly call java -version the next time:)
Now that we successfully run the HotSpot within NetBeans, we can set up a breakpoint and try the debugger. Please be sure to use at least gdb 6.6, otherwise the NetBeans gdb-plugin will abort the debugging session because of warnings which are thrown by older versions of gdb when debugging the HotSpot. Notice that you can not specify which gdb version to use in NetBeans. Instead you have to set the executable path (under Tools ->Options ->C/C++ ->Build Tools ->Current Path) such that the desired version of gdb will be detected first by NB.
The -XX:StopInterpreterAt=<n> option can be used to stop the HotSpot interpreter before the execution of the specified bytecode number. It is implemented by continuously counting each executed bytecode until the n-th bytecode is reached at which point the VM will call the global, empty breakpoint() function. This breakpoint() function is a hook for the debugger which can be used to intercept the execution of the specified bytecode by defining a debugger, breakpoint for it. We choose Run ->New Breakpoint... and enter breakpoint as the Function Name. Then we execute Debug Main Project to debug the project. This time, only the first bytecode will be printed in the output window and the HotSpot will stop in the breakpoint() function:
VM option '+TraceBytecodes'
VM option 'StopInterpreterAt=1'
 static void java.lang.Object.<clinit>()
 1 0 invokestatic 2304 <registerNatives> <()V>
Unfortunately, that's basically all we can currently do with the debugger support in NetBeans. If we have a look at the stack in the Call Stack window, we can see something similar to:
#0 breakpoint ()
#1 0x0665d5f5 in os::breakpoint ()
#2 0x40246164 in ?? ()
#3 0xbfffb658 in ?? ()
#4 0x43316097 in ?? ()
#5 0xbfffb67c in ?? ()
#6 0x43375ab8 in ?? ()
The unknown frames are neither a debugger nor a NetBeans problem. It's just the result of the fact that the HotSpot is more than often in generated code. This is true for both interpreters, the template interpreter as well as for the C++ interpreter to a certain degree, and of course for compiled (JITed) code. Unfortunately, the NB debugger support can not handle assembler (e.g. there is no stepi command, no register window and no assembler code view). Moreover, signal handling is not implemented (crucial for HotSpot debugging) and the thread support doesn't work either. Generally speaking, the debugging support in NetBeans is quite basic right now and only useful for debugging not deeper than to the C/C++ level.
Probably I should have read the NetBeans GDB Debugger Milestones before starting. It lists all the missing features for future milestones. By private communication with some NB developers (thanks to Leonid Lenyashin and Sergey Grinev) I also got the information that the dbx-engine already has disassembler support, stepi, a register view and it works well with multiple threads. However, the dbx-engine is currently only available in Sun Studio which at the time of this writing is still NetBeans 5.5.1 based (although there should be a new NB 6 based express release in couple of weeks). I did some quick experiments and the dbx-engine looks indeed promising, but dbx had problems with the debug information generated by g++, so I gave up. Perhaps I'll try again with the next Sun Studio release that can read my NetBeans 6 project files.
I also had the chance to ask Gordon Prieur , a Sun staff engineer and project lead for Sun Studio IDE some questions about the status of the NetBeans gdb debugging engine. Following you can find his answers:
- NetBeans has no gdb command line (how do I send a command (like for example "stepi" or "x /8i $pc") that is not supported from the Debugger Menu or Debugger Toolbar to gdb)
- There are 2 issues in this question. One has to do with the gdb module missing features. The other with adding command-line support. I'll answer these separately.
- o Missing features (specifically "stepi" and "x ..."): There is currently no support in the module for assembly level debugging. Some assembly support is being added in NB 6.1 (Leonid can give you more details about this than I can) but assembly level debugging won't make it into 6.1. Hopefully it will be part of 7.0. I would expect stepi and x type commands would be part of this.
- o GDB Command-line: This is unlikely to ever be publicly available. The problem is that too many gdb command-line commands change state which the NetBeans gdb module wouldn't know about. To correctly support this we'd either need to process all typed commands before sending them to gdb, or to have gdb tell us any time a command-line change affects us.
- The "Thread View" is not working (it shows no output at all)
- This feature is being added in NB 6.1
- There is no register windows that displays the native registers (and I can not print them manually either because of point 1.)
- A register window has been considered but is not yet planned for a specific release. Its very possible it will be done when we add assembly debugging support.
- If I get a warning from gdb (e.g. "Previous frame inner to this frame (corrupt stack?)") the NB debugging session is aborted (see issue 125932 ). This should be handled more gracefully.
- In general, errors/warnings like this happen when gdb (the debugger, not the gdb module) dumps core or is no longer usable. I don't see any more graceful way of handling a gdb core dump. I'm definitely open to suggestions on how it can be handled...
Congratulation! If you really read all this blog from the beginning up to here please drop me a note . Hopefully you managed to successfully build and run the HotSpot on Linux!
Although this tutorial got quite lengthy in the end, building and running an own version of OpenJDK and the HotSpot VM should be not to hard for any developer with average Linux experience. Depending on the Linux distribution, there will probably always be the need to install some newer and sometimes even some older version (e.g. gcc) of some packages to meet the build requirements of the OpenJDK - but that should be manageable.
Regarding NetBeans, there's no clear recommendation from my side. If you're already familiar with an IDE like NetBeans, it my be worth while going through all the project configuration hassle to get the integrated Mercurial support and a reasonably well working Code Assistance - but you will still have to use the command line for debugging. On the other side, if you're already using Emacs with Cscope and DVC there's probably no killer argument for switching to NetBeans for HotSpot development.
 Kelly O'Hair's Build Cheat Sheet
 Kelly O'Hair's Glossary for JDK Builds
 OpenJDK Build Readme
 NetBeans C/C++ Support Quick Start Tutorial
 Interview with Gordon Prieur about Sun Studio 12