subversionなどコミット時にチェンジログを書き、後からチェンジログにスペルミスなどや間違った事を書いていてチェンジログを編集したいときがある。このような場合はsvnadminのコマンドを使用しチェンジログを編集することができる。この方法は編集すると前のログが消えるので慎重に行わなければならない。

やり方は以下のようになる。

  1. 最初に、リポジトリがあるサーバーにログインする。ssh+svnのようにネット経由では編集できないようだ。
  2. 編集したいリビジョンのチェンジログのバックアップ
  3. チェンジログの編集
  4. リビジョンのアップデート

リビジョン100の内容を編集したいときは以下のように行う。

$ ssh server
$ svn log file:///usr/local/svn/repositoy/ -r 100 > r100.log.orig
$ cp r100.log.orig r100.log
$ vi r100.log #日付や枠を消すこと
$ svnadmin setlog --bypass-hooks repository -r 100 r100.log

svnadming setlog オプションについて

-r
リビジョン番号
--bypass-hooks
デフォルトではフックスクリプトで拒否されるので、強制的に変更するときはこのオプションが必要になる。

Linuxデバイスドライバを書いるとき、insmod module.ko, rmmod moduleを繰り返しているうちに、デバッグメッセージがいつ実行したドライバのメッセージがわからなくなる。いつ実行したか確実にわかるように、insmod, rmmodなどを実行したとき、printk()で現在の時刻を表示したいのだが、ctime(3)のような関数がカーネル内では存在しないようだ。

do_gettimeofday()でUNIX時間を取得し変換する方法もあるが、しかしこの方法は結構めんどくさいのもっと手軽にRTC(Real Time Clock)の情報を使えばいいのではないかと思う。サンプルのプログラムは以下の様になる。

testdrv.c

#include <linux/init.h>
#include <linux/module.h>
#include <asm-generic/rtc.h>
MODULE_LICENSE("Dual BSD/GPL");

static char *months[12] ={"Jan", "Feb", "Mar", "Apr", "May", "Jun",
                          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

#define LOG(msg, ...)				\
	_log(__FILE__, __LINE__);		\
	printk(msg, ## __VA_ARGS__)

static inline void _log(const char *file, const int line)
{
	struct rtc_time t;
	get_rtc_time(&t);

	printk("%s %d %d:%d:%d %d: %s:%d: ",
	       months[t.tm_mon], t.tm_mday, t.tm_hour, t.tm_min,
               t.tm_sec, 2000 + (t.tm_year % 10), file, line);
}

static int testdrv_init(void)
{
	LOG("test driver init");

	return 0;
}

static void testdrv_exit(void)
{
	LOG("test driver exit");
}

module_init(testdrv_init);
module_exit(testdrv_exit);

Makefile

obj-m := testdrv.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)

.PHONY: modules
modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD)

.PHONY: clean
clean:
	rm -rf *.o *~ core .*.cmd *.ko *.mod.c .tmp_versions Module.symvers

上のコードを試すと以下のような出力結果を得られる。

$ make
$ sudo insmod testdrv.ko
$ dmesg
...
...
Sep 17 2:34:20 2007: /home/yusuke/work/testdrv/testdrv.c:22: test driver init
$ sudo rmmod testdrv
$ dmesg
...
...
Sep 17 2:40:41 2007: /home/yusuke/work/testdrv/testdrv.c:29: test driver exit

デバイスドライバなどを書いているときに、便利なマクロがいろいろあるがこのマクロがどのようになっているか知りたいとき、マクロが書かれているソースコードを見るのもいいが他にも方法がある。EmacsからC言語プリプロセッサを呼出しマクロ展開したコードを出力することができる。

Emacsでマクロ展開を行うときは展開したい文字列をリージョンで選択しC-c C-eまたはM-x c-macro-expandを行うことにより、新しいウィンドウにマクロ展開された文字列が出力される。

