Tip: Trimming the tops and bottoms of text files with head and tail

Normally the head and tail applications on Linux are good for what their names imply. head gives you the first few lines of a file, tail gives you last few lines of a file and even lets you watch the end of a file for changes. This is great but what if you want to get an entire file except for the first few or last few lines? It turns out that head and tail have options to do this and it’s incredibly useful for trimming files without knowing exactly how many lines they contain.

I’m writing this because I keep forgetting which one does what. Here’s how you can remember it and use it every day…

Tip #1: If you only want the end of a file use tail like this:

tail -n +3 input.file > output.file

An example file, like a PostgreSQL database dump, might look like this:

column_a | column_b | column_c
---------+----------+---------
    1    |   bob    |  65000
    2    |   joe    |  80000
    3    |   jim    |  54000
(3 rows)

After running

tail -n +3 input.file > output.file

on this we’ll end up with output that looks like this:

    1    |   bob    |  65000
    2    |   joe    |  80000
    3    |   jim    |  54000
(3 rows)

The best way to remember this is that you want everything until the end of the file starting at the third line.

Tip #2: If you only want the beginning of a file use head like this:

head -n -2 input.file > output.file

Using the same example file we end up with:

column_a | column_b | column_c
---------+----------+---------
    1    |   bob    |  65000
    2    |   joe    |  80000
    3    |   jim    |  54000

The best way to remember this is that you want everything from the beginning file excluding the last two lines. Note, there is a blank line after “(3 rows)” and we want to remove that too.

Tip #3: If you need to trim from both side you can pipe like this:

tail -n +3 input.file | head -n -2 > output.file

Using the same example we end up:

    1    |   bob    |  65000
    2    |   joe    |  80000
    3    |   jim    |  54000

This now translates to start at the third line and stop two lines from the end. If you ever forget just come back here and re-read the examples.

Tip: Fix basic build issues when using Maven

When trying to build Hadoop with Maven today I got this ugly error message:


[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[ERROR] FATAL ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Error building POM (may not be this project's POM).

Project ID: org.apache.hadoop:hadoop-project
POM Location: /home/tim/subversion/hadoop-trunk/hadoop-project/pom.xml
Validation Messages:

    [0]  For dependency Dependency {groupId=jdk.tools, artifactId=jdk.tools, version=1.6, type=jar}: system-scoped dependency must specify an absolute path systemPath.

Reason: Failed to validate POM for project org.apache.hadoop:hadoop-project at /home/tim/subversion/hadoop-trunk/hadoop-project/pom.xml

[INFO] ------------------------------------------------------------------------
[INFO] Trace
org.apache.maven.reactor.MavenExecutionException: Failed to validate POM for project org.apache.hadoop:hadoop-project at /home/tim/subversion/hadoop-trunk/hadoop-project/pom.xml
	at org.apache.maven.DefaultMaven.getProjects(DefaultMaven.java:404)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:272)
	at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:138)
	at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)
	at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:60)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:616)
	at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
	at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
	at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
	at org.codehaus.classworlds.Launcher.main(Launcher.java:375)
Caused by: org.apache.maven.project.InvalidProjectModelException: Failed to validate POM for project org.apache.hadoop:hadoop-project at /home/tim/subversion/hadoop-trunk/hadoop-project/pom.xml
	at org.apache.maven.project.DefaultMavenProjectBuilder.processProjectLogic(DefaultMavenProjectBuilder.java:1077)
	at org.apache.maven.project.DefaultMavenProjectBuilder.buildInternal(DefaultMavenProjectBuilder.java:880)
	at org.apache.maven.project.DefaultMavenProjectBuilder.buildFromSourceFileInternal(DefaultMavenProjectBuilder.java:508)
	at org.apache.maven.project.DefaultMavenProjectBuilder.build(DefaultMavenProjectBuilder.java:200)
	at org.apache.maven.DefaultMaven.getProject(DefaultMaven.java:604)
	at org.apache.maven.DefaultMaven.collectProjects(DefaultMaven.java:487)
	at org.apache.maven.DefaultMaven.collectProjects(DefaultMaven.java:560)
	at org.apache.maven.DefaultMaven.getProjects(DefaultMaven.java:391)
	... 12 more
[INFO] ------------------------------------------------------------------------
[INFO] Total time: < 1 second
[INFO] Finished at: Tue Feb 07 13:30:39 EST 2012
[INFO] Final Memory: 3M/361M
[INFO] ------------------------------------------------------------------------

