Skip to contentSkip to conversation

symbols for all out-of-line code

PATCH New John Levon 1 participant 1 message Mar 11, 2002

Subsystems (3)
lore
Share & tools
lore

John Levon

author
#0 lore raw

PATCH 1/1 symbols for all out-of-line code

2.4.18 includes a patch to set a local symbol for the start of out-of-line .text.lock (now subsection 1 of .text) code, but it merges the entire translation unit's out of line code under the one symbol, and can give erroneous results when two files have the same KBUILD_BASENAME. The patch below details exactly where such code comes from, so you can get :

...

    53 __stabslock-at-dec_and_lock.c-line-35 2.2083

233 __stabs&pagemap_lru_lock-at-swap.c-line-49 14.5625 381 __stabs&kernel_flag-at-sched.c-line-701 23.8125
...

from readprofile (actually you need a modified version, attached).

(I haven't yet updated this for kaos' LOCK_START etc. patch)

regards
john

arch/i386/config.in renamed from arch/i386/config.in.old +1 -0
@@ -421,6 +421,7 @@
    bool '  Memory mapped I/O debugging' CONFIG_DEBUG_IOVIRT
    bool '  Magic SysRq key' CONFIG_MAGIC_SYSRQ
    bool '  Spinlock debugging' CONFIG_DEBUG_SPINLOCK
+   bool '  .text.lock debugging symbols' CONFIG_DEBUG_TEXT_LOCK_STABS
    bool '  Verbose BUG() reporting (adds 70K)' CONFIG_DEBUG_BUGVERBOSE
 fi
 
include/asm-i386/text_lock_stabs.h new file +44 -0

44 lines not on this page · raw message

include/linux/rwsem.h renamed from include/linux/rwsem.old.h +28 -40

73 lines not on this page · raw message

include/asm-i386/semaphore.h renamed from include/asm-i386/semaphore.old.h +10 -16
@@ -38,6 +38,7 @@
 
 #include <asm/system.h>
 #include <asm/atomic.h>
+#include <asm/text_lock_stabs.h>
 #include <linux/wait.h>
 #include <linux/rwsem.h>
 #include <linux/stringify.h>
@@ -112,7 +113,7 @@
  * "__down_failed" is a special asm handler that calls the C
  * routine that actually waits. See arch/i386/kernel/semaphore.c
  */
