6404003 optimize_symtab() can be inefficient for large symtabs
authorrh87107
Mon, 30 Jul 2007 01:54:19 -0700
changeset 4753 c0d0fedda3cf
parent 4752 19cfcd43280f
child 4754 0586690ea7f0
6404003 optimize_symtab() can be inefficient for large symtabs 6482731 core file mappings not correctly associated with their files 6527491 file_info_new() could still do a better job of finding associated mappings 6530223 truss -u can malfunction when large pages are in use
usr/src/lib/libproc/common/Pcontrol.c
usr/src/lib/libproc/common/Pcontrol.h
usr/src/lib/libproc/common/Pcore.c
usr/src/lib/libproc/common/Psymtab.c
usr/src/lib/libproc/common/libproc.h
--- a/usr/src/lib/libproc/common/Pcontrol.c	Sun Jul 29 21:23:00 2007 -0700
+++ b/usr/src/lib/libproc/common/Pcontrol.c	Mon Jul 30 01:54:19 2007 -0700
@@ -55,6 +55,9 @@
 #include "P32ton.h"
 
 int	_libproc_debug;		/* set non-zero to enable debugging printfs */
+int	_libproc_no_qsort;	/* set non-zero to inhibit sorting */
+				/* of symbol tables */
+
 sigset_t blockable_sigs;	/* signals to block when we need to be safe */
 static	int	minfd;	/* minimum file descriptor returned by dupfd(fd, 0) */
 char	procfs_path[PATH_MAX] = "/proc";
@@ -93,6 +96,7 @@
 _libproc_init(void)
 {
 	_libproc_debug = getenv("LIBPROC_DEBUG") != NULL;
+	_libproc_no_qsort = getenv("LIBPROC_NO_QSORT") != NULL;
 
 	(void) sigfillset(&blockable_sigs);
 	(void) sigdelset(&blockable_sigs, SIGKILL);
@@ -359,7 +363,7 @@
 		 * to get the information later.
 		 */
 		(void) Pread_string(P, execpath, sizeof (execpath),
-			(off_t)P->status.pr_lwp.pr_sysarg[0]);
+		    (off_t)P->status.pr_lwp.pr_sysarg[0]);
 		if (path != NULL)
 			(void) strncpy(path, execpath, len);
 		/*
@@ -1556,23 +1560,23 @@
 		break;
 	case PR_SIGNALLED:
 		dprintf("%s: SIGNALLED %s\n", caller,
-			proc_signame(lsp->pr_what, name, sizeof (name)));
+		    proc_signame(lsp->pr_what, name, sizeof (name)));
 		break;
 	case PR_FAULTED:
 		dprintf("%s: FAULTED %s\n", caller,
-			proc_fltname(lsp->pr_what, name, sizeof (name)));
+		    proc_fltname(lsp->pr_what, name, sizeof (name)));
 		break;
 	case PR_SYSENTRY:
 		dprintf("%s: SYSENTRY %s\n", caller,
-			proc_sysname(lsp->pr_what, name, sizeof (name)));
+		    proc_sysname(lsp->pr_what, name, sizeof (name)));
 		break;
 	case PR_SYSEXIT:
 		dprintf("%s: SYSEXIT %s\n", caller,
-			proc_sysname(lsp->pr_what, name, sizeof (name)));
+		    proc_sysname(lsp->pr_what, name, sizeof (name)));
 		break;
 	case PR_JOBCONTROL:
 		dprintf("%s: JOBCONTROL %s\n", caller,
-			proc_signame(lsp->pr_what, name, sizeof (name)));
+		    proc_signame(lsp->pr_what, name, sizeof (name)));
 		break;
 	case PR_SUSPENDED:
 		dprintf("%s: SUSPENDED\n", caller);
@@ -1884,8 +1888,8 @@
 	int sbits = (PR_DSTOP | PR_ISTOP | PR_ASLEEP);
 
 	long ctl[1 +					/* PCCFAULT	*/
-		1 + sizeof (siginfo_t)/sizeof (long) +	/* PCSSIG/PCCSIG */
-		2 ];					/* PCRUN	*/
+	    1 + sizeof (siginfo_t)/sizeof (long) +	/* PCSSIG/PCCSIG */
+	    2 ];					/* PCRUN	*/
 
 	long *ctlp = ctl;
 	size_t size;