マクロ展開するソースコードはヘッダファイルを見て展開を行うがデバイスドライバなどを書いているときは/usr/includeからヘッダファイルを読み込むのではなく、/usr/src/linux/includeのヘッダファイルを読み込んでほしい。このような場合は.emacsなどに以下の一文を追加すればうまくいく。

(setq c-macro-cppflags "-I/usr/src/linux/include")

実行すると以下の様になる。

Debian公式のLinux Kernelを使用していたときは、VMware Workstation6が問題なく動いてくれたけど、最新のカーネルをインストールを行い、以下のようにVMWareの設定をするとVMWareコンパイルに失敗しVMWareが使えない。

$ sudo vmware-config.pl

この問題を解決するために、パッチがあるようだ。パッチをダウンロードしrunme.plを実行するだけでいいようだ。この問題はVMware PlayerやVMware Serverなどにも同じような現象が起きているらしく、この方法で解決できるでのではないかと思う。

$ wget http://platan.vc.cvut.cz/ftp/pub/vmware/vmware-any-any-update113.tar.gz
$ tar zxvf vmware-any-any-update113.tar.gz
$ cd vmware-any-any-update113
$ sudo ./runme.pl

Linux Kernelの最新のコードを入手するにはgitやftpで直接Linux Kernel Archiveからダウンロードすることも可能だが、gitはコマンドを覚えるのも大変だし、Linux Kernel Archiveから落とすのは毎回アクセスするのがめんどくさい。そこでketchupを使うことで最新のLinux Kernelのコードを簡単に入手することができる。

インストールの仕方はDebianではapt-get ketchupでインストールが可能、ketchupはpythonのコードなのでhttp://www.selenic.com/ketchup/ここから落とせば他のディストリビューションMacなどでも使用できる。

使いかたは、次の様になる。

$ cd /usr/src
$ mkdir linux # ディレクトリ名はなんでもよい
$ cd linux
$ ketchup -r 2.6  # シグネチャでミスるときは-Gをつけると無視できる。

最新のLinux Kernelのコードが/usr/src/linux-2.6.xx.xの様に展開される。

ketchupは引数 -rに値を変更することによりブランチを変えることができる。詳細はhttp://www.selenic.com/ketchup/wiki/index.cgi/KernelTreesを参照する。
良く使う引数は以下になるだろう。

  • -r 2.6 # stable
  • -r 2.6-rc # RC版
  • -r 2.6-mm

必要なものは最新のカーネルとビルドツールが必要になる。ゲストのUMLからホストOSのファイルシステムをアクセスできるhostfsを使用すると便利である。

UMLコンパイルは以下のようにするののがお薦めである。

$ make defconfig ARCH=um
$ make gconfig ARCH=um # ここでUML-specific optionのHost Filesystemを有効にする。
$ make ARCH=um

次にUMLが使用するルートファイルシステムを用意する。自前で用意してもいいがhttp://uml.nagafix.co.uk/ここにディストリビューションごとのルートファイルシステムがあるので利用すると便利である。

ここではDebianのルートファイルシステムを使い、UMLの実行の仕方は次の様になる。

$ wget http://uml.nagafix.co.uk/Debian-4.0/Debian-4.0-x86-root_fs.bz2
$ bunzip2 Debian-4.0-x86-root_fs.bz2
$ cp /usr/src/uml/linux-2.6.22.6/vmlinux ./
$ ./vmlinux ubda=Debian-4.0-x86-root_fs mem=512M 

実行するとUMLが利用できるはず、次にgdbを使いゲストOSであるUMLデバッグを行う。
手順は次のように行う。

1. Emacsを立ち上げAlt-x gdbgdbを立ち上げ
2. 立ち上げたgdbのプロセスIDをメモする。

$ ps -C gdb
  PID TTY          TIME CMD
21701 pts/6    00:00:00 gdb

3. UMLの起動時の引数にgdb-pid=gdbのプロセスIDを指定し、UMLを実行する。