-static inline void down(struct semaphore * sem)
+static inline void p__down(struct semaphore * sem)
 {
 #if WAITQUEUE_DEBUG
 	CHECK_MAGIC(sem->__magic);
@@ -124,9 +125,6 @@
 		"js 2f\n"
 		"1:\n"
 		".subsection 1\n"
-		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
-		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
-		".endif\n"
 		"2:\tcall __down_failed\n\t"
 		"jmp 1b\n"
 		".subsection 0\n"
@@ -139,7 +137,7 @@
  * Interruptible try to acquire a semaphore.  If we obtained
  * it, return zero.  If we were interrupted, returns -EINTR
  */
-static inline int down_interruptible(struct semaphore * sem)
+static inline int p__down_interruptible(struct semaphore * sem)
 {
 	int result;
 
@@ -154,9 +152,6 @@
 		"xorl %0,%0\n"
 		"1:\n"
 		".subsection 1\n"
-		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
-		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
-		".endif\n"
 		"2:\tcall __down_failed_interruptible\n\t"
 		"jmp 1b\n"
 		".subsection 0\n"
@@ -170,7 +165,7 @@
  * Non-blockingly attempt to down() a semaphore.
  * Returns zero if we acquired it
  */
-static inline int down_trylock(struct semaphore * sem)
+static inline int p__down_trylock(struct semaphore * sem)
 {
 	int result;
 
@@ -185,9 +180,6 @@
 		"xorl %0,%0\n"
 		"1:\n"
 		".subsection 1\n"
-		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
-		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
-		".endif\n"
 		"2:\tcall __down_failed_trylock\n\t"
 		"jmp 1b\n"
 		".subsection 0\n"
@@ -203,7 +195,7 @@
  * The default case (no contention) will result in NO
  * jumps for both down() and up().
  */
-static inline void up(struct semaphore * sem)
+static inline void p__up(struct semaphore * sem)
 {
 #if WAITQUEUE_DEBUG
 	CHECK_MAGIC(sem->__magic);
@@ -214,9 +206,6 @@
 		"jle 2f\n"
 		"1:\n"
 		".subsection 1\n"
-		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
-		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
-		".endif\n"
 		"2:\tcall __up_wakeup\n\t"
 		"jmp 1b\n"
 		".subsection 0\n"
@@ -224,6 +213,11 @@
 		:"c" (sem)
 		:"memory");
 }
+
+#define down(s) __stabs_common(p__down, s)
+#define down_interruptible(s) __stabs_common(p__down_interruptible, s)
+#define down_trylock(s) __stabs_common(p__down_trylock, s)
+#define up(s) __stabs_common(p__up, s)
 
 #endif
 #endif
include/asm-i386/rwsem.h renamed from include/asm-i386/rwsem.old.h +10 -16
@@ -38,6 +38,7 @@
 
 #ifdef __KERNEL__
 
+#include <asm/text_lock_stabs.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
 #include <linux/stringify.h>
@@ -95,7 +96,7 @@
 /*
  * lock for reading
  */
-static inline void __down_read(struct rw_semaphore *sem)
+static inline void p__down_read(struct rw_semaphore *sem)
 {
 	__asm__ __volatile__(
 		"# beginning down_read\n\t"
@@ -103,9 +104,6 @@
 		"  js        2f\n\t" /* jump if we weren't granted the lock */
 		"1:\n\t"
 		".subsection 1\n"
-		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
-		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
-		".endif\n"
 		"2:\n\t"
 		"  pushl     %%ecx\n\t"
 		"  pushl     %%edx\n\t"
@@ -123,7 +121,7 @@
 /*
  * lock for writing
  */
-static inline void __down_write(struct rw_semaphore *sem)
+static inline void p__down_write(struct rw_semaphore *sem)
 {
 	int tmp;
 
@@ -135,9 +133,6 @@
 		"  jnz       2f\n\t" /* jump if we weren't granted the lock */
 		"1:\n\t"
 		".subsection 1\n"
-		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
-		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
-		".endif\n"
 		"2:\n\t"
 		"  pushl     %%ecx\n\t"
 		"  call      rwsem_down_write_failed\n\t"
@@ -153,7 +148,7 @@
 /*
  * unlock after reading
  */
-static inline void __up_read(struct rw_semaphore *sem)
+static inline void p__up_read(struct rw_semaphore *sem)
 {
 	__s32 tmp = -RWSEM_ACTIVE_READ_BIAS;
 	__asm__ __volatile__(
@@ -162,9 +157,6 @@
 		"  js        2f\n\t" /* jump if the lock is being waited upon */
 		"1:\n\t"
 		".subsection 1\n"
-		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
-		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
-		".endif\n"
 		"2:\n\t"
 		"  decw      %%dx\n\t" /* do nothing if still outstanding active readers */
 		"  jnz       1b\n\t"
@@ -182,7 +174,7 @@
 /*
  * unlock after writing
  */
-static inline void __up_write(struct rw_semaphore *sem)
+static inline void p__up_write(struct rw_semaphore *sem)
 {
 	__asm__ __volatile__(
 		"# beginning __up_write\n\t"
@@ -191,9 +183,6 @@
 		"  jnz       2f\n\t" /* jump if the lock is being waited upon */
 		"1:\n\t"
 		".subsection 1\n"
-		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
-		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
-		".endif\n"
 		"2:\n\t"
 		"  decw      %%dx\n\t" /* did the active count reduce to 0? */
 		"  jnz       1b\n\t" /* jump back if not */
@@ -235,5 +224,10 @@
 	return tmp+delta;
 }
 
+#define __down_read(s) __stabs_common(p__down_read, s)
+#define __down_write(s) __stabs_common(p__down_write, s)
+#define __up_read(s) __stabs_common(p__up_read, s)
+#define __up_write(s) __stabs_common(p__up_write, s)
+ 
 #endif /* __KERNEL__ */
 #endif /* _I386_RWSEM_H */
include/asm-i386/spinlock.h renamed from include/asm-i386/spinlock.old.h +8 -6
@@ -3,6 +3,7 @@
 
 #include <asm/atomic.h>
 #include <asm/rwlock.h>
+#include <asm/text_lock_stabs.h>
 #include <asm/page.h>
 #include <linux/config.h>
 #include <linux/stringify.h>
@@ -58,9 +59,6 @@
 	"lock ; decb %0\n\t" \
 	"js 2f\n" \
 	".subsection 1\n" \
-	".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
-	"_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
-	".endif\n" \
 	"2:\t" \
 	"cmpb $0,%0\n\t" \
 	"rep;nop\n\t" \
@@ -127,7 +125,7 @@
 	return oldval > 0;
 }
 
-static inline void spin_lock(spinlock_t *lock)
+static inline void p__spin_lock(spinlock_t *lock)
 {
 #if SPINLOCK_DEBUG
 	__label__ here;
@@ -142,6 +140,7 @@
 		:"=m" (lock->lock) : : "memory");
 }
 
+#define spin_lock(l) __stabs_common(p__spin_lock,l)
 
 /*
  * Read-write spinlocks, allowing multiple readers
@@ -183,7 +182,7 @@
  */
 /* the spinlock helpers are in arch/i386/kernel/semaphore.c */
 
-static inline void read_lock(rwlock_t *rw)
+static inline void p__read_lock(rwlock_t *rw)
 {
 #if SPINLOCK_DEBUG
 	if (rw->magic != RWLOCK_MAGIC)
@@ -192,7 +191,7 @@
 	__build_read_lock(rw, "__read_lock_failed");
 }
 
-static inline void write_lock(rwlock_t *rw)
+static inline void p__write_lock(rwlock_t *rw)
 {
 #if SPINLOCK_DEBUG
 	if (rw->magic != RWLOCK_MAGIC)
@@ -200,6 +199,9 @@
 #endif
 	__build_write_lock(rw, "__write_lock_failed");
 }
+
+#define read_lock(r) __stabs_common(p__read_lock, r)
+#define write_lock(r) __stabs_common(p__write_lock, r)
 
 #define read_unlock(rw)		asm volatile("lock ; incl %0" :"=m" ((rw)->lock) : : "memory")
 #define write_unlock(rw)	asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ",%0":"=m" ((rw)->lock) : : "memory")
include/asm-i386/rwlock.h renamed from include/asm-i386/rwlock.old.h +0 -12
@@ -27,9 +27,6 @@
 		     "js 2f\n" \
 		     "1:\n" \
 		     ".subsection 1\n" \
-		     ".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
-		     "_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
-		     ".endif\n" \
 		     "2:\tcall " helper "\n\t" \
 		     "jmp 1b\n" \
 		     ".subsection 0\n" \
@@ -40,9 +37,6 @@
 		     "js 2f\n" \
 		     "1:\n" \
 		     ".subsection 1\n" \
-		     ".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
-		     "_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
-		     ".endif\n" \
 		     "2:\tpushl %%eax\n\t" \
 		     "leal %0,%%eax\n\t" \
 		     "call " helper "\n\t" \
@@ -63,9 +57,6 @@
 		     "jnz 2f\n" \
 		     "1:\n" \
 		     ".subsection 1\n" \
-		     ".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
-		     "_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
-		     ".endif\n" \
 		     "2:\tcall " helper "\n\t" \
 		     "jmp 1b\n" \
 		     ".subsection 0\n" \
@@ -76,9 +67,6 @@
 		     "jnz 2f\n" \
 		     "1:\n" \
 		     ".subsection 1\n" \
-		     ".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
-		     "_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
-		     ".endif\n" \
 		     "2:\tpushl %%eax\n\t" \
 		     "leal %0,%%eax\n\t" \
 		     "call " helper "\n\t" \
include/asm-i386/softirq.h renamed from include/asm-i386/softirq.old.h +3 -3
@@ -3,6 +3,7 @@
 
 #include <asm/atomic.h>
 #include <asm/hardirq.h>
+#include <asm/text_lock_stabs.h>
 #include <linux/stringify.h>
 
 #define __cpu_bh_enable(cpu) \
@@ -34,10 +35,9 @@
 			"jnz 2f;"					\
 			"1:;"						\
 									\
+			__stabs_string(local_bh_enable)			\
+									\
 			".subsection 1;"				\
-			".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"		\
-			"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"		\
-			".endif\n"					\
 			"2: pushl %%eax; pushl %%ecx; pushl %%edx;"	\
 			"call %c1;"					\
 			"popl %%edx; popl %%ecx; popl %%eax;"		\
include/asm-i386/smplock.h renamed from include/asm-i386/smplock.old.h +4 -25
@@ -40,36 +40,15 @@
  * so we only need to worry about other
  * CPU's.
  */
-static __inline__ void lock_kernel(void)
-{
-#if 1
-	if (!++current->lock_depth)
-		spin_lock(&kernel_flag);
-#else
-	__asm__ __volatile__(
-		"incl %1\n\t"
-		"jne 9f"
-		spin_lock_string
-		"\n9:"
-		:"=m" (__dummy_lock(&kernel_flag)),
-		 "=m" (current->lock_depth));
-#endif
-}
+#define lock_kernel() do { 					\
+	if (!++current->lock_depth) 				\
+		__stabs_common(p__spin_lock, &kernel_flag); 	\
+	} while (0)
 
 static __inline__ void unlock_kernel(void)
 {
 	if (current->lock_depth < 0)
 		BUG();
-#if 1
 	if (--current->lock_depth < 0)
 		spin_unlock(&kernel_flag);
-#else
-	__asm__ __volatile__(
-		"decl %1\n\t"
-		"jns 9f\n\t"
-		spin_unlock_string
-		"\n9:"
-		:"=m" (__dummy_lock(&kernel_flag)),
-		 "=m" (current->lock_depth));
-#endif
 }
Documentation/Configure.help renamed from Documentation/Configure.help.old +6 -0
@@ -24006,6 +24006,12 @@
   verbose debugging messages.  If you suspect a semaphore problem or a
   kernel hacker asks for this option then say Y.  Otherwise say N.
 
+Lock debugging
+CONFIG_DEBUG_TEXT_LOCK_STABS
+  If you say Y here then debugging symbols will be created for each
+  piece of .text.lock code that corresponds to the contention path for
+  the locking primitives.
+ 
 Verbose BUG() reporting (adds 70K)
 CONFIG_DEBUG_BUGVERBOSE
   Say Y here to make BUG() panics output the file name and line number
/Makefile renamed from Makefile.old +12 -0
@@ -229,6 +229,7 @@
 	.version .config* config.in config.old \
 	scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp \
 	scripts/lxdialog/*.o scripts/lxdialog/lxdialog \
+	scripts/add_lock_syms scripts/add_lock_syms.o \
 	.menuconfig.log \
 	include/asm \
 	.hdepend scripts/mkdep scripts/split-include scripts/docproc \
@@ -268,6 +269,17 @@
 		--end-group \
 		-o vmlinux
 	$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map
+	if [ "$(CONFIG_DEBUG_TEXT_LOCK_STABS)" = "y" ]; then \
+		$(MAKE) map-stabs; \
+	fi
+
+map-stabs: scripts/add_lock_syms
+	cp System.map System.map.tmp
+	scripts/add_lock_syms vmlinux >> System.map.tmp
+	cat System.map.tmp | sort -u >System.map
+
+scripts/add_lock_syms: scripts/add_lock_syms.c
+	$(MAKE) -C scripts add_lock_syms
 
 symlinks:
 	rm -f include/asm
scripts/Makefile renamed from scripts/Makefile.old +3 -0
@@ -42,4 +42,7 @@
 clean:
 	rm -f *~ kconfig.tk *.o tkparse mkdep split-include docproc
 
+add_lock_syms: add_lock_syms.c
+	$(HOSTCC) $(HOSTCFLAGS) -o $@ $<
+ 
 include $(TOPDIR)/Rules.make
scripts/add_lock_syms.c new file +102 -0

102 lines not on this page · raw message

/*
* readprofile.c - used to read /proc/profile
*
* Copyright (C) 1994,1996 Alessandro Rubini ([email protected]) *

 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

/*
* 1999-02-22 Arkadiusz Mi¶kiewicz <[email protected]>
* - added Native Language Support
* 1999-09-01 Stephane Eranian <[email protected]>
* - 64bit clean patch
* 3Feb2001 Andrew Morton <[email protected]>
* - -M option to write profile multiplier.
* 2001-11-07 Werner Almesberger <[email protected]>
* - byte order auto-detection and -n option
* 2001-11-09 Werner Almesberger <[email protected]>
* - skip step size (index 0)
* 2002-03-09 John Levon <[email protected]>
* - make maplineno do something
* 2002-03-11 John Levon <[email protected]>
* - rework for lock syms: no static buffers, no early quit */

#define _GNU_SOURCE

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if 0
#include "nls.h"
#else
#define _(s) s
#endif

#define RELEASE "2.1, March 2002"

static char *prgname;

/* These are the defaults */
static char defaultmap[]="/usr/src/linux/System.map"; static char defaultpro[]="/proc/profile"; static char optstring[]="M:m:np:itvarV";

struct qstr {
	char * str;
	size_t len;
};

static void
usage(void) {
fprintf(stderr,
_("%s: Usage: \"%s [options]\n" "\t -m <mapfile> (default = \"%s\")\n" "\t -p <pro-file> (default = \"%s\")\n"
"\t -M <mult> set the profiling multiplier to <mult>\n"
"\t -i print only info about the sampling step\n"
"\t -v print verbose data\n"
"\t -a print all symbols, even if count is 0\n"
"\t -r reset all the counters (root only)\n"
"\t -n disable byte order auto-detection\n"
"\t -V print version and exit\n")
,prgname,prgname,defaultmap,defaultpro);
exit(1);
}

static void *

xmalloc (size_t size) {
	void *t;

	if (size == 0)
		return NULL;

	t = malloc (size);
	if (t == NULL) {
		fprintf(stderr, _("out of memory"));
		exit(1);
	}

	return t;
}

static FILE *

myopen(char *name, char *mode, int *flag) {
	int len = strlen(name);

	if (!strcmp(name+len-3,".gz")) {
		FILE *res;
		char *cmdline = xmalloc(len+6);
		sprintf(cmdline, "zcat %s", name);
		res = popen(cmdline,mode);
		free(cmdline);
		*flag = 1;
		return res;
	}
	*flag = 0;
	return fopen(name,mode);
}

void
realloc_string(struct qstr * str, size_t len)

{
	if (len > str->len) {
		str->str = realloc(str->str, len);
		if (!str->str) {
			fprintf(stderr, "%s: malloc(%u) failed.\n", prgname, len);
			exit(1);
		}
		str->len = len;
	}
}

void
copy_string(struct qstr * str, struct qstr * new)

{
	realloc_string(str, new->len);
	strcpy(str->str, new->str);
}

int

main (int argc, char **argv) {
	FILE *map;
	int proFd;
	char *mapFile, *proFile, *mult=0;
	unsigned long len=0, add0=0, indx=1;
	unsigned int step;
	unsigned int *buf, total, fn_len;
	unsigned long fn_add, next_add; /* current and next address */
	struct qstr fn_name = { NULL, 0 }; /* current and next name */
	struct qstr next_name = { NULL, 0 };
	char mode;
	int c;
	int optAll=0, optInfo=0, optReset=0, optVerbose=0, optNative=0;
	char * mapline = NULL;
	int maplineno=1;
	int popenMap;   /* flag to tell if popen() has been used */
	size_t size;
	ssize_t chars_read;

#define next (current^1)

#if 0
	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);

#endif

	prgname=argv[0];
	proFile=defaultpro;
	mapFile=defaultmap;

	while ((c = getopt(argc,argv,optstring)) != -1) {
		switch(c) {
		case 'm': mapFile=optarg; break;
		case 'n': optNative++;	  break;
		case 'p': proFile=optarg; break;
		case 'a': optAll++;       break;
		case 'i': optInfo++;      break;
		case 'M': mult=optarg;    break;
		case 'r': optReset++;     break;
		case 'v': optVerbose++;   break;
		case 'V': printf(_("%s Version %s\n"),prgname,RELEASE);
			exit(0);
		default: usage();
		}
	}

	if (optReset || mult) {
		int multiplier, fd, to_write;

		/*
		 * When writing the multiplier, if the length of the write is
		 * not sizeof(int), the multiplier is not changed
		 */
		if (mult) {
			multiplier = strtoul(mult, 0, 10);
			to_write = sizeof(int);
		} else {
			multiplier = 0;
			to_write = 1;	/* sth different from sizeof(int) */
		}
		/* try to become root, just in case */
		setuid(0);
		fd = open(defaultpro,O_WRONLY);
		if (fd < 0) {
			perror(defaultpro);
			exit(1);
		}
		if (write(fd, &multiplier, to_write) != to_write) {
			fprintf(stderr, "readprofile: error writing %s: %s\n",
				defaultpro, strerror(errno));
			exit(1);
		}
		close(fd);
		exit(0);
	}

	/*
	 * Use an fd for the profiling buffer, to skip stdio overhead
	 */
	if ( ((proFd=open(proFile,O_RDONLY)) < 0)
	     || ((int)(len=lseek(proFd,0,SEEK_END)) < 0)
	     || (lseek(proFd,0,SEEK_SET)<0) ) {
		fprintf(stderr,"%s: %s: %s\n",prgname,proFile,strerror(errno));
		exit(1);
	}

	if ( !(buf=malloc(len)) ) {
		fprintf(stderr,"%s: malloc(): %s\n",prgname, strerror(errno));
		exit(1);
	}

	if (read(proFd,buf,len) != len) {
		fprintf(stderr,"%s: %s: %s\n",prgname,proFile,strerror(errno));
		exit(1);
	}
	close(proFd);

	if (!optNative) {
		int entries = len/sizeof(*buf);
		int big = 0,small = 0,i;
		unsigned *p;

		for (p = buf+1; p < buf+entries; p++)
			if (*p) {
				if (*p >= 1 << (sizeof(*buf)/2)) big++;
				else small++;
			}
		if (big > small) {
			fprintf(stderr,"Assuming reversed byte order. "
			   "Use -n to force native byte order.\n");
			for (p = buf; p < buf+entries; p++)
				for (i = 0; i < sizeof(*buf)/2; i++) {
					unsigned char *b = (unsigned char *) p;
					unsigned char tmp;

					tmp = b[i];
					b[i] = b[sizeof(*buf)-i-1];
					b[sizeof(*buf)-i-1] = tmp;
				}
		}
	}

	step=buf[0];
	if (optInfo) {
		printf(_("Sampling_step: %i\n"),step);
		exit(0);
	}

	total=0;

	if (!(map=myopen(mapFile,"r",&popenMap))) {
		fprintf(stderr,"%s: ",prgname);perror(mapFile);
		exit(1);
	}

	while ((chars_read = getline(&mapline, &size, map)) != -1) {
		realloc_string(&fn_name, (size_t)(chars_read + 1));
		if (sscanf(mapline,"%lx %c %s",&fn_add, &mode, fn_name.str) != 3) {
			fprintf(stderr,_("%s: %s(%i): wrong map line\n"),
				prgname, mapFile, maplineno);
			exit(1);
		}

		if (!strcmp(fn_name.str,"_stext")) /* only elf works like this */ {
			add0=fn_add;
			break;
		}
		maplineno++;
	}

	if (!add0) {
		fprintf(stderr,_("%s: can't find \"_stext\" in %s\n"),
			prgname, mapFile);
		exit(1);
	}

	/*
	 * Main loop.
	 */
	while ((chars_read = getline(&mapline, &size, map)) != -1) {
		unsigned int this=0;

		realloc_string(&next_name, (size_t)(chars_read + 1));

		if (sscanf(mapline,"%lx %c %s",&next_add, &mode, next_name.str)!=3) {
			fprintf(stderr,_("%s: %s(%i): wrong map line\n"),
				prgname,mapFile, maplineno);
			exit(1);
		}

		/* ignore anything past _etext */
		if (mode == 'A')
			break;
		
		if (mode != 'T' && mode != 't')
			continue;

		if (indx >= len / sizeof(*buf)) {
			fprintf(stderr, _("%s: profile address out of range for %s. "
					  "Wrong map file?\n"), prgname, next_name.str);
			exit(1);
		}

		while (indx < (next_add-add0)/step)
			this += buf[indx++];
		total += this;

		fn_len = next_add-fn_add;
		if (fn_len && (this || optAll)) {
			if (optVerbose)
				printf("%08lx %-60s %6i %8.4f\n", fn_add,
				       fn_name.str,this,this/(double)fn_len);
			else
				printf("%6i %-60s %8.4f\n",
				       this,fn_name.str,this/(double)fn_len);
		}
		fn_add=next_add;

		copy_string(&fn_name, &next_name);

		maplineno++;
	}

	free(mapline);

	/* trailer */
	if (optVerbose)
		printf("%08x %-60s %6i %8.4f\n",
		       0,"total",total,total/(double)(fn_add-add0));
	else
		printf("%6i %-60s %8.4f\n",
		       total,_("total"),total/(double)(fn_add-add0));
	
	popenMap ? pclose(map) : fclose(map);
	exit(0);
}