Tuesday Jun 17, 2008

Real-Time Java in a Zone

As is often the case, Sun's technologies and offerings are being applied in ways which we hadn't necessarily anticipated.  Yet another example has reared its head in the govenrment/military space where customers have expressed interest in using Sun's Java Real-Time System with Solaris Trusted Extensions.  As it stands right now, Java RTS will neither operate nor install within the confines of such an environment.  Let's investigate why this is so, and see what current opportunities there are for working around this shortcoming.

So what is it that causes Trusted Extensions and Java RTS not to play together nicely?   It happens to revolve around Trusted Extension's extensive usage of Solaris zones to limit access between differing security levels.  Solaris packages must be specifically configured to accommodate zones, which has yet to formally take place with Java RTS.  As zones are a core component of Solaris, we can, for the sake of simplicity, just use standard Solaris to demonstrate how we can work around this temporary limitation.  These modifications should apply to Trusted Extensions just as well.   To get Java RTS to work within a zone, follow these steps:

1. Install the Java RTS cyclic driver (only) in the global zone.

global# pkgadd -d . SUNWrtjc

Processing package instance <SUNWrtjc> from </cyclic/src/packages_i386>
## Installing package <SUNWrtjc> in global zone

Java Real-Time System cyclic driver(i386) 2.1.0,REV=2008.
Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
Use is subject to license terms.
Using </> as the package base directory.
## Processing package information.
## Processing system information.
   5 package pathnames are already properly installed.
## Verifying package dependencies.
## Verifying disk space requirements.
## Checking for conflicts with packages already installed.
## Checking for setuid/setgid programs.

This package contains scripts which will be executed with super-user
permission during the process of installing this package.

Do you want to continue with the installation of <SUNWrtjc> [y,n,?] y

Installing Java Real-Time System cyclic driver as <SUNWrtjc>

2. Create a zone called 'rtjzone':
global# mkdir -p /zone
bash-3.00# zonecfg -z rtjzone
rtjzone: No such zone configured
Use 'create' to begin configuring a new zone.
zonecfg:rtjzone> create
zonecfg:rtjzone> set zonepath=/zone/rtjzone
zonecfg:rtjzone> verify
zonecfg:rtjzone> commit
zonecfg:rtjzone> exit
global# zoneadm list -vc
  ID NAME             STATUS     PATH                           BRAND    IP
   0 global           running    /                              native   shared
   - rtjzone          configured /zone/rtjzone                  native   shared
global# zoneadm -z rtjzone install
Preparing to install zone <rtjzone>.
Creating list of files to copy from the global zone.
Copying <6984> files to the zone.
Initializing zone product registry.
Determining zone package initialization order.
Preparing to initialize <1074> packages on the zone.
Initialized <1074> packages on zone.
Zone <rtjzone> is initialized.
Installation of <1> packages was skipped.
The file </zone/rtjzone/root/var/sadm/system/logs/install_log> contains a log of the zone installation.

3.  Modify the zone to allow access to the cyclic device, and to allow additional privileges

global# zonecfg -z rtjzone
zonecfg:rtjzone> set limitpriv=default,proc_priocntl,proc_lock_memory,proc_clock_highres
zonecfg:rtjzone> add device
zonecfg:rtjzone:device> set match=/dev/cyclic
zonecfg:rtjzone:device> end
zonecfg:rtjzone> verify
zonecfg:rtjzone> commit
zonecfg:rtjzone> exit

global# zoneadm -z rtjzone reboot
Note:  One privilege that is useful with Java RTS is sys_res_config.  This is typically used to assign a real-time process to a processor set.  Unfortunately zones cannot currently be given this privilege.  You can however, from the global zone, assign a processor set to a zone, which might be a reasonable workaround.

4.  Get a copy of the SUNWrtjv package and modify it so that it will install in a zone.  The postinstall script and postremove script must replaced with those provided by these hyperlinks just mentioned.

rtjzone# cd /scratch
rtjzone# ls
SUNWrtjv	postinstall	postremove
rtjzone# cp postinstall SUNWrtjv/install/
rtjzone# cp postremove SUNWrtjv/install/

5. Modify the SUNWrtjv pkgmap file with the appropriate sizes, checksums and last access dates.  The source code for a sample C program, called pkgmap_info, which prints out the necessary information, can be found here.

rtjzone# cd SUNWrtjv
rtjzone# grep post pkgmap 1 i postinstall 5402 42894 1194344457 1 i postremove 2966 34854 1194344457 rtjzone# cp pkgmap_info.c /tmp rtjzone# cc -o /tmp/pkgmap_info /tmp/pkgmap_info.c rtjzone# cd /scratch/SUNWrtjv/install/ rtjzone# /tmp/pkgmap_info postinstall postinstall 5820 9488 1213727841 rtjzone# /tmp/pkgmap_info postremove postremove 3092 45039 1213727538
Replace the postinstall and postremove entries in the pkgmap file with those produced by the pkgmap_info program.  You cannot simply use the example data above because the last access times will not match.  Doing so will cause the install to fail.

6. Install the Java RTS SUNWrtjv package inside the zone.

rtjzone# cd /scratch
rtjzone# pkgadd -d . SUNWrtjv

Processing package instance <SUNWrtjv> from </scratch>

Java Real-Time System runtime environment(i386) 1.5.0_13_Java-RTS-2.0_01-b08_RTSJ-1.0.2,REV=2007.
Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
Use is subject to license terms.

Where should this package be installed? (/opt): /opt

To achieve predictable temporal behavior, the Java Real-Time System
must be granted access to a number of privileged Solaris resources.
By default, access to these privileged resources is only granted to
the superuser (root). They can also be granted to additional users
by creating a rights profile, that is, a collection of authorizations,
that can later be assigned to an administrative role or directly
to a user.

As part of this package installation, a local 'Java Real-Time System User'
rights profile can be created on this machine.
This rights profile should NOT be created if such an action conflicts
with your computer security management policies. If unsure, contact
your system administrator or your computer security manager.
Also refer to the product's release notes for further details regarding
the privileges required by the Java Real-Time System.

Should a local 'Java Real-Time System User' rights profile be created? [y,n] (no): y
Using </opt> as the package base directory.
## Processing package information.
## Processing system information.
## Verifying package dependencies.
## Verifying disk space requirements.
## Checking for conflicts with packages already installed.
## Checking for setuid/setgid programs.