$ ./vmlinux ubda=Debian-4.0-x86-root_fs mem=512M gdb-pid=21701

4. UMLのプロセスIDをメモする。たくさん表示されるが最初の一つだけでいい。

$ ps -C vmlinux
  PID TTY          TIME CMD
22610 pts/1    00:00:02 vmlinux
22616 pts/1    00:00:00 vmlinux
22617 pts/1    00:00:00 vmlinux
22618 pts/1    00:00:00 vmlinux
22619 pts/1    00:00:00 vmlinux
23190 pts/1    00:00:00 vmlinux
23200 pts/1    00:00:00 vmlinux
23232 pts/1    00:00:00 vmlinux
23277 pts/1    00:00:00 vmlinux
23281 pts/1    00:00:00 vmlinux

5. Emacsgdbモードでattachの引数にUMLのプロセスIDを指定する。アッタチ後はいつもの様にnext, step実行でプログラムを実行することができる。デバッグを抜けたいならdettachを実行することにより、元の状態に戻る。

(gdb) attach 22610

スクリーンショット

Emacsの画面でソースコードを見ながらデバッグをできるのでカーネルデバッグの効率がとてもいい。

VBEのデバイスドライバを書いた後、画面に点や線、四角などは簡単に表示をできるが文字(フォント)を表示することは難しい。しかしビットマップのフォントデータさえあれば簡単に文字を表示することができる。Linux Kernelの/usr/src/linux/drivers/video/のディレクトリにビットマップフォントのファイルがあるのでこれを利用すれば簡単に画面に文字を表示することができる。

フォントファイルはC言語のファイルで保存されている。フォーマットは以下のようになっている。

static const unsigned char fontdata_8x16[FONTDATAMAX] = {

    ...

    /* 65 0x41 'A' */
    0x00, /* 00000000 */
    0x00, /* 00000000 */
    0x10, /* 00010000 */
    0x38, /* 00111000 */
    0x6c, /* 01101100 */
    0xc6, /* 11000110 */
    0xc6, /* 11000110 */
    0xfe, /* 11111110 */
    0xc6, /* 11000110 */
    0xc6, /* 11000110 */
    0xc6, /* 11000110 */
    0xc6, /* 11000110 */
    0x00, /* 00000000 */
    0x00, /* 00000000 */
    0x00, /* 00000000 */
    0x00, /* 00000000 */

    ...
}

フォントの表示はビットをマスクすることによって1ビットずつ表示すれば画面に文字が表示できる。
例えば、8xNビット限定のビットマップフォントを表示するfont.cとfont.hは以下の様になる。

font.h

#define BIT(x) (1 << x)

struct font_descr {
	const char *name;
	uint32_t width;
	uint32_t height;
	uint32_t count;
	const unsigned char *data;
};

extern const struct font_descr font_vga_10x18;
extern const struct font_descr font_vga_7x14;
extern const struct font_descr font_vga_8x14;
extern const struct font_descr font_vga_8x16;
extern const struct font_descr font_vga_8x8;
extern const struct font_descr font_acorn_8x8;
extern const struct font_descr font_mini_4x6;
extern const struct font_descr font_pearl_8x8;
extern const struct font_descr font_profont_6x11;
extern const struct font_descr font_sun_12x22;
extern const struct font_descr font_sun_8x16;

font.c

int putfont(int c, uint32_t offset_x, uint32_t offset_y)
{
	uint32_t x, y;
	struct font_descr *font = &font_vga_8x16;

	if (c < 0 || 255 < c)
		return -1;

	for (y = 0; y < font->height; y++) {
		for (x = 0; x < font->width; x++) {
			if (font->data[c * font->height + y] & BIT(font->width - x))
				set_color(WHITE);
			else
				set_color(BLACK);
			set_pixel(x + offset_x, y + offset_y);
		}
	}

	return 0;
}

スクリーンショット

文字を表示してみた。