giovedì 19 agosto 2010

Maxine VM & Eclipse

This post is a brief howto for the installation of the Maxine VM in order to be used from Eclipse. This post is not meant to substitute the official howto, but to add a few details on the installation instructions obtained from my experience in this process.
I personally did the following steps using my Ubuntu Linux 10.4 (kernel 2.6.32-22) x86_64, using Eclipse Helios (3.6), Mercurial 1.4.3, MercurialEclipse 1.6.0, CDT 7.0.0 and the JDK 1.6.0_20 from the Ubuntu repositories. The following is also the solution of my problem thread on the users mailing list.

Downloading the Maxine source tree
Maxine is organized into several projects, and in order to have a complete environment you have to check out all the projects. Being a Sun's project, Maxine exploits Mercurial (hg) to manage the source tree. In order to checkout the source tree you have to build a directory to contain all the tree, for instance maxine-hg (I've this convention of placing the name of SCM in the directory name since I use different SCM for different projects). Enter the above directory and clone the repository:

cd maxine-hg
hg clone https://kenai.com/hg/maxine~maxine maxine
The process will start downloading the source tree, this will take a while depending on the network speed. At the end of the process you will find a maxine directory into the maxine-hg one, inside the former you will find a directory for every project as shown below:


$ ls maxine-hg/maxine
Assembler  bin  C1X  CRI        JDWP       Native  TeleJDWP  VMDI
Base       C0X  CPS  Inspector  MaxineC1X  Tele    VM

Now it's time to compile the maxine vm in order to see if the system is ready to host it. In order to compile you need to set the JUNIT4_CP to the jar of Junit4 and JAVA_HOME to the home of the JDK (that on Ubuntu is /usr/lib/jvm/java-6-sun). So do the following:

export JUNIT4_CP=/sviluppo/java/jars/junit4.4jar
export JAVA_HOME=/usr/lib/jvm/java-6-sun
maxine-hg/maxine/bin/max build

If everything goes well, after a couple of minutes you will have the maxine machine compiled. This means that your system is ready to compile it and you are able to build it also in Eclipse. Do a max clean to clean all the compiled stuff and start Eclipse.


Importing the projects into Eclipse
Once the Maxine source tree is ready, it can be imported into Eclipse. The official Maxine/Eclipse HowTo states that you should do a plain import of the maxine container directory (maxine-hg/maxine in the above). This could generate some problems because such kind of import does a copy of the resource you are importing into the workspace. This means, as emphasized by the official guide, that you are working on an isolated source tree and that, after an update of the source tree itself, you should rei-mport the source tree again. But this could cause other problems: if you don't delete the projects removing their disk content too, your import will be incomplete and you'll be stumbled with strange compilation errors.
My suggestion is therefore to connect Eclipse to the Mercurial source tree:
  • select Import from the File menù;
  • instead of importing an existing Java project, import an existing Mercurial project from a local repository;
  • specify the maxine-hg/maxine directory as the root directory of the project, that is the Mercurial local repository. The dialog will show you all the projects to import, check all of them and if you are working with working sets select a working set to which the projects will be added (in my case Maxine-RVM). Click Finish to complete the import process;
  • when the import finishes, you will see the projects listed in the project explorer, and all the projects will be initially opened. Please note that after a while, depending on your computer speed, all the projects will be decorated with Mercurial informations, like the tip;

  •  now you can compile the projects from within Eclipse, but before that you have to specify the JAVA_HOME variable (or at least check it exists) as you did for the command-line compilation. This variable is needed for compiling the native code (Native project). To set it, open the Window, Preferences menu', go to the C/C++ preference page and select the building variables, then add the variable and point it to the JAVA_HOME you used for the command line compilation. The CDT will use gmake to compile the sources, using the JAVA_HOME variable defined here;

  • now you can start the compiling process, using the Project, Build All or Project, Build Working Set menu items. The compilation should complete without errors (but with around 1000 warnings). I had different compilation problems in the first trial:
    • cannot compile the Native project due to a missing jni.h: this means that the JAVA_HOME variable is pointing to a wrong location or that it is not set at all. Double check it!
    • inexistent imports or wrong access to methods/fiels: this means that your sources are not updated to the last version, or that you have done a wrong import over an existing one. Try removing all the projects from Eclipse (remove them also from the workspace) and re-import them again.
    • errors related to @Override annotations: there is something wrong with the project settings. Check if you enabled to report errors instead of warnings about such annotations.
  • once the compilation has been completed, you can generate the boot image. To do so, run the BootImageGenerator-default from the Run menu;

  • the image generator will start compiling the boot image, and you will see a lot of messages scrolling on the console. The production of the image will took around 2/3 minutes;
  •  you are almost done, before launching the VM or the Inspetor you have to check if the $JAVA_HOME directory includes a jdk directory, if not define a symbolic link to fake on the JDK installation layout:
cd $JAVA_HOME
sudo ln -s `pwd` jdk
  • now you can launch the Inspector using, for example, the MaxineInspector-HelloWorld from the Debug menu;
  •  after a while the Inspector window will appear, and the system will stop at a predefined breackpoint. Click on the Resume button of the Inspector GUI to complete the process. After a few seconds the Inspector menu bar will change color passing from green to red. This means that the thread has completed its execution, and scrolling on the Eclipse console you can see the "Hello World" message.


Problems launching the Inspector
During my tests I was unable to launche the Inpsector, having Eclipse to stop at a breackpoint and resulting in the following stack trace and debug information:


FATAL VM ERROR[1]: error in initializeSystemClass
Faulting thread: main[id=1]
------ Stack dump for thread main[id=1] ------
        -> com.sun.max.vm.runtime.FatalError.dumpStackAndThreadLocals(Lcom/sun/max/unsafe/Pointer;Z)V [0x43be38a0+101]
        -> com.sun.max.vm.runtime.FatalError.unexpected(Ljava/lang/String;ZLjava/lang/Throwable;Lcom/sun/max/unsafe/Pointer;)Lcom/sun/max/vm/runtime/FatalError; [0x43b8fce8+412]
        -> com.sun.max.vm.runtime.FatalError.unexpected(Ljava/lang/String;Ljava/lang/Throwable;)Lcom/sun/max/vm/runtime/FatalError; [0x43be8530+54]
        -> com.sun.max.vm.run.java.JavaRunScheme.initialize(Lcom/sun/max/vm/MaxineVM$Phase;)V [0x43be81a0+117]
        -> com.sun.max.vm.VMConfiguration.initializeSchemes(Lcom/sun/max/vm/MaxineVM$Phase;)V [0x43b92f78+233]
        -> com.sun.max.vm.run.java.JavaRunScheme.initializeBasicFeatures()V [0x43b9cd28+94]
        -> com.sun.max.vm.run.java.JavaRunScheme.run()V [0x43af4ba8+38]
        -> com.sun.max.vm.thread.VmThread.executeRunnable(Lcom/sun/max/vm/thread/VmThread;)V [0x43b936a8+208]
        -> com.sun.max.vm.thread.VmThread.run(Lcom/sun/max/unsafe/Pointer;Lcom/sun/max/unsafe/Pointer;Lcom/sun/max/unsafe/Pointer;)V [0x43af20f8+895]
        -> native{/sviluppo/java/src/maxine-hg/maxine/Native/generated/linux/libjvm.so (0x7fb0ff2e5000) at thread_run (0x7fb0ff2fd10a+390)}