@@ -2036,7 +2040,7 @@
 Psetbkpt(struct ps_prochandle *P, uintptr_t address, ulong_t *saved)
 {
 	long ctl[1 + sizeof (priovec_t) / sizeof (long) +	/* PCREAD */
-		1 + sizeof (priovec_t) / sizeof (long)];	/* PCWRITE */
+	    1 + sizeof (priovec_t) / sizeof (long)];	/* PCWRITE */
 	long *ctlp = ctl;
 	size_t size;
 	priovec_t *iovp;
@@ -2129,15 +2133,15 @@
 	ulong_t saved)			/* the saved instruction */
 {
 	long ctl[
-		1 + sizeof (sigset_t) / sizeof (long) +		/* PCSHOLD */
-		1 + sizeof (fltset_t) / sizeof (long) +		/* PCSFAULT */
-		1 + sizeof (priovec_t) / sizeof (long) +	/* PCWRITE */
-		2 +						/* PCRUN */
-		1 +						/* PCWSTOP */
-		1 +						/* PCCFAULT */
-		1 + sizeof (priovec_t) / sizeof (long) +	/* PCWRITE */
-		1 + sizeof (fltset_t) / sizeof (long) +		/* PCSFAULT */
-		1 + sizeof (sigset_t) / sizeof (long)];		/* PCSHOLD */
+	    1 + sizeof (sigset_t) / sizeof (long) +		/* PCSHOLD */
+	    1 + sizeof (fltset_t) / sizeof (long) +		/* PCSFAULT */
+	    1 + sizeof (priovec_t) / sizeof (long) +		/* PCWRITE */
+	    2 +							/* PCRUN */
+	    1 +							/* PCWSTOP */
+	    1 +							/* PCCFAULT */
+	    1 + sizeof (priovec_t) / sizeof (long) +		/* PCWRITE */
+	    1 + sizeof (fltset_t) / sizeof (long) +		/* PCSFAULT */
+	    1 + sizeof (sigset_t) / sizeof (long)];		/* PCSHOLD */
 	long *ctlp = ctl;
 	sigset_t unblock;
 	size_t size;
@@ -2231,8 +2235,8 @@
 	Psync(P);
 
 	error = execute_bkpt(ctlfd,
-		&P->status.pr_flttrace, &P->status.pr_lwp.pr_lwphold,
-		P->status.pr_lwp.pr_reg[R_PC], saved);
+	    &P->status.pr_flttrace, &P->status.pr_lwp.pr_lwphold,
+	    P->status.pr_lwp.pr_reg[R_PC], saved);
 	rv = Pstopstatus(P, PCNULL, 0);
 
 	if (error != 0) {
@@ -2425,7 +2429,7 @@
 
 	Psync(P);
 	error = execute_wapt(ctlfd,
-		&P->status.pr_flttrace, &P->status.pr_lwp.pr_lwphold, wp);
+	    &P->status.pr_flttrace, &P->status.pr_lwp.pr_lwphold, wp);
 	rv = Pstopstatus(P, PCNULL, 0);
 
 	if (error != 0) {
@@ -2820,7 +2824,7 @@
 
 		for (i = 0; i < P->core->core_nlwp; i++, lwp = list_prev(lwp)) {
 			sp = (lwp->lwp_psinfo.pr_sname == 'Z')? NULL :
-				&lwp->lwp_status;
+			    &lwp->lwp_status;
 			if ((rv = func(cd, sp, &lwp->lwp_psinfo)) != 0)
 				break;
 		}
@@ -3040,9 +3044,9 @@
 	L->lwp_ctlfd = fd;
 
 	L->lwp_state =
-		((L->lwp_status.pr_flags & (PR_STOPPED|PR_ISTOP))
-		== (PR_STOPPED|PR_ISTOP))?
-		PS_STOP : PS_RUN;
+	    ((L->lwp_status.pr_flags & (PR_STOPPED|PR_ISTOP))
+	    == (PR_STOPPED|PR_ISTOP))?
+	    PS_STOP : PS_RUN;
 
 	*perr = 0;
 	(void) mutex_unlock(&P->proc_lock);
@@ -3412,8 +3416,8 @@
 	int sbits = (PR_DSTOP | PR_ISTOP | PR_ASLEEP);
 
 	long ctl[1 +					/* PCCFAULT	*/
-		1 + sizeof (siginfo_t)/sizeof (long) +	/* PCSSIG/PCCSIG */
-		2 ];					/* PCRUN	*/
+	    1 + sizeof (siginfo_t)/sizeof (long) +	/* PCSSIG/PCCSIG */
+	    2 ];					/* PCRUN	*/
 
 	long *ctlp = ctl;
 	size_t size;