Tsk, tsk on me. All I had to do was set my JAVA_HOME variable. Not sure how to set yours? Just do this:

export JAVA_HOME=$(readlink -f /usr/bin/java | sed "s:bin/java::")

I’m actually running 64-bit Debian Wheezy so I had to do some other things to get my system prepped. I needed to get off of Maven 2 and onto Maven 3, add the JDK, install the protocol buffers compiler, install zlib and its development files, and use a slightly different path. Here’s what I did:

sudo apt-get install maven openjdk-6-jdk libprotoc-dev protobuf-compiler zlib1g-dev
export JAVA_HOME="/usr/lib/jvm/java-6-openjdk-amd64/

After that Maven started humming away when I ran:

mvn compile -Pnative

Good luck! Post in the comments if you run into trouble.

Tip: Fix NoClassDefFoundError on org/apache/hadoop/thirdparty/guava/common/collect/LinkedListMultimap

If you’re writing code that accesses HDFS and you get an exception that looks like this:

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/hadoop/thirdparty/guava/common/collect/LinkedListMultimap
	at org.apache.hadoop.hdfs.SocketCache.<init>(SocketCache.java:48)
	at org.apache.hadoop.hdfs.DFSClient.<init>(DFSClient.java:240)
	at org.apache.hadoop.hdfs.DFSClient.<init>(DFSClient.java:208)
	at org.apache.hadoop.hdfs.DistributedFileSystem.initialize(DistributedFileSystem.java:89)
	at org.apache.hadoop.fs.FileSystem.createFileSystem(FileSystem.java:1563)
	at org.apache.hadoop.fs.FileSystem.access$200(FileSystem.java:67)
	at org.apache.hadoop.fs.FileSystem$Cache.getInternal(FileSystem.java:1597)
	at org.apache.hadoop.fs.FileSystem$Cache.get(FileSystem.java:1579)
	at org.apache.hadoop.fs.FileSystem.get(FileSystem.java:228)
	at org.apache.hadoop.fs.FileSystem.get(FileSystem.java:111)

Make sure you include the guava-r09-jarjar.jar JAR in your build path. This is usually located in /usr/lib/hadoop-0.20/lib.

How-To: Debug HDFS applications in Eclipse

I started using the HDFS API in Java recently in order to port some legacy applications over to HDFS. One thing that I noticed is that when running the application via “hadoop jar” it properly accessed HDFS and stored its files there but if I ran it in the debugger the API calls succeeded but the files never showed up.

After a bit more investigation I saw that the HDFS API was unable to read my configuration files and find the NameNode so it defaulted to writing the files on the local file system instead. This is nice behavior for debugging sometimes but can be dangerous if you’re running an application that must put its files in HDFS like a mission critical application that doesn’t fulfill its operational contract if data is lost. In the case of an application like that accidentally writing to the local file system could be disastrous and expensive so it’s good to know how to detect when this happens, and/or overcome it in a situation where you’re trying to debug against your HDFS cluster.

Let’s look at a simple code snippet that connects to HDFS that is just a cleaned up version of Yahoo’s Hadoop tutorial:

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class TestClass {
	private static final String theFilename = "timmattison.txt";
	private static final String message = "This is the message that gets put into the file";

	public static void main(String[] args) {
		try {
			// Get the configuration and connect to the file system
			Configuration conf = new Configuration();
			FileSystem fs = FileSystem.get(conf);

			// Create the path object for our output file
			Path filenamePath = new Path(theFilename);

			// Does it exist already?
			if (fs.exists(filenamePath)) {
				// Yes, remove it first
				fs.delete(filenamePath);
			}

			// Create the output file and write the data into it
			FSDataOutputStream out = fs.create(filenamePath);
			out.writeUTF(message);
			out.close();

			// Open the output file as an input file and read it
			FSDataInputStream in = fs.open(filenamePath);
			String messageIn = in.readUTF();

			// Print its contents and close the file
			System.out.print(messageIn);
			in.close();
		} catch (IOException ioe) {
			System.err.println("IOException during operation: "
					+ ioe.toString());
			System.exit(1);
		}
	}
}

If you run this code with “hadoop jar” you’ll see that it creates the expected file (timmattison.txt) in the current user’s default path in HDFS. If you run this code with Eclipse either in Run or Debug mode you’ll see that the file is not created in HDFS, it is created relative to where Eclipse starts the JVM for the new process.

