HPCToolkit
Linemap.cpp
Go to the documentation of this file.
1 // -*-Mode: C++;-*-
2 
3 // * BeginRiceCopyright *****************************************************
4 //
5 // $HeadURL$
6 // $Id$
7 //
8 // --------------------------------------------------------------------------
9 // Part of HPCToolkit (hpctoolkit.org)
10 //
11 // Information about sources of support for research and development of
12 // HPCToolkit is at 'hpctoolkit.org' and in 'README.Acknowledgments'.
13 // --------------------------------------------------------------------------
14 //
15 // Copyright ((c)) 2002-2019, Rice University
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are
20 // met:
21 //
22 // * Redistributions of source code must retain the above copyright
23 // notice, this list of conditions and the following disclaimer.
24 //
25 // * Redistributions in binary form must reproduce the above copyright
26 // notice, this list of conditions and the following disclaimer in the
27 // documentation and/or other materials provided with the distribution.
28 //
29 // * Neither the name of Rice University (RICE) nor the names of its
30 // contributors may be used to endorse or promote products derived from
31 // this software without specific prior written permission.
32 //
33 // This software is provided by RICE and contributors "as is" and any
34 // express or implied warranties, including, but not limited to, the
35 // implied warranties of merchantability and fitness for a particular
36 // purpose are disclaimed. In no event shall RICE or contributors be
37 // liable for any direct, indirect, incidental, special, exemplary, or
38 // consequential damages (including, but not limited to, procurement of
39 // substitute goods or services; loss of use, data, or profits; or
40 // business interruption) however caused and on any theory of liability,
41 // whether in contract, strict liability, or tort (including negligence
42 // or otherwise) arising in any way out of the use of this software, even
43 // if advised of the possibility of such damage.
44 //
45 // ******************************************************* EndRiceCopyright *
46 
47 // This file reads libdwarf directly and builds a single map of line
48 // map info for queries in Struct.cpp.
49 //
50 // Note: in the parallel case, readFile() is serial code and should be
51 // called first. Then, can make parallel queries to getLineRange().
52 //
53 // FIXME and TODO:
54 //
55 // 1. fix error/warning handling.
56 //
57 // 2. read debug info from alternate file.
58 //
59 // 3. handle cases of line map entries that come out of order.
60 //
61 // 4. use map insert hint when building line map.
62 //
63 // 5. add cached values for libdwarf case in Struct.cpp.
64 //
65 // 6. replace binutils line map in makeStructureSimple.
66 
67 //***************************************************************************
68 
69 #include <sys/types.h>
70 #include <sys/stat.h>
71 #include <err.h>
72 #include <errno.h>
73 #include <fcntl.h>
74 #include <limits.h>
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <stdint.h>
78 #include <string.h>
79 #include <unistd.h>
80 
81 #include <algorithm>
82 #include <iostream>
83 #include <iomanip>
84 #include <ostream>
85 #include <string>
86 #include <map>
87 #include <vector>
88 
89 #include <include/uint.h>
90 #include <lib/isa/ISATypes.hpp>
92 
93 #include "Linemap.hpp"
94 #include "ElfHelper.hpp"
95 
96 #include "dwarf.h"
97 #include "libdwarf.h"
98 
99 using namespace std;
100 
101 #define DEBUG_RAW_LINE_MAP 0
102 #define DEBUG_FULL_LINE_MAP 0
103 
104 //----------------------------------------------------------------------
105 
106 // Add line map info for one comp unit.
107 //
108 void
109 LineMap::do_line_map(Dwarf_Debug dw_dbg, Dwarf_Die dw_die)
110 {
111  Dwarf_Line *linebuf;
112  Dwarf_Signed count;
113  Dwarf_Signed file_count;
114  Dwarf_Error dw_error;
115  char **file_names;
116  int ret, n;
117 
118  ret = dwarf_srclines(dw_die, &linebuf, &count, &dw_error);
119  if (ret != DW_DLV_OK) {
120  // warnx("dwarf_srclines failed");
121  return;
122  }
123 
124  ret = dwarf_srcfiles(dw_die, &file_names, &file_count, &dw_error);
125  if (ret != DW_DLV_OK) {
126  // warnx("dwarf_srcfiles failed");
127  return;
128  }
129 
130  // make map of dwarf src file index to m_str_tab index
131  vector <uint> fileVec;
132  fileVec.resize(file_count);
133 
134  for (n = 0; n < file_count; n++) {
135  fileVec[n] = m_str_tab.str2index(file_names[n]);
136  }
137 
138  // create unsigned copy of count to avoid type errors in the loop
139  Dwarf_Unsigned ufile_count = (Dwarf_Unsigned) file_count;
140 
141  for (n = 0; n < count; n++) {
142  Dwarf_Addr addr;
143  Dwarf_Unsigned lineno = 0;
144  Dwarf_Unsigned fileno;
145  Dwarf_Bool is_end = 0;
146  uint file_index = m_empty_index;
147  int ret;
148 
149  ret = dwarf_lineaddr(linebuf[n], &addr, &dw_error);
150  if (ret != DW_DLV_OK) {
151  // warnx("dwarf_lineaddr failed");
152  }
153 
154  ret = dwarf_lineno(linebuf[n], &lineno, &dw_error);
155  if (ret != DW_DLV_OK) {
156  // warnx("dwarf_lineno failed");
157  }
158 
159  ret = dwarf_line_srcfileno(linebuf[n], &fileno, &dw_error);
160  if (ret != DW_DLV_OK) {
161  // warnx("dwarf_line_srcfileno failed");
162  }
163 
164  if (fileno > 0 && fileno <= ufile_count && lineno > 0) {
165  file_index = fileVec[fileno - 1];
166  }
167 
168  ret = dwarf_lineendsequence(linebuf[n], &is_end, &dw_error);
169  if (ret != DW_DLV_OK) {
170  // warnx("dwarf_lineendsequence failed");
171  }
172 
173 #if DEBUG_RAW_LINE_MAP
174  cout << "0x" << hex << addr << dec;
175  if (is_end) {
176  cout << " (end)\n";
177  }
178  else {
179  cout << " " << setw(6) << lineno
180  << " " << m_str_tab.index2str(file_index) << "\n";
181  }
182 #endif
183 
184  if (n == count - 1) {
185  is_end = 1;
186  }
187 
188  if (lineno > 0 && ! is_end) {
189  // always insert a valid entry
190  m_line_map[addr] = LineMapInfo(file_index, lineno);
191  }
192  else {
193  // don't overwrite a valid entry with empty info
194  auto it = m_line_map.find(addr);
195  if (it == m_line_map.end()) {
196  m_line_map[addr] = LineMapInfo(m_empty_index, 0);
197  }
198  }
199  }
200 
201  // free src files and line map info
202  for (n = 0; n < file_count; n++) {
203  dwarf_dealloc(dw_dbg, file_names[n], DW_DLA_STRING);
204  }
205  dwarf_dealloc(dw_dbg, file_names, DW_DLA_LIST);
206  dwarf_srclines_dealloc(dw_dbg, linebuf, count);
207 }
208 
209 //----------------------------------------------------------------------
210 
211 void
212 LineMap::do_comp_unit(Dwarf_Debug dw_dbg, int num, int vers, long hdr_len, long hdr_off)
213 {
214  Dwarf_Die dw_die;
215  Dwarf_Error dw_error;
216  int ret;
217 
218  ret = dwarf_siblingof_b(dw_dbg, NULL, 1, &dw_die, &dw_error);
219 
220  if (ret == DW_DLV_OK) {
221  do_line_map(dw_dbg, dw_die);
222  dwarf_dealloc(dw_dbg, dw_die, DW_DLA_DIE);
223  }
224 }
225 
226 //----------------------------------------------------------------------
227 
228 void
230 {
231  Dwarf_Debug dw_dbg;
232  Dwarf_Error dw_error;
233  Dwarf_Unsigned cu_hdr_len;
234  Dwarf_Half cu_vers;
235  Dwarf_Unsigned cu_hdr_off;
236  int num, ret;
237 
238  ret = dwarf_elf_init(elfFile->getElf(), DW_DLC_READ, NULL, NULL, &dw_dbg, &dw_error);
239  if (ret == DW_DLV_NO_ENTRY) {
240  errx(1, "no debug section in file: %s", elfFile->getFileName().c_str());
241  }
242  else if (ret != DW_DLV_OK) {
243  errx(1, "dwarf_init failed");
244  }
245 
246  // iterate over comp units
247  for (num = 0;; num++) {
248  ret = dwarf_next_cu_header_d(dw_dbg, 1, &cu_hdr_len, &cu_vers, NULL, NULL,
249  NULL, NULL, NULL, NULL, &cu_hdr_off, NULL, &dw_error);
250 
251  if (ret == DW_DLV_NO_ENTRY) {
252  break;
253  }
254  else if (ret != DW_DLV_OK) {
255  errx(1, "dwarf_next_cu_header_d failed");
256  }
257 
258 #if DEBUG_RAW_LINE_MAP
259  cout << "\ncomp unit # " << num << ":\n";
260 #endif
261 
262  do_comp_unit(dw_dbg, num, cu_vers, cu_hdr_len, cu_hdr_off);
263  }
264 
265  ret = dwarf_finish(dw_dbg, &dw_error);
266  if (ret != DW_DLV_OK) {
267  // warnx("dwarf_finish failed");
268  }
269 }
270 
271 //----------------------------------------------------------------------
272 
273 // Initialize empty line map.
274 //
276 {
277  // put sentinels at each end, so we don't have to deal with
278  // map.begin() and end().
279  m_empty_index = m_str_tab.str2index("");
280  m_line_map[0] = LineMapInfo(m_empty_index, 0);
281  m_line_map[VMA_MAX] = LineMapInfo(m_empty_index, 0);
282 }
283 
284 
285 // Read one file and put into InternalLineMap.
286 //
287 void
289 {
290  do_dwarf(elfFile);
291 
292 #if DEBUG_FULL_LINE_MAP
293  cout << "\nfull line map:\n\n";
294 
295  for (auto it = m_line_map.begin(); it != m_line_map.end(); ++it) {
296  cout << "0x" << hex << it->first << dec
297  << " " << setw(6) << it->second.line
298  << " " << m_str_tab.index2str(it->second.file) << "\n";
299  }
300 #endif
301 }
302 
303 
304 // Fill in LineRange object for one VMA.
305 //
306 void
308 {
309  // using sentinels, we know both it and --it are real objects, not
310  // map.begin() or end().
311  auto it = m_line_map.upper_bound(vma);
312  lr.end = it->first;
313  --it;
314  lr.start = it->first;
315  lr.filenm = m_str_tab.index2str(it->second.file).c_str();
316  lr.lineno = it->second.line;
317 }
void do_line_map(Dwarf_Debug, Dwarf_Die)
Definition: Linemap.cpp:109
void do_comp_unit(Dwarf_Debug, int, int, long, long)
Definition: Linemap.cpp:212
LineMap()
Definition: Linemap.cpp:275
uint lineno
Definition: Linemap.hpp:99
bfd_vma VMA
Definition: ISATypes.hpp:79
VMA end
Definition: Linemap.hpp:97
Elf * getElf()
Definition: ElfHelper.hpp:85
#define VMA_MAX
Definition: ISATypes.hpp:82
void getLineRange(VMA, LineRange &)
Definition: Linemap.cpp:307
unsigned int uint
Definition: uint.h:124
void readFile(ElfFile *elf)
Definition: Linemap.cpp:288
std::string getFileName()
Definition: ElfHelper.hpp:88
void do_dwarf(ElfFile *elf)
Definition: Linemap.cpp:229
#define NULL
Definition: ElfHelper.cpp:85
cct_addr_t * addr
Definition: cct.c:130
VMA start
Definition: Linemap.hpp:96
errx
Definition: names.cpp:1
const char * filenm
Definition: Linemap.hpp:98
<!-- ********************************************************************--> n<!-- HPCToolkit Experiment DTD --> n<!-- Version 2.1 --> n<!-- ********************************************************************--> n<!ELEMENT HPCToolkitExperiment(Header,(SecCallPathProfile|SecFlatProfile) *)> n<!ATTLIST HPCToolkitExperiment\n version CDATA #REQUIRED > n n<!-- ******************************************************************--> n n<!-- Info/NV:flexible name-value pairs:(n) ame;(t) ype;(v) alue --> n<!ELEMENT Info(NV *)> n<!ATTLIST Info\n n CDATA #IMPLIED > n<!ELEMENT NV EMPTY > n<!ATTLIST NV\n n CDATA #REQUIRED\n t CDATA #IMPLIED\n v CDATA #REQUIRED > n n<!-- ******************************************************************--> n<!-- Header --> n<!-- ******************************************************************--> n<!ELEMENT Header(Info *)> n<!ATTLIST Header\n n CDATA #REQUIRED > n n<!-- ******************************************************************--> n<!-- Section Header --> n<!-- ******************************************************************--> n<!ELEMENT SecHeader(MetricTable?, MetricDBTable?, TraceDBTable?, LoadModuleTable?, FileTable?, ProcedureTable?, Info *)> n n<!-- MetricTable:--> n<!ELEMENT MetricTable(Metric) * > n n<!-- Metric:(i) d;(n) ame --> n<!--(v) alue-type:transient type of values --> n<!--(t) ype:persistent type of metric --> n<!-- fmt:format;show;--> n<!ELEMENT Metric(MetricFormula *, Info?)> n<!ATTLIST Metric\n i CDATA #REQUIRED\n n CDATA #REQUIRED\n es CDATA #IMPLIED\n em CDATA #IMPLIED\n ep CDATA #IMPLIED\n v(raw|final|derived-incr|derived) \"raw\\ t (inclusive|exclusive|nil) \nil\\ partner CDATA #IMPLIED\ fmt CDATA #IMPLIED\ show (1|0) \1\\ show-percent (1|0) \1> n n<!-- MetricFormula represents derived metrics: (t)ype; (frm): formula --> n<!ELEMENT MetricFormula (Info?)> n<!ATTLIST MetricFormula\ t (combine|finalize) \finalize\\ i CDATA #IMPLIED\ frm CDATA #REQUIRED> n n<!-- Metric data, used in sections: (n)ame [from Metric]; (v)alue --> n<!ELEMENT M EMPTY> n<!ATTLIST M\ n CDATA #REQUIRED\ v CDATA #REQUIRED> n n<!-- MetricDBTable: --> n<!ELEMENT MetricDBTable (MetricDB)*> n n<!-- MetricDB: (i)d; (n)ame --> n<!-- (t)ype: persistent type of metric --> n<!-- db-glob: file glob describing files in metric db --> n<!-- db-id: id within metric db --> n<!-- db-num-metrics: number of metrics in db --> n<!-- db-header-sz: size (in bytes) of a db file header --> n<!ELEMENT MetricDB EMPTY> n<!ATTLIST MetricDB\ i CDATA #REQUIRED\ n CDATA #REQUIRED\ t (inclusive|exclusive|nil) \nil\\ partner CDATA #IMPLIED\ db-glob CDATA #IMPLIED\ db-id CDATA #IMPLIED\ db-num-metrics CDATA #IMPLIED\ db-header-sz CDATA #IMPLIED> n n<!-- TraceDBTable: --> n<!ELEMENT TraceDBTable (TraceDB)> n n<!-- TraceDB: (i)d --> n<!-- db-min-time: min beginning time stamp (global) --> n<!-- db-max-time: max ending time stamp (global) --> n<!ELEMENT TraceDB EMPTY> n<!ATTLIST TraceDB\ i CDATA #REQUIRED\ db-glob CDATA #IMPLIED\ db-min-time CDATA #IMPLIED\ db-max-time CDATA #IMPLIED\ db-header-sz CDATA #IMPLIED> n n<!-- LoadModuleTable assigns a short name to a load module --> n<!ELEMENT LoadModuleTable (LoadModule)*> n n<!ELEMENT LoadModule (Info?)> n<!ATTLIST LoadModule\ i CDATA #REQUIRED\ n CDATA #REQUIRED> n n<!-- FileTable assigns a short name to a file --> n<!ELEMENT FileTable (File)*> n n<!ELEMENT File (Info?)> n<!ATTLIST File\ i CDATA #REQUIRED\ n CDATA #REQUIRED> n n<!-- ProcedureTable assigns a short name to a procedure --> n<!ELEMENT ProcedureTable (Procedure)*> n n<!-- Info/NV: flexible name-value pairs: (n)ame; (t)ype; (v)alue --> n<!-- f: family of the procedure (fake, root, ...)--> n<!ELEMENT Procedure (Info?)> n<!ATTLIST Procedure\ i CDATA #REQUIRED\ n CDATA #REQUIRED\ f CDATA #IMPLIED> n n<!-- ****************************************************************** --> n<!-- Section: Call path profile --> n<!-- ****************************************************************** --> n<!ELEMENT SecCallPathProfile (SecHeader, SecCallPathProfileData)> n<!ATTLIST SecCallPathProfile\ i CDATA #REQUIRED\ n CDATA #REQUIRED> n n<!ELEMENT SecCallPathProfileData (PF|M)*> n<!-- Procedure frame --> n<!-- (i)d: unique identifier for cross referencing --> n<!-- (s)tatic scope id --> n<!-- (n)ame: a string or an id in ProcedureTable --> n<!-- (lm) load module: a string or an id in LoadModuleTable --> n<!-- (f)ile name: a string or an id in LoadModuleTable --> n<!-- (l)ine range: \beg-end\ (inclusive range) --> n<!-- (a)lien: whether frame is alien to enclosing P --> n<!-- (str)uct: hpcstruct node id --> n<!-- (t)ype: hpcrun node type: memory access, variable declaration, ... --> n<!-- (v)ma-range-set: \{[beg-end), [beg-end)...}\ --> n<!ELEMENT PF (PF|Pr|L|C|S|M)*> n<!ATTLIST PF\ i CDATA #IMPLIED\ s CDATA #IMPLIED\ n CDATA #REQUIRED\ lm CDATA #IMPLIED\ f CDATA #IMPLIED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Procedure (static): GOAL: replace with 'P' --> n<!ELEMENT Pr (Pr|L|C|S|M)*> n<!ATTLIST Pr\ i CDATA #IMPLIED\ s CDATA #IMPLIED\ n CDATA #REQUIRED\ lm CDATA #IMPLIED\ f CDATA #IMPLIED\ l CDATA #IMPLIED\ a (1|0) \0\\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Callsite (a special StatementRange) --> n<!ELEMENT C (PF|M)*> n<!ATTLIST C\ i CDATA #IMPLIED\ s CDATA #IMPLIED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n n<!-- ****************************************************************** --> n<!-- Section: Flat profile --> n<!-- ****************************************************************** --> n<!ELEMENT SecFlatProfile (SecHeader, SecFlatProfileData)> n<!ATTLIST SecFlatProfile\ i CDATA #REQUIRED\ n CDATA #REQUIRED> n n<!ELEMENT SecFlatProfileData (LM|M)*> n<!-- Load module: (i)d; (n)ame; (v)ma-range-set --> n<!ELEMENT LM (F|P|M)*> n<!ATTLIST LM\ i CDATA #IMPLIED\ n CDATA #REQUIRED\ v CDATA #IMPLIED> n<!-- File --> n<!ELEMENT F (P|L|S|M)*> n<!ATTLIST F\ i CDATA #IMPLIED\ n CDATA #REQUIRED> n<!-- Procedure (Note 1) --> n<!ELEMENT P (P|A|L|S|C|M)*> n<!ATTLIST P\ i CDATA #IMPLIED\ n CDATA #REQUIRED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Alien (Note 1) --> n<!ELEMENT A (A|L|S|C|M)*> n<!ATTLIST A\ i CDATA #IMPLIED\ f CDATA #IMPLIED\ n CDATA #IMPLIED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Loop (Note 1,2) --> n<!ELEMENT L (A|Pr|L|S|C|M)*> n<!ATTLIST L\ i CDATA #IMPLIED\ s CDATA #IMPLIED\ l CDATA #IMPLIED\ f CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Statement (Note 2) --> n<!-- (it): trace record identifier --> n<!ELEMENT S (S|M)*> n<!ATTLIST S\ i CDATA #IMPLIED\ it CDATA #IMPLIED\ s CDATA #IMPLIED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Note 1: Contained Cs may not contain PFs --> n<!-- Note 2: The 's' attribute is not used for flat profiles --> n