HPCToolkit
ArgsHPCProf.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 //***************************************************************************
48 //
49 // File:
50 // $HeadURL$
51 //
52 // Purpose:
53 // [The purpose of this file]
54 //
55 // Description:
56 // [The set of functions, macros, etc. defined in the file]
57 //
58 //***************************************************************************
59 
60 //************************* System Include Files ****************************
61 
62 #include <iostream>
63 using std::cerr;
64 using std::endl;
65 
66 #include <string>
67 using std::string;
68 
69 //*************************** User Include Files ****************************
70 
71 #include <include/hpctoolkit-config.h>
72 
73 #include "ArgsHPCProf.hpp"
74 
75 #include "CallPath.hpp" /* for normalizeFilePath */
76 
77 #include <lib/analysis/Util.hpp>
78 
80 #include <lib/support/Trace.hpp>
81 #include <lib/support/StrUtil.hpp>
82 
83 //*************************** Forward Declarations **************************
84 
85 // Cf. DIAG_Die.
86 #define ARG_ERROR(streamArgs) \
87  { std::ostringstream WeIrDnAmE; \
88  WeIrDnAmE << streamArgs /*<< std::ends*/; \
89  printError(std::cerr, WeIrDnAmE.str()); \
90  exit(1); }
91 
92 //***************************************************************************
93 
94 static const char* version_info = HPCTOOLKIT_VERSION_STRING;
95 
96 static const char* usage_summary =
97 "[options] <measurement-group>...\n";
98 
99 static const char* usage_details = "\
100 hpcprof and hpcprof-mpi analyze call path profile performance measurements,\n\
101 attribute them to static source code structure, and generate an Experiment\n\
102 database for use with hpcviewer. hpcprof-mpi is a scalable (parallel)\n\
103 version of hpcprof.\n\
104 \n\
105 Both hpcprof and hpcprof-mpi expect a list of measurement-groups, where a\n\
106 group is a call path profile directory or an individual profile file.\n\
107 \n\
108 N.B.: For best results (a) compile your application with debugging\n\
109 information (e.g., -g); (b) pass recursive search paths with the -I option;\n\
110 and (c) pass structure information with the -S option.\n\
111 \n\
112 Options: General:\n\
113  -v [<n>], --verbose [<n>]\n\
114  Verbose: generate progress messages to stderr at\n\
115  verbosity level <n>. {1}\n\
116  -V, --version Print version information.\n\
117  -h, --help Print this help.\n\
118  --debug [<n>] Debug: use debug level <n>. {1}\n\
119 \n\
120 Options: Source Code and Static Structure:\n\
121  --name <name>, --title <name>\n\
122  Set the database's name (title) to <name>.\n\
123  -I <path>, --include <path>\n\
124  Use <path> when searching for source files. For a\n\
125  recursive search, append a + after the last slash,\n\
126  e.g., /mypath/+ . May use multiple -I options.\n\
127  -S <file>, --structure <file>\n\
128  Use hpcstruct structure file <file> for correlation.\n\
129  May pass multiple times (e.g., for shared libraries).\n\
130  -R '<old-path>=<new-path>', --replace-path '<old-path>=<new-path>'\n\
131  Substitute instances of <old-path> with <new-path>;\n\
132  apply to all paths (profile's load map, source code)\n\
133  for which <old-path> is a prefix. Use '\\' to escape\n\
134  instances of '=' within a path. May pass multiple\n\
135  times.\n\
136 \n\
137 Options: Metrics:\n\
138  -M <metric>, --metric <metric>\n\
139  Specify metrics to compute, where <metric> is one of\n\
140  the following:\n\
141  sum: sum over threads/processes\n\
142  stats: sum, mean, standard dev, coef of var, min, &\n\
143  max over threads/processes\n\
144  thread: per-thread metrics\n\
145  Default: {sum}. May pass multiple times.\n\
146  hpcprof-mpi does not compute 'thread'.\n\
147  --force-metric Force hpcprof to show all thread-level metrics,\n\
148  regardless of their number.\n\
149 \n\
150 Options: Output:\n\
151  -o <db-path>, --db <db-path>, --output <db-path>\n\
152  Specify Experiment database name <db-path>.\n\
153  {./" Analysis_DB_DIR "}\n\
154  Experiment format {" Analysis_OUT_DB_EXPERIMENT "}\n\
155  --metric-db <yes|no>\n\
156  Control whether to generate a thread-level metric\n\
157  value database for hpcviewer scatter plots. {yes}\n\
158  --remove-redundancy \n\
159  Eliminate procedure name redundancy in experiment.xml\n\
160  --struct-id Add 'str=nnn' field to profile data with the hpcstruct\n\
161  node id (for debug, default no).\n\
162 ";
163 
164 
165 
166 #define CLP CmdLineParser
167 #define CLP_SEPARATOR "!!!"
168 
169 // Note: Changing the option name requires changing the name in Parse()
171  { 0 , "agent-cilk", CLP::ARG_NONE, CLP::DUPOPT_CLOB, NULL,
172  NULL },
173  { 0 , "agent-mpi", CLP::ARG_NONE, CLP::DUPOPT_CLOB, NULL,
174  NULL },
175  { 0 , "agent-pthread", CLP::ARG_NONE, CLP::DUPOPT_CLOB, NULL,
176  NULL },
177 
178  // Source Code and Static Structure
179  { 0 , "name", CLP::ARG_REQ, CLP::DUPOPT_CLOB, NULL,
180  NULL },
181  { 0 , "title", CLP::ARG_REQ, CLP::DUPOPT_CLOB, NULL,
182  NULL },
183 
184  { 'I', "include", CLP::ARG_REQ, CLP::DUPOPT_CAT, CLP_SEPARATOR,
185  NULL },
186  { 'S', "structure", CLP::ARG_REQ, CLP::DUPOPT_CAT, CLP_SEPARATOR,
187  NULL },
188  { 'R', "replace-path", CLP::ARG_REQ, CLP::DUPOPT_CAT, CLP_SEPARATOR,
189  NULL},
190 
191  { 'N', "normalize", CLP::ARG_REQ, CLP::DUPOPT_CLOB, NULL,
192  NULL },
193 
194  // Metrics
195  { 'M', "metric", CLP::ARG_REQ, CLP::DUPOPT_CAT, CLP_SEPARATOR,
196  NULL },
197  { 0 , "force-metric", CLP::ARG_NONE, CLP::DUPOPT_CLOB, NULL,
198  NULL },
199 
200  // Output options
201  { 'o', "output", CLP::ARG_REQ , CLP::DUPOPT_CLOB, NULL,
202  NULL },
203  { 0 , "db", CLP::ARG_REQ , CLP::DUPOPT_CLOB, NULL,
204  NULL },
205  { 0 , "metric-db", CLP::ARG_REQ, CLP::DUPOPT_CLOB, NULL,
206  NULL },
207  { 0 , "struct-id", CLP::ARG_NONE, CLP::DUPOPT_CLOB, NULL,
208  NULL },
209 
210  // General
211  { 'v', "verbose", CLP::ARG_OPT, CLP::DUPOPT_CLOB, NULL,
212  CLP::isOptArg_long },
213  { 'V', "version", CLP::ARG_NONE, CLP::DUPOPT_CLOB, NULL,
214  NULL },
215  { 'h', "help", CLP::ARG_NONE, CLP::DUPOPT_CLOB, NULL,
216  NULL },
217  { 0, "remove-redundancy", CLP::ARG_NONE, CLP::DUPOPT_CLOB, NULL,
218  NULL },
219  { 0 , "debug", CLP::ARG_OPT, CLP::DUPOPT_CLOB, NULL, // hidden
220  CLP::isOptArg_long },
221  CmdLineParser_OptArgDesc_NULL_MACRO // SGI's compiler requires this version
222 };
223 
224 #undef CLP
225 
226 
227 //***************************************************************************
228 // ArgsHPCProf
229 //***************************************************************************
230 
231 namespace Analysis {
232 
234 {
236 
237  // Analysis::Args
239 
240  db_makeMetricDB = true;
241  remove_redundancy = false;
242 }
243 
244 
246 {
247 }
248 
249 
250 void
251 ArgsHPCProf::printVersion(std::ostream& os) const
252 {
253  os << getCmd() << ": " << version_info << endl;
254 }
255 
256 
257 void
258 ArgsHPCProf::printUsage(std::ostream& os) const
259 {
260  os << "Usage: " << getCmd() << " " << usage_summary << endl
261  << usage_details << endl;
262 }
263 
264 
265 void
266 ArgsHPCProf::printError(std::ostream& os, const char* msg) const
267 {
268  os << getCmd() << ": " << msg << endl
269  << "Try '" << getCmd() << " --help' for more information." << endl;
270 }
271 
272 
273 void
274 ArgsHPCProf::printError(std::ostream& os, const std::string& msg) const
275 {
276  printError(os, msg.c_str());
277 }
278 
279 
280 void
281 ArgsHPCProf::parse(int argc, const char* const argv[])
282 {
283  try {
284  // -------------------------------------------------------
285  // Parse the command line
286  // -------------------------------------------------------
287  parser.parse(optArgs, argc, argv);
288 
289  // -------------------------------------------------------
290  // Sift through results, checking for semantic errors
291  // -------------------------------------------------------
292 
293  // Special options that should be checked first
294  if (parser.isOpt("debug")) {
295  int dbg = 1;
296  if (parser.isOptArg("debug")) {
297  const string& arg = parser.getOptArg("debug");
298  dbg = (int)CmdLineParser::toLong(arg);
299  }
301  trace = dbg;
302  }
303  if (parser.isOpt("help")) {
304  printUsage(std::cerr);
305  exit(1);
306  }
307  if (parser.isOpt("remove-redundancy")) {
308  remove_redundancy = true;
309  }
310  if (parser.isOpt("version")) {
311  printVersion(std::cerr);
312  exit(1);
313  }
314  if (parser.isOpt("verbose")) {
315  int verb = 1;
316  if (parser.isOptArg("verbose")) {
317  const string& arg = parser.getOptArg("verbose");
318  verb = (int)CmdLineParser::toLong(arg);
319  }
321  }
322 
323  // Check for agent options
324  if (parser.isOpt("agent-cilk")) {
325  if (!agent.empty()) { ARG_ERROR("Only one agent is supported!"); }
326  agent = "agent-cilk";
327  }
328  if (parser.isOpt("agent-mpi")) {
329  if (!agent.empty()) { ARG_ERROR("Only one agent is supported!"); }
330  agent = "agent-mpi";
331  }
332  if (parser.isOpt("agent-pthread")) {
333  if (!agent.empty()) { ARG_ERROR("Only one agent is supported!"); }
334  agent = "agent-pthread";
335  }
336 
337  // Check for other options: Source code and static structure
338  if (parser.isOpt("name")) {
339  title = parser.getOptArg("name");
340  }
341  if (parser.isOpt("title")) {
342  title = parser.getOptArg("title");
343  }
344  if (parser.isOpt("include")) {
345  string str = parser.getOptArg("include");
346 
347  std::vector<std::string> searchPaths;
348  StrUtil::tokenize_str(str, CLP_SEPARATOR, searchPaths);
349 
350  for (uint i = 0; i < searchPaths.size(); ++i) {
351  searchPathTpls.push_back(Analysis::PathTuple(searchPaths[i],
353  }
354  }
355  if (parser.isOpt("structure")) {
356  string str = parser.getOptArg("structure");
358  }
359  if (parser.isOpt("normalize")) {
360  const string& arg = parser.getOptArg("normalize");
361  doNormalizeTy = parseArg_norm(arg, "--normalize/-N option");
362  }
363 
364  if (parser.isOpt("replace-path")) {
365  string arg = parser.getOptArg("replace-path");
366 
367  std::vector<std::string> replacePaths;
368  StrUtil::tokenize_str(arg, CLP_SEPARATOR, replacePaths);
369 
370  for (uint i = 0; i < replacePaths.size(); ++i) {
371  int occurancesOfEquals =
372  Analysis::Util::parseReplacePath(replacePaths[i]);
373 
374  if (occurancesOfEquals > 1) {
375  ARG_ERROR("Too many occurances of \'=\'; make sure to escape any \'=\' in your paths");
376  }
377  else if(occurancesOfEquals == 0) {
378  ARG_ERROR("The \'=\' between the old path and new path is missing");
379  }
380  }
381  }
382 
383  // Check for other options: Metrics
384  if (parser.isOpt("metric")) {
386 
387  string str = parser.getOptArg("metric");
388 
389  std::vector<std::string> metricVec;
390  StrUtil::tokenize_str(str, CLP_SEPARATOR, metricVec);
391 
392  for (uint i = 0; i < metricVec.size(); ++i) {
393  parseArg_metric(metricVec[i], "--metric/-M option");
394  }
395  }
396  // N.B.: hpcprof checks for "force-metric": src/tool/hpcprof/Args.cpp
397 
398  // Check for other options: Output options
399  bool isDbDirSet = false;
400  if (parser.isOpt("output")) {
401  db_dir = parser.getOptArg("output");
402  isDbDirSet = true;
403  }
404  if (parser.isOpt("db")) {
405  db_dir = parser.getOptArg("db");
406  isDbDirSet = true;
407  }
408  if (parser.isOpt("metric-db")) {
409  const string& arg = parser.getOptArg("metric-db");
410  db_makeMetricDB = CmdLineParser::parseArg_bool(arg, "--metric-db option");
411  }
412  if (parser.isOpt("struct-id")) {
413  db_addStructId = true;
414  }
415 
416  // Check for required arguments
417  uint numArgs = parser.getNumArgs();
418  if ( !(numArgs >= 1) ) {
419  ARG_ERROR("Incorrect number of arguments!");
420  }
421 
422  profileFiles.resize(numArgs);
423  for (uint i = 0; i < numArgs; ++i) {
424  profileFiles[i] = parser.getArg(i);
425  }
426 
427 
428  // For now, parse first file name to determine name of database
429  if (!isDbDirSet) {
430  std::string nm = makeDBDirName(profileFiles[0]);
431  if (!nm.empty()) {
432  db_dir = nm;
433  }
434  }
435  }
436  catch (const CmdLineParser::ParseError& x) {
437  ARG_ERROR(x.what());
438  }
439  catch (const CmdLineParser::Exception& x) {
440  DIAG_EMsg(x.message());
441  exit(1);
442  }
443 }
444 
445 
446 void
447 ArgsHPCProf::dump(std::ostream& os) const
448 {
449  os << "ArgsHPCProf.cmd= " << getCmd() << endl;
451 }
452 
453 
454 //***************************************************************************
455 
456 bool
457 ArgsHPCProf::parseArg_norm(const string& value, const char* errTag)
458 {
459  if (value == "all") {
460  return true;
461  }
462  else if (value == "none") {
463  return false;
464  }
465  else {
466  ARG_ERROR(errTag << ": Unexpected value received: '" << value << "'");
467  }
468 }
469 
470 
471 // Cf. hpcproftt/Args::parseArg_metric()
472 void
473 ArgsHPCProf::parseArg_metric(const std::string& value, const char* errTag)
474 {
475  if (value == "thread") {
476  // TODO: issue error with hpcprof-mpi
479  }
480  else if (value == "sum") {
485  }
486  else if (value == "stats") {
491  }
492  else {
493  ARG_ERROR(errTag << ": Unexpected value received: '" << value << "'");
494  }
495 }
496 
497 
498 std::string
499 ArgsHPCProf::makeDBDirName(const std::string& profileArg)
500 {
501  static const string hpctk = "hpctoolkit-";
502  static const string meas = "-measurements";
503 
504  string db_dir = "";
505 
506  // 'profileArg' has the following structure:
507  // <path>/[pfx]hpctoolkit-<nm>-measurements[sfx]/<file>.hpcrun
508 
509  const string& fnm = profileArg;
510  size_t pos_hpctk = -1;
511  size_t pos_meas = string::npos;
512 
513  // Find the last "hpctoolkit-" string that is followed
514  // by a "-measurements" string (with overlap possible).
515  for (;;) {
516  size_t pos1, pos2;
517  // find next "hpctoolkit-"
518  pos1 = fnm.find(hpctk, pos_hpctk + 1);
519  if (pos1 == string::npos)
520  break;
521  // find next "'measurements" after "hpctoolkit"
522  pos2 = fnm.find(meas, pos1 + hpctk.length() - 1);
523  if (pos2 == string::npos)
524  break;
525  pos_hpctk = pos1;
526  pos_meas = pos2;
527  }
528 
529  // If both strings are found, and have no '/ between them,
530  // assemble db_dir.
531  if (fnm.find_first_of("/", pos_hpctk + hpctk.length()) > pos_meas) {
532  // ---------------------------------
533  // prefix
534  // ---------------------------------
535  size_t pfx_a = fnm.find_last_of('/', pos_hpctk);
536  size_t pfx_beg = (pfx_a == string::npos) ? 0 : pfx_a + 1; // [inclusive
537  size_t pfx_end = pos_hpctk; // exclusive)
538  string pfx = fnm.substr(pfx_beg, pfx_end - pfx_beg);
539 
540  // ---------------------------------
541  // nm (N.B.: can have 'negative' length with fnm='hpctoolkit-measurements')
542  // ---------------------------------
543  size_t nm_beg = pos_hpctk + hpctk.length(); // [inclusive
544  size_t nm_end = (nm_beg > pos_meas) ? nm_beg : pos_meas; // exclusive)
545  string nm = fnm.substr(nm_beg, nm_end - nm_beg);
546 
547  // ---------------------------------
548  // suffix
549  // ---------------------------------
550  string sfx;
551  size_t sfx_beg = pos_meas + meas.length(); // [inclusive
552  size_t sfx_end = fnm.find_first_of('/', sfx_beg); // exclusive)
553  if (sfx_end == string::npos) {
554  sfx_end = fnm.size();
555  }
556  if (sfx_beg < sfx_end) {
557  sfx = fnm.substr(sfx_beg, sfx_end - sfx_beg);
558  }
559 
560  db_dir = pfx + Analysis_DB_DIR_pfx;
561  if (!nm.empty()) {
562  db_dir += "-" + nm;
563  }
564  db_dir += "-" Analysis_DB_DIR_nm + sfx;
565  }
566 
567  return db_dir;
568 }
569 
570 
571 //***************************************************************************
572 
573 } // namespace Analysis
574 
bool isOpt(const char swShort) const
#define Analysis_DB_DIR_nm
Definition: Args.hpp:192
PathTupleVec searchPathTpls
Definition: Args.hpp:125
std::string db_dir
Definition: Args.hpp:199
const std::string & getOptArg(const char swShort) const
#define ARG_ERROR(streamArgs)
Definition: ArgsHPCProf.cpp:86
int parseReplacePath(const std::string &arg)
Definition: Util.cpp:242
virtual void dump(std::ostream &os=std::cerr) const
uint prof_metrics
Definition: Args.hpp:176
#define DIAG_EMsg(...)
Definition: diagnostics.h:251
bool parseArg_norm(const std::string &value, const char *errTag)
virtual const std::string & what() const
Definition: Exception.hpp:126
bool remove_redundancy
Definition: Args.hpp:241
void printError(std::ostream &os, const char *msg) const
static const char * usage_summary
Definition: ArgsHPCProf.cpp:96
std::vector< std::string > structureFiles
Definition: Args.hpp:128
bool isOptArg(const char swShort) const
static void MetricFlg_clear(uint &flags, MetricFlg x)
Definition: Args.hpp:162
void parse(const OptArgDesc *optArgDescs, int argc, const char *const argv[])
static const char * version_info
Definition: ArgsHPCProf.cpp:94
unsigned int getNumArgs() const
bool db_addStructId
Definition: Args.hpp:205
void tokenize_str(const std::string &tokenstr, const char *delim, std::vector< std::string > &tokenvec)
Definition: StrUtil.cpp:122
virtual void dump(std::ostream &os=std::cerr) const
Definition: Args.cpp:144
virtual std::string message() const
Definition: Exception.hpp:134
exit
Definition: names.cpp:1
unsigned int uint
Definition: uint.h:124
int trace
Definition: Trace.cpp:57
void parseArg_metric(const std::string &value, const char *errTag)
bool db_makeMetricDB
Definition: Args.hpp:204
virtual void parse(int argc, const char *const argv[])
void printUsage(std::ostream &os) const
#define Analysis_DB_DIR
Definition: Args.hpp:193
#define Analysis_OUT_DB_EXPERIMENT
Definition: Args.hpp:188
const std::string DefaultPathTupleTarget
Definition: Args.hpp:85
#define CmdLineParser_OptArgDesc_NULL_MACRO
std::vector< std::string > profileFiles
Definition: Args.hpp:138
#define CLP_SEPARATOR
std::string agent
Definition: Args.hpp:114
void Diagnostics_SetDiagnosticFilterLevel(int lvl)
Definition: diagnostics.cpp:80
const std::string & getArg(unsigned int i) const
#define NULL
Definition: ElfHelper.cpp:85
static std::string makeDBDirName(const std::string &profileArg)
std::string title
Definition: Args.hpp:121
static const char * usage_details
Definition: ArgsHPCProf.cpp:99
static bool parseArg_bool(const std::string &value, const char *errTag)
CmdLineParser parser
static CmdLineParser::OptArgDesc optArgs[]
#define Analysis_DB_DIR_pfx
Definition: Args.hpp:191
bool doNormalizeTy
Definition: Args.hpp:140
std::pair< std::string, std::string > PathTuple
Definition: Args.hpp:82
static void MetricFlg_set(uint &flags, MetricFlg x)
Definition: Args.hpp:158
virtual const std::string getCmd() const =0
static long toLong(const std::string &str)
void printVersion(std::ostream &os) const