This package contains scripts which will be executed with super-user
permission during the process of installing this package.

Do you want to continue with the installation of <SUNWrtjv> [y,n,?] y


## Executing postinstall script.
Creating the 'Java Real-Time System User' rights profile.

Refer to the 'System Administration Guide: Security Services'
documentation for further information regarding the way to assign the
'Java Real-Time System User' rights profile to users, groups, or
administrative roles using the Solaris Management Console, smc(1M) or
the usermod(1M), groupmod(1M) and rolemod(1M) commands.

Installation of <SUNWrtjv> was successful.

6.  Try running a real-time Java application in the zone.

rtjzone# /opt/SUNWrtjv/bin/java -version
java version "1.5.0_13"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_13_Java-RTS-2.0_01-b08_RTSJ-1.0.2)
Java Real-Time System HotSpot(TM) Client VM (build 1.5.0_13-b08, mixed mode)

We hope to more formally support Solaris zones usage with Java RTS in the future.  In the interim this workaround can help you get started.  Many thanks to Jim Clarke, who did the lion's share of the legwork to find this solution.

Thursday May 29, 2008

Are Solaris RAM Disks Swappable?

As memory access is typically orders of magnitude faster than disk access, the idea of using a part of RAM as an in-memory storage device has been one of the earliest performance optimizations realized by the computer science community.  Even though today this type of optimization takes place transparently inside modern operating systems (via mechanisms like the disk buffer cache),  there are still circumstances where manually creating a RAM disk might still be quite useful.

A Solaris customer using the ramdiskadm(1m) utility to create a Ram disk device for a real-time application posed the following question: "Are RAM disks swappable?". From a real-time perspective, a RAM disk not only yields significantly better seek performance but also provides for more deterministic behavior compared to the traditional rotating disk platter.  The customer's concern here is that, under dire circumstances, is it possible for operating system swap out the RAM disk memory?  Needless to say, if a swap out occurred it would put a big crimp in the real-time application's predictability.

To get an idea of what's going on when a RAM disk is created, let's use the Solaris kstat(1m) kernel statitics utility to see how memory is being allocated.  First let's see what memory looks like before creating a RAM disk:

# pagesize
# kstat -n system_pages | grep pagesfree
       pagesfree                       96334
# kstat -n system_pages | grep pageslocke      pageslocked                     26170

So, on this particular system, where a page is 4096 bytes,  there are currently 96334 pages free, and 26170 pages that are locked.  Now let's create a 50MB RAM disk:

# ramdiskadm -a rd 50m
# ramdiskadm
Block Device                                                  Size 
/dev/ramdisk/rd                                           52428800    Yes 
# kstat -n system_pages | grep pagesfree
       pagesfree                       83507
# kstat -n system_pages | grep pageslocked
       pageslocked                     38988

Let's subtract the original number of pageslocked from the latest value and multiply by the pagesize:

# pagesize
# bc

The increase in locked pages can be attributed to the creation of the RAM disk (50m + a small amount of overhead).  So yes, these pages are locked into memory.  But it would be nice to get a definitive statement on what pageslocked actually means.  According to McDougal, Mauro and Gregg's Performance and Tools: DTrace and MDB Techniques for Solaris 10 and OpenSolaris, pageslocked is "Total number of pages locked into memory by the kernel and user processes".  Furthermore, the man page for plock(3C), a related library routine which enables programmers to lock memory segments, states that "Locked segments are immune to all routine swapping".  What's routine swapping?

Hmm.  Anybody care to shed some light on this?

Monday May 19, 2008

Working Around a Simple Directory Server Install Issue with Windows

While working with a customer evaluating Sun's Directory Server Enterprise Edition 6.3 for Windows, we came across a problem during very basic install and setup.  For posterity's sake, I thought it might make sense to document the issue and offer a few potential workarounds. 

How to replicate

1. Install the directory server.  In this case the downloadable image has been unzipped into the c:\\tmp\\dsee63\\DSEE_ZIP_DISTRIBUTION directory

  C:\\tmp\\dsee63\\DSEE_ZIP_Distribution> dsee_deploy --no-inter -i /tmp/ds63                                                                              
Unzipping sun-ldap-shared-l10n.zip ...
Unzipping sun-ldap-directory.zip ...
Unzipping sun-ldap-console-gui-help-l10n.zip ...
Configuring Cacao at /tmp/ds63/dsee6/cacao_2
You can now start your Directory Server Instances
You can now start your Directory Proxy Server Instances

2. Create a new Directory Server instance and start the instance

  C:\\tmp\\dsee63\\DSEE_ZIP_Distribution>cd \\tmp\\ds63
C:\\tmp\\ds63> set PATH=c:\\tmp\\ds63\\ds6\\bin;c:\\tmp\\ds63\\dsrk6\\bin;%PATH%
C:\\tmp\\ds63>dsadm create /tmp/instance
Choose the Directory Manager password:
Confirm the Directory Manager password:
Use 'dsadm start '/tmp/instance'' to start the instance
C:\\tmp\\ds63>dsadm start /tmp/instance
Waiting for Directory Server instance 'C:/tmp/instance' to start...
Directory Server instance 'C:/tmp/instance' started: pid=2144

3. Create a suffix.  This is where the installation fails:

C:\\tmp\\ds63>dsconf create-suffix -h localhost -p 1389 dc=example,dc=com
Enter "cn=Directory Manager" password:
Unable to bind securely on "localhost:1389".
The "create-suffix" operation failed on "localhost:1389".

The problem here is described in The Sun Java System Directory Server Enterprise Edition 6.3 Release Notes:

On Windows systems, Directory Server does not allow Start TLS by default.

This issue affects server instances on Windows systems only. This issue is due to performance on Windows systems when Start TLS is used.

To work around this issue, consider using the -P option with the dsconf command to connect using the SSL port directly. Alternatively, if your network connection is already secured, consider using the -e option with the dsconf command. The option lets you connect to the standard port without requesting a secure connection.


Two Potential Workarounds

Workaround 1: Issue the 'dsconf create-suffix' command by directly connecting to the SSL port (-P 1636)