@@ -3503,8 +3507,8 @@
 
 	Lsync(L);
 	error = execute_bkpt(L->lwp_ctlfd,
-		&P->status.pr_flttrace, &L->lwp_status.pr_lwphold,
-		L->lwp_status.pr_reg[R_PC], saved);
+	    &P->status.pr_flttrace, &L->lwp_status.pr_lwphold,
+	    L->lwp_status.pr_reg[R_PC], saved);
 	rv = Lstopstatus(L, PCNULL, 0);
 
 	if (error != 0) {
@@ -3539,7 +3543,7 @@
 
 	Lsync(L);
 	error = execute_wapt(L->lwp_ctlfd,
-		&P->status.pr_flttrace, &L->lwp_status.pr_lwphold, wp);
+	    &P->status.pr_flttrace, &L->lwp_status.pr_lwphold, wp);
 	rv = Lstopstatus(L, PCNULL, 0);
 
 	if (error != 0) {
--- a/usr/src/lib/libproc/common/Pcontrol.h	Sun Jul 29 21:23:00 2007 -0700
+++ b/usr/src/lib/libproc/common/Pcontrol.h	Mon Jul 30 01:54:19 2007 -0700
@@ -263,6 +263,7 @@
 	int (*)(const char *, void *), void *);
 extern	int	getlwpstatus(struct ps_prochandle *, lwpid_t, lwpstatus_t *);
 int	Pstopstatus(struct ps_prochandle *, long, uint32_t);
+extern	file_info_t *file_info_new(struct ps_prochandle *, map_info_t *);
 
 extern	int	Padd_mapping(struct ps_prochandle *, off64_t, file_info_t *,
     prmap_t *);
--- a/usr/src/lib/libproc/common/Pcore.c	Sun Jul 29 21:23:00 2007 -0700
+++ b/usr/src/lib/libproc/common/Pcore.c	Mon Jul 30 01:54:19 2007 -0700
@@ -1382,29 +1382,28 @@
 		return (1); /* No mapping; advance to next mapping */
 	}
 
-	if ((fp = mp->map_file) == NULL) {
-		if ((fp = malloc(sizeof (file_info_t))) == NULL) {
-			P->core->core_errno = errno;
-			dprintf("failed to malloc mapping data\n");
-			return (0); /* Abort */
-		}
-
-		(void) memset(fp, 0, sizeof (file_info_t));
+	/*
+	 * Create a new file_info_t for this mapping, and therefore for
+	 * this load object.
+	 *
+	 * If there's an ELF header at the beginning of this mapping,
+	 * file_info_new() will try to use its section headers to
+	 * identify any other mappings that belong to this load object.
+	 */
+	if ((fp = mp->map_file) == NULL &&
+	    (fp = file_info_new(P, mp)) == NULL) {
+		P->core->core_errno = errno;
+		dprintf("failed to malloc mapping data\n");
+		return (0); /* Abort */
+	}
+	fp->file_map = mp;
 
-		list_link(fp, &P->file_head);
-		mp->map_file = fp;
-		P->num_files++;
-
-		fp->file_ref = 1;
-		fp->file_fd = -1;
-	}
-
+	/* Create a local copy of the load object representation */
 	if ((fp->file_lo = malloc(sizeof (rd_loadobj_t))) == NULL) {
 		P->core->core_errno = errno;
 		dprintf("failed to malloc mapping data\n");
 		return (0); /* Abort */
 	}