We can tell where the HDFS library will attempt to write our files by very simply checking the type of the FileSystem object that is created by the call to FileSystem.get(conf). If that object’s type is LocalFileSystem we are not connecting to HDFS. However if that object’s type is DistributedFileSystem then you know that you’re connected to a Hadoop cluster and writing to a real instance of HDFS.

In your code you can leverage this in a few ways. First, if you always need to be sure you’re writing to the cluster you can check the fs variable and see if it is an instance of LocalFileSystem. If it is you can signal an error, e-mail an admin, etc. Configuration changes in the field could cause this to happen so it is important to be aware of. In general running programs through “hadoop jar” will make sure this doesn’t happen but a little defensive programming usually can’t hurt. Just consider what the cost of running your code against the wrong file system would be and trap this condition accordingly.

If you’re interested in handling this automatically in your development environment I’ve come up with a simple pattern that works for me. In some instances such as running your code outside of Eclipse without “hadoop jar” this pattern could fail so only use it specifically for debugging in Eclipse. Here’s what I do:

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.LocalFileSystem;

public class TestClass {
	private static final String CORE_SITE_NAME = "core-site.xml";
	private static final String CORE_SITE_LOCATION = "/etc/hadoop-0.20/conf.empty/"
			+ CORE_SITE_NAME;
	private static final String LOCAL_SEARCH_PATH = "bin/";
	private static final String LOCAL_CORE_SITE_LOCATION = LOCAL_SEARCH_PATH
			+ CORE_SITE_NAME;

	private static boolean updatedConfiguration = false;

	public static void main(String[] args) throws IOException {
		try {
			// Get the configuration and connect to the file system
			Configuration conf = new Configuration();
			FileSystem fs = FileSystem.get(conf);

			// Is this the local file system?
			if (fs instanceof LocalFileSystem) {
				// Yes, we need to do use the cluster. Update the configuration.
				updatedConfiguration = true;

				/**
				 * Remove the file if it already exists. Just in case this is a
				 * symlink or something.
				 */
				removeTemporaryConfigurationFile();

				// Copy the core-site.xml file to where our JVM can see it
				copyConfigurationToTemporaryLocation();

				// Recreate the configuration object
				conf = new Configuration();

				// Get a new file system object
				fs = FileSystem.get(conf);

				// Is this the local file system?
				if (fs instanceof LocalFileSystem) {
					// Yes, give up. We cannot connect to the cluster.
					System.err.println("Failed to connect to the cluster.");
					System.exit(2);
				}
			}

			// Do your HDFS related work here...
		} catch (IOException ioe) {
			// An IOException occurred, give up
			System.err.println("IOException during operation: "
					+ ioe.toString());
			System.exit(1);
		} finally {
			// Did we update the configuration?
			if (updatedConfiguration) {
				// Yes, clean up the temporary configuration file
				removeTemporaryConfigurationFile();
			}
		}
	}

	private static void copyConfigurationToTemporaryLocation()
			throws IOException {
		Runtime.getRuntime().exec(
				new String[] { "cp", CORE_SITE_LOCATION,
						LOCAL_CORE_SITE_LOCATION });
	}

	private static void removeTemporaryConfigurationFile() throws IOException {
		Runtime.getRuntime().exec(
				new String[] { "rm", LOCAL_CORE_SITE_LOCATION });
	}
}

Now where it says “Do your HDFS related work here…” you can put your code and be sure that it’s accessing the cluster, not your local file system.

In a future article and on github I’ll wrap this up in a reusable chunk so that you won’t have to copy and paste this every time you start a new project.

Why I wrote dirhash.pl (a whole directory filename and contents hashing system)

Over the years I have accumulated many, many disks full of data. Ordinarily I migrate them to my latest disk and just keep a backup or two and get rid of the old disks. However, every once in a while I come across an old disk that has a directory structure that I really want to keep but don’t have the time to go through. Recently this happened with a large directory of old projects and to make matters worse there were multiple copies of this directory in different places.

I looked around and found some interesting stuff so I certainly didn’t want to lose this time capsule of code but I now had an interesting problem. How do I take multiple copies of what appear like identical directories and compare them so I can nuke them if they are duplicates?

I could rsync one to the other but that would involve modifying one or both copies and I wasn’t excited about that since there’s the chance that one is newer than the other and I could mix them up. There are flags to check that but I didn’t want to go the potentially destructive route.

