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
dwarf_getsrc_file(Dwarf * dbg,const char * fname,int lineno,int column,Dwarf_Line *** srcsp,size_t * nsrcs)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