symintLoad.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* File: symintLoad.c
* Date: 12/15/88
*
* This file provides code to build the profiling symbol array
* (array of PROF_SYMBOL). This array contains all of the
* symbol table information plus selected debug information for
* each file and each function that has a coverage array.
*
* The symbol table contains entries for every file, every
* function, and every coverage array. The debug information
* has corresponding entries except that there are no entries
* for the coverage arrays. (This may change later.)
*
* The algorithm for building the profiling symbol array
* consists of scanning the symbol table for file, function,
* and coverage array entries and building an entry for each.
* The construction of an entry is constrained by the
* following factors:
*
* - An entry is built for every file.
*
* - An entry is built for a function only if there
* is a corresponding coverage array for the function.
*
* - Entries must be ordered in the sense that each
* non-file entry points to its owner file and each
* file entry points to the next file (or null).
*
* - The assembler specification (see C Issue 5 3B2
* Assembler System Test Specification by Howe, p. 28)
* states that all local symbols follow their file
* symbol in the symbol table. This allows us to relate
* a function and its coverage array to the file that
* contains it.
*
* - For each symbol included in the profiling symbol
* array, all corresponding symbol table information must
* be present together with selected debug information.
* Therefore, the correspondence between a symbol table
* entry and a debug entry must be established.
*
* - Although duplicate (static) function names may appear,
* the names are unique within a given file. Also, the
* value (address) of each function is included in both
* the symbol table information and the debug information.
* This provides a verifable correspondence between these
* information sets.
*
* The algorithm used in this file is as follows:
*/
/*
* This is a discussion of the problem of multiple files with a single
* name. (See also, the _err_exit call in the routine "add_function".)
*
* Currently, when the executable contains more than one file with
* a common name, we sometimes mix a set of functions with the wrong
* file. Because the addresses don't match, add_profsymbol tends to
* fail (with _err_exit). The problem is to consistently choose the
* correct file for the set of functions that are about to be processed.
* This aspect of the problem has been addressed by the code below,
* but there is another part to the story.
*
* In order to match the symbol table with the debug information, we
* have to strip the path (if any) off of the file name; that is,
* the function _CAleaf is used to find the name of the file. This
* means that even if we make the match, we may still have trouble
* finding the file. One solution might be to retain a pointer to
* the full name for use at the proper point (when lprof is trying
* to find the source file). I have not traced this down completely;
* it may or may not work depending upon whether the full path is
* always included in the debug information. If it is not possible
* to depend on the complete path, then there may be no way to completely
* solve the problem. (Consider talking to the debugger people about
* this problem; they have to deal with it also.)
*
* Below I have included the code I used to solve the first part of
* the problem. I have also included an explanation of what each part
* of the code does. When the code is implemented this way, it does
* work for some cases, but I'm not sure that the assumptions it makes
* are valid. In particular, it makes implicit assumptions about the
* ordering of names and pointers; you should check that these assumptions
* do not include that the values returned from malloc are monotone
* increasing (which I think they do).
*
* With the following change, add_profsymbol will scan to the first
* file entry of the given name that has not yet been processed. It
* detects that a file has been processed by noting that sn_value_p
* has been set to zero; it accepts the first one whose value is not
* zero and calls dbfill_tag to "refill" the tag. Warning: setting
* sn_value_p to zero is dangerous; in particular, you must avoid
* trying to use this value when it is zero. Some cpus will produce
* a segmentation violation, but the 3b2 does not.
*
* add_profsymbol()
* {
* ...
* } else if (stchk_file(prsym_p)) {
* if (
* (sn_p + 1) < (dblist + dblist_cnt)
* && strcmp(sn_p[0].sn_name_p, sn_p[1].sn_name_p) == 0
* ) {
* _err_warn(
* "File name %s was used more than once.",
* sn_p->sn_name_p
* );
* while (
* sn_p->sn_value_p == 0
* && sn_p < (dblist + dblist_cnt)
* ) {
* sn_p++;
* }
* dbfill_tag(sn_p->sn_value_p, &tag);
* }
* sn_p->sn_value_p = 0;
* add_file(&tag);
* } else {
* ...
* }
*
* The change to sn_search is only to prepare for a call to sn_compare
* which has been changed to compare on both the name and the pointer
* value (instead of just the name). (Here, we might be using the
* incorrect assumption that malloc is monotone increasing; this scheme
* should be carefully thought out.) The change consists of setting
* the sn_value_p in the local tnode to zero; this allows sn_compare
* to ignore the value and compare only the name.
*
* sn_search()
* {
* ...
* tnode.sn_name_p = name_p;
* tnode.sn_value_p = 0;
* ...
* }
*
* This routine used to compare only the name; now it compares both
* the name and the sn_value_p pointer (see note above sn_search).
* When the value pointer is zero, there is no use in comparing
* that part of the item.
*
* sn_compare()
* {
* register int i;
*
* if (i = strcmp(a_p->sn_name_p, b_p->sn_name_p)) {
* return(i);
* } else if (a_p->sn_value_p == 0 || b_p->sn_value_p == 0) {
* return(i);
* } else if (a_p->sn_value_p < b_p->sn_value_p) {
* return(-1);
* } else {
* return(1);
* }
* }
*/
#include "string.h"
#include "symint.h"
#include "debug.h"
/* search node */
typedef struct {
char *sn_name_p;
char *sn_value_p;
} SEARCH_NODE;
PROF_SYMBOL * _symintLoad();
#if isLPROF
static int addcovset();
static SEARCH_NODE *build_stlist();
static int stscan();
static int stchk_focov();
static int stchk_filowog();
static int stchk_gowf();
static int stchk_func();
static int stchk_file();
static int stchk_cov();
static int stchk_match();
static void init_dblist();
static int dbscan_tag();
static void dbfill_tag();
static char * dbseek_att();
static int dbchk_stmnts();
static int dbchk_lowpc();
static int dbchk_highpc();
static int dbchk_filosub();
static PROF_SYMBOL * add_profsymbol();
static int add_function();
static void add_file();
static void check_capacity();
static char * debName();
static SEARCH_NODE * sn_search();
static int sn_compare();
#ifdef DEBUG
static void sn_dump();
static void profsym_dump();
#endif
static void verify_match();
#endif
/* debug tag */
typedef struct {
char *tg_att_p;
int tg_attlen;
} DB_TAG;
/*
* Debug list used to connect a symbol table entry to a debug entry.
*/
static int dblist_cnt; /* number of elements in array */
/*
* Global symbol table list used to connect coverage array entries
* with their (global) owner functions. This list contains all
* global and weak functions.
*/
static int gstlist_cnt; /* number of elements in array */
/*
* NOTE: When you change MATCH_STR, also change pcrt1.s.
* (See notes on "verify_match" below.)
*/
#ifdef __STDC__
#define MATCH_NAME _edata
#define MATCH_STR "_edata"
#else
#define MATCH_NAME edata
#define MATCH_STR "edata"
#endif
static int prsym_cnt = 0; /* #entries in the list */
static int prsym_cap = 0; /* #entries capacity allocated */
static int prstsym_size; /* size of a symbol table symbol */
static int add_profsym_search_fail; /* see add_profsymbol() */
#if isLPROF
static int prsym_size; /* size of a PROF_SYMBOL */
/* * * * * *
* addr of line information, and of the PROF_DEBUGE,
* associated with the last file (symbol) seen.
*
* also, the DEBUGE for the file in effect Before the current one!
*/
static char *curf_lp;
static LEN4 curf_lncnt;
static PROF_LINE *curf_lns_p;
static PROF_DEBUGE *curf_dbp;
static PROF_DEBUGE *priorFile_dbp;
#endif
/* * * * * *
* _symintLoad(proffilePtr)
* proffilePtr - PROF_FILE pointer returned by _symintOpen().
*
* returns PROF_SYMBOL * - pointer to the malloc-ed array of
* symbol information entries, or
* NULL if fails.
*
*
* This routine builds the interface data structure from the data
* already loaded during _symintOpen().
*
* There are two different incarnations of this routine:
* one for Prof, and one for Lprof.
*
* Lprof:
*
* 1. Pass through the symbol table and
* populate an extended PROF_SYMBOL array.
*
* 2. Include only certain symbols (see intro).
*
* 3. Find and include the debug information
* for each included symbol.
*
* Prof:
*
* 1. Allocate a duplicate copy of the symbol table
* data. (For Prof, a PROF_SYMBOL is just
* a structure containing an Elf32_Sym!)
*
* 2. Set internal parameters to reflect this.
*
*
* Problems are dealt with by issuing an _err_exit().
*
*/
{
int symcount = 0;
#if isLPROF
#endif
DEBUG_LOC("_symintLoad: top");
/* * * * * *
* sanity checks.
*/
#if isLPROF
/* * * * * *
* Initialize structure parameters. Updated by add_profsymbol().
*/
init_dblist();
verify_match();
priorFile_dbp = 0;
}
if (!stchk_file(next_p)) {
}
}
#ifdef DEBUG
printf("before profsym_dump\n");
printf("after profsym_dump\n");
#endif
#else /* isPROF */
/* * * * * *
* alloc a new copy of the array, and
* do a bit-wise copy since the structures
* ARE THE SAME SIZE & (effectively) HAVE THE SAME FIELDS!
* Set the descriptive `parameters' accordingly.
*
* (We'll take a copy, to simplify the 'Drop'
* logic.)
*/
{
int st_size; /* size of symbol table data */
NO_DEBUG_LOC("_symintLoad: before malloc for symbol list (PROF)");
NO_DEBUG_LOC("_symintLoad: after malloc for symbol list (PROF)");
NO_DEBUG_LOC("_symintLoad: before memcpy for symbol list (PROF)");
}
#endif
DEBUG_LOC("_symintLoad: bottom");
return( prsym_list_p );
}
#ifdef isLPROF
/*
* addcovset: Add coverage array set to PROF_SYMBOL array.
*
* The (local) symbols between the given file symbol and the
* end contain at least one coverage array. Sort all function
* and coverage array symbols (by name) within the given bounds
* and process each of the coverage array symbols by finding
* its corresponding (local or global) function and adding entries
* for both the coverage array and the function to the profile
* symbol array. Note that the file is also added to the profile
* symbol array and that pointers are managed accordingly (the file
* entries are linked and each of the non-file entries points
* to its owner file).
*
* - Add the file to the PROF_SYMBOL array. If the file is not
* found (i.e., the filename in the debug information does not
* match the filename in the symbol table), then fail.
* - Build (allocate) a sorted list of all function and coverage
* array symbols within the given limits.
* - Find the top of the coverage array subset of the pointer list.
* - For each coverage array pointer:
* - Find its function (look in local list, then global list).
* - Add function and assoc coverage array to PROF_SYMBOL array.
* - Free the sorted list.
*
* Note: "k" is used to avoid having "cov_p" increment beyond
* the last allocated search node and thereby (possibly) cause
* a segmentation violation.
*/
static int
{
int k, stlcount;
char *fname_p;
int symcount = 0;
DEBUG_LOC("addcovset: top");
if (add_profsym_search_fail) {
_err_exit("Unable to locate file %s in debug information.\n",
);
}
symcount++;
if (priorFile_dbp) {
}
while (
&& strncmp(
sizeof(COV_PREFIX)-1
) == 0
) {
sncov_p--;
}
k = stlcount;
if (
) {
symcount++;
symcount++;
}
sncov_p++;
}
DEBUG_LOC("addcovset: bottom");
return(symcount);
}
/*
* build_stlist: Build a tailored list of symbol table entries.
*/
static SEARCH_NODE *
int (*filter_p)();
int *count_p;
{
int i, count;
DEBUG_LOC("build_stlist: top");
count = 0;
count++;
}
i = 0;
i++;
}
DEBUG_LOC("build_stlist: bottom");
return(list_p);
}
/*
* stscan - symbol table scan
*
* Scan the symbol table until the given limit is reached or
* the filter function returns true. Neither the starting
* symbol (**sym_pp) nor the limit symbol (*lim_p) are legal
* return values. Instead, if the starting pointer is NULL,
* then the first item in the table is a valid return value.
* This allows the routine to be used as a generator by
* starting from where the last call stopped.
*/
static int
int (*filter_p)();
{
} else {
}
return(1);
}
}
return(0);
}
/*
* These routines check the type of a symbol table entry.
*/
static int
}
static int
return(
);
}
static int
return(
&& (
)
);
}
static int
}
static int
}
static int
return(
);
}
static int
return(
);
}
/*
* Initialize debug array (dblist).
*
* This routine prepares the debug array for searching (see
* also fillout_sym_dbinfo).
*
* Initialization proceeds as follows:
*
* - Count the debug entries that we care about.
* - _Malloc space to contain the pointers.
* - Extract pointers and fill in array.
* - Sort entries alphabetically by name.
*/
static void
{
char *cur_p;
char *lim_p;
int k;
extern char *_CAleaf();
DEBUG_LOC("init_dblist: top");
));
dblist_cnt = 0;
dblist_cnt++;
}
k = 0;
k++;
}
DEBUG_LOC("init_dblist: bottom");
}
/*
* Search for a given tag from the given starting point in
* the debug information. If found, fill in the tag at the
* given pointer and return 1. Otherwise, return 0.
*/
static int
char **dbpos_pp;
char *dblim_p;
int (*filter_p)();
{
NO_DEBUG_LOC("dbscan_tag: top");
} else {
}
goto success;
}
}
return(0);
success:;
return(1);
}
static void
char *dbpos_p;
{
}
/*
* Search the given tag for the given attribute(s).
* Return a pointer to the attribute or NULL if not found.
*/
static char *
int (*filter_p)();
{
int size;
char *att_p;
NO_DEBUG_LOC("dbseek_att: top");
while (size > 0) {
return(att_p);
}
}
return((char *) 0);
}
/*
* dbchk...: Routines that check debug tags and attributes.
*/
static int
{
return(value == AT_stmt_list);
}
static int
{
}
static int
{
return(value == AT_high_pc);
}
static int
{
switch(value) {
case TAG_source_file:
case TAG_subroutine:
case TAG_global_subroutine:
case TAG_inline_subroutine:
return(1);
default:
return(0);
}
}
/*
* add_profsymbol: Add a new entry to the profsymbol array.
*
* This routine allocates the space required (as needed) for
* the PROF_SYMBOL array, extracts the required information
* from the symbol table and from the debug information, if
* available (none is recorded for the coverage structures).
*
* - Check current capacity, assuming one new symbol is to be added.
* - Copy all of the symbol table information.
* - Search for the symbol in the debug list. If this search fails,
* then flag this failure with "add_profsym_search_fail". This is used
* by "addcovset()" for an error exit when a file is not found in the
* debug information. This applies only to files - other symbols
* which are not found may still be valid.
* - If the symbol is a function, it may be either global or local
* and local functions are not unique. Therefore we must compare the
* address (value) in the symbol table with the address (low_pc)
* given in the debug information to verify the match.
* - If the symbol is a file, no verification is needed, but we
* must change to a new statement list.
* - If the symbol is a coverage structure, then we are finished with it.
*/
static PROF_SYMBOL *
{
char *att_p;
DEBUG_LOC("add_profsymbol: top");
goto theend;
}
if (stchk_func(prsym_p)) {
break;
}
sn_p++;
}
} else if (stchk_file(prsym_p)) {
if (
) {
"File name %s was used more than once.",
);
}
} else {
goto theend;
}
theend:;
DEBUG_LOC("add_profsymbol: bottom");
return(ps_p);
}
static void
{
int i;
char *tp;
char *att_p;
i = 0;
lnp = curf_lns_p;
while (i++ < curf_lncnt) {
tp += DBG_LINE_SIZE;
}
#ifdef DEBUG
printf("File Debug Line Information\n");
printf("Dump of line numbers\n");
}
#endif
}
/*
* add_function -- add to function's PROF_DEBUGE, line# pointer info.
*
* Warning: Because we are reading directly from memory, we
* cannot depend upon the form of the structures we are reading
* (e.g., pl_delta in PROF_LINE). Thus, line_p is a "char *"
* and NOT a "PROF_LINE *".
*
*
* Note from below(***):
*
* Note that this routine finds the range of .line section
* entries that should be associated with this function, from
* those which belong to this file.
*
* The FIRST line entry for a fcn is selected because
* it is the first with a ``delta,'' or memory offset
* from the file ``base address (curf_base),''
* whose value is GREATER OR EQUAL to the effective offset
* associated with this function (lo_delta).
*
* The LAST line entry is selected because it is
* the LAST with a ``delta'' whose value is LESS THAN
* the effective offset of the END of this function (hi_delta)
* - i.e. it is the last line number associated with
* code that is wholly included in this function!
*
* If no line number is found with a delta value that
* exceeds hi_delta (i.e. is part of the next function),
* then it is assumed that the last line number entry seen
* should simply be accepted as part of this function's set
* of line numbers; it simply has no ``bounding line entry.''
*
*/
static int
{
char *att_p;
char *line_p;
int first_found = 0;
int i;
DEBUG_LOC("add_function: top");
DEBUG_LOC("add_function: returning - failed");
return(0);
}
i = 0;
while (i++ < curf_lncnt) {
pl_p++;
DEBUG_LOC("found first line");
first_found = 1;
DEBUG_LOC("found last line");
break;
}
line_p += DBG_LINE_SIZE;
}
/*
* If the first line is not found, then we have failed
* and must return zero. It is possible (e.g., sometimes
* when the function is the last one in the file) for the
* first line to be found, but the last not. In this case,
* we assume it simply the last possible line.
*/
"Unable to locate line information for function %s.",
);
}
DEBUG_LOC("found last line (by default)");
}
DEBUG_LOC("add_function: bottom");
return(1);
}
/* * * * * *
* If capacity will be exceeded with a new symbol, then
* increase the capacity.
*/
static void
{
if ( prsym_cap == 0 ) {
prsym_list_p = (PROF_SYMBOL *)
} else {
prsym_list_p = (PROF_SYMBOL *)
_Realloc( (char *) prsym_list_p,
prsym_size * prsym_cap );
}
}
}
/* * * * * *
* debName -- return ptr to name value for name attr type:attr value pair.
*
* this routine is called by fillout_sym_dbinfo, to scan
* through a list of debug attributes and return a ptr
*/
static
char *
char *att_p; /* ptr to list of (attr_type,attr_value) pairs */
int att_size; /* byte length attribute list */
{
char *name_p = "";
/* * * * * *
* loop through the entries. when you find a name,
* return the addr of the related data (a char string).
*/
NO_DEBUG_LOC("debName: top");
while ( att_size>0 ) {
break;
}
}
NO_DEBUG_LOC("debName: bottom");
return( name_p );
}
/* * * * * *
* bytesFor - indicate the number of bytes of attribute data
* expected to be defined for an attribute type,
* given a ptr to the attribute type:value pair.
*
* we don't particularly care about the specific attribute;
* more, we are interested in the 'form' of the value
* associated with this attribute type; hence the 'bit un-masking'.
*
* used by fillout1().
*/
static LEN4
char *attr_p;
{
NO_DEBUG_LOC("bytesFor: top");
switch( form )
{
case FORM_STRING: /* NUL-terminated string */
/* * * * * *
* len of string is #chars plus one for NULL.
*/
break;
case FORM_DATA2: /* 2 bytes */
len = 2;
break;
case FORM_ADDR: /* relocated address */
case FORM_REF: /* reference to another .debug entry */
case FORM_DATA4: /* 4 bytes */
len = 4;
break;
case FORM_DATA8: /* 8 bytes (two 4-byte values) */
len = 8;
break;
case FORM_BLOCK2: /* block with 2-byte length, then data */
break;
case FORM_BLOCK4: /* block with 4-byte length, then data */
break;
case FORM_NONE: /* error */
default:
len = 0;
break;
}
if (len==0)
_err_exit("Invalid FORM_value %#x for attribute type %#x\n",
NO_DEBUG_LOC("bytesFor: bottom");
return(len);
}
/*
* sn_search: Search sorted list of SEARCH_NODE for given name.
*
* Search the list for the entry with the given name. If there
* is no such entry, return 0. Otherwise return a pointer to
* the *first* entry in the list that matches.
*/
static SEARCH_NODE *
char *name_p;
int count;
{
int index;
(char *) &tnode,
(char *) list_p,
sizeof(*list_p),
);
return(NULL);
}
index--;
}
static int
{
}
#ifdef DEBUG
static void
char *title_p;
int sncount;
{
int i;
for (i = 0; i < sncount; i++) {
" name = %s, pointer = 0x%lx\n"
, snlist_p[i].sn_value_p
);
}
}
static void
int count;
{
int i;
PROF_LINE *p;
printf("\tDebug Information\n");
printf("\t *pd_line_p = %d\n", (p ? *p : 0));
printf("\t *pd_lali_p = %d\n", (p ? *p : 0));
}
}
#endif
/*
* alignment routines
*
* These routines are used to avoid the EMT trap that occurs
* when moving a unit of data (of 2 or more bytes) across a
* word boundry.
*/
static LEN2
alignval2(p)
char *p;
{
return(tmp);
}
static LEN4
alignval4(p)
char *p;
{
return(tmp);
}
/*
* Disscussion of the argv[0] problem and solution.
*
* If a process is run with a misleading first argument (argv[0]),
* the profiler will be confused when trying to read the file and
* match the information against that in memory. As a confidence
* check, we compare the address of MATCH_STR as seen in the symbol
* table to the address of MATCH_STR as seen while running the code.
* If these are the same, it is *very* unlikely that the file
* does not correspond to the code in memory.
*
* Because this code may be run from a shared object, we must
* insure that our reference to MATCH_STR is that of the main routine.
* We are depending on MATCH_NAME to not be defined by any shared object
* (including this one - libprof.so - when so built).
*
* The search for MATCH_STR in the symbol table is done in _symintLoad
* because SymintLoad has an ordered version of selected entries from the
* symbol table which makes searching very efficient (O(log n)).
*
* See also soqueue.c.
*/
int _prof_check_match;
static void
{
extern char MATCH_NAME;
if (_prof_check_match) {
_err_exit("Cannot find match name.");
}
_err_exit("Location of file for this process unknown.");
}
}
}
#endif