I could hash each file and determine which are dupes but with over 20,000 files it becomes tedious work. Dupe removers are OK but it still felt clunky. I just wanted a quick yea or nay as to whether I could give these files the good old “rm -rf”.

Hashing the files, dumping it to a list, and then hashing the list sounds good but there are a few problems:

  • If the files are in different order the results will be different but I can resolve this with “sort”
  • If the hashing program reports the relative path names then the file list will definitely be different and therefore the final hash will be different. I could resolve this with some vi-fu or sed-fu but that leaves me with some work that is just lost when I need to do it again.

My solution was to write a Perl script that rolled all of that logic into one package. It does the following:

  • Gets a list of files in the specified directory
  • Sorts that list
  • Hashes each entry in the list. If it is a file it is hashed and its filename is appended to the hash, if it is a directory it is opened up and those files are run through this process recursively.
  • The final list of hashes and filenames itself is hashed to make sure we get one hash for the entire directory

This solves the order problem and the script removes the relative path elements from each filename automatically so that’s no longer an issue as well.

If you’re looking to see if two directory structures are equal (filenames all equal, no files added or removed between the two of them, and file contents equal) give it a shot and let me know how it works for you.

Why I wrote prcp.pl (copies files with a progress indicator)

Just last night I had a few large files I wanted to bring to a friends house. They were HD videos that I took a long time ago and were about 7 GB in total. I figured I would just plop them on a USB drive and in a few minutes I’d be ready to go.

It didn’t quite work out so easily. 15 minutes after I tried to drag the files to the drive in Nautilus it wasn’t apparent that anything was happening. The progress bar had frozen, the flash drive was still doing something, and some of the files looked like they were there. I tried to do it again on the command line using “cp” and got the same results (with no progress bar). I knew there had to be a better way to copy these files and know what was actually going on but first I had to figure out what was really happening.

The initial discovery was that when “cp” copies a file to a vfat (FAT32) formatted flash drive it immediately allocates enough space for the entire file to fit. This is smart because it makes sure you can’t get stuck copying a file to a drive where it won’t fit. It’s also a pain because I can’t open another terminal window and see the file growing as it is being written to.

My next discovery was that disk caching was making the process difficult to understand. Nautilus appeared to copy the file quickly at first and then it just looked like it hung completely.

So I set out to write a script to fix these problems. I knew I wanted all of the following things:

  • A command-line utility – I wanted to be able to use this program without a GUI
  • A progress bar – I wanted to get visual feedback without babysitting the script
  • Files that “grew” while they were being copied – I wanted to be able to check it remotely in a different terminal session if necessary
  • Predictable behavior with regard to the disk cache – I couldn’t have the disk cache making it seem like the devices were writing very fast, then stalling, then writing very fast again

After an hour I came up with prcp.pl. It is a Perl script that uses mainly Term::ProgressBar and File::Sync to do what I needed. It’s not finished but it’s very functional. What it does is copy the file using sysread and syswrite while fsyncing the output file after each write. This forces what you see on the disk to be consistent with what the program is copying since it largely bypasses the disk cache.

If you’re brave give it a try and let me know what you think. Only use it on data that you have backed up since it has undergone minimal testing so far.

Free tool collection available on github

Today I just released a few home brew tools that I’ve been meaning to write for a while. I’m going to write full articles on them soon but just as a preliminary taste here are descriptions of the two programs I released today:

  • prcp.pl – Copies files and has a progress indicator
  • dirhash.pl – Hashes the names and contents of an entire directory to a single hash

If you use them please post in the comments below. If you want to contribute to them just send me merge requests and open tickets!

Mini Hack: Parallel vacuuming in PostgreSQL

I run several development environments that I need to sync with production databases to do bug fixes and new feature development. It’s always good to vacuum these databases before using them especially if you’re doing it via rsync on a live system. If you don’t you could end up with rows that are inaccessible from your indicies and get strange results from your database.

Full vacuums are slow but we can’t get around it here. What I noticed is that normally in production vacuuming the database is an I/O bound operation but in development where we’re working with dedicated development machines with tons of RAM we typically end up with a lot of the database for our smaller projects (< 5 GB) in the cache. This makes vacuuming a CPU bound process again and where there's a CPU bound process there's usually room for parallelism.

Today I decided to test out how well GNU Parallel could speed up my development machine’s vacuuming process and I’m happy to report that it cuts it nearly in half. If you’re running into the same issue and waiting for vacuuming is eating into your development time try this one liner (make sure you have GNU Parallel installed first):