C:\\tmp\\ds63>dsconf create-suffix -h localhost -P 1636 dc=example,dc=com
Certificate "CN=TECRA-A1, CN=1636, CN=Directory Server, O=Sun Microsystems" pres
ented by the server is not trusted.
Type "Y" to accept, "y" to accept just once, "n" to refuse, "d" for more details
: Y
Enter "cn=Directory Manager" password:

Workaround 2: Modify the ds-start-tls-enabled attribute that is stored in the directory server configuration.

a. Create a file, say c:\\tmp\\modify.ldif which looks like:

  dn: cn=config
  changetype: modify
  replace: ds-start-tls-enabled
  ds-start-tls-enabled: on

b. Issue an ldapmodify command something like this:

  C:\\tmp>ldapmodify -h localhost -p 1389 -D "cn=Directory Manager" -w password < c:\\tmp\\modify.ldif
modifying entry cn=config

c. Confirm modification via ldapsearch command:

  C:\\tmp>ldapsearch -b "cn=config" -h localhost -p 1389 -D "cn=Directory Manager" -w password "cn=config" ds-start-tls-enabled
version: 1
dn: cn=config
ds-start-tls-enabled: on

dn: cn=config,cn=chaining database,cn=plugins,cn=config

dn: cn=config,cn=ldbm database,cn=plugins,cn=config

c. Stop and restart the directory server instance

  C:\\tmp\\ds63>dsadm stop /tmp/instance
Directory Server instance 'C:/tmp/instance' stopped

C:\\tmp\\ds63>dsadm start /tmp/instance
Directory Server instance 'C:/tmp/instance' started: pid=3560

d. Try creating a suffix with the standard port (1389):

  C:\\tmp\\ds63>dsconf create-suffix -h localhost -p 1389 dc=example1,dc=com
Enter "cn=Directory Manager" password:

Note:  Directory Server Enterprise Edition 6.3 is supported on Windows Server 2003, but not for Windows XP.  Although not formally supported, it is possible to experiment with XP.

Tuesday May 06, 2008

Using Solaris to Burn the OpenSolaris 2008.05 CD Image

If you're using Solaris or OpenSolaris as a platform to download and burn the recently announced OpenSolaris 2008.05 image, the instructions found here may need a slight modification.  After downloading the Live CD, you are instructed to use the cdrw(1) command as follows to burn the image to CD:

cdrw -i /path/to/iso/image/os200805.iso

Here's what might happen on your system when you attempt to use the 'cdrw -i' command:

The cdrw(1) command fails because it thinks the ISO image is larger than the default capacity of the CD.  The solution to this problem is explained in these excerpts from the cdrw(1) man page:

"Most commercially available drives allow writing beyond 74 minutes as long as the media has the capacity (such as 80–minute media). However, the ability to write beyond 74 minutes might not be supported by the drive in use. If the drive supports this feature, then use the -C option to indicate that the tool should rely on the capacity indicated by the media.

"-C [option] Uses stated media capacity. Without this option, cdrw uses a default value for writable CD media, which is 74 minutes for an audio CD, 681984000 bytes for a data CD, or 4.7 Gbytes for a DVD." 
This time let's invoke 'cdrw' with the -C option.


Monday Apr 21, 2008

Modifying and Respinning a Bootable Solaris ISO Image

As an adjunct to the previous blog post, a slightly customized boot environment capable of enabling serial console kernel debugging was required to diagnose Solaris install problems.  The post itself mentioned that a nice way to accomplish this was to set up PXE network boot via Solaris jumpstart.  It is indeed flexible and enables one to experiment with modifications and quickly test whether they perform as expected.  The one downside to this environment is that an additional DHCP/TFTP boot server has to be configured.  To eliminate that service, you could, once the customizations are well understood, simply create a new version of the Solaris install DVD with your customizations.  Let's run through the process for a simple example.

1. Get a Solaris DVD.  For this example, we'll use an interim build of the upcoming Solaris 10 Update 5.

2. Extract the the entire contents of the DVD.

# lofiadm -a /export/iso/s10x_u5b10_dvd.iso
# mkdir -p /iso
# mount -r -F hsfs /dev/lofi/1 /iso
# cd /iso
# mkdir /export/modified-s10x_u5b10
# find . -depth -print | cpio -pudm /export/modified-s10x_u5b10
4516208 blocks

3. Modify the content contained in /export/modified-s10x_u5b10.  In this case, we'll change the boot/grub/menu.lst file found in this directory to look like:

#pragma ident "@(#)install_menu 1.1 05/09/01 SMI"
serial --unit=0 --speed=9600
terminal serial
title Solaris Serial Console ttya
kernel /boot/multiboot kernel/unix -B install_media=cdrom,console=ttya
module /boot/x86.miniroot

4. Issue the following magic incantation to create a bootable ISO image based on the contents of the /export/modified-s10x_u5b10 directory.

# mkisofs -R -b boot/grub/stage2_eltorito -no-emul-boot -boot-load-size 4 -R -L -r -D -U \\
-joliet-long -max-iso9660-filenames -boot-info-table -o \\
/export/iso/modified-s10x_u5b10.iso /export/modified-s10x_u5b10


Size of boot image is 4 sectors -> No emulation
1.80% done, estimate finish Fri Apr 18 15:55:13 2008
2.25% done, estimate finish Fri Apr 18 15:59:07 2008
2.70% done, estimate finish Fri Apr 18 15:58:37 2008
3.15% done, estimate finish Fri Apr 18 15:58:48 2008

98.43% done, estimate finish Fri Apr 18 15:59:37 2008
98.88% done, estimate finish Fri Apr 18 15:59:37 2008
99.33% done, estimate finish Fri Apr 18 15:59:36 2008
99.78% done, estimate finish Fri Apr 18 15:59:36 2008
Total translation table size: 2048
Total rockridge attributes bytes: 3075577
Total directory bytes: 18921472
Path table size(bytes): 148014
Max brk space used 1b24000
1112471 extents written (2172 MB) 

Voila!  Acknowledgements to Tom Haynes and this blog post which served as an excellent guide.

Monday Apr 14, 2008

Enabling Remote Console Debugging of Solaris x86 Boot/Install

