HPCToolkit
Struct-Inline.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 uses SymtabAPI to provide support for the location
48 // manager for sequences of inlined function calls. Symtab provides
49 // the file name and line number for each step of a inlined sequence.
50 // This file just calls symtab and packages the results into a C++
51 // list for the location manager.
52 //
53 // Notes:
54 //
55 // 1. Symtab does not work cross platform. You can't run symtab on
56 // x86 and analyze a ppc binary, or vice versa. So, we isolate the
57 // code as much as possible, compile banal twice and link it into
58 // hpcstruct but not hpcprof.
59 //
60 // 2. Many symtab functions have asserts in the code. In particular,
61 // openFile() on a binary of the wrong arch type raises SIGABRT rather
62 // than returning failure. (argh!) We want the inline support to be
63 // optional, not required. So, wrap the symtab calls with sigsetjmp()
64 // so that failure does not kill the process.
65 //
66 // But be careful about overusing sigsetjmp(). There is only one
67 // handler per process, so this could break other libraries or the
68 // main program.
69 //
70 // 3. Apologies for the single static buffer. Ideally, we would read
71 // the dwarf info from inside lib/binutils and carry it down into
72 // makeStructure() and determineContext(). But that requires
73 // integrating symtab too deeply into our code. We may rework this,
74 // especially if we rewrite the inline support directly from libdwarf
75 // (which is cross platform).
76 
77 //***************************************************************************
78 
79 #include <setjmp.h>
80 #include <signal.h>
81 #include <string.h>
82 
83 #include <list>
84 #include <set>
85 #include <string>
86 #include <utility>
87 #include <vector>
88 
92 #include <lib/support/FileUtil.hpp>
95 
96 #include "ElfHelper.hpp"
97 #include "Struct-Inline.hpp"
98 
99 #include <Module.h>
100 #include <Symtab.h>
101 #include <Function.h>
102 
103 using namespace Dyninst;
104 using namespace SymtabAPI;
105 using namespace std;
106 
107 static const string UNKNOWN_PROC ("unknown-proc");
108 
109 // FIXME: uses a single static buffer.
110 static Symtab *the_symtab = NULL;
111 
112 static struct sigaction old_act_abrt;
113 static struct sigaction old_act_segv;
114 static sigjmp_buf jbuf;
115 static int jbuf_active = 0;
116 
117 static int num_queries = 0;
118 static int num_errors = 0;
119 
120 #define DEBUG_INLINE_NAMES 0
121 
122 //***************************************************************************
123 
124 static void restore_sighandler(void);
125 
126 static void
128 {
129  if (jbuf_active) {
130  siglongjmp(jbuf, 1);
131  }
132 
133  // caught a signal, but it didn't come from symtab
135  DIAG_Die("banal caught unexpected signal " << sig);
136 }
137 
138 static void
140 {
141  struct sigaction act;
142 
143  memset(&act, 0, sizeof(act));
144  act.sa_handler = banal_sighandler;
145  act.sa_flags = 0;
146  sigemptyset(&act.sa_mask);
147 
148  jbuf_active = 0;
149  sigaction(SIGABRT, &act, &old_act_abrt);
150  sigaction(SIGSEGV, &act, &old_act_segv);
151 }
152 
153 static void
155 {
156  sigaction(SIGABRT, &old_act_abrt, NULL);
157  sigaction(SIGSEGV, &old_act_segv, NULL);
158  jbuf_active = 0;
159 }
160 
161 //***************************************************************************
162 
163 namespace Inline {
164 
165 // These functions return true on success.
166 Symtab *
168 {
169  bool ret = false;
170 
171  init_sighandler();
172  num_queries = 0;
173  num_errors = 0;
174 
175  if (sigsetjmp(jbuf, 1) == 0) {
176  // normal return
177  jbuf_active = 1;
178  ret = Symtab::openFile(the_symtab, elfFile->getMemory(), elfFile->getLength(),
179  elfFile->getFileName());
180  if (ret) {
181  the_symtab->parseTypesNow();
182  the_symtab->parseFunctionRanges();
183  }
184  }
185  else {
186  // error return
187  ret = false;
188  }
189  jbuf_active = 0;
190 
191  if (! ret) {
192  DIAG_WMsgIf(1, "SymtabAPI was unable to open: " << elfFile->getFileName());
193  the_symtab = NULL;
194  }
195 
196  return the_symtab;
197 }
198 
199 bool
201 {
202  bool ret = false;
203 
204  if (the_symtab != NULL) {
206  }
207  the_symtab = NULL;
208 
210 
211  if (num_errors > 0) {
212  DIAG_WMsgIf(1, "SymtabAPI had " << num_errors << " errors in "
213  << num_queries << " queries.");
214  }
215 
216  return ret;
217 }
218 
219 //***************************************************************************
220 
221 // Lookup the Module (comp unit) containing 'vma' to see if it is from
222 // a source file that mangles function names. A full Symtab Function
223 // already does this, but inlined functions do not, so we have to
224 // decide this ourselves.
225 //
226 // Returns: true if 'vma' is from a C++ module (mangled names).
227 //
228 static string cplus_exts[] = {
229  ".C", ".cc", ".cpp", ".cxx", ".c++",
230  ".CC", ".CPP", ".CXX", ".hpp", ".hxx", ""
231 };
232 
233 static bool
235 {
236  if (the_symtab == NULL) {
237  return false;
238  }
239 
240  // find module (comp unit) containing vma
241  set <Module *> modSet;
242  the_symtab->findModuleByOffset(modSet, vma);
243 
244  if (modSet.empty()) {
245  return false;
246  }
247 
248  Module * mod = *(modSet.begin());
249  if (mod == NULL) {
250  return false;
251  }
252 
253  // languages that need demangling
254  supportedLanguages lang = mod->language();
255  if (lang == lang_CPlusPlus || lang == lang_GnuCPlusPlus) {
256  return true;
257  }
258  if (lang != lang_Unknown) {
259  return false;
260  }
261 
262  // if language is unknown, then try file name
263  string filenm = mod->fileName();
264  long file_len = filenm.length();
265 
266  if (filenm == "") {
267  return false;
268  }
269 
270  for (auto i = 0; cplus_exts[i] != ""; i++) {
271  string ext = cplus_exts[i];
272  long len = ext.length();
273 
274  if (file_len > len && filenm.compare(file_len - len, len, ext) == 0) {
275  return true;
276  }
277  }
278 
279  return false;
280 }
281 
282 
283 // Returns nodelist as a list of InlineNodes for the inlined sequence
284 // at VMA addr. The front of the list is the outermost frame, back is
285 // innermost.
286 //
287 bool
288 analyzeAddr(InlineSeqn & nodelist, VMA addr, RealPathMgr * realPath)
289 {
290  FunctionBase *func, *parent;
291  bool ret = false;
292 
293  if (the_symtab == NULL) {
294  return false;
295  }
296  nodelist.clear();
297 
298  num_queries++;
299 
300  if (sigsetjmp(jbuf, 1) == 0) {
301  //
302  // normal return
303  //
304  jbuf_active = 1;
305  if (the_symtab->getContainingInlinedFunction(addr, func))
306  {
307  bool demangle = analyzeDemangle(addr);
308 
309  parent = func->getInlinedParent();
310  while (parent != NULL) {
311  //
312  // func is inlined iff it has a parent
313  //
314  InlinedFunction *ifunc = static_cast <InlinedFunction *> (func);
315  pair <string, Offset> callsite = ifunc->getCallsite();
316  string filenm = callsite.first;
317  if (filenm != "") { realPath->realpath(filenm); }
318  long lineno = callsite.second;
319 
320  // symtab does not provide mangled and pretty names for
321  // inlined functions, so we have to decide this ourselves
322  string procnm = func->getName();
323  string prettynm = procnm;
324 
325  if (procnm == "") {
326  procnm = UNKNOWN_PROC;
327  prettynm = UNKNOWN_PROC;
328  }
329  else if (demangle) {
330  prettynm = BinUtil::demangleProcName(procnm);
331  }
332 
333 #if DEBUG_INLINE_NAMES
334  cout << "raw-inline: 0x" << hex << addr << dec
335  << " link: " << procnm << " pretty: " << prettynm << "\n";
336 #endif
337 
338  nodelist.push_front(InlineNode(filenm, procnm, prettynm, lineno));
339 
340  func = parent;
341  parent = func->getInlinedParent();
342  }
343  ret = true;
344  }
345  }
346  else {
347  // error return
348  num_errors++;
349  ret = false;
350  }
351  jbuf_active = 0;
352 
353  return ret;
354 }
355 
356 //***************************************************************************
357 
358 // Insert one statement range into the map.
359 //
360 // Note: we pass the stmt info in 'sinfo', but we don't link sinfo
361 // itself into the map. Instead, insert a copy.
362 //
363 void
364 StmtMap::insert(StmtInfo * sinfo)
365 {
366  if (sinfo == NULL) {
367  return;
368  }
369 
370  VMA vma = sinfo->vma;
371  VMA end_vma = vma + sinfo->len;
372  long file = sinfo->file_index;
373  long base = sinfo->base_index;
374  long line = sinfo->line_num;
375 
376  StmtInfo * info = NULL;
377  StmtInfo * left = NULL;
378  StmtInfo * right = NULL;
379  VMA left_end = 0;
380 
381  // fixme: this is the single interval implementation (for now).
382  // one stmt may merge with an adjacent stmt, but we assume that it
383  // doesn't extend past its immediate neighbors.
384 
385  // find stmts left and right of vma, if they exist
386  auto sit = this->upper_bound(vma);
387 
388  if (sit != this->begin()) {
389  auto it = sit; --it;
390  left = it->second;
391  left_end = left->vma + left->len;
392  }
393  if (sit != this->end()) {
394  right = sit->second;
395  }
396 
397  // compare vma with stmt to the left
398  if (left == NULL || left_end < vma) {
399  // intervals don't overlap, insert new one
400  info = new StmtInfo(vma, end_vma - vma, file, base, line);
401  (*this)[vma] = info;
402  }
403  else if (left->base_index == base && left->line_num == line) {
404  // intervals overlap and match file and line
405  // merge with left stmt
406  end_vma = std::max(end_vma, left_end);
407  left->len = end_vma - left->vma;
408  info = left;
409  }
410  else {
411  // intervals overlap but don't match file and line
412  // truncate interval to start at left_end and insert
413  if (left_end < end_vma && (right == NULL || left_end < right->vma)) {
414  vma = left_end;
415  info = new StmtInfo(vma, end_vma - vma, file, base, line);
416  (*this)[vma] = info;
417  }
418  }
419 
420  // compare interval with stmt to the right
421  if (info != NULL && right != NULL && end_vma >= right->vma) {
422 
423  if (right->base_index == base && right->line_num == line) {
424  // intervals overlap and match file and line
425  // merge with right stmt
426  end_vma = right->vma + right->len;
427  info->len = end_vma - info->vma;
428  this->erase(sit);
429  delete right;
430  }
431  else {
432  // intervals overlap but don't match file and line
433  // truncate info at right vma
434  info->len = right->vma - info->vma;
435  }
436  }
437 }
438 
439 //***************************************************************************
440 
441 // Add one terminal statement to the inline tree and merge with
442 // adjacent stmts if their file and line match.
443 //
444 void
445 addStmtToTree(TreeNode * root, HPC::StringTable & strTab, RealPathMgr * realPath,
446  VMA vma, int len, string & filenm, SrcFile::ln line)
447 {
448  InlineSeqn path;
449  TreeNode *node;
450 
451  analyzeAddr(path, vma, realPath);
452 
453  // follow 'path' down the tree and insert any edges that don't exist
454  node = root;
455  for (auto it = path.begin(); it != path.end(); ++it) {
456  FLPIndex flp(strTab, *it);
457  auto nit = node->nodeMap.find(flp);
458 
459  if (nit != node->nodeMap.end()) {
460  node = nit->second;
461  }
462  else {
463  // add new node with edge 'flp'
464  TreeNode *child = new TreeNode();
465  node->nodeMap[flp] = child;
466  node = child;
467  }
468  }
469 
470  // insert statement at this level
471  long file = strTab.str2index(filenm);
472  long base = strTab.str2index(FileUtil::basename(filenm.c_str()));
473  StmtInfo info(vma, len, file, base, line);
474 
475  node->stmtMap.insert(&info);
476 }
477 
478 
479 // Merge the StmtInfo nodes from the 'src' node to 'dest' and delete
480 // them from src. If there are duplicate statement vma's, then we
481 // keep the original.
482 //
483 void
485 {
486  if (src == dest) {
487  return;
488  }
489 
490  for (auto sit = src->stmtMap.begin(); sit != src->stmtMap.end(); ++sit) {
491  dest->stmtMap.insert(sit->second);
492  }
493  src->stmtMap.clear();
494 }
495 
496 
497 // Merge the edge 'flp' and tree 'src' into 'dest' tree. Delete any
498 // leftover nodes from src that are not linked into dest.
499 //
500 void
502 {
503  auto dit = dest->nodeMap.find(flp);
504 
505  // if flp not in dest, then attach and done
506  if (dit == dest->nodeMap.end()) {
507  dest->nodeMap[flp] = src;
508  return;
509  }
510 
511  // merge src into dest's subtree with flp index
512  mergeInlineTree(dit->second, src);
513 }
514 
515 
516 // Merge the tree 'src' into 'dest' tree. Merge the statements, then
517 // iterate through src's subtrees. Delete any leftover nodes from src
518 // that are not linked into dest.
519 //
520 void
522 {
523  if (src == dest) {
524  return;
525  }
526 
527  mergeInlineStmts(dest, src);
528  src->stmtMap.clear();
529 
530  // merge the loops
531  for (auto lit = src->loopList.begin(); lit != src->loopList.end(); ++lit) {
532  dest->loopList.push_back(*lit);
533  }
534  src->loopList.clear();
535 
536  // merge the subtrees
537  for (auto sit = src->nodeMap.begin(); sit != src->nodeMap.end(); ++sit) {
538  mergeInlineEdge(dest, sit->first, sit->second);
539  }
540  src->nodeMap.clear();
541 
542  // now empty
543  delete src;
544 }
545 
546 
547 // Merge the detached loop 'info' into tree 'dest'. If dest is a
548 // detached loop, then 'path' is the FLP path above it.
549 void
550 mergeInlineLoop(TreeNode * dest, FLPSeqn & path, LoopInfo * info)
551 {
552  // follow source and dest paths and remove any common prefix.
553  auto dit = path.begin();
554  auto sit = info->path.begin();
555 
556  while (dit != path.end() && sit != info->path.end() && *dit == *sit) {
557  dit++;
558  sit++;
559  }
560 
561  // follow or insert the rest of source path in the dest tree.
562  while (sit != info->path.end()) {
563  FLPIndex flp = *sit;
564  auto nit = dest->nodeMap.find(flp);
565  TreeNode *node;
566 
567  if (nit != dest->nodeMap.end()) {
568  node = nit->second;
569  }
570  else {
571  node = new TreeNode();
572  dest->nodeMap[flp] = node;
573  }
574 
575  dest = node;
576  sit++;
577  }
578 
579  dest->loopList.push_back(info);
580 }
581 
582 } // namespace Inline
list< InlineNode > InlineSeqn
unsigned int ln
Definition: SrcFile.hpp:66
siglongjmp
Definition: names.cpp:1
long str2index(const std::string &str)
bfd_vma VMA
Definition: ISATypes.hpp:79
char * getMemory()
Definition: ElfHelper.hpp:86
void insert(StmtInfo *)
cct_node_t * node
Definition: cct.c:128
Symtab * openSymtab(ElfFile *elfFile)
static Symtab * the_symtab
static void init_sighandler(void)
void mergeInlineLoop(TreeNode *dest, FLPSeqn &path, LoopInfo *info)
static int num_queries
static bool analyzeDemangle(VMA vma)
static void banal_sighandler(int sig)
bool realpath(std::string &pathNm) const
static struct sigaction old_act_abrt
list< FLPIndex > FLPSeqn
static string cplus_exts[]
bool closeSymtab()
void addStmtToTree(TreeNode *root, HPC::StringTable &strTab, RealPathMgr *realPath, VMA vma, int len, string &filenm, SrcFile::ln line)
void mergeInlineStmts(TreeNode *dest, TreeNode *src)
std::string getFileName()
Definition: ElfHelper.hpp:88
static void restore_sighandler(void)
static int num_errors
void mergeInlineEdge(TreeNode *dest, FLPIndex flp, TreeNode *src)
static struct sigaction old_act_segv
void mergeInlineTree(TreeNode *dest, TreeNode *src)
static const string UNKNOWN_PROC("unknown-proc")
#define NULL
Definition: ElfHelper.cpp:85
static int jbuf_active
string basename(const char *fName)
Definition: FileUtil.cpp:90
static sigjmp_buf jbuf
#define DIAG_Die(...)
Definition: diagnostics.h:267
cct_addr_t * addr
Definition: cct.c:130
bool analyzeAddr(InlineSeqn &nodelist, VMA addr, RealPathMgr *realPath)
size_t getLength()
Definition: ElfHelper.hpp:87
string demangleProcName(const std::string &name)
Definition: BinUtils.cpp:86