Imported Upstream version 0.155
[platform/upstream/elfutils.git] / libdw / dwarf_getsrc_file.c
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@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 }