Home | History | Annotate | Download | only in libdw
      1 /* Find line information for given file/line/column triple.
      2    Copyright (C) 2005-2009 Red Hat, Inc.
      3    This file is part of elfutils.
      4    Written by Ulrich Drepper <drepper (at) redhat.com>, 2005.
      5 
      6    This file is free software; you can redistribute it and/or modify
      7    it under the terms of either
      8 
      9      * the GNU Lesser General Public License as published by the Free
     10        Software Foundation; either version 3 of the License, or (at
     11        your option) any later version
     12 
     13    or
     14 
     15      * the GNU General Public License as published by the Free
     16        Software Foundation; either version 2 of the License, or (at
     17        your option) any later version
     18 
     19    or both in parallel, as here.
     20 
     21    elfutils is distributed in the hope that it will be useful, but
     22    WITHOUT ANY WARRANTY; without even the implied warranty of
     23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     24    General Public License for more details.
     25 
     26    You should have received copies of the GNU General Public License and
     27    the GNU Lesser General Public License along with this program.  If
     28    not, see <http://www.gnu.org/licenses/>.  */
     29 
     30 #ifdef HAVE_CONFIG_H
     31 # include <config.h>
     32 #endif
     33 
     34 #include <assert.h>
     35 #include <limits.h>
     36 #include <stdlib.h>
     37 #include <string.h>
     38 
     39 #include "libdwP.h"
     40 
     41 
     42 int
     43 dwarf_getsrc_file (Dwarf *dbg, const char *fname, int lineno, int column,
     44 		   Dwarf_Line ***srcsp, size_t *nsrcs)
     45 {
     46   if (dbg == NULL)
     47     return -1;
     48 
     49   bool is_basename = strchr (fname, '/') == NULL;
     50 
     51   size_t max_match = *nsrcs ?: ~0u;
     52   size_t act_match = *nsrcs;
     53   size_t cur_match = 0;
     54   Dwarf_Line **match = *nsrcs == 0 ? NULL : *srcsp;
     55 
     56   size_t cuhl;
     57   Dwarf_Off noff;
     58   for (Dwarf_Off off = 0;
     59        INTUSE(dwarf_nextcu) (dbg, off, &noff, &cuhl, NULL, NULL, NULL) == 0;
     60        off = noff)
     61     {
     62       Dwarf_Die cudie_mem;
     63       Dwarf_Die *cudie = INTUSE(dwarf_offdie) (dbg, off + cuhl, &cudie_mem);
     64       if (cudie == NULL)
     65 	continue;
     66 
     67       /* Get the line number information for this file.  */
     68       Dwarf_Lines *lines;
     69       size_t nlines;
     70       if (INTUSE(dwarf_getsrclines) (cudie, &lines, &nlines) != 0)
     71 	{
     72 	  /* Ignore a CU that just has no DW_AT_stmt_list at all.  */
     73 	  int error = INTUSE(dwarf_errno) ();
     74 	  if (error == 0)
     75 	    continue;
     76 	  __libdw_seterrno (error);
     77 	  return -1;
     78 	}
     79 
     80       /* Search through all the line number records for a matching
     81 	 file and line/column number.  If any of the numbers is zero,
     82 	 no match is performed.  */
     83       unsigned int lastfile = UINT_MAX;
     84       bool lastmatch = false;
     85       for (size_t cnt = 0; cnt < nlines; ++cnt)
     86 	{
     87 	  Dwarf_Line *line = &lines->info[cnt];
     88 
     89 	  if (lastfile != line->file)
     90 	    {
     91 	      lastfile = line->file;
     92 	      if (lastfile >= line->files->nfiles)
     93 		{
     94 		  __libdw_seterrno (DWARF_E_INVALID_DWARF);
     95 		  return -1;
     96 		}
     97 
     98 	      /* Match the name with the name the user provided.  */
     99 	      const char *fname2 = line->files->info[lastfile].name;
    100 	      if (is_basename)
    101 		lastmatch = strcmp (basename (fname2), fname) == 0;
    102 	      else
    103 		lastmatch = strcmp (fname2, fname) == 0;
    104 	    }
    105 	  if (!lastmatch)
    106 	    continue;
    107 
    108 	  /* See whether line and possibly column match.  */
    109 	  if (lineno != 0
    110 	      && (lineno > line->line
    111 		  || (column != 0 && column > line->column)))
    112 	    /* Cannot match.  */
    113 	    continue;
    114 
    115 	  /* Determine whether this is the best match so far.  */
    116 	  size_t inner;
    117 	  for (inner = 0; inner < cur_match; ++inner)
    118 	    if (match[inner]->files == line->files
    119 		&& match[inner]->file == line->file)
    120 	      break;
    121 	  if (inner < cur_match
    122 	      && (match[inner]->line != line->line
    123 		  || match[inner]->line != lineno
    124 		  || (column != 0
    125 		      && (match[inner]->column != line->column
    126 			  || match[inner]->column != column))))
    127 	    {
    128 	      /* We know about this file already.  If this is a better
    129 		 match for the line number, use it.  */
    130 	      if (match[inner]->line >= line->line
    131 		  && (match[inner]->line != line->line
    132 		      || match[inner]->column >= line->column))
    133 		/*  Use the new line.  Otherwise the old one.  */
    134 		match[inner] = line;
    135 	      continue;
    136 	    }
    137 
    138 	  if (cur_match < max_match)
    139 	    {
    140 	      if (cur_match == act_match)
    141 		{
    142 		  /* Enlarge the array for the results.  */
    143 		  act_match += 10;
    144 		  Dwarf_Line **newp = realloc (match,
    145 					       act_match
    146 					       * sizeof (Dwarf_Line *));
    147 		  if (newp == NULL)
    148 		    {
    149 		      free (match);
    150 		      __libdw_seterrno (DWARF_E_NOMEM);
    151 		      return -1;
    152 		    }
    153 		  match = newp;
    154 		}
    155 
    156 	      match[cur_match++] = line;
    157 	    }
    158 	}
    159 
    160       /* If we managed to find as many matches as the user requested
    161 	 already, there is no need to go on to the next CU.  */
    162       if (cur_match == max_match)
    163 	break;
    164     }
    165 
    166   if (cur_match > 0)
    167     {
    168       assert (*nsrcs == 0 || *srcsp == match);
    169 
    170       *nsrcs = cur_match;
    171       *srcsp = match;
    172 
    173       return 0;
    174     }
    175 
    176   __libdw_seterrno (DWARF_E_NO_MATCH);
    177   return -1;
    178 }
    179