-
 	*fp->file_lo = *rlp;
 
 	if (fp->file_lname == NULL &&
@@ -1444,51 +1443,60 @@
 	if (fp->file_lname != NULL)
 		fp->file_lbase = basename(fp->file_lname);
 
-	/*
-	 * Associate the file and the mapping, and attempt to build
-	 * a symbol table for this file.
-	 */
-	(void) strcpy(fp->file_pname, mp->map_pmap.pr_mapname);
-	fp->file_map = mp;
-
-	Pbuild_file_symtab(P, fp);
-
-	if (fp->file_elf == NULL) {
-		dprintf("core_iter_mapping: no symtab - going to next\n");
-		return (1); /* No symbol table; advance to next mapping */
-	}
+	/* Associate the file and the mapping. */
+	(void) strncpy(fp->file_pname, mp->map_pmap.pr_mapname, PRMAPSZ);
+	fp->file_pname[PRMAPSZ - 1] = '\0';
 
 	/*
-	 * Locate the start of a data segment associated with this file.
-	 * Starting with that data segment, name all mappings that
-	 * fall within this file's address range after the file and
-	 * establish their mp->map_file links.
+	 * If no section headers were available then we'll have to
+	 * identify this load object's other mappings with what we've
+	 * got: the start and end of the object's corresponding
+	 * address space.
 	 */
-	if ((mp = core_find_data(P, fp->file_elf, fp->file_lo)) != NULL) {
-		dprintf("found data for %s at %p (pr_offset 0x%llx)\n",
-		    fp->file_pname, (void *)fp->file_lo->rl_data_base,
-		    mp->map_pmap.pr_offset);
+	if (fp->file_saddrs == NULL) {
+		for (mp = fp->file_map + 1; mp < P->mappings + P->map_count &&
+		    mp->map_pmap.pr_vaddr < rlp->rl_bend; mp++) {
 
-		for (; mp < P->mappings + P->map_count; mp++) {
-			if (mp->map_pmap.pr_vaddr > fp->file_lo->rl_bend)
-				break;
 			if (mp->map_file == NULL) {
-				dprintf("%s: associating segment at %p\n",
+				dprintf("core_iter_mapping %s: associating "
+				    "segment at %p\n",
 				    fp->file_pname,
 				    (void *)mp->map_pmap.pr_vaddr);
 				mp->map_file = fp;
 				fp->file_ref++;
 			} else {
-				dprintf("%s: segment at %p already associated "
-				    "with %s\n", fp->file_pname,
+				dprintf("core_iter_mapping %s: segment at "
+				    "%p already associated with %s\n",
+				    fp->file_pname,
 				    (void *)mp->map_pmap.pr_vaddr,
-				    mp->map_file->file_pname);
+				    (mp == fp->file_map ? "this file" :
+				    mp->map_file->file_pname));
 			}
+		}
+	}
 
-			if (!(mp->map_pmap.pr_mflags & MA_BREAK))
-				(void) strcpy(mp->map_pmap.pr_mapname,
-				    fp->file_pname);
+	/* Ensure that all this file's mappings are named. */
+	for (mp = fp->file_map; mp < P->mappings + P->map_count &&
+	    mp->map_file == fp; mp++) {
+		if (mp->map_pmap.pr_mapname[0] == '\0' &&
+		    !(mp->map_pmap.pr_mflags & MA_BREAK)) {
+			(void) strncpy(mp->map_pmap.pr_mapname, fp->file_pname,
+			    PRMAPSZ);
+			mp->map_pmap.pr_mapname[PRMAPSZ - 1] = '\0';
 		}
+	}
+
+	/* Attempt to build a symbol table for this file. */
+	Pbuild_file_symtab(P, fp);
+	if (fp->file_elf == NULL)
+		dprintf("core_iter_mapping: no symtab for %s\n",
+		    fp->file_pname);
+
+	/* Locate the start of a data segment associated with this file. */
+	if ((mp = core_find_data(P, fp->file_elf, fp->file_lo)) != NULL) {
+		dprintf("found data for %s at %p (pr_offset 0x%llx)\n",
+		    fp->file_pname, (void *)fp->file_lo->rl_data_base,
+		    mp->map_pmap.pr_offset);
 	} else {
 		dprintf("core_iter_mapping: no data found for %s\n",
 		    fp->file_pname);
--- a/usr/src/lib/libproc/common/Psymtab.c	Sun Jul 29 21:23:00 2007 -0700
+++ b/usr/src/lib/libproc/common/Psymtab.c	Mon Jul 30 01:54:19 2007 -0700
@@ -171,13 +171,13 @@
 /*
  * Allocation function for a new file_info_t
  */
-static file_info_t *
+file_info_t *
 file_info_new(struct ps_prochandle *P, map_info_t *mptr)
 {
 	file_info_t *fptr;
 	map_info_t *mp;
-	uintptr_t addr;
-	uint_t i, j;
+	uintptr_t mstart, mend, sstart, send;
+	uint_t i;
 
 	if ((fptr = calloc(1, sizeof (file_info_t))) == NULL)
 		return (NULL);
@@ -201,22 +201,38 @@
 	    &fptr->file_nsaddrs)) == NULL)
 		return (fptr);
 
-	i = j = 0;
 	mp = P->mappings;
-	while (j < P->map_count && i < fptr->file_nsaddrs) {
-		addr = fptr->file_saddrs[i];
-		if (addr >= mp->map_pmap.pr_vaddr &&
-		    addr < mp->map_pmap.pr_vaddr + mp->map_pmap.pr_size &&
-		    mp->map_file == NULL) {
-			mp->map_file = fptr;
-			fptr->file_ref++;
-		}
-
-		if (addr < mp->map_pmap.pr_vaddr + mp->map_pmap.pr_size) {
-			i++;
+	i = 0;
+	while (mp < P->mappings + P->map_count && i < fptr->file_nsaddrs) {
+
+		/* Calculate the start and end of the mapping and section */
+		mstart = mp->map_pmap.pr_vaddr;
+		mend = mp->map_pmap.pr_vaddr + mp->map_pmap.pr_size;
+		sstart = fptr->file_saddrs[i];
+		send = fptr->file_saddrs[i + 1];
+
+		if (mend <= sstart) {
+			/* This mapping is below the current section */
+			mp++;
+		} else if (mstart >= send) {
+			/* This mapping is above the current section */
+			i += 2;
 		} else {
+			/* This mapping overlaps the current section */
+			if (mp->map_file == NULL) {
+				dprintf("file_info_new: associating "
+				    "segment at %p\n",
+				    (void *)mp->map_pmap.pr_vaddr);
+				mp->map_file = fptr;
+				fptr->file_ref++;
+			} else {
+				dprintf("file_info_new: segment at %p "
+				    "already associated with %s\n",
+				    (void *)mp->map_pmap.pr_vaddr,
+				    (mp == mptr ? "this file" :
+				    mp->map_file->file_pname));
+			}
 			mp++;
-			j++;
 		}
 	}
 
@@ -596,9 +612,17 @@
 		file_info_t *fptr = build_map_symtab(P, mptr);
 		const prmap_t *pmp = &mptr->map_pmap;
 
+		/*
+		 * Assume that if rl_data_base is NULL, it means that no
+		 * data section was found for this load object, and that
+		 * a section must be text. Otherwise, a section will be
+		 * text unless it ends above the start of the data
+		 * section.
+		 */
 		if (fptr != NULL && fptr->file_lo != NULL &&
-		    fptr->file_lo->rl_base >= pmp->pr_vaddr &&
-		    fptr->file_lo->rl_base < pmp->pr_vaddr + pmp->pr_size)
+		    (fptr->file_lo->rl_data_base == NULL ||
+		    pmp->pr_vaddr + pmp->pr_size <
+		    fptr->file_lo->rl_data_base))
 			return (pmp);
 	}
 
@@ -906,6 +930,7 @@
 	prmap_t *pmap = &mptr->map_pmap;
 	rd_loadobj_t *lop = fptr->file_lo;
 	uint_t i;
+	uintptr_t mstart, mend, sstart, send;
 
 	/*
 	 * We can get for free the start address of the text and data
@@ -927,7 +952,7 @@
 	 * only one will be seen to enclose that section's start address.
 	 * Thus, to be rigorous, we ask not whether this mapping encloses
 	 * the start of a section, but whether there exists a section that
-	 * encloses the start of this mapping.
+	 * overlaps this mapping.
 	 *
 	 * If we don't already have the section addresses, and we successfully
 	 * get them, then we cache them in case we come here again.
@@ -936,10 +961,14 @@
 	    (fptr->file_saddrs = get_saddrs(P,
 	    fptr->file_map->map_pmap.pr_vaddr, &fptr->file_nsaddrs)) == NULL)
 		return (0);
+
+	mstart = mptr->map_pmap.pr_vaddr;
+	mend = mptr->map_pmap.pr_vaddr + mptr->map_pmap.pr_size;
 	for (i = 0; i < fptr->file_nsaddrs; i += 2) {
-		/* Does this section enclose the start of the mapping? */
-		if (fptr->file_saddrs[i] <= pmap->pr_vaddr &&
-		    fptr->file_saddrs[i + 1] > pmap->pr_vaddr)
+		/* Does this section overlap the mapping? */
+		sstart = fptr->file_saddrs[i];
+		send = fptr->file_saddrs[i + 1];
+		if (!(mend <= sstart || mstart >= send))
 			return (1);
 	}
 
@@ -1513,18 +1542,31 @@
 	}
 
 	/*
-	 * Sort the two tables according to the appropriate criteria.
+	 * Sort the two tables according to the appropriate criteria,
+	 * unless the user has overridden this behaviour.
+	 *
+	 * An example where we might not sort the tables is the relatively
+	 * unusual case of a process with very large symbol tables in which
+	 * we perform few lookups. In such a case the total time would be
+	 * dominated by the sort. It is difficult to determine a priori
+	 * how many lookups an arbitrary client will perform, and
+	 * hence whether the symbol tables should be sorted. We therefore
+	 * sort the tables by default, but provide the user with a
+	 * "chicken switch" in the form of the LIBPROC_NO_QSORT
+	 * environment variable.
 	 */
-	(void) mutex_lock(&sort_mtx);
-	sort_strs = symtab->sym_strs;
-	sort_syms = syms;
-
-	qsort(symtab->sym_byaddr, count, sizeof (uint_t), byaddr_cmp);
-	qsort(symtab->sym_byname, count, sizeof (uint_t), byname_cmp);
-
-	sort_strs = NULL;
-	sort_syms = NULL;
-	(void) mutex_unlock(&sort_mtx);
+	if (!_libproc_no_qsort) {
+		(void) mutex_lock(&sort_mtx);
+		sort_strs = symtab->sym_strs;
+		sort_syms = syms;
+
+		qsort(symtab->sym_byaddr, count, sizeof (uint_t), byaddr_cmp);
+		qsort(symtab->sym_byname, count, sizeof (uint_t), byname_cmp);
+
+		sort_strs = NULL;
+		sort_syms = NULL;
+		(void) mutex_unlock(&sort_mtx);
+	}
 
 	free(syms);
 }