------ Thread locals for thread main[id=1] ------
                              SAFEPOINT_LATCH: {E} 0xb94118  {D} 0xb94238  {T}
             SAFEPOINTS_ENABLED_THREAD_LOCALS: {E} 0xb94118  {D} 0xb94118  {T} 0xb94118
            SAFEPOINTS_DISABLED_THREAD_LOCALS: {E} 0xb94238  {D} 0xb94238  {T} 0xb94238
           SAFEPOINTS_TRIGGERED_THREAD_LOCALS: {E} 0xb93ff8  {D} 0xb93ff8  {T} 0xb93ff8
                         NATIVE_THREAD_LOCALS: {E} 0xb94358  {D} 0xb94358  {T} 0xb94358
                                 FORWARD_LINK: {E} 0  {D} 0  {T} 0
                                BACKWARD_LINK: {E} 0xb97118  {D} 0xb97118  {T} 0xb97118
                       AT_SAFEPOINT_PROCEDURE: {E} 0  {D} 0  {T} 0
                             EXCEPTION_OBJECT: {E} 0  {D} 0  {T} 0
                                           ID: {E} 0x1  {D} 0x1  {T} 0x1
                                    VM_THREAD: {E} 0x40fa4008 main[id=1]  {D} 0x40fa4008 main[id=1]  {T} 0x40fa4008 main[id=1]
                                      JNI_ENV: {E} 0x7fb0ff508540  {D} 0x7fb0ff508540  {T} 0x7fb0ff508540
                       LAST_JAVA_FRAME_ANCHOR: {E} 0x7fb100021e18  {D} 0  {T} 0
                                MUTATOR_STATE: {E} 0x1  {D} 0  {T} 0
                                       FROZEN: {E} 0  {D} 0  {T} 0
                                  TRAP_NUMBER: {E} 0  {D} 0  {T} 0
                     TRAP_INSTRUCTION_POINTER: {E} 0  {D} 0  {T} 0
                           TRAP_FAULT_ADDRESS: {E} 0  {D} 0  {T} 0
                          TRAP_LATCH_REGISTER: {E} 0  {D} 0  {T} 0
                   HIGHEST_STACK_SLOT_ADDRESS: {E} 0x7fb100023000  {D} 0x7fb100023000  {T} 0x7fb100023000
                    LOWEST_STACK_SLOT_ADDRESS: {E} 0x7fb0fffe4000  {D} 0x7fb0fffe4000  {T} 0x7fb0fffe4000
             LOWEST_ACTIVE_STACK_SLOT_ADDRESS: {E} 0  {D} 0  {T} 0
                          STACK_REFERENCE_MAP: {E} 0xb943a8  {D} 0xb943a8  {T} 0xb943a8
                         STACK_REFERENCE_SIZE: {E} 0x1008  {D} 0x1008  {T} 0x1008
                  IMMORTAL_ALLOCATION_ENABLED: {E} 0  {D} 0  {T} 0
                           INTERPRETED_METHOD: {E} 0  {D} 0  {T} 0
                       NATIVE_CALL_STACK_SIZE: {E} 0  {D} 0  {T} 0
                                     TLAB_TOP: {E} 0x7fb0df448000  {D} 0  {T} 0
                                    TLAB_MARK: {E} 0x7fb0df43f8b0  {D} 0  {T} 0
                                 TLAB_TOP_TMP: {E} 0  {D} 0  {T} 0
                                TLAB_MARK_TMP: {E} 0  {D} 0  {T} 0
                                TLAB_DISABLED: {E} 0  {D} 0  {T} 0
                           TLAB_REFILL_POLICY: {E} 0x7fb0df0d8000  {D} 0  {T} 0
                               ERROR_CONTEXTS: {E} 0x42792b10  {D} 0  {T: {E} 0  {D} 0  {T} 0
                       AT_SAFEPOINT_PROCEDURE: {E} 0  {D} 0  {T} 0
                             EXCEPTION_O


As you can see from the debug information, there is a link error because the library for amd64 cannot be found under the jdk directory. This is because the JDK installed by the Ubuntu repositories as a layout that is not the same of the official Java JDK. The solution, as detailed above, is to fake this layout creating a link to the jdk directory.

sabato 14 agosto 2010

JFK: implementing a raw yield return

A new feature has been added to JFK: a raw support for the yield return.
Before explaining it, please consider the cases when a yield return is useful: you are iterating over an iterator and want to return, one at a time, the next value extracted from the iterator.
Consider the following method:

public int cycle(){
    for( int i : this.myIterable )
        return (i*i);
   
    return 0;
}

Now, supposing myIterable is an integer iterable that returns numbers from 0 to 1000, the above method does not cycle on all the values, but returns always the first element in the iterator (so it is 0). The adoption of yield return allows the iterator to return the next value each time the method is called.
At the time of writing, JFK supports yield return when using iterators, and to get this support developers have to:
  1. use a class-level iterator (i.e., not in a local method scope);
  2. annotate the iterator with the @Yield annotation to inform JFK that the adoption of such iterator can be yielded. Since JFK must have access to the iterator, in the case the latter is private, developers can indicate in the annotation a get method for the iterator itself.
  3. ask JFK to build an instance that exploits the yield return.
The important thing to note in the above duties is that no special bareword or special objects must be used by developers: the methods are written as in normal Java. 
So, taking back the above cycle method example, supposing the myIterable
object has been annotatoted with the @Yield annotation, the following code will produce a progressive output:


IYieldBuilder yBuilder = JFK.getYieldBuilder();
// GoodYielder is an objet with the cycle method
GoodYielder gy = (GoodYielder) yBuilder.buildYielder( GoodYielder.class );
for( int i = 0; i < 10; i++ )
     System.out.println("Cycling with yield: " + gy.cycle() );

will produce

        Cycling with yield: 1
        Cycling with yield: 4
        Cycling with yield: 9
        Cycling with yield: 16
        Cycling with yield: 25
        Cycling with yield: 36
        Cycling with yield: 49
        Cycling with yield: 64
        Cycling with yield: 81
        Cycling with yield: 100
    

The current support to yield return is really minimal and useful for a limited set of use cases. I'm currently investigating a more complete and general approach, keeping into account that no syntactic or semantic overhead will be introduced: the yielding must be declarative!
In the meantime, if you need a more complete yielding mechanism (which is very different in syntax and semantic to JFK aim) have a look at Java Yielder here.