dmitrymatveev.co.uk
BlogRussian blogGSoC 2011GSoC 2010AboutMaintenance
Back to posts
Building Mozilla Seamonkey 2.17 on NetBSD

Recently I've decided to use Mozilla Seamonkey as a default browser, because

  • It reminds me about that good-old-days;
  • I find it less bloated and slow, than today's mainstream Firefox and Chromium (even if Seamonkey has its own mail client, irc client, and so on).

I am running NetBSD-current (6.99.19) on amd64. The build invoked with

$ cd /usr/pkgsrc/www/seamonkey
$ make install clean
went fine, however there was an issue on the installation stage:

===> Building binary package for seamonkey-2.17
=> Creating binary package /usr/pkgsrc/packages/All/seamonkey-2.17.tgz
pkg_create: can't stat 
`/usr/pkgsrc/www/seamonkey/work/.destdir/usr/pkg/lib/seamonkey/extensions/inspector@mozilla.org/defaults/preferences/inspector.js'
pkg_create: can't stat 
`/usr/pkgsrc/www/seamonkey/work/.destdir/usr/pkg/lib/seamonkey/extensions/inspector@mozilla.org/install.rdf'
pkg_create: lstat failed for file 
lib/seamonkey/extensions/inspector@mozilla.org/defaults/preferences/inspector.js: 
No such file or directory
*** Error code 2

Stop.
make: stopped in /usr/pkgsrc/www/seamonkey
*** Error code 1

Stop.
make: stopped in /usr/pkgsrc/www/seamonkey

Looks like it is not a pkgsrc issue, since, as Google search results have shown, the same occurs on GNU/Linux systems. To fix it, simply do

$ cd /usr/pkgsrc/www/seamonkey 
$ cp work/comm-release/mozilla/dist/xpi-stage/inspector/install.rdf \
work/.destdir/usr/pkg/lib/seamonkey/extensions/inspector\@mozilla.org/
$ cp -rv work/comm-release/mozilla/dist/xpi-stage/inspector/defaults/ \
work/.destdir/usr/pkg/lib/seamonkey/extensions/inspector\@mozilla.org/
And then invoke make install again.

Add a comment
Debugging GNU Smalltalk on NetBSD: Episode II

See also: Episode I

Setting up the NetBSD kernel debugging environment

NetBSD has its own kernel debugger named DDB. As the original documentation states, is useful for gathering crash tracebacks, examining the values of variables, and other minor debugging tasks, but if youre doing serious kernel hacking you will want to setup to work with the remote debugger, KGDB, instead.

There is a few HOWTOs on how to setup remote debugging for NetBSD kernel on the Internet.

The first one is the official NetBSD documentation chapter. It describes how to set up debugging using two computers connected with null-model cable.

The second one is a tutorial by Alexander Shishkin. It involves QEMU to host the debugged system, so the work can be done on a single PC. However, it uses a custom script for generating disk image with kernel and the basic userland. It looks a bit tricky.

I have wanted to use a normal system from the official distribution ISO image. Also I had only one laptop with NetBSD and QEMU was the solution. So, my way is a combination of the both methods mentioned above.

Building the kernel

Building kernel is fairly easy. All we need is just to modify the configuration to enable KGDB and to generate a full symbol table. The following steps are taken from the already referred official documentation:

# cd /usr/src/sys/arch/i386/conf
# cp GENERIC DEBUGGING

GENERIC and DEBUGGING are build configuration files. These files determine what will be included into the kernel, which options will be enabled or disabled and so on. GENERIC is a basic configuration file and the stock NetBSD kernel is built as GENERIC.