echo "\dt" | psql DB_NAME | head -n -2 | tail -n +4 | awk ' { print $3 } ' | parallel -I {} vacuumdb -f -v -d DB_NAME -t {}

What this does is:

  • Sends the string “\dt” to PostgreSQL to list the table names
  • Pipes that through head and removes the last two lines (they don’t contain table names)
  • Pipes that through tail and removes the first four lines (they also don’t contain table names)
  • Pipes that to awk and extracts the third field (the table name)
  • Pipes that to parallel, runs vacuumdb with a full vacuum (-f) in verbose mode (-v), placing the table names where we included the curly bracket pair

UPDATE: Ole Tange, the author of the fantastic GNU Parallel package, wrote in with his own one-liner to do the same thing as mine. His is a bit shorter and requires fewer pipes. Take a look:

sql -n --list-tables pg:///DB_NAME | parallel -j0 -r --colsep '\|' sql pg:///DB_NAME vacuum full verbose {2}

What his does is:

  • Gets a table list from GNU sql (which I had never used before, it’s great to know that it exists!)
  • Pipes that to GNU Parallel specifying it should run as many jobs as the machine has cores (-j0), should not run if there is no input (-r), and uses the pipe character as a column separator
  • GNU Parallel then calls GNU sql, connects to the proper database executes a full, verbose vacuum on the second field it extracted from the table list (the table name)

I added in the “full verbose” to Ole’s example so the two scripts are doing the same work instead of just a plain vacuum.

Compare that against the run time for a normal vacuum and report your results in the comments. For databases that won’t fit in your RAM it may not help that much but I’d like to hear either way.

How-To: Set up multi-head X in Debian using Nvidia cards

I just had to set up a multi-head system in Debian and it consumed way too much of my time. I did see a lot of articles about it online but they all had a few common threads.

First, they all mentioned Xinerama was deprecated which initially scared me off of it. Xinerama is deprecated but there’s no replacement so for now I’m using it until it’s replacement is mature. Wayland is a system that keeps getting mentioned but seems too far off to be considered for me.

Second, they didn’t clearly outline how they determined all of their display information. Some were better than others but I need to build a real list of commands to get things done properly and repeatably since I know I will have to do this again at some point.

So, let’s go over what you need to do and how I got to a working system with three side-by-side displays.

My system consists of two Nvidia GTX 470 cards. Each card has two DVI outputs but I only have three displays. Therefore one card has two displays connected to it (the left and right displays) and another card has one display connected to it (the center display).

What you’ll want to do is first figure out which display is connected to which card and how Linux enumerates them in your system. To do this you should be in text mode (not X!) and go to the “/sys/class/drm” directory. Let’s take a look at mine:


tim@desktop:/sys/class/drm$ ls -la
total 0
drwxr-xr-x  2 root root    0 Jan 15 19:21 .
drwxr-xr-x 40 root root    0 Jan 15 19:21 ..
lrwxrwxrwx  1 root root    0 Jan 15 19:21 card0 -> ../../devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/card0
lrwxrwxrwx  1 root root    0 Jan 15 19:21 card0-DVI-I-1 -> ../../devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/card0/card0-DVI-I-1
lrwxrwxrwx  1 root root    0 Jan 15 19:21 card0-DVI-I-2 -> ../../devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/card0/card0-DVI-I-2
lrwxrwxrwx  1 root root    0 Jan 15 19:21 card0-HDMI-A-1 -> ../../devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/card0/card0-HDMI-A-1
lrwxrwxrwx  1 root root    0 Jan 15 19:21 card1 -> ../../devices/pci0000:00/0000:00:03.0/0000:02:00.0/drm/card1
lrwxrwxrwx  1 root root    0 Jan 15 19:21 card1-DVI-I-3 -> ../../devices/pci0000:00/0000:00:03.0/0000:02:00.0/drm/card1/card1-DVI-I-3
lrwxrwxrwx  1 root root    0 Jan 15 19:21 card1-DVI-I-4 -> ../../devices/pci0000:00/0000:00:03.0/0000:02:00.0/drm/card1/card1-DVI-I-4
lrwxrwxrwx  1 root root    0 Jan 15 19:21 card1-HDMI-A-2 -> ../../devices/pci0000:00/0000:00:03.0/0000:02:00.0/drm/card1/card1-HDMI-A-2
lrwxrwxrwx  1 root root    0 Jan 15 19:21 controlD64 -> ../../devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/controlD64
lrwxrwxrwx  1 root root    0 Jan 15 19:21 controlD65 -> ../../devices/pci0000:00/0000:00:03.0/0000:02:00.0/drm/controlD65
lrwxrwxrwx  1 root root    0 Jan 15 19:21 ttm -> ../../devices/virtual/drm/ttm
-r--r--r--  1 root root 4096 Jan 15 19:21 version

