HPCToolkit
DotGraph.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 makes a dot (graphviz) file for the Control-Flow Graph
48 // for each function in the binary. This file is split off from
49 // Struct.cpp for two reasons.
50 //
51 // 1. ParseAPI does not work on cuda binaries.
52 //
53 // 2. It's easier to add command-line options for things like subset
54 // of functions and addresses as a separate program, not part of
55 // hpcstuct.
56 //
57 // Note: we can't use our library functions due to build order. Need
58 // to move this to src/tool if that is necessary.
59 
60 //***************************************************************************
61 
62 #include <err.h>
63 #include <errno.h>
64 #include <stdlib.h>
65 #include <string.h>
66 
67 #include <map>
68 #include <string>
69 #include <ostream>
70 #include <fstream>
71 
72 #include <CFG.h>
73 #include <CodeObject.h>
74 #include <CodeSource.h>
75 #include <Function.h>
76 #include <Symtab.h>
77 
78 #include <include/hpctoolkit-config.h>
79 
80 #ifdef ENABLE_OPENMP
81 #include <omp.h>
82 #endif
83 
84 using namespace Dyninst;
85 using namespace SymtabAPI;
86 using namespace ParseAPI;
87 using namespace std;
88 
89 class Options {
90 public:
91  char * filename;
92  int jobs;
93 
95  {
96  filename = (char *) "";
97  jobs = 1;
98  }
99 };
100 
101 //----------------------------------------------------------------------
102 
103 // Make a dot (graphviz) file for the Control-Flow Graph for each
104 // procedure and write to the ostream 'dotFile'.
105 //
106 static void
107 makeDotFile(ofstream * dotFile, CodeObject * code_obj)
108 {
109  const CodeObject::funclist & funcList = code_obj->funcs();
110 
111  for (auto fit = funcList.begin(); fit != funcList.end(); ++fit)
112  {
113  ParseAPI::Function * func = *fit;
114  map <Block *, int> blockNum;
115  map <Block *, int>::iterator mit;
116  int num;
117 
118  *dotFile << "--------------------------------------------------\n"
119  << "Procedure: '" << func->name() << "'\n\n"
120  << "digraph " << func->name() << " {\n"
121  << " 1 [ label=\"start\" shape=\"diamond\" ];\n";
122 
123  const ParseAPI::Function::blocklist & blist = func->blocks();
124 
125  // write the list of nodes (blocks)
126  num = 1;
127  for (auto bit = blist.begin(); bit != blist.end(); ++bit) {
128  Block * block = *bit;
129  num++;
130 
131  blockNum[block] = num;
132  *dotFile << " " << num << " [ label=\"0x" << hex << block->start()
133  << dec << "\" ];\n";
134  }
135  int endNum = num + 1;
136  *dotFile << " " << endNum << " [ label=\"end\" shape=\"diamond\" ];\n";
137 
138  // in parseAPI, functions have a unique entry point
139  mit = blockNum.find(func->entry());
140  if (mit != blockNum.end()) {
141  *dotFile << " 1 -> " << mit->second << ";\n";
142  }
143 
144  // write the list of internal edges
145  num = 1;
146  for (auto bit = blist.begin(); bit != blist.end(); ++bit) {
147  Block * block = *bit;
148  const ParseAPI::Block::edgelist & elist = block->targets();
149  num++;
150 
151  for (auto eit = elist.begin(); eit != elist.end(); ++eit) {
152  mit = blockNum.find((*eit)->trg());
153  if (mit != blockNum.end()) {
154  *dotFile << " " << num << " -> " << mit->second << ";\n";
155  }
156  }
157  }
158 
159  // add any exit edges
160  const ParseAPI::Function::const_blocklist & eblist = func->exitBlocks();
161  for (auto bit = eblist.begin(); bit != eblist.end(); ++bit) {
162  Block * block = *bit;
163  mit = blockNum.find(block);
164  if (mit != blockNum.end()) {
165  *dotFile << " " << mit->second << " -> " << endNum << ";\n";
166  }
167  }
168 
169  *dotFile << "}\n" << endl;
170  }
171 }
172 
173 //----------------------------------------------------------------------
174 
175 static void
176 usage(string mesg)
177 {
178  cout << "usage: dotgraph [options]... filename\n\n"
179  << "options:\n"
180  << " -j num use num threads for ParseAPI::parse()\n"
181  << " -h, --help display usage message and exit\n"
182  << "\n";
183 
184  if (! mesg.empty()) {
185  errx(1, "%s", mesg.c_str());
186  }
187  exit(0);
188 }
189 
190 static void
191 getOptions(int argc, char **argv, Options & opts)
192 {
193  if (argc < 2) {
194  usage("");
195  }
196 
197  int n = 1;
198  while (n < argc) {
199  string arg(argv[n]);
200 
201  if (arg == "-h" || arg == "-help" || arg == "--help") {
202  usage("");
203  }
204  else if (arg == "-j") {
205  if (n + 1 >= argc) {
206  usage("missing arg for -j");
207  }
208  opts.jobs = atoi(argv[n + 1]);
209  if (opts.jobs <= 0) {
210  errx(1, "bad arg for -j: %s", argv[n + 1]);
211  }
212  n += 2;
213  }
214  else if (arg[0] == '-') {
215  usage("invalid option: " + arg);
216  }
217  else {
218  break;
219  }
220  }
221 
222  // filename (required)
223  if (n < argc) {
224  opts.filename = argv[n];
225  }
226  else {
227  usage("missing file name");
228  }
229 
230 #ifndef ENABLE_OPENMP
231  opts.jobs = 1;
232 #endif
233 }
234 
235 //----------------------------------------------------------------------
236 
237 // Usage: dotgraph [-j num] <binary>
238 // output: <binary>.dot
239 int
240 main(int argc, char **argv)
241 {
242  Options opts;
243 
244  getOptions(argc, argv, opts);
245 
246  // open <filename>.dot for output
247  char * base = basename(strdup(opts.filename));
248  string dotname = string(base) + ".dot";
249  ofstream dotFile;
250 
251  dotFile.open(dotname, ios::out | ios::trunc);
252  if (! dotFile.is_open()) {
253  errx(1, "unable to open for output: %s", dotname.c_str());
254  }
255 
256 #ifdef ENABLE_OPENMP
257  omp_set_num_threads(1);
258 #endif
259 
260  // read input file and parse
261  Symtab * symtab = NULL;
262 
263  if (! Symtab::openFile(symtab, opts.filename)) {
264  errx(1, "Symtab::openFile failed: %s", opts.filename);
265  }
266  symtab->parseTypesNow();
267  symtab->parseFunctionRanges();
268 
269 #ifdef ENABLE_OPENMP
270  omp_set_num_threads(opts.jobs);
271 #endif
272 
273  SymtabCodeSource * code_src = new SymtabCodeSource(symtab);
274  CodeObject * code_obj = new CodeObject(code_src);
275  code_obj->parse();
276 
277  makeDotFile(&dotFile, code_obj);
278 
279  return 0;
280 }
static void getOptions(int argc, char **argv, Options &opts)
Definition: DotGraph.cpp:191
int jobs
Definition: DotGraph.cpp:92
static void usage(string mesg)
Definition: DotGraph.cpp:176
exit
Definition: names.cpp:1
Options()
Definition: DotGraph.cpp:94
static BAnal::Struct::Options opts
Definition: Struct.cpp:160
char * filename
Definition: DotGraph.cpp:91
static void makeDotFile(ofstream *dotFile, CodeObject *code_obj)
Definition: DotGraph.cpp:107
#define NULL
Definition: ElfHelper.cpp:85
string basename(const char *fName)
Definition: FileUtil.cpp:90
errx
Definition: names.cpp:1
<!-- ********************************************************************--> 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
int main(int argc, char **argv)
Definition: DotGraph.cpp:240