Subject: Symbol lookups.
To: None <tech-userlevel@netbsd.org>
From: None <ragge@ludd.luth.se>
List: tech-userlevel
Date: 05/07/2003 23:51:50
I have just added some code to do fast symbol table lookups, and
when testing the ioctl's to retrieve symbols from the kernel I found
that it is 3-5 times faster than doing a lookup in kvm.db.

Example (on a Sparc Classic):
Timing 77520 calls to db...took 42 seconds.
Timing 77520 calls to /dev/ksyms...took 16 seconds.

On an Alpha 433au:
Timing 1083400 calls to db...took 44 seconds.
Timing 1083400 calls to /dev/ksyms...took 8 seconds.

This shows that it is quite a gain in speed on some archs :-)

As a result of this, I think there may be a reason to remove the
kvm database alltogether, especially because it would need a bunch
of extra massage anyway to update it if modules are loaded/unloaded;
and fall back to the old "nlist" way of retreiving kernel symbols
if /dev/ksyms is not in the kernel?

Or some other strategy? I want comments here.

Attached here is a small test program that shows the lookup time
difference.

Use it by just compiling:

# cc -o foo foo.c
# nm -p -g /netbsd | awk '{print $3}' > ksfil
# ./foo < ksfil

-- Ragge

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/ksyms.h>
#include <sys/time.h>

#include <stdio.h>
#include <strings.h>
#include <err.h>
#include <db.h>
#include <paths.h>
#include <fcntl.h>
#include <unistd.h>

#define	NITER 100

char *syms[50000];

int
main(int argc, char *argv[])
{
	struct ksyms_gsymbol kg;
	DB *db;
	DBT rec;
	struct timeval tv, tv2, tv3, tv4;
	long val;
	int nstr, fd, i, j, rv;
	char *str;
	char buf[80];

	/* Read in symbol strings */
	nstr = 0;
	while ((str = gets(&buf[1])) != NULL) {
		buf[0] = '_';
		syms[nstr++] = strdup(buf);
	}


	if ((fd = open("/dev/ksyms", O_RDONLY)) < 0)
		err(1, "open /dev/ksyms");

	if ((db = dbopen(_PATH_KVMDB, O_RDONLY, 0, DB_HASH, NULL)) == NULL)
		err(1, "dbopen");

	printf("Timing %d calls to db...", nstr * NITER);
	gettimeofday(&tv, NULL);

	for (j = 0; j < NITER; j++) {
		for (i = 0; i < nstr; i++) {
			rec.data = syms[i];
			rec.size = strlen(syms[i]);
			if ((rv = (db->get)(db, (DBT *)&rec, (DBT *)&rec, 0))) {
				if (rv < 0)
					warn("no sym %s", syms[i]);
				else
					warnx("cannot find %s", syms[i]);
			}

		}
	}
	gettimeofday(&tv2, NULL);
	printf("took %ld seconds.\n\n", tv2.tv_sec - tv.tv_sec);

	printf("Timing %d calls to /dev/ksyms...", nstr * NITER);
	gettimeofday(&tv3, NULL);

	for (j = 0; j < NITER; j++) {
		for (i = 0; i < nstr; i++) {
			kg.kg_name = &syms[i][1];
			kg.kg_value = &val;
			if (ioctl(fd, KIOCGVALUE, &kg) < 0)
				warnx("cannot get %s", syms[i]);
		}
	}
	gettimeofday(&tv4, NULL);
	printf("took %ld seconds.\n\n", tv4.tv_sec - tv3.tv_sec);

	close(fd);
	(db->close)(db);
}