HPCToolkit
hpcrun.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 // Launch the PAPI profiler by setting up a preloaded library that
54 // will intercept an application's execution and start the
55 // profiler. This file processes hpcrun arguments and passes them
56 // to the profiling library through environment variables.
57 //
58 // Description:
59 // [The set of functions, macros, etc. defined in the file]
60 //
61 // Author:
62 // Written by John Mellor-Crummey and Nathan Tallent, Rice University.
63 //
64 //***************************************************************************
65 
66 //************************* System Include Files ****************************
67 
68 #include <iostream>
69 using std::ostream;
70 
71 #include <iomanip>
72 
73 #include <string>
74 using std::string;
75 
76 #include <stdlib.h>
77 #include <string.h>
78 #include <errno.h>
79 
80 #include <sys/types.h> /* for wait() */
81 #include <sys/wait.h> /* for wait() */
82 #include <unistd.h> /* for getpid(), fork(), etc. */
83 
84 //*************************** User Include Files ****************************
85 
86 #include <include/hpctoolkit-config.h>
87 #include <include/uint.h>
88 
89 #include "Args.hpp"
90 #include "hpcpapi.h" /* <papi.h>, etc. */
91 #include "dlpapi.h"
92 #include "hpcrun.h"
93 
96 #include <lib/support/StrUtil.hpp>
97 
98 //*************************** Forward Declarations **************************
99 
100 #define LD_LIBRARY_PATH "LD_LIBRARY_PATH"
101 #define LD_PRELOAD "LD_PRELOAD"
102 
103 //*************************** Forward Declarations **************************
104 
105 static int
106 list_available_events(char* argv[], Args::EventList_t listType);
107 
108 static void
110 
111 static int
112 launch_with_profiling(const char* installpath, const Args& args);
113 
114 static int
116 
117 static int
118 prepend_to_ld_lib_path(const char* str);
119 
120 static int
121 prepend_to_ld_preload(const char* str);
122 
123 //***************************************************************************
124 //
125 //***************************************************************************
126 
127 static int
128 real_main(int argc, char* argv[]);
129 
130 
131 int
132 main(int argc, char* argv[])
133 {
134  try {
135  return real_main(argc, argv);
136  }
137  catch (const Diagnostics::Exception& x) {
138  DIAG_EMsg(x.message());
139  exit(1);
140  }
141  catch (const std::bad_alloc& x) {
142  DIAG_EMsg("[std::bad_alloc] " << x.what());
143  exit(1);
144  }
145  catch (const std::exception& x) {
146  DIAG_EMsg("[std::exception] " << x.what());
147  exit(1);
148  }
149  catch (...) {
150  DIAG_EMsg("Unknown exception encountered!");
151  exit(2);
152  }
153 }
154 
155 
156 static int
157 real_main(int argc, char* argv[])
158 {
159  int ret = 0;
160  Args args(argc, argv);
161 
162  if (args.listEvents != Args::LIST_NONE) {
163  ret = list_available_events(argv, args.listEvents);
164  }
165  else if (args.printPaths) {
167  }
168  else {
169  // Launch and profile
170  char* installpath = findinstall(argv[0], HPCRUN_NAME);
171  DIAG_Assert(installpath, "Cannot locate installation path for '"HPCRUN_NAME"'");
172 
173  ret = launch_with_profiling(installpath, args);
174  // only returns on error
175  }
176 
177  return ret;
178 }
179 
180 
181 //***************************************************************************
182 // Profile
183 //***************************************************************************
184 
185 static int
186 prepare_env_for_profiling(const char* installpath, const Args& args);
187 
188 
189 static int
190 launch_with_profiling(const char* installpath, const Args& args)
191 {
192  pid_t pid;
193  int status;
194 
195  // Gather <command> into a NULL-terminated argv list
196  char** profArgV = new char*[args.profArgV.size() + 1];
197  for (uint i = 0; i < args.profArgV.size(); ++i) {
198  profArgV[i] = (char*)args.profArgV[i].c_str();
199  }
200  profArgV[args.profArgV.size()] = NULL;
201 
202  DIAG_Msg(1, HPCRUN_NAME" (pid " << getpid() << ") ==> " << profArgV[0]);
203 
204  prepare_env_for_profiling(installpath, args);
205 
206  // Fork and exec the command to profile
207  if ((pid = fork()) == 0) {
208  // Child process
209  const char* cmd = profArgV[0];
210  if (execvp(cmd, profArgV) == -1) {
211  DIAG_Throw("Error exec'ing '" << cmd << "': " << strerror(errno));
212  }
213  // never reached
214  }
215 
216  // Parent process
217  wait(&status);
218  return WEXITSTATUS(status);
219 }
220 
221 
222 static int
223 prepare_env_for_profiling(const char* installpath, const Args& args)
224 {
225  char buf[PATH_MAX] = "";
226 
227  // -------------------------------------------------------
228  // Prepare LD_LIBRARY_PATH (in reverse order)
229  // -------------------------------------------------------
230 
231  // To support multi-lib we pack LIB_LIBRARY_PATH with all versions
232 
233  // LD_LIBRARY_PATH for libpapi (even though we link with libpapi,
234  // this may be needed to resolve PAPI dependencies such as libpfm)
236 
237  // LD_LIBRARY_PATH for hpcrun (dynamically determined)
238 #if defined(HAVE_OS_MULTILIB)
239  snprintf(buf, PATH_MAX, "%s/lib64/hpctoolkit", installpath);
241  snprintf(buf, PATH_MAX, "%s/lib32/hpctoolkit", installpath);
243 #endif
244  snprintf(buf, PATH_MAX, "%s/lib/hpctoolkit", installpath);
246 
247  // LD_LIBRARY_PATH for libmonitor (statically or dynamically determined)
248 #ifdef HAVE_MONITOR
249  const char* MON = HPC_MONITOR;
250  if (MON[0] == '/') {
251  // statically determined
252 #if defined(HAVE_OS_MULTILIB)
253  prepend_to_ld_lib_path(HPC_MONITOR "/lib32/:" HPC_MONITOR "/lib64/");
254 #endif
255  prepend_to_ld_lib_path(HPC_MONITOR "/lib/");
256  }
257  else {
258  // dynamically determined
259 #if defined(HAVE_OS_MULTILIB)
260  snprintf(buf, PATH_MAX, "%s/lib64/" HPC_MONITOR, installpath);
262  snprintf(buf, PATH_MAX, "%s/lib32/" HPC_MONITOR, installpath);
264 #endif
265  snprintf(buf, PATH_MAX, "%s/lib/" HPC_MONITOR, installpath);
267  }
268 #endif /* HAVE_MONITOR */
269 
270 
271  // -------------------------------------------------------
272  // Prepare LD_PRELOAD
273  // -------------------------------------------------------
274 
275  prepend_to_ld_preload(HPCRUN_LIB " " HPC_LIBMONITOR_SO);
276 
277  DIAG_Msg(1, HPCRUN_NAME" (pid " << getpid() << "): LD_LIBRARY_PATH=" << getenv(LD_LIBRARY_PATH));
278  DIAG_Msg(1, HPCRUN_NAME" (pid " << getpid() << "): LD_PRELOAD=" << getenv(LD_PRELOAD));
279 
280 
281  // -------------------------------------------------------
282  // Prepare environment: Profiler options
283  // -------------------------------------------------------
284  if (!args.profRecursive.empty()) {
285  const char* val = (args.profRecursive == "yes") ? "1" : "0";
286  setenv("HPCRUN_RECURSIVE", val, 1);
287  }
288  if (!args.profThread.empty()) {
289  const char* val = HPCRUN_THREADPROF_EACH_STR;
290  if (args.profThread == "all") {
292  }
293  setenv("HPCRUN_THREAD", val, 1);
294  }
295  if (!args.profEvents.empty()) {
296  setenv("HPCRUN_EVENT_LIST", args.profEvents.c_str(), 1);
297  }
298  if (!args.profOutput.empty()) {
299  setenv("HPCRUN_OUTPUT", args.profOutput.c_str(), 1);
300  setenv("HPCRUN_OPTIONS", "DIR", 1); // hpcex extensions
301  }
302  if (!args.profPAPIFlag.empty()) {
303  setenv("HPCRUN_EVENT_FLAG", args.profPAPIFlag.c_str(), 1);
304  }
305  DIAG_If(1) {
307  setenv("HPCRUN_DEBUG", val.c_str(), 1);
308  setenv("MONITOR_DEBUG", val.c_str(), 1);
309  }
310  DIAG_If(1) {
311  // PAPI_DEBUG=PROFILE | SUBSTRATE | THREADS | OVERFLOW
312  //setenv("PAPI_DEBUG", "PROFILE, OVERFLOW, SUBSTRATE", 1);
313  }
314  return 0;
315 }
316 
317 
318 
319 //***************************************************************************
320 // List profiling events
321 //***************************************************************************
322 
323 static void
325 
326 static int
328 
329 static void
330 init_papi();
331 
332 
333 /*
334  * List available events.
335  */
336 static int
338 {
339  static const char* HPCRUN_TAG = "HPCRUN_SUPER_SECRET_TAG";
340  char* envtag = NULL;
341  pid_t pid;
342  int status = 0;
343 
344  // For a (security?) reason I do not understand, dlopen may *ignore*
345  // a call to setenv() modifying LD_LIBRARY_PATH. Thus we set the
346  // environment (adding a special tag to prevent infinite recursion)
347  // and then fork() and exec a call to ourself.
348  envtag = getenv(HPCRUN_TAG);
349  if (!envtag) {
350  // 1. No hpcrun tag: prepare env, add the tag and fork/exec
352  status |= setenv(HPCRUN_TAG, "1", 1);
353  if (status != 0) {
354  DIAG_Throw("Error preparing environment.");
355  }
356 
357  // Fork and exec
358  if ((pid = fork()) == 0) {
359  // Child process
360  const char* cmd = argv[0];
361  if (execvp(cmd, argv) == -1) {
362  DIAG_Throw("Error exec'ing myself: " << strerror(errno));
363  }
364  // never reached
365  }
366 
367  // Parent process
368  wait(&status);
369  return WEXITSTATUS(status);
370  }
371  else {
372  // 2. List the events
373  DIAG_Msg(1, "LD_LIBRARY_PATH=" << getenv(LD_LIBRARY_PATH));
374  dlopen_papi();
375  list_available_events_helper(listType);
376  dlclose_papi();
377  return 0;
378  }
379 }
380 
381 
382 /*
383  * List available system, PAPI and native events. (Mostly based on
384  * PAPI's src/ctests/avail.c)
385  */
386 static void
388 {
389  using std::setfill;
390  using std::setw;
391 
392 #define SEPARATOR_MAJOR setfill('=') << setw(77) << "" << "\n\n"
393 #define SEPARATOR_MINOR setfill('-') << setw(77) << "" << "\n"
394 
395  ostream& os = std::cout;
396 
397  if (listType == Args::LIST_NONE) {
398  return;
399  }
400 
401  // -------------------------------------------------------
402  // Ensure PAPI is initialized
403  // -------------------------------------------------------
404  init_papi();
405 
406  // -------------------------------------------------------
407  // Hardware information
408  // -------------------------------------------------------
409  const PAPI_hw_info_t* hwinfo = NULL;
410  if ((hwinfo = dl_PAPI_get_hardware_info()) == NULL) {
411  DIAG_Throw("PAPI_get_hardware_info failed");
412  }
413  os << "*** Hardware information ***\n";
414  os << SEPARATOR_MINOR;
415  os << "Vendor string and code : "
416  << hwinfo->vendor_string << " (" << hwinfo->vendor <<")\n";
417  os << "Model string and code : "
418  << hwinfo->model_string << " (" << hwinfo->model << ")\n";
419  os << "CPU Revision : " << hwinfo->revision << "\n";
420  os << "CPU Megahertz : " << hwinfo->mhz << "\n";
421  os << "CPU's in this Node : " << hwinfo->ncpu << "\n";
422  os << "Nodes in this System : " << hwinfo->nnodes << "\n";
423  os << "Total CPU's : " << hwinfo->totalcpus << "\n";
424  os << "Number Hardware Counters: " << dl_PAPI_get_opt(PAPI_MAX_HWCTRS, NULL) << "\n";
425  os << "Max Multiplex Counters : " << dl_PAPI_get_opt(PAPI_MAX_MPX_CTRS, NULL) << "\n";
426  os << SEPARATOR_MAJOR;
427 
428  // -------------------------------------------------------
429  // Wall clock time
430  // -------------------------------------------------------
431  os << "*** Wall clock time ***\n";
432  os << HPCRUN_EVENT_WALLCLK_STR << " wall clock time (1 millisecond period)\n";
433  // os << HPCRUN_EVENT_FWALLCLK_STR << " fast wall clock time (1 millisecond period)\n";
434  os << SEPARATOR_MAJOR;
435 
436  // -------------------------------------------------------
437  // PAPI events
438  // -------------------------------------------------------
439  os << "*** Available PAPI preset events ***\n";
440  os << SEPARATOR_MINOR;
441  if (listType == Args::LIST_SHORT) {
442  os << "Name\t\tDescription\n";
443  }
444  else if (listType == Args::LIST_LONG) {
445  os << "Name\t Profilable\tDescription (Implementation Note)\n";
446  }
447  else {
449  }
450  os << SEPARATOR_MINOR;
451 
452  int i = PAPI_PRESET_MASK;
453  int count = 0;
454  do {
455  PAPI_event_info_t info;
456  if (dl_PAPI_query_event(i) != PAPI_OK) {
457  continue;
458  }
459  if (dl_PAPI_get_event_info(i, &info) == PAPI_OK) {
460  /* NOTE: Although clumsy, this test has official sanction. */
461  const char* profilable = "Yes";
462  if ((info.count > 1) && strcmp(info.derived, "DERIVED_CMPD") != 0) {
463  profilable = "No";
464  }
465 
466  if (listType == Args::LIST_SHORT) {
467  os << info.symbol << "\t" << info.long_descr << "\n";
468  }
469  else if (listType == Args::LIST_LONG) {
470  os << info.symbol << "\t" << profilable << "\t" << info.long_descr
471  << " (" << info.note << ")\n";
472  }
473  else {
475  }
476  count++;
477  }
478  } while (dl_PAPI_enum_event(&i, PAPI_PRESET_ENUM_AVAIL) == PAPI_OK);
479  os << "Total PAPI events reported: " << count << "\n";
480  os << SEPARATOR_MAJOR;
481 
482  // -------------------------------------------------------
483  // Native events
484  // -------------------------------------------------------
485 
486  /* PAPI does not always correctly return a vendor id */
487  os << "*** Available native events ***\n";
488  os << SEPARATOR_MINOR;
489  if (listType == Args::LIST_SHORT) {
490  os << "Name\t\t\t\tDescription\n";
491  }
492  else if (listType == Args::LIST_LONG) {
493  os << "Name\t\t\t\tDescription\n";
494  }
495  else {
497  }
498  os << SEPARATOR_MINOR;
499  os << std::left << setfill(' ');
500 
501  i = PAPI_NATIVE_MASK;
502  count = 0;
503  do {
504  PAPI_event_info_t info;
505  if (dl_PAPI_get_event_info(i, &info) == PAPI_OK) {
506  const char* desc = (info.long_descr) ? info.long_descr : "";
507 
508  if (listType == Args::LIST_SHORT || listType == Args::LIST_LONG) {
509  if (strncmp(info.symbol, desc, strlen(info.symbol)) == 0) {
510  desc += strlen(info.symbol);
511  }
512  os << setw(31) << info.symbol << " " << desc << "\n";
513  }
514  else {
516  }
517  count++;
518  }
519  } while (dl_PAPI_enum_event(&i, PAPI_ENUM_EVENTS) == PAPI_OK);
520 
521  os << "Total native events reported: " << count << "\n";
522  os << SEPARATOR_MAJOR;
523 }
524 
525 
526 static int
528 {
530 }
531 
532 
533 static void
535 {
536  /* Initialize PAPI library */
538  exit(-1); /* message already printed */
539  }
540 }
541 
542 
543 //***************************************************************************
544 // Misc
545 //***************************************************************************
546 
547 static int
549 {
550 #if defined(HAVE_OS_MULTILIB)
551  prepend_to_ld_lib_path(HPC_PAPI "/lib32:" HPC_PAPI "/lib64");
552 #endif
553  return prepend_to_ld_lib_path(HPC_PAPI "/lib");
554 }
555 
556 
557 static void
559 {
560  DIAG_Msg(0, "Using PAPI installation: '"HPC_PAPI"'");
561  const char* MON = HPC_MONITOR;
562  if (MON[0] == '/') {
563  DIAG_Msg(0, "Using MONITOR installation: '"HPC_MONITOR"'\n");
564  }
565 }
566 
567 
568 static int
569 prepend_to_env_var(const char* env_var, const char* str, char sep);
570 
571 static int
572 prepend_to_ld_lib_path(const char* str)
573 {
574  return prepend_to_env_var(LD_LIBRARY_PATH, str, ':');
575 }
576 
577 
578 static int
579 prepend_to_ld_preload(const char* str)
580 {
581  return prepend_to_env_var(LD_PRELOAD, str, ' ');
582 }
583 
584 
585 static int
586 prepend_to_env_var(const char* env_var, const char* str, char sep)
587 {
588  char newval[PATH_MAX] = "";
589  char *oldval;
590  int sz;
591 
592  strncpy(newval, str, PATH_MAX);
593  oldval = getenv(env_var);
594  if (oldval) {
595  sz = PATH_MAX - (strlen(newval) + 1); /* 'path:' */
596  snprintf(newval + strlen(newval), sz, "%c%s", sep, oldval);
597  }
598  newval[PATH_MAX-1] = '\0';
599  setenv(env_var, newval, 1);
600  return 0;
601 }
602 
Definition: Args.hpp:79
dl_PAPI_is_initialized_t dl_PAPI_is_initialized
Definition: dlpapi.c:75
#define HPCRUN_EVENT_WALLCLK_STR
Definition: hpcrun.h:109
#define LD_LIBRARY_PATH
Definition: hpcrun.cpp:100
int hpc_init_papi(int(*is_init)(void), int(*init)(int))
Definition: hpcpapi.c:78
#define DIAG_If(level)
Definition: diagnostics.h:134
EventList_t listEvents
Definition: Args.hpp:111
#define DIAG_EMsg(...)
Definition: diagnostics.h:251
string toStr(const int x, int base)
Definition: StrUtil.cpp:243
dl_PAPI_get_hardware_info_t dl_PAPI_get_hardware_info
Definition: dlpapi.c:81
#define SEPARATOR_MAJOR
const char * DIAG_Unimplemented
static void init_papi()
Definition: hpcrun.cpp:534
static int real_main(int argc, char *argv[])
Definition: hpcrun.cpp:157
static int prepend_to_env_var(const char *env_var, const char *str, char sep)
Definition: hpcrun.cpp:586
#define HPCRUN_NAME
Definition: hpcrun.h:93
dl_PAPI_get_opt_t dl_PAPI_get_opt
Definition: dlpapi.c:79
int Diagnostics_GetDiagnosticFilterLevel()
Definition: diagnostics.cpp:87
Definition: fmt.c:108
std::string profThread
Definition: Args.hpp:116
dl_PAPI_library_init_t dl_PAPI_library_init
Definition: dlpapi.c:77
#define HPCRUN_THREADPROF_ALL_STR
EventList_t
Definition: Args.hpp:80
static int prepend_to_ld_preload(const char *str)
Definition: hpcrun.cpp:579
static __thread u32 pid
#define SEPARATOR_MINOR
static void print_external_lib_paths()
Definition: hpcrun.cpp:558
static int prepare_ld_lib_path_for_papi()
Definition: hpcrun.cpp:548
virtual std::string message() const
Definition: Exception.hpp:134
exit
Definition: names.cpp:1
unsigned int uint
Definition: uint.h:124
std::vector< std::string > profArgV
Definition: Args.hpp:122
dl_PAPI_enum_event_t dl_PAPI_enum_event
Definition: dlpapi.c:87
std::string profOutput
Definition: Args.hpp:118
dl_PAPI_query_event_t dl_PAPI_query_event
Definition: dlpapi.c:83
std::string profRecursive
Definition: Args.hpp:115
std::string profPAPIFlag
Definition: Args.hpp:119
int dlclose_papi()
Definition: dlpapi.c:134
#define DIAG_Msg(level,...)
Definition: diagnostics.h:241
char * findinstall(const char *cmd, const char *base_cmd)
Definition: findinstall.c:75
static int check_and_prepare_env_for_eventlisting()
Definition: hpcrun.cpp:527
#define HPCRUN_LIB
Definition: hpcrun.h:96
int main(int argc, char *argv[])
Definition: hpcrun.cpp:132
static int prepend_to_ld_lib_path(const char *str)
Definition: hpcrun.cpp:572
#define NULL
Definition: ElfHelper.cpp:85
dl_PAPI_get_event_info_t dl_PAPI_get_event_info
Definition: dlpapi.c:85
static void list_available_events_helper(Args::EventList_t listType)
Definition: hpcrun.cpp:387
bool printPaths
Definition: Args.hpp:112
static int list_available_events(char *argv[], Args::EventList_t listType)
Definition: hpcrun.cpp:337
std::string profEvents
Definition: Args.hpp:117
#define LD_PRELOAD
Definition: hpcrun.cpp:101
#define DIAG_Die(...)
Definition: diagnostics.h:267
#define HPCRUN_THREADPROF_EACH_STR
Definition: hpcrun.h:100
int dlopen_papi()
Definition: dlpapi.c:100
static int launch_with_profiling(const char *installpath, const Args &args)
Definition: hpcrun.cpp:190
static int prepare_env_for_profiling(const char *installpath, const Args &args)
Definition: hpcrun.cpp:223