@@ -2084,12 +2126,11 @@
 }
 
 /*
- * Look up a symbol by address in the specified symbol table.
- * Adjustment to 'addr' must already have been made for the
- * offset of the symbol if this is a dynamic library symbol table.
+ * Use a binary search to do the work of sym_by_addr().
  */
 static GElf_Sym *
-sym_by_addr(sym_tbl_t *symtab, GElf_Addr addr, GElf_Sym *symp, uint_t *idp)
+sym_by_addr_binary(sym_tbl_t *symtab, GElf_Addr addr, GElf_Sym *symp,
+    uint_t *idp)
 {
 	GElf_Sym sym, osym;
 	uint_t i, oid, *byaddr = symtab->sym_byaddr;
@@ -2154,10 +2195,70 @@
 }
 
 /*
- * Look up a symbol by name in the specified symbol table.
+ * Use a linear search to do the work of sym_by_addr().
  */
 static GElf_Sym *
-sym_by_name(sym_tbl_t *symtab, const char *name, GElf_Sym *symp, uint_t *idp)
+sym_by_addr_linear(sym_tbl_t *symtab, GElf_Addr addr, GElf_Sym *symbolp,
+    uint_t *idp)
+{
+	size_t symn = symtab->sym_symn;
+	char *strs = symtab->sym_strs;
+	GElf_Sym sym, *symp = NULL;
+	GElf_Sym osym, *osymp = NULL;
+	int i, id;
+
+	if (symtab->sym_data_pri == NULL || symn == 0 || strs == NULL)
+		return (NULL);
+
+	for (i = 0; i < symn; i++) {
+		if ((symp = symtab_getsym(symtab, i, &sym)) != NULL) {
+			if (addr >= sym.st_value &&
+			    addr < sym.st_value + sym.st_size) {
+				if (osymp)
+					symp = sym_prefer(
+					    symp, strs + symp->st_name,
+					    osymp, strs + osymp->st_name);
+				if (symp != osymp) {
+					osym = sym;
+					osymp = &osym;
+					id = i;
+				}
+			}
+		}
+	}
+	if (osymp) {
+		*symbolp = osym;
+		if (idp)
+			*idp = id;
+		return (symbolp);
+	}
+	return (NULL);
+}
+
+/*
+ * Look up a symbol by address in the specified symbol table.
+ * Adjustment to 'addr' must already have been made for the
+ * offset of the symbol if this is a dynamic library symbol table.
+ *
+ * Use a linear or a binary search depending on whether or not we
+ * chose to sort the table in optimize_symtab().
+ */
+static GElf_Sym *
+sym_by_addr(sym_tbl_t *symtab, GElf_Addr addr, GElf_Sym *symp, uint_t *idp)
+{
+	if (_libproc_no_qsort) {
+		return (sym_by_addr_linear(symtab, addr, symp, idp));
+	} else {
+		return (sym_by_addr_binary(symtab, addr, symp, idp));
+	}
+}
+
+/*
+ * Use a binary search to do the work of sym_by_name().
+ */
+static GElf_Sym *
+sym_by_name_binary(sym_tbl_t *symtab, const char *name, GElf_Sym *symp,
+    uint_t *idp)
 {
 	char *strs = symtab->sym_strs;
 	uint_t i, *byname = symtab->sym_byname;
@@ -2192,6 +2293,48 @@
 }
 
 /*
+ * Use a linear search to do the work of sym_by_name().
+ */
+static GElf_Sym *
+sym_by_name_linear(sym_tbl_t *symtab, const char *name, GElf_Sym *symp,
+    uint_t *idp)
+{
+	size_t symn = symtab->sym_symn;
+	char *strs = symtab->sym_strs;
+	int i;
+
+	if (symtab->sym_data_pri == NULL || symn == 0 || strs == NULL)
+		return (NULL);
+
+	for (i = 0; i < symn; i++) {
+		if (symtab_getsym(symtab, i, symp) &&
+		    strcmp(name, strs + symp->st_name) == 0) {
+			if (idp)
+				*idp = i;
+			return (symp);
+		}
+	}
+
+	return (NULL);
+}
+
+/*
+ * Look up a symbol by name in the specified symbol table.
+ *
+ * Use a linear or a binary search depending on whether or not we
+ * chose to sort the table in optimize_symtab().
+ */
+static GElf_Sym *
+sym_by_name(sym_tbl_t *symtab, const char *name, GElf_Sym *symp, uint_t *idp)
+{
+	if (_libproc_no_qsort) {
+		return (sym_by_name_linear(symtab, name, symp, idp));
+	} else {
+		return (sym_by_name_binary(symtab, name, symp, idp));
+	}
+}
+
+/*
  * Search the process symbol tables looking for a symbol whose
  * value to value+size contain the address specified by addr.
  * Return values are:
--- a/usr/src/lib/libproc/common/libproc.h	Sun Jul 29 21:23:00 2007 -0700
+++ b/usr/src/lib/libproc/common/libproc.h	Mon Jul 30 01:54:19 2007 -0700
@@ -82,6 +82,8 @@
 struct ps_lwphandle;
 
 extern	int	_libproc_debug;	/* set non-zero to enable debugging fprintfs */
+extern	int	_libproc_no_qsort;	/* set non-zero to inhibit sorting */
+					/* of symbol tables */
 
 #if defined(__sparc)
 #define	R_RVAL1	R_O0		/* register holding a function return value */