Our partners do a fair amount of business supplying ruggedized Solaris-powered Panasonic Toughbook computers to their US government/military customers.  As a regular part of the product cycle, Sun usually works with both the integrators and Panasonic to assure that as new models become available, Solaris runs on these systems properly.  Furthermore, when we can get our grubby little hands on the systems, we'll run them through our certification suite of tests and formally place them on the Solaris Hardware Compatibility List.  As an example, here's the certification report for one of the versions of the Panasonic Toughbook CF-29.

Panasonic recently introduced a new version of the Toughbook CF-30 (referred to as revision F) which tweaks some of the computer subsystems resulting in an all-too-familiar scenario: namely, these differences cause the current version of Solaris to fail to install.  Note: Solaris is not alone here, all Operating Systems must continually play this cat and mouse game to support the latest hardware/firmware advances.

Our initial hypothesis lead us to believe that the problem was related to the introduction of the Intel ICH8 SATA chipset.  So we called on some of our Solaris I/O experts, based out of Sun's Beijing office, to take a peek at what was going on.  As the laptop is currently in New York, we needed a way for folks half way around the world to have access to this system.  There are lots of mechanisms available to remotely diagnose systems, what's somewhat unique here is the following: (1) the diagnosis takes place very early in the boot stage, way before any windowing or networking is set up and (2) The system in question is a laptop, not a server, where things like Lights Out Management (LOM) service processors are non-existent.

The solution here was to utilize decades old RS-232 technology combined with some of features of the GRUB bootloader.  Here are two requirements needed:

  • A serial connection must be established between the system to be diagnosed, which will be referred to henceforth as the target, and the system which accesses the target which we'll refer to as the remote host.   Unfortunately most laptop manufacturers have seen fit to eliminate serial port connectors in lieu of using USB to serial converters as a replacement technology.  At the early stages of boot, USB/serial capability is not yet available, so these systems are not good candidates for this type of diagnosis.  Thankfully the target in question here, the Panasonic CF-30 Toughbook, still comes with a serial port.
  • A Jumpstart environment capable of installing Solaris on x86/x64 systems is strongly recommended.  As part of the process described below, we'll be modifying the target's GRUB environment  (menu.lst).  If you chose to use a DVD boot/install environment instead, you'd need to modify and burn a new DVD for each change made to the target's boot environment.  It took a bit of time to find the right incantations in the menu.lst file to get what was needed here; continually re-burning DVDs would have been excruciating.  This exercise is left to the reader, here's a good start to understanding the jumpstart setup process.

Here's how to set up the remote console environment:

1. A null modem cable must be physically connected between the remote host and target.  The most common cable required will be a DB-9 female-to-female connector.  Your configuration may vary.

2. Check the BIOS of the remote host and target and make sure serial ports are enabled.

3. Running Solaris on the remote host, we'll be using the tip(1) command to access the target via serial port.  Edit the /etc/remote file and look for the hardwire: entry.  Modify it to look like this:


4. As part of setting up a jumpstart install for the target, a series of files are created in the /tftpboot directory of the jumpstart server.  Under /tftpboot, there should be a custom menu.lst file for each managed install, suffixed by the unique network MAC address of the system in question.  For example, the network MAC address for the CF-30 in question is 0:b:97:db:c0:97.  The related /tftpboot file for the CF-30 turns out to be /tftpboot/menu.lst.01000B97DBC097. As your target will have a different MAC address, it's menu.lst  file will have a different suffix in the /tftpboot directory.  Edit that custom menu.lst file (for example, /tftpboot/menu.lst.01000B97DBC097) to look as follows:

serial --unit=0 --speed=9600
terminal serial
title Solaris_10 s10x_u5b10
kernel /I86PC.Solaris_10-1/multiboot kernel/unix -B console=ttya,install_media= -vkd
module /I86PC.Solaris_10-1/x86.miniroot

The key modifications here involve (1) inclusion of the serial --unit=0 --speed=9600 and terminal serial lines plus (2) additional arguments added to the kernel directive.  Grub is very fussy about the order and placement of arguments; playing around with these will likely change grub's behavior. 

5.  From the remote host, access the serial console of the target by issuing:

$ tip hardwire

6. Inside a terminal window, here's what the serial console looks like, after the system has been power cycled and runs through the POST sequence:

After the miniroot is loaded, you'll be presented with an mdb prompt and a screen which looks like this:

You can now issue mdb commands to diagnose.  In this scenario you should also be able to reboot the  system without any other manual intervention, like this:


Here's what issuing the mdb commands ':c' and '$c' look like in this environment.   From this simple  trace we can ascertain that the SATA drivers were never even loaded.  Turns out this is likely a VM problem.  Here's the filed bug report.

Wednesday Mar 26, 2008

Reduced Footprint Java SE: Bringing Java Standard Edition Down to Size

A previous blog post demonstrated how you can, with minimal effort, lessen the disk footprint of a typical Java SE 5.0 runtime environment by about a third without violating the Java Standard Edition Licensing agreement. That post focused primarily on removing optional files and compressing class library jar files.  It turns out that with a little more engineering,  there is significant opportunity for further space optimization.