I have named a new configuration as "DEBUGGING". In the DEBUGGING configuration file, the following lines have to be commented with the preceiding pragma (#):

#options    DDB                     # in-kernel debugger
#options    DDB_HISTORY_SIZE=100    # enable history editing

and the following lines have to be uncommented by removing the preceiding pragma:

options     KGDB                    # remote debugger
options     "KGDB_DEVNAME=\"com\"",KGDB_DEVADDR=0x3f8,KGDB_DEVRATE=9600
makeoptions DEBUG="-g"              # compile full symbol table

KGDB_DEVADDR option sets the address of the serial port which will be used for debugging. 0x3f8 is tty00, 0x2f8 is tty01.

There is just a few things left to build the kernel:

# config DEBUGGING
# cd ../compile/DEBUGGING
# make depend
# make

That is all! After the successful compilation we will get netbsd and netbsd.gdb files in the current directory. It is a new kernel and debugging symbols for GDB respectively.

Preparing the guest system

Now we need to get a basic system working in QEMU. The following commands will create a 2GB QEMU hard disk image and run the NetBSD installer for it:

$ qemu-img create netbsd.img 2G
$ qemu -hda netbsd.img -cdrom i386cd-5.0.2.iso -boot d -m 196 -localtime

All QEMU options in the last command are quite straightforward, the most interesting are:

  • -hda -- specifies a hard disk image that we have created a step before (netbsd.img);
  • -cdrom -- specifies an installation CD ISO image (i386cd-5.0.2.iso in my case)
  • -boot d -- tells QEMU to boot from a virtual CD-ROM instead of the hard disk image;

After the successful installation we will need to restart QEMU with some different options:

$ qemu -hda netbsd.img -boot c -m 196 -localtime \
       -net user -net nic,model=rtl8139 \
       -redir tcp:5555::22

Two -net options will enable networking in the guest. The last -redir option will allow us to connect to the guest with ssh via the localhost port 5555.

After the system will boot, log in as root and run

# dhclient

to obtain an IP address. ping will not work, but pkgsrc will. I have installed Git and OpenSSHd.

Installation of a new kernel is quite simple. With QEMU networking the host machine usually appears as 10.0.2.2, so the kernel can be transferred with scp:

# mv /netbsd /netbsd.old
# scp user@10.0.2.2:/usr/src/sys/arch/i386/compile/DEBUGGING/netbsd /

Final steps

Again, we will need to restart QEMU in a new configuration:

$ qemu -hda netbsd.img -boot c -m 196 -localtime \
       -net user -net nic,model=rtl8139 \
       -redir tcp:5555::22 \
       -serial tcp::4444,server

The last option -serial tcp::4444,server creates a pipe from a host's port 4444 to guest's serial port (remember KGDB options in the kernel configuration).

QEMU will start but will not launch the guest system until we connect to this port. It is time to open gdb:

(gdb) symbol-file /usr/src/sys/arch/i386/compile/DEBUGGING/netbsd.gdb 
(gdb) target remote :4444

QEMU window will appear and we will need to boot the guest system with a different command in the NetBSD bootloader:

boot -d

After some seconds the guest system will enter an initial breakpoint and in the gdb shell we will get something like this:

0xc053556c in breakpoint ()
(gdb)

Great! Now everything is ready for debugging.

(to be continued)

Add a comment
Debugging GNU Smalltalk on NetBSD: Episode I

In the previous post I have told how to build GNU Smalltalk on the fascinating operating system NetBSD. The interpreter worked pretty fine, but I wanted something more than just simple scripts.

The problem

So I have tried to run Seaside. netstat said that the port 8080 was opened, but I could not reach http://localhost:8080/seaside in the browser.

The first suspiction has fallen on sockets. Of course, it would be hard to debug sockets on such complicated tools as Swazoo and Seaside, so I have took a simple Samuel Montgomery-Blinn's TCP echo server example for tests. The code has been slightly simplified to run only in a single green thread, to serve a single client and to work only for a single message:

Eval [
    | client server string |

    PackageLoader fileInPackage: #TCP.

    server := TCP.ServerSocket port: 8000.
    server waitForConnection.

    client := server accept.
    
    string := client nextLine.
    client nextPutAll: string; nextPut: Character nl.

    client flush.
    client close.
]

This sample works fine on GNU/Linux, but does not work on NetBSD. I have successfully connected on port 8000 with telnet, but after typing a message and hitting Enter the server has not replied to me with echo. Server process still hanged in memory.

Great, it is time to take a look under the hood and to understand how GNU Smalltalk sockets work.

Sockets: it is streams all the way down

GNU Smalltalk sockets are implented in a cute way. "End-user" objects are not actually sockets, it is just adaptors that implement a Stream interface over a concrete socket implementations.

Stream
  Sockets.AbstractSocket
    Sockets.DatagramSocket
      Sockets.MulticastSocket
    Sockets.ServerSocket
    Sockets.StreamSocket
      Sockets.Socket

End-user class hierarchy

It is obvious that a socket class does actually implement methods like #nextLine -- it is abstract and is implemented somewhere in the Stream class. Design patterns call it "template methods", I call it good OO design. The template methods are expressed with another methods whose behavior may be specified or changed in the subclasses.

The underlying implementations are actually FileDescriptors.

Stream
  FileDescriptor
    Sockets.AbstractSocketImpl
      Sockets.DatagramSocketImpl
        Sockets.MulticastSocketImpl
          Sockets.UDPSocketImpl
        Sockets.OOBSocketImpl
        Sockets.RawSocketImpl
          Sockets.ICMP6SocketImpl
          Sockets.ICMPSocketImpl
        Sockets.UnixDatagramSocketImpl
      Sockets.SocketImpl
        Sockets.TCPSocketImpl
        Sockets.UnixSocketImpl

Implementation class hierarchy

Again, it is quite logical -- the core BSD sockets are represented as file descriptors in the user space (remember that everything is file in Unix). Depending on the type of a file descriptor, calling common system calls (such as read(2), write(2), fcntl(2)) on it will result in invoking a different code at the kernel space.

Files, sockets and all the I/O as well is the intercommunication with the outside world. It can not be implemented in pure Smalltalk, at the lowest level we have to deal with the API, which the operating system provides for us. In the case of files and sockets we are working with file descriptors -- integer values in Unix systems.

In GNU Smalltalk, file descriptors are represented with FileDescriptor class. Every object of this class holds a numeric instance variable fd -- actually the Unix file descriptor.

All the high-level I/O methods, which the programmer uses in the application, are expressed with low-level access methods like #fileOp:, #fileOp:ifFail:, #fileOp:with:, #fileOp:with:ifFail: and so on. These methods call the same primitive VMpr_FileDescriptor_fileOp and the succeeding processing goes on the VM side. Depending on an index passed to the #fileOp: from a higher-level method, a different file operation will be performed.

The basic socket implementation class AbstractSocketImpl overrides the #fileOp: methods to call VMpr_FileDescriptor_socketOp primitive instead of VMpr_FileDescriptor_fileOp.

Now, after digging into the implementation details, lets return back to the echo server example. If we will interrupt the hanged-up server process, we will receive the following stack trace:

Sockets.TCPSocketImpl(Sockets.AbstractSocketImpl)>>ensureReadable
optimized [] in Sockets.StreamSocket>>newReadBuffer:
Sockets.ReadBuffer>>atEnd
Sockets.Socket(Sockets.StreamSocket)>>peek
Sockets.Socket(Sockets.StreamSocket)>>atEnd
Sockets.Socket(Stream)>>nextLine

As we can see, our process has stuck on the call to AbstractSocketImpl>>ensureReadable, which was implicitly invoked via a chain of calls from Stream>>nextLine.

Stream>>nextLine method does a simple thing: it checks weither there is data available and reads it byte by byte until a newline character will be reached.

AbstractSocketImpl>>ensureReadable is a little bit more interesting. It blocks the current Smalltalk thread and waits until there will be data available for reading. It involves the VMpr_FileDescriptor_socketOp primitive too. Lets now go down from Smalltalk to the virtual machine side.

Asynchronous I/O for the win

Our sample server is synchronous. First of all, it waits for a client connection, and then it waits again while client will send us a line of text. All these operations are synchronous -- we can not do something else inside a single Smalltalk thread while waiting for an event.

Such operations are called "blocking". If we wrote our echo server on C, we would use a blocking sockets, so system calls like accept(2) and recv(2) would block our server process until a client will connect and send some data respectively. It is a very simple and straightforward scheme that is often used in simple applications.

We could assume that GNU Smalltalk's #waitForConnection and #nextLine are implemented in the same way, since these method provides us the same blocking behavior, but actually it is not true.

GNU Smalltalk implements green threads (aka Smalltalk Processes) for multitasking inside VM, it does not support native system threads, so calling accept(2) or recv(2) on a true blocking socket would block the entire virtual machine on a time of the call. It is completely unacceptable, so socket IO is implemented in a more cute way with non-blocking sockets.

When a Smalltalk process needs to wait for a specific event (client connection or incoming data) on a specific socket, the AbstractSocketImpl>>ensureReadable is called. #ensureReadable creates and locks a Semaphore to block the current Smalltalk process.

On the virtual machine side, via call to the primitive VMpr_FileDescriptor_socketOp with operational codes 14 and 13, the following happens:

  1. SIGIO signal handler is installed on the socket;
  2. Socket is added to a table of polled descriptors;
  3. If there is no code to execute and all Smalltalk processes are sleeping (waiting for data), sigsuspend(2) is called. In this state the virtual machine process will sleep in waiting of the arrival of any Unix signal. I did not tested it, but I assume that the VM process can handle SIGIO even without of calling sigsuspend(2).
  4. If there is an activity on a file descriptor, i.e. incoming connection or data, the VM process will receive SIGIO and the signal handler (installed on the first step) will be executed;
  5. This handler will check the table of polled descriptors. For every ready for I/O descriptor VM will unlock the appropriate semaphore and the appropriate Smalltalk process will resume its execution;
  6. The descriptor is removed from a table of polled descriptors.

Now we get back on the Smalltalk side. After resuming from #ensureReadable, we know that a descriptor is ready for IO and calling accept(2) or recv(2) will not block the interpreter. That's it!

A set of simple debugging printfs has been inserted in the VM and has shown that the VM really goes to sleep after the call to the #nextLine. Looks like the gst process just does not receive SIGIO on incoming data. I saw the only way to check it -- to debug the NetBSD kernel.

See also: Episode II

Add a comment
How to build GNU Smalltalk on NetBSD

GNU Smalltalk is being developed under GNU/Linux (primarily), so if you have used it on GNU/Linux, everything should work well.

But if you will change your working environment to a different operating system, like NetBSD, you can encounter some troubles, even (mostly?) on the compilation stage.

Okay, so what we need to build GNU Smalltalk on NetBSD properly?

First of all, BSD Make is not GNU Make. I could not build GNU Smalltalk with BSD Make (remember that BSD is Unix and GNU is Not Unix?).

Next, even with gmake compilation has failed. In my case, the linker has thrown an 'undefined reference to...' error, mentioning one of pthread functions. Okay, I do not know why autotools did not do it, all we need to fix it is just to add -lpthread to LDFLAGS.

After it the compilation completed successfully... but it is not the end of the story. After installation I have tried to create a basic Seaside image:

$ gst-load -iI seaside.im Seaside

...and the DLD (GNU Smalltalk's Dynamic Library Loader) has said that there is no such module 'iconv'.

Knowning about GNU Smalltalk binding development features, I have decided to check the Iconv package:

<package>
  <name>Iconv</name>
  <namespace>I18N</namespace>
  <test>
    <namespace>I18N</namespace>
    <prereq>Iconv</prereq>
    <prereq>SUnit</prereq>
    <sunit>I18N.IconvTest</sunit>
    <filein>iconvtests.st</filein>
  </test>
  <module>iconv</module>

  <filein>Sets.st</filein>
  <filein>UTF7.st</filein>
</package>

Okay, the Iconv package dynamically loads the iconv dynamic library on startup. But there were no any dynamic libraries in GNU Smalltalk source & build directory!

In the compilation logs I have found a lot of 'Warning: linker path does not have real file for library ...' libtool warnings. It could not find m and other standard libraries. All they are available in /usr/lib. Okay, so we need to say about it to libtool and add -L/usr/lib to LDFLAGS. And it has worked!

So the building is:

$ autoreconf -vi
$ ./configure
$ gmake LDFLAGS="-L/usr/lib -lpthread"

Add a comment
smalltalk (5)
netbsd (4)
howto (2)
kgdb (1)
seaside (1)
nginx (1)
seamonkey (1)
io (1)
qemu (1)
security (1)
haskell (1)
template haskell (1)
gc (1)
pkgsrc (1)
gdb (1)
vm (1)
amber (1)