Here you can see that I have two cards named card0 and card1. Card 0 has three connectors on it named card0-DVI-I-1, card0-DVI-2, and card0-HDMI-A-1. Card 1 has three connectors on it named card1-DVI-3, card1-DVI-4, card1-HDMI-A-2. We will need this information later to build our xorg.conf so keep it handy.

Now you’ll want to determine which monitor is hooked up to which card and port. If you are using this setup in Windows already you are not guaranteed that the cards are recognized in the same order so just forget what Windows has told you. To figure out which is which first make sure all of your displays are connected to your cards. Then ask Linux which displays are connected by executing this command:

ls /sys/class/drm/*/status | xargs -I {} -i bash -c "echo -n {}: ; cat {}"

This will show you which connectors think that they have something connected to them. My output looks like this:

tim@desktop:/sys/class/drm$ ls /sys/class/drm/*/status | xargs -I {} -i bash -c "echo -n {}: ; cat {}"
/sys/class/drm/card0-DVI-I-1/status:connected
/sys/class/drm/card0-DVI-I-2/status:connected
/sys/class/drm/card0-HDMI-A-1/status:disconnected
/sys/class/drm/card1-DVI-I-3/status:connected
/sys/class/drm/card1-DVI-I-4/status:disconnected
/sys/class/drm/card1-HDMI-A-2/status:disconnected

So now I know that card0 is the card that has my two displays attached to it and card1 is the card that has my two displays attached to it. That tells me that “DVI-I-3″ is my center display and “DVI-I-1″ and “DVI-I-2″ are my left and right displays. We’ll figure out which is left and which is right in a minute.

For now, let’s figure out which PCI device Linux considers these devices. You can do this by running this command:

ls -la /sys/class/drm/card?

My output looks like this:

tim@desktop:~$ ls -la /sys/class/drm/card?
lrwxrwxrwx 1 root root 0 Jan 15 19:21 /sys/class/drm/card0 -> ../../devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/card0
lrwxrwxrwx 1 root root 0 Jan 15 19:21 /sys/class/drm/card1 -> ../../devices/pci0000:00/0000:00:03.0/0000:02:00.0/drm/card1

Look at the numbers directly before “drm”. They are “0000:01:00.0″ for card 0 and “0000:02:00.0″ for card 1. This tells us that card 0 is PCI device 1 and card 1 is PCI device 2. Keep these numbers handy too.

The next step is to figure out which connectors is the left and which is the right. You can do this by guessing but let’s just get a definitive answer. Type the same command we used before to get the list of all of our displays (or press the up arrow if your shell supports that to get to the last command) but don’t run it yet. Now disconnect one display, press enter, and reconnect it. I suggest you do this so that just in case you disconnect the display that your terminal is running on you can still run the command and see the results when you reconnect it.

In my case I got this output when my right display was disconnected:

tim@desktop:/sys/class/drm$ ls /sys/class/drm/*/status | xargs -I {} -i bash -c "echo -n {}: ; cat {}"
/sys/class/drm/card0-DVI-I-1/status:disconnected
/sys/class/drm/card0-DVI-I-2/status:connected
/sys/class/drm/card0-HDMI-A-1/status:disconnected
/sys/class/drm/card1-DVI-I-3/status:connected
/sys/class/drm/card1-DVI-I-4/status:disconnected
/sys/class/drm/card1-HDMI-A-2/status:disconnected

Now I know that “DVI-I-1″ is my right display since before it was connected and when I ran the command it was disconnected. By elimination I know “DVI-I-2″ is my right display. If you have more displays you’ll need to do this a few more times.

Let’s recap. I know that card 0 has two displays on it, its DVI-I-1 connector is my right display, its DVI-I-2 connector is my left display. Card 1 has one display on it and its DVI-I-3 connector is my center display. With that information I can start building my xorg.conf file. I’ll show you what I ended up with and then break it out and explain it:

Section "ServerLayout"
	Identifier  "X.org Configured"
	Option      "Xinerama"            "on"
	Screen      0  "Screen0" 0 0
	Screen      1  "Screen1" RightOf  "Screen0"
	Screen      2  "Screen2" LeftOf   "Screen0"
	InputDevice    "Mouse0"           "CorePointer"
	InputDevice    "Keyboard0"        "CoreKeyboard"
EndSection

Section "Files"
	ModulePath   "/usr/lib/xorg/modules"
	FontPath     "/usr/share/fonts/X11/misc"
	FontPath     "/usr/share/fonts/X11/cyrillic"
	FontPath     "/usr/share/fonts/X11/100dpi/:unscaled"
	FontPath     "/usr/share/fonts/X11/75dpi/:unscaled"
	FontPath     "/usr/share/fonts/X11/Type1"
	FontPath     "/usr/share/fonts/X11/100dpi"
	FontPath     "/usr/share/fonts/X11/75dpi"
	FontPath     "/var/lib/defoma/x-ttcidfont-conf.d/dirs/TrueType"
	FontPath     "built-ins"
EndSection

Section "Module"
	Load  "glx"
	Load  "dbe"
	Load  "dri"
	Load  "record"
	Load  "dri2"
	Load  "extmod"
EndSection

Section "InputDevice"
	Identifier  "Keyboard0"
	Driver      "kbd"
EndSection

Section "InputDevice"
	Identifier  "Mouse0"
	Driver      "mouse"
	Option	    "Protocol" "auto"
	Option	    "Device" "/dev/input/mice"
	Option	    "ZAxisMapping" "4 5 6 7"
EndSection

Section "Device"
	Identifier  "Device1"
	Driver      "nouveau"
	BusID       "PCI:1:0:0"
	Option      "ZaphodHeads" "DVI-I-1"
	Screen      0
EndSection

Section "Device"
	Identifier  "Device2"
	Driver      "nouveau"
	BusID       "PCI:1:0:0"
	Option      "ZaphodHeads" "DVI-I-2"
	Screen      1
EndSection

Section "Device"
	Identifier  "Device0"
	Driver      "nouveau"
	BusID       "PCI:2:0:0"
	Option      "ZaphodHeads" "DVI-I-3"
	Screen      0
EndSection

Section "Screen"
	Identifier	"Screen0"
	Device		"Device0"
EndSection

Section "Screen"
	Identifier	"Screen1"
	Device		"Device1"
EndSection

Section "Screen"
	Identifier	"Screen2"
	Device		"Device2"
EndSection

Here’s the explanation of each section:

Section "ServerLayout"
	Identifier  "X.org Configured"
	Option      "Xinerama"            "on"
	Screen      0  "Screen0" 0 0
	Screen      1  "Screen1" RightOf  "Screen0"
	Screen      2  "Screen2" LeftOf   "Screen0"
	InputDevice    "Mouse0"           "CorePointer"
	InputDevice    "Keyboard0"        "CoreKeyboard"
EndSection

This is the server layout section that declares how the screens are laid out and what input devices we’re using. It specifies a name of “X.org Configured” since I used another configuration as a baseline for this. You can change that name to whatever you’d like. Then it specifies that Xinerama is on. This means that you can drag windows back and forth between screens. It has some downsides but they are outweighed by the convenience of placing windows anywhere across all of your displays.

Then it specifies three “screen” entries. Later on “Screen0″ will by my center screen. “0 0″ just means that that screen starts at pixel 0,0. “Screen1″ will be might right screen so it will be to the right of my center screen. In xorg.conf language that is done with “RightOf”. “Screen2″ will be my left screen so it is “LeftOf” my center screen.

Section "Files"
	ModulePath   "/usr/lib/xorg/modules"
	FontPath     "/usr/share/fonts/X11/misc"
	FontPath     "/usr/share/fonts/X11/cyrillic"
	FontPath     "/usr/share/fonts/X11/100dpi/:unscaled"
	FontPath     "/usr/share/fonts/X11/75dpi/:unscaled"
	FontPath     "/usr/share/fonts/X11/Type1"
	FontPath     "/usr/share/fonts/X11/100dpi"
	FontPath     "/usr/share/fonts/X11/75dpi"
	FontPath     "/var/lib/defoma/x-ttcidfont-conf.d/dirs/TrueType"
	FontPath     "built-ins"
EndSection

Section "Module"
	Load  "glx"
	Load  "dbe"
	Load  "dri"
	Load  "record"
	Load  "dri2"
	Load  "extmod"
EndSection

I left these two sections completely stock. They’re magic for now so don’t make any changes to them.

Section "InputDevice"
	Identifier  "Keyboard0"
	Driver      "kbd"
EndSection

Section "InputDevice"
	Identifier  "Mouse0"
	Driver      "mouse"
	Option	    "Protocol" "auto"
	Option	    "Device" "/dev/input/mice"
	Option	    "ZAxisMapping" "4 5 6 7"
EndSection

These two sections specify our input devices. These are stock also so you can leave them alone too.

Section "Device"
	Identifier  "Device1"
	Driver      "nouveau"
	BusID       "PCI:1:0:0"
	Option      "ZaphodHeads" "DVI-I-1"
	Screen      0
EndSection

Here’s some more configuration we have to do. Remember that DVI-I-1 is our right display and we are calling it “Screen1″. In this section we need to create a device for it that tells X which drive, card, and connector it uses as well as which screen it is relative to its specific graphics card. We’ll call it “Device1″, use the open-source Nvidia driver called “Nouveau”, tell it to use PCI device 1 (since card 0 was device 1) with the “PCI:1:0:0″ string, then tell it the connector is “DVI-I-1″ with the “ZaphodHeads” option, and finally that this is the first display (display 0) on this graphics card.

Section "Device"
	Identifier  "Device2"
	Driver      "nouveau"
	BusID       "PCI:1:0:0"
	Option      "ZaphodHeads" "DVI-I-2"
	Screen      1
EndSection

For our left display we have similar settings but the connector is “DVI-I-2″ and this is the second display (display 1) on this graphics card. We also call this “Device2″.

Section "Device"
	Identifier  "Device0"
	Driver      "nouveau"
	BusID       "PCI:2:0:0"
	Option      "ZaphodHeads" "DVI-I-3"
	Screen      0
EndSection

Our center screen is “Device0″ and it is on a different graphics card. Remember card 1 was PCI device 2 so we use “PCI:2:0:0″ here and specify the connector as “DVI-I-3″. This is the first display (display 0) on this graphics card.

Section "Screen"
	Identifier	"Screen0"
	Device		"Device0"
EndSection

Section "Screen"
	Identifier	"Screen1"
	Device		"Device1"
EndSection

Section "Screen"
	Identifier	"Screen2"
	Device		"Device2"
EndSection

Finally, these sections map our devices to name screens. In this case DeviceX is ScreenX. Nothing fancy goes on here.

Replace or create your xorg.conf using the steps you see here and you should be up and running in no time. I had to change from Gnome to Xfce since Gnome had a really tall black bar that took over my center display that I couldn’t get rid of. If you run into the same problem try Xfce or another window manager instead.

Good luck and post in the comments if you found this useful or need some assistance.

How-To: Find and fix duplicate Java object serial numbers

In Java making your objects serializable is often required to work with certain libraries (ORM, GWT-RPC, etc). It’s tempting when Eclipse tells us that our object is missing a unique identifier to have it generate one on the fly. However, in practice what happens sometimes in the field is that you’ll create a serializable class and then copy it to make a new class. When you do this you’re inadvertently copying the objects “serialVersionUID” field.

Why is this an issue? Well, the serialVersionUID is supposed to be a universal identifier. If it’s not universal then you’re asking for trouble. When you deserialize a new version of this object or try to deserialize an object to the wrong object type you can end up with strange results or have your application crash altogether.

How do we fix it? I’ve written a command-line one liner (even though it looks like three lines on the site) that will print out a list of the files that have duplicated serialVersionUID values. Here it is:

grep -r serialVersionUID * | grep -o "private.*" | sort | uniq -d | sed 's/^.*=//' | sed 's/;//' | sed 's/^ //' | sed 's/^-/\\\\-/' | xargs -I PATTERNS grep -r PATTERNS . | cut -f1 -d ':'

Go into each of these files, remove their serialVersionUID field, and regenerate them. Don’t concern yourself with which ones overlap unless you already have serialized versions of these objects lying around. If you do have serialized versions you should remove the field and reserialize your objects into your data store without it. Then you can try to generate new values and again reserialize them into the data store. DO NOT do this in production, always do it in a test environment to make sure that nothing breaks. You do have unit tests that’ll test these things, right? :)

Post in the comments if you find this useful and keep hacking!

Mobile Analytics by Mixpanel