These additional savings involve more intimate knowledge of the inner workings of Java SE.  Sun performs this engineering work and provides a series of reduced footprint versions of Java SE, in binary form, for some of the most common embedded platforms.  They include some of these enhancements:1

  • Headless configuration - The inclusion of graphics subsystems like AWT and Swing comprise a large chunk of the Java SE footprint.  If your device has no graphics capability (i.e. is headless) why would you need to include this functionality?  Headless configurations:
  1. Do not support keyboard or mouse input
  2. Cannot create windows or display graphics
  3. Throw a HeadlessException when a graphics API is called
  4. Still support a functional Java 2D API for printing and off-screen rendering
  5. Are still 100% Java SE compatible
  • Eliminate client or server compiler - Sun's Java SE implementations include two HotSpot compilers, tuned and designed for specific environments.  The client compiler focuses on things like fast user interactivity and quick startup, while the server compiler's priority is on optimizing large, long-lived server applications.  The Java SE VM can only use one of these compilers at a time, so removing the unused one can save considerable space.
  • Minimizes memory consumption - Standard Java SE allocates RAM to house things like the JIT code cache and the object heap.  By default, each one of these areas (and others) can be assigned 64 MB.  In an embedded platform where the total amount of RAM might only be 64MB, Java SE will simply not have enough memory to run.  Java SE Embedded on the other hand, will automatically adapt memory usage to the amount of available RAM.  Consequently, 64MB is a reasonable amount of RAM for a large set of embedded Java applications.
  • Space vs. speed tradeoffs - (1) Java SE implements a thread lookup table, which in layman's terms, helps save a few instructions when switching between Java threads.  By eliminating this table, a couple of MBs of RAM can be spared from your application's working set. (2) Java SE also sets aside an area (mmap'ed) for loading jar files into random access memory, which as was explained to me, may help performance, but may also result in duplicate copies of jar files in memory.  Removal of this area further reduces the Resident Set Size.

An Example

Following through with the exmaple in the last post., let's start with an unmodified version of Java SE 5.0 Update 13 for Linux/x86.  By default, the static footprint is approximately 88MB.

   jimc@jalopy:/tmp> du -sk ./jre1.5.0_13/
88185   ./jre1.5.0_13/

After following the directions in the previous post, we can pare it down to roughly 60MB.
   jimc@jalopy:/tmp> du -sk /tmp/jre1.5.0_13/
59358 /tmp/jre1.5.0_13/

Downloading Sun's reduced footprint version of Java SE for x86/Linux yields:

   jimc@jalopy:/tmp> du -sk /tmp/jre1.5.0_10/
31003 /tmp/jre1.5.0_10/

This version of the JRE is about one-third it's original size, and furthermore has been modified to use significantly less memory than the standard Java SE offerings.  Note: we are comparing slightly different updates of Java SE 1.5 (update 13 vs. update 10).  They are indeed not identical but their differences should be considered, for the sake of argument, negligible. 

[1] Many thanks to Bob Vandette, who though presentation and conversation, supplied this information.  One of many sources comes from Bob's Java ONE 2007 session called, Deploying Java Platform Standard Edition (Java SE Platform) in Today's Embedded Devices (TS-2602)

Thursday Feb 14, 2008

Reducing Your Java SE Runtime Environment Footprint (Legally)

Because the resource requirements for Java Standard Edition are by no means insignificant, developers interested in utilizing Java on smaller platforms have traditionally opted for one of the Java Micro Edition configurations.   It should therefore come as no surprise that some of the Standard Edition functionality has to be sacrificed in these constrained Java ME environments.  However, as the trend towards more capable devices continues, it becomes reasonable to once again consider the advantages of utilizing Java SE.  Unfortunately, with a static footprint that could easily exceed 100MB, Java SE may still be too large to swallow for a lot of applications.

It is duly noted that the criticism leveled at Sun for exacting too much control over the Java platform has been widespread.  Like it or not though, one benefit of Sun's initial stewardship has been that Java SE has remained a standard platform, and threats to splinter it have thus far been reasonably thwarted.  Accordingly, in order to avoid incompatibilities, there are restrictions spelled out in the Java SE Licensing Agreement which prohibit modification of the Java SE binary distribution.

That  being said, there are a list of optional files, specified by the Java Runtime Environment's README file which can be removed, without ramification, to lessen the footprint.  They include:

  • Deployment tools (e.g. Java Web Start, Java plugin)
  • IDL and RMI tools (e.g. rmid, tnameserv)
  • Security tools (e.g. policytool, keytool)
  • orbd
  • Localization (charsets.jar)

In addition, further space optimization can be achieved by compressing the class library files contained in the rt.jar file.  By default, Java SE ships this jar file uncompressed.  The tradeoff here is space vs. performance, i.e. the classloader must expend cycles to uncompress the Java classes as they are being loaded.

An Example

So let's download a sample JRE from java.sun.com and see how it's footprint can be minimized.   For this example, we'll use Java SE 1.5.0 Update 13 for Linux x86.

After installation, the JRE is approximately 88MB

   jimc@jalopy:/tmp> du -sk ./jre1.5.0_13/
88185   ./jre1.5.0_13/

Here's what it looks like after removal of the optional files

   jimc@jalopy:/tmp> cd jre1.5.0_13/

jimc@jalopy:/tmp/jre1.5.0_13> /bin/rm -rf lib/charsets.jar lib/ext/sunjce_provider.jar \\
lib/ext/localedata.jar lib/ext/ldapsec.jar lib/ext/dnsns.jar bin/rmid \\
bin/rmiregistry bin/tnameserv bin/keytool bin/kinit bin/klist bin/ktab \\
bin/policytool bin/orbd bin/servertool bin/javaws, lib/javaws/ and lib/javaws.jar

jimc@jalopy:/tmp/jre1.5.0_13> cd ..
jimc@jalopy:/tmp> du -sk ./jre1.5.0_13/
77227 ./jre1.5.0_13/

And after rt.jar has been compressed

   jimc@jalopy:/tmp> mkdir rtjar
jimc@jalopy:/tmp> cd rtjar/

jimc@jalopy:/tmp/rtjar> jar -xf /tmp/jre1.5.0_13/lib/rt.jar
jimc@jalopy:/tmp/rtjar> zip -q -r /tmp/rt .
jimc@jalopy:/tmp/rtjar> mv /tmp/rt.zip /tmp/jre1.5.0_13/lib/rt.jar

jimc@jalopy:/tmp/rtjar> du -sk /tmp/jre1.5.0_13/
59358 /tmp/jre1.5.0_13/


In many cases, you can lop off about a third of the Java Runtime Environment footprint with no ill effects.  In a future post, we'll discuss how Sun has further reduced Java SE significantly, not only from the point of view of static footprint, but also from a RAM usage perspective too.  For a preview you can check out Sun's Java SE Embedded technology.

Thursday Jan 31, 2008

Good Things Come To Those Who Wait

Java RTS 2.1 EA (Early Access) marks the arrival of a commitment made some time back, namely that Sun would provide a version of the Java Real-Time System for Linux.  Perhaps to some, it was long time in the making, but in fact there are at least 2 good reasons why a Linux version wasn't available till now:

  1. Until recently, there was no standard Linux release/kernel which had true real-time support.  Typically the versions available were non-standard and did not constitute any considerable volume.  Mainstream Linux distributions are only now incorporating the necessary real-time underpinnings.
  2. Porting the Java Real-Time System VM to additional platforms is non-trivial.

Support and testing for Java RTS 2.1 EA at this time is limited to the currently shipping SUSE Linux Enterprise Real Time10 platform and the upcoming Red Hat Enterprise MRG 1.0 release.  It is however possible that other versions of Linux could run Java RTS 2.1 EA as it utilizes the real-time POSIX programming interface.  At minimum they would require a 2.6.21 kernel or greater and a glibc of 2.5 or greater.  In addition, the latest RT patch would also be needed.

This announcement pertains only to Linux, but of course a 2.1 EA version for both Solaris x86/x64 and Sparc will be shortly forthcoming. In the interim, a version of Java RTS 2.0 update 1 is readily available.  Documentation for both Java RTS 2.0 and 2.1 EA can be found here.

Regardless of platform, an evaluation version of the software is available for download at the Java RTS download page.


Wednesday Dec 19, 2007

Java RTS 2.0 Update 1 Released

After blogging about the relationship between high resolution timers and Java RTS, a comment was posted by a reader stating that they couldn't use Java RTS 2.0 with the latest Solaris 10 update.  Unfortunately that reader was correct.

At the time of the post, Sun had released Solaris 10 Update 4, while Java RTS 2.0 would only support Solaris 10 update 3.  To further exacerbate the situation, once Sun releases a new version of Solaris, they do their best to encourage users to use the latest version by making it difficult (if not impossible) for them to locate previous update versions.

There is a dependency between the aforementioned high resolution timer interface and Solaris which is update specific.  We're working to rectify this situation, but as the process behind Solaris is deliberately bureaucratic, this might take a while.  In the interim we'll need to keep in better lock step with Solaris releases.

On November 27, Java RTS 2.0 update 1 was released: 

  • It supports the latest Solaris 10 update (called 08/07 or update 4)
  • Support for Java SE has been updated to Java SE 5 update 13
  • A new Java RTS DTrace Provider is available
  • Other enhancements are listed here


Monday Oct 15, 2007

General Purpose Always Wins ... Why Not for the Real-Time Market Too?

The brief but brilliant era of computing has seen companies come and go, many relegated to the ash heap of history because they failed to heed this simple rule:  
  In the long run general purpose solutions will always win out over specialized proprietary ones. 
As a long time employee of Sun Microsystems, I've witnessed firsthand the effects, both positive and negative, that this law has had on my company.  Sun can attribute it's initial meteoric rise to the fact that it offered a viable alternative to the popular minicomputer platform of the day.  The first Sun workstations were built from commercial off-the-shelf components, and although nearly equal in performance to the minicomputer, they were so much more affordable that they became good enough.  And of course over time, as Moore's law might dictate, those initial deficiencies quickly dissipated, and in fact surpassed traditional minicomputer capabilities.
Somewhere along the line Sun lost sight of this ideal and began incorporating more proprietary technology into their products.  At first the strategy appeared to be successful as Sun was well positioned to reap huge benefits during the Internet bubble.  Meanwhile, low-cost general purpose servers were continuously improving.  When the bubble burst they in turn became good enough alternatives to the powerful but costly Sun servers.  The resulting decline of Sun was rapid, and it's taken the better part of a decade for us to recover.   This story has been told -- and will continue to be again and again -- for those refusing to learn this lesson.  A professor of mine once told me, "If there's anything we've learned from history, it's that we haven't learned anything from history".

When markets mature, even those where technology is paramount, economic considerations dominate. General purpose systems scale on every dimension (unit, management, training and integration costs) whereas proprietary systems do not.  A switch to more standard components should in no way be construed as stifling innovation.  Rather, general purpose systems help create new innovation by building from common elements economically appealing in their own right, and presumably even more economically beneficial once combined.1

[1] The above paragraph was taken in bits and pieces from an email exchange with Dave Hofert, Java Embedded and Real-Time Marketing Manager.  His points were so compelling I couldn't help myself.

Real-time industrial controllers could be the next market ripe for disruption.  Admittedly this is an entrenched and conservative lot.  But the economics of general purpose computing cannot be denied.  As organizations strive to further eliminate cost out of their system, revisiting usage and deployment of industrial controllers, typically via custom proprietary PLCs, is falling under review.  Consequently, at the behest of one of the world's largest industrial corporations, Sun has partnered with iGoLogic, a systems integrator, and Axiomtek, an industrial PC board manufacturer, to create the real-time controller platform pictured below.
Among others, here are some of the compelling benefits of this platform: 

  • It's based on standard x86 hardware.  The motherboards aren't much larger than a postcard, are energy efficient, and yet have PC class processing power.  The number of competing manufacturers in this space eliminates vendor lock-in and insures price sensitivity and further rapid innovation.
  • Real-time applications are developed using Sun's Java Real-Time System, enabling you to leverage the largest development community on the planet.  Obscure development languages and highly specialized environments are longer needed.
  • Industrial Networking Protocols (e.g PROFIBUS, EtherCAT) are easily migrated to this platform, partly because of the wealth of development tools available.
  • The system utilizes an IDE flash drive from Apacer.  In addition to eliminating the moving parts associated with a traditional disk drive, it consumes less power and makes the system more resistant to shock and vibration.  Overcoming the longevity limitations of flash memory, Apacer has done some interesting work on wear leveling algorithms effectively extending the lifetime of the flash device well past the expected lifetime of the industrial controller.

Let this be our first salvo fired over the proprietary industrial encampment.  We believe the opportunity is immense, but also understand that to achieve any measure of success, partnering with organizations who are truly experts in this arena is critical.  If you think you can add further value, we'd love to talk.

Tuesday Jun 19, 2007

Real-Time Java and High Resolution Timers

Any modern x86/x64 processor worth its salt comes equipped with an Advanced Programmable Interrupt Controller, or APIC.  Among the features that an APIC provides is access to high resolution timers.  Without such capability, the default interrupt source for timer and cyclic operations has a precision on the order of 10 milliseconds -- hardly fine-grained enough for any serious real-time work.

The cyclic subsystem, introduced in Solaris 8, gives Solaris the capability to support high resolution timers.  The Sun Java Real-Time System version 2.0, available for Solaris on both x86 and Sparc platforms, includes an additional package called SUNWrtjcJava Real-Time System cyclic driver.  This package exposes an interface to the cyclic subsystem, normally only available to the kernel.  For those already familiar with Sun's Java RTS, you've no doubt noticed that you either need to run as superuser or assign a set of fine-grained privileges to an ordinary user account. (sys_res_config, proc_priocntl, proc_lock_memory and proc_clock_highres).  The proc_clock_highres privilege gives access to those timers.

Originally developed on an AMD Athlon-based PC, I recently moved a Real-Time Java project over to my Toshiba Tecra A1 laptop running the same version of Solaris and Java RTS.  With the goal of getting in a little development time during a long flight, that migration suddenly casued timings to be all wrong.  Why, might you ask, would moving an application from one seemingly identical Solaris environment to another cause this unusual behavior?  Turns out that even though the laptop, a Pentium 4M based system, has an APIC, it was never exposed by the laptop BIOS.  In this scenario, regardless what you do from a Solaris perspective, you'll never get access to the high-res timers. 

This phenomenon appears to be most prevelant in laptops.  As they account for about 50% (or more?) of PCs sold today, developers have a realistic chance of running into this situation.  You can issue this magic Solaris incantation to determine if your system has high-res timer support:

   # echo "psm_timer_reprogram::print"  | mdb -k

If anything but 'apic_timer_reprogram' is then displayed, your machine has no exposed APIC, and is probably unsuitable for Java RTS.  In some cases the BIOS may be configurable with regards to APIC support; in many others it is simply not available.

In the absence of an APIC, there is the potential to improve the high-resolution timing by setting the following tunable in /etc/system:

   set hires_tick=1

Following a reboot, this would change the clock tick frequency from 100 to 1000 ticks per second. This frequency can then be further tuned by setting the hires_hz tunable to the desired value:

   set hires_hz=10000

The default value is 1000 ticks per second; higher values are not officially supported.

Note that tuning your machine in this way does not come without cost.  It is likely to degrade overall performance, as the system will need to spend a larger proportion of time handling the larger frequency of clock interrupts.1

   [1] Thank you  Christophe Lizzi for your explanation of the problem and potential workaround.


Monday Jun 11, 2007

Audio Transcript and Slides Available for "Real-Time Java Meets Wall Street"

Better late than never ...

As Promised, you can find the audio transcript for the Java ONE 2007 technical session TS-1205: The Sun  Java Real-Time System Meets Wall Street at the aforementioned link.  The slide deck should also be available there, or can be downloaded here.

Friday Mar 30, 2007

Real-time Java Meets Wall Street

Jim Clarke, Eric Bruno and I have been selected to present this year at Java ONE.  The title of our session is called "TS-1205 The Sun Java Real-Time System Meets Wall Street".  We're scheduled to present on Wednsday, May 9 at 10:55AM - 11:55AM Pacific in the Moscone Convention Center in San Francisco.  If you plan on attending, please stop by and see us.  For those that can't make the event, we'll be sure to post the slides once we're permitted.

To get an idea of what we'll discuss, take a look at the abstract below; 


Today's financial institutions use program trading systems to automatically execute split-second trades based on sophisticated algorithms that assess current market conditions. Trade decisions and execution must occur in a timely fashion to capitalize on the market. Missing a trade opportunity, even by a few seconds, can lead to significant losses.

With improvements in Java technology performance, firms are starting to use Java technology to implement these algorithms and thereby realizing productivity gains over more-traditional C/C++ development. However, in these time-critical systems, there is still no guarantee that at any instant, the process will not be interrupted by the Java platform garbage collector. Although trade execution occurs most of the time within an acceptable response time, the trade execution is delayed every once in a while, due to garbage collection or some other system event.

Sun's implementation of the Real-Time Specification for Java (JSR 001), the Sun Java Real-Time System, enables real-time processing through techniques that protect important threads from garbage collection and other system interrupts. This means that trading systems can confidently monitor the market and take action consistently within a calculated window of opportunity.

To demonstrate the impact of these techniques, Sun's OEM Software Systems Engineering technical team has written a demonstration of a trading system that uses real trade data. The demonstration compares a regular Java virtual machine against the Sun Java Real-Time System. For each run, a graph shows the difference in the actual trade price and the price when the trade should have executed. Running this trading system with the Sun Java Real-Time System shows that no money is lost due to garbage collection latencies. The results will be contrasted with the same application run with the standard (non-real-time) Java virtual machine.

This presentation demonstrates a working system and proves real-time Java technology's ability to satisfy hard real-time trading requirements.

Thursday Mar 08, 2007

Crosstool Environment in a Solaris Zone

The task of building Java ME CDC-HI binaries and their associated cross development environments tends to be very linux-centric.  Utilities like Crosstool, which make this process much more tolerable, also make various linux and GNU assumptions that differ from standard Solaris.  By introducing new paths and GNU versions of applications, it is possible to mimic this environment in Solaris.   Furthermore, by creating a new zone with this environment, we can isolate these changes without affecting other Solaris system settings.

1. Create a Solaris zone called 'toolzone'

Note: This step is system specific.  For example, the hostname and IP address for 'toolzone' was predefined.  In addition, the network interface (i.e. 'set physical=iprb0') is likely to be different also.

Here's the command-line session for creating the zone:

phoenix://# mkdir /zone
phoenix://# zonecfg -z toolzone
toolzone: No such zone configured
Use 'create' to begin configuring a new zone.
zonecfg:toolzone> create
zonecfg:toolzone> set zonepath=/zone/toolzone
zonecfg:toolzone> set autoboot=true
zonecfg:toolzone> add net
zonecfg:toolzone:net> set address=toolzone
zonecfg:toolzone:net> set physical=iprb0
zonecfg:toolzone:net> end
zonecfg:toolzone> info
zonepath: /zone/toolzone
autoboot: true
dir: /lib
dir: /platform
dir: /sbin
dir: /usr
address: toolzone
physical: iprb0
zonecfg:toolzone> verify
zonecfg:toolzone> commit
zonecfg:toolzone> \^D

phoenix://# zoneadm list -vc
0 global running /
- toolzone configured /zone/toolzone

phoenix://# zoneadm -z toolzone install
Preparing to install zone <toolzone>.
Creating list of files to copy from the global zone.
Copying <6666> files to the zone.
Initializing zone product registry.
Determining zone package initialization order.
Preparing to initialize <945> packages on the zone.
Initialized <945> packages on zone.
Zone <toolzone> is initialized.
Installation of <2> packages was skipped.
Installation of these packages generated warnings: <SUNWcsu SUNWsogm>
The file </zone/toolzone/root/var/sadm/system/logs/install_log> contains a log of the zone installation.
2.  Configure the zone.

2a. Create a zone-specific /usr/bin directory and copy the contents of the global zone's /usr/bin to this new directory.
phoenix://# mkdir -p /zone/toolzone/usr/bin
phoenix://# cd /usr/bin
phoenix://# tar cf - . | (cd /zone/toolzone/usr/bin; tar xfp -)
2b. Create a loopback mount for toolzone to this new version of /usr/bin:
phoenix://# zonecfg -z toolzone
zonecfg:toolzone> addfs
zonecfg:toolzone:fs> set dir=/usr/bin
zonecfg:toolzone:fs> set special=/zone/toolzone/usr/bin
zonecfg:toolzone:fs> set type=lofs
zonecfg:toolzone:fs> end
zonecfg:toolzone> \^D
2c. Boot and configure the newly created zone:
phoenix://# zoneadm -z toolzone boot
phoenix://# zlogin -C toolzone
2d. Change the path of the default shell to /usr/bin/bash.  This is required for crosstool to operate correctly.
toolzone://# cd /usr/bin
toolzone:bin/# mv sh sh.ORIG
toolzone:bin/# ln -s /usr/bin/bash sh
2e. Create a new user in toolzone.  For this example, we'll use 'cdc'.  This exercise is left to the user.
toolzone://# grep cdc /etc/passwd
cdc:x:600:10:CDC-HI build user:/export/home/cdc:/usr/bin/bash
2f. Create a directoy called /opt/gnulinks and make sure the 'cdc' user owns it.  This directory will house versions and links  to GNU utilities which differ from their Solaris counterparts.
toolzone://# mkdir /opt/gnulinks
toolzone://# chown cdc:staff /opt/gnulinks
toolzone://# ls -ld /opt/gnulinks
drwxr-xr-x 2 cdc staff 512 Jun 19 13:14 /opt/gnulinks

3. As the newly created 'cdc' user, configure the zone to build both the crosstool environment and CDC-HI

3a. Login to the zone as the 'cdc' user.
phoenix://# zlogin -l cdc toolzone
[Connected to zone 'toolzone' pts/6]
Last login: Mon Jun 19 12:57:05 on pts/6
Sun Microsystems Inc. SunOS 5.10 Generic January 2005
3b. To get a zone-specific prompt, you might want to have an entry in ~cdc/.bash_profile like:
export PS1='\\h:\\W/\\$ '
3c.  Create links in /opt/gnulinks/bin which point to GNU versions of unix utilities.  This can be accomplised by executing the gnulinks.sh script which looks like:
GNU_PROGS="ar as egrep grep m4 make nm objcopy objdump strings strip tar thumb"

mkdir -p ${GNULINKS_DIR}
for PROG in ${GNU_PROGS}
ln -s /usr/sfw/bin/g${PROG} ${PROG}

toolzone:~/$ sh gnulinks.sh
3d. Modify PATH of 'cdc' user by putting the following line in ~cdc/.bash_profile:
export PATH=/opt/gnulinks/bin:/usr/sfw/bin:$PATH

toolzone:~/$ which tar

3e.  Build and install GNU specific utilities required for crosstool which differ from those provided by Solaris.  They include binutils, fileutils, gawk(1), patch(1) and sed(1).  A build-gnu-bin.sh is furnished to automate this, and looks like:


mkdir -p ${HOME}/GNU

for PROG in $\*
cd ${HOME}/GNU
PREFIX=`echo ${PROG} | sed 's/-.\*//'`
wget ftp://ftp.gnu.org/gnu/${PREFIX}/${PROG}.tar.gz
tar xzf ${PROG}.tar.gz
cd ${PROG}
./configure --prefix=${GNULINKS_DIR}
make install

toolzone:~/$ sh build-gnu-bin.sh fileutils-4.1 gawk-3.1.5 patch-2.5.4 sed-4.1.4

3f. Create a native version of gcc(1).  The Solaris 10 version of gcc found in /usr/sfw/bin uses the stock Solaris linker, ld(1), found in /usr/ccs/bin.  Later versions of glibc will only accept the GNU version of ld(1).

toolzone:~/$ cd ~/GNU
toolzone:GNU/$ wget ftp://ftp.gnu.org/gnu/gcc/gcc-3.4.3/gcc-3.4.3.tar.bz2
toolzone:GNU/$ bunzip2 gcc-3.4.3.tar.bz2
toolzone:GNU/$ tar -xf gcc-3.4.3.tar
toolzone:GNU/$ cd gcc-3.4.3
toolzone:gcc-3.4.3/$ ./configure --prefix=/opt/gnulinks
toolzone:gcc-3.4.3/$ make
toolzone:gcc-3.4.3/$ make install

3g. Build and install a GNU version of binutils using the aforementioned build-gnu-bin.sh script.


toolzone:~/$ sh build-gnu-bin.sh binutils-2.16

 4a.  As the 'cdc' user, build a cross development environment using crosstool.  These instructions are taken from http://www.kegel.com/crosstool/crosstool-0.43/doc/crosstool-howto.html#quick

toolzone:~/$ cd
toolzone:~/$ wget http://kegel.com/crosstool/crosstool-0.43.tar.gz
toolzone:~/$ tar -xzvf crosstool-0.43.tar.gz
toolzone:~/$ su -
toolzone://# mkdir /opt/crosstool
toolzone://# chown cdc:staff /opt/crosstool
toolzone://# exit
4b. As an example, use these configuration files to build a crosstool for a Sharp Zaurus SL-5000D running OpenZaurus 3.5.1:
toolzone:~/$ cd ~/crosstool-0.43
toolzone:crosstool-0.43/$ cat oz-3.5.1.sh
set -ex

# Really, you should do the mkdir before running this,
# and chown /opt/crosstool to yourself so you don't need to run as root.
mkdir -p $RESULT_TOP

# Build the toolchain. Takes a couple hours and a couple gigabytes.

eval `cat arm-softfloat.dat oz-3.5.1.dat` sh all.sh --notest

echo Done.

toolzone:crosstool-0.43/$ cat oz-3.5.1.dat
4c. Build the toolchain
toolzone:crosstool-0.43/$ sh oz-3.5.1.sh > OUT.oz-3.5.1 2>&1 &

Jim Connors


« April 2014