Loading [MathJax]/extensions/tex2jax.js
The Gaudi Framework  v31r0 (aeb156f0)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
PerfMonAuditor.cpp
Go to the documentation of this file.
1 #include "GaudiKernel/Auditor.h"
2 #include "GaudiKernel/HashMap.h"
5 #include "GaudiKernel/IToolSvc.h"
8 
9 /*BEGIN: perfmon*/
10 #include <cstring>
11 #include <fstream>
12 #include <iostream>
13 #include <map>
14 #include <perfmon/pfmlib.h>
15 #include <perfmon/pfmlib_core.h>
17 #include <sstream>
18 #include <string>
19 #include <utility>
20 #include <vector>
21 
22 #include <perfmon/perfmon.h>
24 
26 
28 
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <getopt.h>
32 #include <signal.h>
33 #include <stdarg.h>
34 #include <stdint.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/mman.h>
39 #include <sys/ptrace.h>
40 #include <sys/resource.h>
41 #include <sys/time.h>
42 #include <sys/types.h>
43 #include <sys/wait.h>
44 #include <time.h>
45 #include <unistd.h>
46 #include <zlib.h>
47 
48 #include "IgHook_IgHookTrace.h"
49 #include <algorithm>
50 #include <cmath>
51 #include <list>
52 #include <stack>
53 #include <sys/stat.h>
54 
55 // dlopen (link with -ldl)
56 #include <dlfcn.h>
57 
58 #define MAX_EVT_NAME_LEN 256
59 #define NUM_PMCS PFMLIB_MAX_PMCS
60 #define NUM_PMDS PFMLIB_MAX_PMDS
61 #define FMT_NAME PFM_DFL_SMPL_NAME
62 #define BPL ( sizeof( uint64_t ) << 3 )
63 #define LBPL 6
64 
65 #define SYM_NAME_MAX_LENGTH 10000
66 #define MAX_OUTPUT_FILENAME_LENGTH 1024
67 #define MAX_EVENT_NAME_LENGTH 500
68 #define MAX_PREFIX_NAME_LENGTH 1024
69 #define FILENAME_MAX_LENGTH 1024
70 
71 #define MAX_NUMBER_OF_PROGRAMMABLE_COUNTERS 4
72 
73 #define cpuid( func, ax, bx, cx, dx ) \
74  __asm__ __volatile__( "cpuid" : "=a"( ax ), "=b"( bx ), "=c"( cx ), "=d"( dx ) : "a"( func ) );
75 
76 static pfarg_pmd_t pd_smpl[NUM_PMDS];
77 static uint64_t collected_samples, collected_partial;
78 static int ctx_fd;
79 static pfm_dfl_smpl_hdr_t* hdr;
80 static uint64_t ovfl_count;
81 static size_t entry_size;
82 static unsigned int num_smpl_pmds;
84  samples( MAX_NUMBER_OF_PROGRAMMABLE_COUNTERS ); // a map of modules each containing numbers of samples of their
85  // addresses
87  results( MAX_NUMBER_OF_PROGRAMMABLE_COUNTERS ); // a map of modules and their result values across multiple events
88 static uint64_t last_overflow;
89 static uint64_t last_count;
91 
93 /*END: perfmon*/
94 
95 namespace {
97  template <typename T>
98  inline T function_cast( void* p ) {
99  union {
100  void* object;
101  T function;
102  } caster;
103  caster.object = p;
104  return caster.function;
105  }
106  class PFMon {
107  public:
108  bool loaded;
109  typedef void ( *pfm_stop_t )( int );
110  pfm_stop_t pfm_stop;
111  typedef void ( *pfm_self_stop_t )( int );
112  pfm_self_stop_t pfm_self_stop;
113  typedef os_err_t ( *pfm_restart_t )( int );
114  pfm_restart_t pfm_restart;
115  typedef int ( *pfm_read_pmds_t )( int, pfarg_pmd_t*, int );
116  pfm_read_pmds_t pfm_read_pmds;
117  typedef pfm_err_t ( *pfm_initialize_t )();
118  pfm_initialize_t pfm_initialize;
119  typedef pfm_err_t ( *pfm_find_full_event_t )( const char*, pfmlib_event_t* );
120  pfm_find_full_event_t pfm_find_full_event;
121  typedef pfm_err_t ( *pfm_dispatch_events_t )( pfmlib_input_param_t*, void*, pfmlib_output_param_t*, void* );
122  pfm_dispatch_events_t pfm_dispatch_events;
123  typedef os_err_t ( *pfm_create_context_t )( pfarg_ctx_t*, char*, void*, size_t );
124  pfm_create_context_t pfm_create_context;
125  typedef os_err_t ( *pfm_write_pmcs_t )( int, pfarg_pmc_t*, int );
126  pfm_write_pmcs_t pfm_write_pmcs;
127  typedef os_err_t ( *pfm_write_pmds_t )( int, pfarg_pmd_t*, int );
128  pfm_write_pmds_t pfm_write_pmds;
129  typedef os_err_t ( *pfm_load_context_t )( int, pfarg_load_t* );
130  pfm_load_context_t pfm_load_context;
131  typedef os_err_t ( *pfm_start_t )( int fd, pfarg_start_t* );
132  pfm_start_t pfm_start;
133  typedef char* ( *pfm_strerror_t )( int );
134  pfm_strerror_t pfm_strerror;
135  typedef pfm_err_t ( *pfm_set_options_t )( pfmlib_options_t* );
136  pfm_set_options_t pfm_set_options;
137  typedef pfm_err_t ( *pfm_get_num_counters_t )( unsigned int* );
138  pfm_get_num_counters_t pfm_get_num_counters;
139  static PFMon& instance() { return s_instance; }
140 
141  private:
142  // static void failure() { throw 1; }
143 
144  void* handle;
145 
146  PFMon() {
147  handle = dlopen( "libpfm.so", RTLD_NOW );
148  if ( handle ) {
149  loaded = true;
150  } else {
151  loaded = false;
152  }
153  if ( loaded ) {
154  pfm_start = function_cast<pfm_start_t>( dlsym( handle, "pfm_start" ) );
155  pfm_stop = function_cast<pfm_stop_t>( dlsym( handle, "pfm_stop" ) );
156  pfm_self_stop = function_cast<pfm_self_stop_t>( dlsym( handle, "pfm_stop" ) ); // it's the same
157  pfm_restart = function_cast<pfm_restart_t>( dlsym( handle, "pfm_restart" ) );
158  pfm_read_pmds = function_cast<pfm_read_pmds_t>( dlsym( handle, "pfm_read_pmds" ) );
159  pfm_initialize = function_cast<pfm_initialize_t>( dlsym( handle, "pfm_initialize" ) );
160  pfm_find_full_event = function_cast<pfm_find_full_event_t>( dlsym( handle, "pfm_find_full_event" ) );
161  pfm_dispatch_events = function_cast<pfm_dispatch_events_t>( dlsym( handle, "pfm_dispatch_events" ) );
162  pfm_create_context = function_cast<pfm_create_context_t>( dlsym( handle, "pfm_create_context" ) );
163  pfm_write_pmcs = function_cast<pfm_write_pmcs_t>( dlsym( handle, "pfm_write_pmcs" ) );
164  pfm_write_pmds = function_cast<pfm_write_pmds_t>( dlsym( handle, "pfm_write_pmds" ) );
165  pfm_load_context = function_cast<pfm_load_context_t>( dlsym( handle, "pfm_load_context" ) );
166  pfm_strerror = function_cast<pfm_strerror_t>( dlsym( handle, "pfm_strerror" ) );
167  pfm_set_options = function_cast<pfm_set_options_t>( dlsym( handle, "pfm_set_options" ) );
168  pfm_get_num_counters = function_cast<pfm_get_num_counters_t>( dlsym( handle, "pfm_get_num_counters" ) );
169  } else {
170  // pfm_start = pfm_stop = pfm_self_stop = pfm_restart = pfm_read_pmds = pfm_initialize = pfm_find_full_event =
171  // pfm_dispatch_events = pfm_create_context = pfm_write_pmcs = pfm_write_pmds = pfm_load_context = pfm_strerror
172  // = pfm_set_options = pfm_get_num_counters = failure;
173  }
174  }
175  ~PFMon() {
176  if ( handle ) dlclose( handle );
177  }
178 
179  static PFMon s_instance;
180  };
181 
182  PFMon PFMon::s_instance;
183 } // namespace
184 
185 // ============================================================================
186 // GaudiAlg
187 // ============================================================================
188 // ============================================================================
198 class PerfMonAuditor : virtual public Auditor {
199 public:
200  void before( StandardEventType evt, INamedInterface* alg ) override;
201  void after( StandardEventType evt, INamedInterface* alg, const StatusCode& sc ) override;
202  using Auditor::after;
203  using Auditor::before;
204 
205 private:
206  void i_beforeInitialize( INamedInterface* alg );
207  void i_afterInitialize( INamedInterface* alg );
208  void i_beforeExecute( INamedInterface* alg );
209  void i_afterExecute( INamedInterface* alg );
210 
211 public:
212  StatusCode initialize() override;
213  StatusCode finalize() override;
214  int is_nehalem() {
215 #ifdef __ICC
216 // Disable ICC remark #593: variable "x" was set but never used
217 # pragma warning( push )
218 # pragma warning( disable : 593 )
219 #endif
220  int a, b, c, d;
221  cpuid( 1, a, b, c, d );
222  int sse4_2_mask = 1 << 20;
223  if ( c & sse4_2_mask )
224  return 1;
225  else
226  return 0;
227 #ifdef __ICC
228 # pragma warning( pop )
229 #endif
230  }
231 
232 private:
233  PFMon& m_pfm;
234  /*
235  typedef void (*pfm_stop_t)(int);
236  pfm_stop_t pfm_stop;
237  typedef void (*pfm_self_stop_t)(int);
238  pfm_self_stop_t pfm_self_stop;
239 
240  typedef os_err_t (*pfm_restart_t)(int);
241  pfm_restart_t pfm_restart;
242 
243  //typedef int (*pfm_read_pmds_t)(int, pfarg_pmd_t*, int);
244  //pfm_read_pmds_t pfm_read_pmds;
245 
246  typedef pfm_err_t (*pfm_initialize_t)();
247  pfm_initialize_t pfm_initialize;
248  typedef pfm_err_t (*pfm_find_full_event_t)(const char *, pfmlib_event_t *);
249  pfm_find_full_event_t pfm_find_full_event;
250  typedef pfm_err_t (*pfm_dispatch_events_t)(pfmlib_input_param_t *, void *, pfmlib_output_param_t *, void *);
251  pfm_dispatch_events_t pfm_dispatch_events;
252  typedef os_err_t (*pfm_create_context_t)(pfarg_ctx_t *, char *, void *, size_t);
253  pfm_create_context_t pfm_create_context;
254  typedef os_err_t (*pfm_write_pmcs_t)(int, pfarg_pmc_t *, int);
255  pfm_write_pmcs_t pfm_write_pmcs;
256  typedef os_err_t (*pfm_write_pmds_t)(int, pfarg_pmd_t *, int);
257  pfm_write_pmds_t pfm_write_pmds;
258  typedef os_err_t (*pfm_load_context_t)(int, pfarg_load_t *);
259  pfm_load_context_t pfm_load_context;
260  typedef os_err_t (*pfm_start_t)(int fd, pfarg_start_t *);
261  pfm_start_t pfm_start;
262  typedef char* (*pfm_strerror_t)(int);
263  pfm_strerror_t pfm_strerror;
264  typedef pfm_err_t (*pfm_set_options_t)(pfmlib_options_t *);
265  pfm_set_options_t pfm_set_options;
266  typedef pfm_err_t (*pfm_get_num_counters_t)(unsigned int *);
267  pfm_get_num_counters_t pfm_get_num_counters;
268  */
269 
270 public:
272  : // standard constructor
273  Auditor( name, pSvc )
274  , m_pfm( PFMon::instance() )
275  , m_map()
276  , m_indent( 0 )
277  , m_inEvent( false ) {
278  is_nehalem_ret = is_nehalem();
279  declareProperty( "EVENT0", event_str[0] );
280  declareProperty( "EVENT1", event_str[1] );
281  declareProperty( "EVENT2", event_str[2] );
282  declareProperty( "EVENT3", event_str[3] );
283  declareProperty( "FAMILY", family );
284  declareProperty( "PREFIX", prefix );
285  declareProperty( "INV0", inv[0] );
286  declareProperty( "INV1", inv[1] );
287  declareProperty( "INV2", inv[2] );
288  declareProperty( "INV3", inv[3] );
289  declareProperty( "CMASK0", cmask[0] );
290  declareProperty( "CMASK1", cmask[1] );
291  declareProperty( "CMASK2", cmask[2] );
292  declareProperty( "CMASK3", cmask[3] );
293  declareProperty( "SP0", sp[0] );
294  declareProperty( "SP1", sp[1] );
295  declareProperty( "SP2", sp[2] );
296  declareProperty( "SP3", sp[3] );
297  declareProperty( "SAMPLE", sampling );
298  declareProperty( "START_AT_EVENT", start_at_event );
299  declareProperty( "IS_NEHALEM", is_nehalem_ret );
300 
302  /*
303  // loading functions from PFM library
304  void* handle = dlopen("libpfm.so", RTLD_NOW);
305  if (!handle) {
306  // error() << "Cannot open library: " << dlerror() << endmsg;
307  }
308  typedef void (*hello_t)();
309  hello_t hello = (hello_t) dlsym(handle, "hello");
310  if (!hello) {
311  // error() << "Cannot load symbol 'hello': " << dlerror() << endmsg;
312  dlclose(handle);
313  }
314 
315  pfm_start = (pfm_start_t) dlsym(handle, "pfm_start");
316  pfm_stop = (pfm_stop_t) dlsym(handle, "pfm_stop");
317  pfm_self_stop = (pfm_self_stop_t) dlsym(handle, "pfm_stop"); //it's the same
318  pfm_restart = (pfm_restart_t) dlsym(handle, "pfm_restart");
319  //pfm_read_pmds = (pfm_read_pmds_t) dlsym(handle, "pfm_read_pmds");
320  pfm_initialize = (pfm_initialize_t) dlsym(handle, "pfm_initialize");
321  pfm_find_full_event = (pfm_find_full_event_t) dlsym(handle, "pfm_find_full_event");
322  pfm_dispatch_events = (pfm_dispatch_events_t) dlsym(handle, "pfm_dispatch_events");
323  pfm_create_context = (pfm_create_context_t) dlsym(handle, "pfm_create_context");
324  pfm_write_pmcs = (pfm_write_pmcs_t) dlsym(handle, "pfm_write_pmcs");
325  pfm_write_pmds = (pfm_write_pmds_t) dlsym(handle, "pfm_write_pmds");
326  pfm_load_context = (pfm_load_context_t) dlsym(handle, "pfm_load_context");
327  pfm_strerror = (pfm_strerror_t) dlsym(handle, "pfm_strerror");
328  pfm_set_options = (pfm_set_options_t) dlsym(handle, "pfm_set_options");
329  pfm_get_num_counters = (pfm_get_num_counters_t) dlsym(handle, "pfm_get_num_counters");
330  // use it to do the calculation
331  // info() << "Calling hello..." << endmsg;
332  // hello();
333 
334  // close the library
335  // info() << "Closing library..." << endmsg;
336  dlclose(handle);
337  */
338 
340  }
341 
342  virtual ~PerfMonAuditor() {} // virtual destructor
343 
344 private:
345  PerfMonAuditor(); // the default constructor is disabled
346  PerfMonAuditor( const PerfMonAuditor& ); // copy constructor is disabled
347  PerfMonAuditor& operator=( const PerfMonAuditor& ); // assignement operator is disabled
348 
349 private:
351  Map m_map;
352  int m_indent; // indentation level
353  bool m_inEvent; // "In event" flag
354 
355 private:
357 
358  pfmlib_input_param_t inp;
359  pfmlib_output_param_t outp;
360  pfarg_ctx_t ctx;
361  pfarg_pmd_t pd[NUM_PMDS];
362  pfarg_pmc_t pc[NUM_PMCS];
364  int fd;
365  unsigned int i;
366  int ret;
367  void startpm();
368  void pausepm();
369  void stoppm();
370  void finalizepm();
375  char prefix_cstr[MAX_PREFIX_NAME_LENGTH];
376  unsigned int ph_ev_count;
379  unsigned int start_at_event;
383  bool nehalem;
384  bool westmere;
385  bool core;
386 
387  bool sampling;
388  int detect_unavail_pmu_regs( int fd, pfmlib_regmask_t* r_pmcs, pfmlib_regmask_t* r_pmds );
389  int detect_unavail_pmcs( int fd, pfmlib_regmask_t* r_pmcs ) { return detect_unavail_pmu_regs( fd, r_pmcs, NULL ); }
390  void pfm_bv_set( uint64_t* bv, uint16_t rnum ) { bv[rnum >> LBPL] |= 1UL << ( rnum & ( BPL - 1 ) ); }
391  int pfm_bv_isset( uint64_t* bv, uint16_t rnum ) {
392  return bv[rnum >> LBPL] & ( 1UL << ( rnum & ( BPL - 1 ) ) ) ? 1 : 0;
393  }
395  if ( n <= BPL )
396  *d = *j;
397  else {
398  memcpy( d, j, ( n >> LBPL ) * sizeof( uint64_t ) );
399  }
400  }
401  static void process_smpl_buf( pfm_dfl_smpl_hdr_t* hdr, size_t entry_size );
402  static void sigio_handler( int, siginfo_t*, void* ); // dlopen ==>
403  // void sigio_handler(int, struct siginfo *, struct sigcontext *);
404  void start_smpl();
405  void stop_smpl();
406  void finalize_smpl();
409  void* buf_addr;
410  unsigned num_counters;
411  unsigned int max_pmd;
413 
414  int level;
415 
416  bool first_alg;
419 };
420 
422  memset( &ctx, 0, sizeof( ctx ) );
423  memset( &inp, 0, sizeof( inp ) );
424  memset( &outp, 0, sizeof( outp ) );
425  memset( pd, 0, sizeof( pd ) );
426  memset( pc, 0, sizeof( pc ) );
427  memset( &load_arg, 0, sizeof( load_arg ) );
428  memset( &params, 0, sizeof( params ) );
429  memset( &nhm_params, 0, sizeof( nhm_params ) );
430 
431  for ( int i = 0; i < used_counters_number; i++ ) {
432  ret = m_pfm.pfm_find_full_event( event_cstr[i], &inp.pfp_events[i] );
433  if ( ret != PFMLIB_SUCCESS ) {
434  error() << "ERROR: cannot find event: " << event_cstr[i] << ". Aborting..." << endmsg;
435  }
436  }
437  inp.pfp_dfl_plm = PFM_PLM3;
438  inp.pfp_event_count = 4;
439  for ( int i = 0; i < used_counters_number; i++ ) {
440  if ( inv[i] ) {
441  ( params.pfp_core_counters[i] ).flags |= PFM_CORE_SEL_INV;
442  ( nhm_params.pfp_nhm_counters[i] ).flags |= PFM_NHM_SEL_INV;
443  }
444  if ( cmask[i] > 0 ) {
445  ( params.pfp_core_counters[i] ).cnt_mask = cmask[i];
446  ( nhm_params.pfp_nhm_counters[i] ).cnt_mask = cmask[i];
447  }
448  }
449  if ( nehalem || westmere ) {
450  ret = m_pfm.pfm_dispatch_events( &inp, &nhm_params, &outp, NULL );
451  } else {
452  ret = m_pfm.pfm_dispatch_events( &inp, &params, &outp, NULL );
453  }
454  if ( ret != PFMLIB_SUCCESS ) {
455  error() << "ERROR: cannot dispatch events: " << m_pfm.pfm_strerror( ret ) << ". Aborting..." << endmsg;
456  }
457  for ( unsigned int i = 0; i < outp.pfp_pmc_count; i++ ) {
458  pc[i].reg_num = outp.pfp_pmcs[i].reg_num;
459  pc[i].reg_value = outp.pfp_pmcs[i].reg_value;
460  }
461  for ( unsigned int i = 0; i < outp.pfp_pmd_count; i++ ) {
462  pd[i].reg_num = outp.pfp_pmds[i].reg_num;
463  pd[i].reg_value = 0;
464  }
465  fd = m_pfm.pfm_create_context( &ctx, NULL, 0, 0 );
466  if ( fd == -1 ) { error() << "ERROR: Context not created. Aborting..." << endmsg; }
467  if ( m_pfm.pfm_write_pmcs( fd, pc, outp.pfp_pmc_count ) == -1 ) {
468  error() << "ERROR: Could not write pmcs. Aborting..." << endmsg;
469  }
470  if ( m_pfm.pfm_write_pmds( fd, pd, outp.pfp_pmd_count ) == -1 ) {
471  error() << "ERROR: Could not write pmds. Aborting..." << endmsg;
472  }
473  load_arg.load_pid = getpid();
474  if ( m_pfm.pfm_load_context( fd, &load_arg ) == -1 ) {
475  error() << "ERROR: Could not load context. Aborting..." << endmsg;
476  // error() << "Could not read pmds" << endmsg;
477  }
478 
479  m_pfm.pfm_start( fd, NULL );
480 }
481 
482 // stoppm()
483 // const ModuleDescription& desc : description of the module that just finished its execution (we are only interested in
484 // its name)
485 // stops the counting calling pfm_stop() and stores the counting results into the "results" map
487  m_pfm.pfm_stop( fd );
488  if ( m_pfm.pfm_read_pmds( fd, pd, inp.pfp_event_count ) == -1 ) { error() << "Could not read pmds" << endmsg; }
489  for ( int i = 0; i < used_counters_number; i++ ) {
490  results[i][( alg_stack.top().first )->name()].push_back( alg_stack.top().second[i] + pd[i].reg_value );
491  }
492 
493  close( fd );
494 }
495 
497  m_pfm.pfm_stop( fd );
498  if ( m_pfm.pfm_read_pmds( fd, pd, inp.pfp_event_count ) == -1 ) { error() << "Could not read pmds" << endmsg; }
499 
500  for ( int i = 0; i < used_counters_number; i++ ) { alg_stack.top().second[i] += pd[i].reg_value; }
501 
502  close( fd );
503 }
504 
505 // finalizepm()
506 // called when all the countings of the current event are finished, it dumps the results
507 // into the output file corresponding to the event being counted
509  info() << "start of finalizepm ucn:" << used_counters_number << endmsg;
510  for ( int i = 0; i < used_counters_number; i++ ) {
511  std::string filename = prefix_cstr;
512  filename += '_';
513  filename += event_cstr[i];
514 
515  for ( auto& c : filename )
516  if ( c == ':' ) c = '-';
517 
518  if ( inv[i] ) filename += "_INV_1";
519  if ( cmask[i] > 0 ) filename += "_CMASK_" + std::to_string( cmask[i] );
520  filename += ".txt";
521 
522  info() << "Filename:" << filename << endmsg;
523  FILE* outfile = fopen( filename.c_str(), "w" );
524  if ( nehalem ) {
525  fprintf( outfile, "NHM " );
526  } else if ( westmere ) {
527  fprintf( outfile, "WSM " );
528  } else if ( core ) {
529  fprintf( outfile, "CORE " );
530  }
531  fprintf( outfile, "%s %d %d %d\n", event_cstr[i], cmask[i], inv[i], sp[i] );
532  for ( std::map<std::string, std::vector<unsigned long int>>::iterator it = ( results[i] ).begin();
533  it != ( results[i] ).end(); it++ ) {
534  fprintf( outfile, "%s\n", ( it->first ).c_str() );
535  for ( std::vector<unsigned long int>::iterator j = ( it->second ).begin(); j != ( it->second ).end(); j++ ) {
536  fprintf( outfile, "%lu\n", *j );
537  }
538  }
539  fclose( outfile );
540  }
541 }
542 
544  if ( !m_pfm.loaded ) {
545  error() << "pfm library could not be loaded" << endmsg;
546  return StatusCode::FAILURE;
547  }
548 
549  info() << "Initializing..." << endmsg;
551  if ( sc.isFailure() ) { return sc; }
552  used_counters_number = 0;
553  for ( int i = 0; i < MAX_NUMBER_OF_PROGRAMMABLE_COUNTERS; i++ ) {
554  if ( event_str[i].length() > 0 ) used_counters_number++;
555  }
556  for ( int i = 0; i < MAX_NUMBER_OF_PROGRAMMABLE_COUNTERS; i++ ) { strcpy( event_cstr[i], event_str[i].c_str() ); }
557  strcpy( prefix_cstr, prefix.c_str() );
558 
559  if ( m_pfm.pfm_initialize() != PFMLIB_SUCCESS ) { error() << "Cannot initialize perfmon!!" << endmsg; }
560  ph_ev_count = 0;
561  first_alg = true;
562  event_count_reached = false;
563  nehalem = false;
564  core = false;
565  westmere = false;
566  if ( family.compare( "CORE" ) == 0 )
567  core = true;
568  else if ( family.compare( "NEHALEM" ) == 0 )
569  nehalem = true;
570  else if ( family.compare( "WESTMERE" ) == 0 )
571  westmere = true;
572  else {
573  error() << "ERROR: Unsupported processor family " << family << ". aborting..." << endmsg;
574  }
575 
576  info() << "Initialized!" << endmsg;
577  return StatusCode::SUCCESS;
578 }
579 
580 // process_smpl_buf()
581 // pfm_dfl_smpl_hdr_t *hdr : pointer to header of the buffer containing addresses sampled during the sampling process
582 // size_t entry_size : size of each entry, used to navigate through the various entries
583 // called when the sampling buffer is full, saves the samples into memory ("samples" map)
584 void PerfMonAuditor::process_smpl_buf( pfm_dfl_smpl_hdr_t* hdr, size_t entry_size ) {
587  size_t pos, count;
588  uint64_t entry;
589  if ( hdr->hdr_overflows == last_overflow && hdr->hdr_count == last_count ) {
590  printf( "skipping identical set of samples...\n" );
591  return;
592  }
593  count = hdr->hdr_count;
594  ent = (pfm_dfl_smpl_entry_t*)( hdr + 1 );
595  pos = (unsigned long)ent;
596  entry = collected_samples;
597  while ( count-- ) {
598  // if(ent->ovfl_pmd>=0 && ent->ovfl_pmd<=3)
599  if ( ent->ovfl_pmd <= 3 ) {
600  ( ( samples[ent->ovfl_pmd] )[( alg_stack.top().first )->name()] )[(unsigned long)( ent->ip )]++;
601  }
602  pos += entry_size;
603  ent = (pfm_dfl_smpl_entry_t*)pos;
604  entry++;
605  }
606  collected_samples = entry;
607  last_overflow = hdr->hdr_overflows;
608  if ( last_count != hdr->hdr_count && ( last_count || last_overflow == 0 ) ) { collected_partial += hdr->hdr_count; }
609  last_count = hdr->hdr_count;
610  return;
611 }
612 
613 // sigio_handler()
614 // int n : signal number of the signal being delivered
615 // siginfo_t *info : pointer to a siginfo_t structure containing info about the signal
616 // signal handler used to catch sampling buffer overflows. When they occur it calls the process_smpl_buf() function
617 void PerfMonAuditor::sigio_handler( int /*n*/, siginfo_t* /*info*/, void* ) {
618  PFMon& pfm = PFMon::instance();
620  int fd = ctx_fd;
621  int r;
622  if ( fd != ctx_fd ) {
623  // error() << "ERROR: handler does not get valid file descriptor. Aborting..." << endmsg;
624  }
625  if ( pfm.pfm_read_pmds( fd, pd_smpl + 1, 1 ) == -1 ) {
626  // error() << "ERROR: pfm_read_pmds: " << strerror(errno) << ". Aborting..." << endmsg;
627  }
628  while ( true ) {
629  r = read( fd, &msg, sizeof( msg ) );
630  if ( r != sizeof( msg ) ) {
631  if ( r == -1 && errno == EINTR ) {
632  printf( "read interrupted, retrying\n" );
633  continue;
634  }
635  // error() << "ERROR: cannot read overflow message: " << strerror(errno) << ". Aborting..." << endmsg;
636  }
637  break;
638  }
639  switch ( msg.type ) {
640  case PFM_MSG_OVFL: // the sampling buffer is full
641  process_smpl_buf( hdr, entry_size );
642  ovfl_count++;
643  if ( pfm.pfm_restart( fd ) ) {
644  if ( errno != EBUSY ) {
645  // error() << "ERROR: pfm_restart error errno " << errno << ". Aborting..." << endmsg;
646  } else {
647  printf( "pfm_restart: task probably terminated \n" );
648  }
649  }
650  break;
651  default:
652  // error() << "ERROR: unknown message type " << msg.type << ". Aborting..." << endmsg;
653  break;
654  }
655 }
656 
657 // start_smpl()
658 // const ModuleDescription& desc : description of the module that is just starting its execution (we are only interested
659 // in its name)
660 // initializes all the necessary structures to start the sampling, calling pfm_self_start()
662  ovfl_count = 0;
663  num_smpl_pmds = 0;
664  last_overflow = ~0;
665  max_pmd = 0;
666  memset( &pfmlib_options, 0, sizeof( pfmlib_options ) );
667  pfmlib_options.pfm_debug = 0;
668  pfmlib_options.pfm_verbose = 0;
669  m_pfm.pfm_set_options( &pfmlib_options );
670  ret = m_pfm.pfm_initialize();
671  if ( ret != PFMLIB_SUCCESS ) {
672  error() << "ERROR: Cannot initialize library: " << m_pfm.pfm_strerror( ret ) << ". Aborting..." << endmsg;
673  }
674  struct sigaction act;
675  memset( &act, 0, sizeof( act ) );
676  act.sa_sigaction = sigio_handler; // dlopen() ==>
677  // act.sa_handler = (sig_t)&sigio_handler;
678  sigaction( SIGIO, &act, 0 );
679  memset( &ctx, 0, sizeof( ctx ) );
680  memset( &buf_arg, 0, sizeof( buf_arg ) );
681  memset( &inp, 0, sizeof( inp ) );
682  memset( &outp, 0, sizeof( outp ) );
683  memset( pd_smpl, 0, sizeof( pd_smpl ) );
684  memset( pc, 0, sizeof( pc ) );
685  memset( &load_args, 0, sizeof( load_args ) );
686  m_pfm.pfm_get_num_counters( &num_counters );
687  memset( &params, 0, sizeof( params ) );
688  memset( &nhm_params, 0, sizeof( nhm_params ) );
689 
690  for ( int i = 0; i < used_counters_number; i++ ) {
691  ret = m_pfm.pfm_find_full_event( event_cstr[i], &inp.pfp_events[i] );
692  if ( ret != PFMLIB_SUCCESS ) {
693  error() << "ERROR: cannot find event: " << event_cstr[i] << ". Aborting..." << endmsg;
694  }
695  }
696  inp.pfp_dfl_plm = PFM_PLM3;
697  inp.pfp_event_count = 4;
698  for ( int i = 0; i < used_counters_number; i++ ) {
699  if ( inv[i] ) {
700  ( params.pfp_core_counters[i] ).flags |= PFM_CORE_SEL_INV;
701  ( nhm_params.pfp_nhm_counters[i] ).flags |= PFM_NHM_SEL_INV;
702  }
703  if ( cmask[i] > 0 ) {
704  ( params.pfp_core_counters[i] ).cnt_mask = cmask[i];
705  ( nhm_params.pfp_nhm_counters[i] ).cnt_mask = cmask[i];
706  }
707  }
708  if ( nehalem || westmere ) {
709  ret = m_pfm.pfm_dispatch_events( &inp, &nhm_params, &outp, NULL );
710  } else {
711  ret = m_pfm.pfm_dispatch_events( &inp, &params, &outp, NULL );
712  }
713  if ( ret != PFMLIB_SUCCESS ) {
714  error() << "ERROR: cannot configure events: " << m_pfm.pfm_strerror( ret ) << ". Aborting..." << endmsg;
715  }
716  for ( unsigned int i = 0; i < outp.pfp_pmc_count; i++ ) {
717  pc[i].reg_num = outp.pfp_pmcs[i].reg_num;
718  pc[i].reg_value = outp.pfp_pmcs[i].reg_value;
719  }
720  for ( unsigned int i = 0; i < outp.pfp_pmd_count; i++ ) {
721  pd_smpl[i].reg_num = outp.pfp_pmds[i].reg_num;
722  if ( i ) {
723  pfm_bv_set( pd_smpl[0].reg_smpl_pmds, pd_smpl[i].reg_num );
724  if ( pd_smpl[i].reg_num > max_pmd ) { max_pmd = pd_smpl[i].reg_num; }
725  num_smpl_pmds++;
726  }
727  }
728  for ( int i = 0; i < used_counters_number; i++ ) {
730  pfm_bv_copy( pd_smpl[i].reg_reset_pmds, pd_smpl[i].reg_smpl_pmds, max_pmd );
731  pd_smpl[i].reg_value = ( uint64_t )( sp[i] * -1 );
732  pd_smpl[i].reg_short_reset = ( uint64_t )( sp[i] * -1 );
733  pd_smpl[i].reg_long_reset = ( uint64_t )( sp[i] * -1 );
734  pd_smpl[i].reg_random_seed = 5; // tocheck
735  pd_smpl[i].reg_random_mask = 0xff; // tocheck
736  }
737  entry_size = sizeof( pfm_dfl_smpl_entry_t ) + ( num_smpl_pmds << 3 );
738  ctx.ctx_flags = 0;
739  buf_arg.buf_size = 3 * getpagesize() + 512;
740  ctx_fd = m_pfm.pfm_create_context( &ctx, (char*)FMT_NAME, &buf_arg, sizeof( buf_arg ) );
741  if ( ctx_fd == -1 ) {
742  if ( errno == ENOSYS ) {
743  error() << "ERROR: Your kernel does not have performance monitoring support! Aborting..." << endmsg;
744  }
745  error() << "ERROR: Can't create PFM context " << strerror( errno ) << ". Aborting..." << endmsg;
746  }
747  buf_addr = mmap( NULL, buf_arg.buf_size, PROT_READ, MAP_PRIVATE, ctx_fd, 0 );
748  if ( buf_addr == MAP_FAILED ) {
749  error() << "ERROR: cannot mmap sampling buffer: " << strerror( errno ) << ". Aborting..." << endmsg;
750  }
751  hdr = (pfm_dfl_smpl_hdr_t*)buf_addr;
752  if ( PFM_VERSION_MAJOR( hdr->hdr_version ) < 1 ) {
753  error() << "ERROR: invalid buffer format version. Aborting..." << endmsg;
754  }
755  if ( m_pfm.pfm_write_pmcs( ctx_fd, pc, outp.pfp_pmc_count ) ) {
756  error() << "ERROR: pfm_write_pmcs error errno " << strerror( errno ) << ". Aborting..." << endmsg;
757  }
758  if ( m_pfm.pfm_write_pmds( ctx_fd, pd_smpl, outp.pfp_pmd_count ) ) {
759  error() << "ERROR: pfm_write_pmds error errno " << strerror( errno ) << ". Aborting..." << endmsg;
760  }
761  load_args.load_pid = getpid();
762  if ( m_pfm.pfm_load_context( ctx_fd, &load_args ) ) {
763  error() << "ERROR: pfm_load_context error errno " << strerror( errno ) << ". Aborting..." << endmsg;
764  }
765  ret = fcntl( ctx_fd, F_SETFL, fcntl( ctx_fd, F_GETFL, 0 ) | O_ASYNC );
766  if ( ret == -1 ) { error() << "ERROR: cannot set ASYNC: " << strerror( errno ) << ". Aborting..." << endmsg; }
767  ret = fcntl( ctx_fd, F_SETOWN, getpid() );
768  if ( ret == -1 ) { error() << "ERROR: cannot setown: " << strerror( errno ) << ". Aborting..." << endmsg; }
769  // pfm_self_start(ctx_fd); ==>
770  m_pfm.pfm_start( ctx_fd, NULL );
771 }
772 
773 // stop_smpl()
774 // const ModuleDescription& desc : description of the module that just finished its execution (we are only interested in
775 // its name)
776 // stops the sampling and calls process_smpl_buf() one last time to process all the remaining samples
778  m_pfm.pfm_self_stop( ctx_fd );
779  process_smpl_buf( hdr, entry_size );
780  close( ctx_fd );
781  ret = munmap( hdr, buf_arg.buf_size );
782  if ( ret ) { error() << "Cannot unmap buffer: %s" << strerror( errno ) << endmsg; }
783  return;
784 }
785 
786 // finalize_smpl()
787 // processes the sampling results in order to find library and offset of each sampled address, using the symbol() and
788 // tosymbol() functions,
789 // and then dumps the new found information into gzipped output files, to be processed later
791  int err;
792  for ( int i = 0; i < used_counters_number; i++ ) {
793  std::string filename = prefix_cstr;
794  filename += '_';
795  filename += event_cstr[i];
796 
797  for ( auto& c : filename )
798  if ( c == ':' ) c = '-';
799 
800  if ( inv[i] ) filename += "_INV_1";
801  if ( cmask[i] > 0 ) filename += "_CMASK_" + std::to_string( cmask[i] );
802  filename += ".txt.gz";
803 
804  gzFile outfile = gzopen( filename.c_str(), "wb" );
805  if ( outfile != NULL ) {
806  if ( nehalem ) {
807  gzprintf( outfile, "NHM " );
808  } else if ( westmere ) {
809  gzprintf( outfile, "WSM " );
810  } else if ( core ) {
811  gzprintf( outfile, "CORE " );
812  }
813  if ( gzprintf( outfile, "%s %d %d %d\n", event_cstr[i], cmask[i], inv[i], sp[i] ) <
814  (int)strlen( event_cstr[i] ) ) {
815  error() << "ERROR: gzputs err: " << gzerror( outfile, &err ) << ". Aborting..." << endmsg;
816  }
817  for ( std::map<std::string, std::map<unsigned long, unsigned int>>::iterator it = samples[i].begin();
818  it != samples[i].end(); it++ ) {
819  unsigned long long sum = 0;
820  for ( std::map<unsigned long, unsigned int>::iterator jt = ( it->second ).begin(); jt != ( it->second ).end();
821  jt++ ) {
822  sum += jt->second;
823  }
824  if ( gzprintf( outfile, "%s%%%llu\n", ( it->first ).c_str(), sum ) < (int)( ( it->first ).length() ) ) {
825  error() << "ERROR: gzputs err: " << gzerror( outfile, &err ) << ". Aborting..." << endmsg;
826  }
827  for ( std::map<unsigned long, unsigned int>::iterator jt = ( it->second ).begin(); jt != ( it->second ).end();
828  jt++ ) {
829  char sym_name[SYM_NAME_MAX_LENGTH];
830  bzero( sym_name, SYM_NAME_MAX_LENGTH );
831  const char* libName;
832  const char* symbolName;
833  int libOffset = 0;
834  int offset = 0;
835  void* sym_addr = IgHookTrace::tosymbol( (void*)( jt->first ) );
836  if ( sym_addr != NULL ) {
837  bool success = IgHookTrace::symbol( sym_addr, symbolName, libName, offset, libOffset );
838  if ( success ) {
839  if ( symbolName != NULL && strlen( symbolName ) > 0 ) {
840  strcpy( sym_name, symbolName );
841  strcat( sym_name, " " );
842  } else {
843  strcpy( sym_name, "??? " );
844  }
845  if ( libName != NULL && strlen( libName ) > 0 ) {
846  strcat( sym_name, libName );
847  strcat( sym_name, " " );
848  } else {
849  strcat( sym_name, "??? " );
850  }
851  sprintf( sym_name + strlen( sym_name ), "%d ", libOffset );
852  if ( strlen( sym_name ) <= 0 ) { error() << "ERROR: Symbol name length is zero. Aborting..." << endmsg; }
853  } else {
854  strcpy( sym_name, "??? ??? 0 " );
855  }
856  } else {
857  strcpy( sym_name, "??? ??? 0 " );
858  }
859  if ( gzprintf( outfile, "%s %d\n", sym_name, jt->second ) < (int)strlen( sym_name ) ) {
860  error() << "ERROR: gzputs err: " << gzerror( outfile, &err ) << endmsg;
861  }
862  }
863  }
864  } else {
865  error() << "ERROR: Could not open file: " << filename << ". Aborting..." << endmsg;
866  }
867  gzclose( outfile );
868  }
869 }
870 
872  if ( sampling == 0 )
873  finalizepm();
874  else
875  finalize_smpl();
876  return Auditor::finalize();
877 }
878 
879 void PerfMonAuditor::before( StandardEventType evt, INamedInterface* alg ) {
880  switch ( evt ) {
882  i_beforeInitialize( alg );
883  break;
884  case IAuditor::Execute:
885  i_beforeExecute( alg );
886  break;
887  default:
888  break;
889  }
890  return;
891 }
892 
893 void PerfMonAuditor::after( StandardEventType evt, INamedInterface* alg, const StatusCode& ) {
894  switch ( evt ) {
896  i_afterInitialize( alg );
897  break;
898  case IAuditor::Execute:
899  i_afterExecute( alg );
900  break;
901  default:
902  break;
903  }
904  return;
905 }
906 
908  if ( !alg ) return;
909  return;
910 }
911 
913  if ( !alg ) return;
914  return;
915 }
916 
918  if ( !alg ) return;
919  // info() << "before:inside! " << alg->name() << endmsg;
920  if ( first_alg ) {
921  first_alg = false;
922  first_alg_name = alg->name();
923  // info() << "first_alg_name= " << alg->name() << endmsg;
924  }
925  if ( !event_count_reached ) {
926  if ( !first_alg_name.compare( alg->name() ) ) {
927  ph_ev_count++;
928  // info() << "EVENT COUNT: " << ph_ev_count << endmsg;
929  if ( ph_ev_count == start_at_event ) {
930  event_count_reached = true;
931  // info() << "!!! EVENT COUNT REACHED: " << ph_ev_count << endmsg;
932  }
933  }
934  }
935  if ( event_count_reached ) {
936  // info() << "before:inside! " << alg->name() << endmsg;
937 
938  if ( !alg_stack.empty() ) {
939  if ( sampling == 0 )
940  pausepm(); // pausing father algorithm counting
941  else
942  stop_smpl();
943  }
944  ++m_indent;
945  std::vector<unsigned long int> zeroes( 4, 0 );
946  alg_stack.push( std::make_pair( alg, zeroes ) );
947  if ( sampling == 0 )
948  startpm();
949  else
950  start_smpl();
951  }
952 }
953 
955  if ( !alg ) { return; } // info() << "after:inside! " << alg->name() << endmsg;
956 
957  if ( event_count_reached ) {
958  // info() << "after:inside! " << alg->name() << endmsg;
959 
960  if ( sampling == 0 )
961  stoppm();
962  else
963  stop_smpl();
964  alg_stack.pop();
965  --m_indent;
966  if ( !alg_stack.empty() ) {
967  if ( sampling == 0 )
968  startpm();
969  else
970  start_smpl(); // resuming father algorithm counting
971  }
972  }
973 }
974 
pfarg_load_t load_args
int pfm_err_t
Definition: pfmlib.h:150
uint64_t reg_short_reset
Definition: perfmon_v2.h:44
pfmlib_core_input_param_t params
os_err_t pfm_write_pmds(int fd, pfarg_pmd_t *pmds, int count)
#define cpuid(func, ax, bx, cx, dx)
virtual StatusCode finalize()
Definition: Auditor.cpp:191
#define PFM_REGFL_OVFL_NOTIFY
Definition: perfmon.h:112
#define MAX_PREFIX_NAME_LENGTH
T empty(T...args)
#define PFM_NHM_SEL_INV
The ISvcLocator is the interface implemented by the Service Factory in the Application Manager to loc...
Definition: ISvcLocator.h:25
os_err_t pfm_create_context(pfarg_ctx_t *ctx, char *smpl_name, void *smpl_arg, size_t smpl_size)
#define PFM_PLM3
Definition: pfmlib.h:53
PerfMonAuditor(const std::string &name, ISvcLocator *pSvc)
std::string family
char * pfm_strerror(int code)
#define PFM_CORE_SEL_INV
Definition: pfmlib_core.h:68
static void * tosymbol(void *address)
double sum(double x, double y, double z)
#define PFMLIB_SUCCESS
Definition: pfmlib.h:267
T to_string(T...args)
constexpr static const auto SUCCESS
Definition: StatusCode.h:85
pfm_err_t pfm_get_num_counters(unsigned int *num)
#define SYM_NAME_MAX_LENGTH
T pop(T...args)
constexpr double pc
class MergingTransformer< Out(const vector_of_const_< In > void
T top(T...args)
int os_err_t
Definition: perfmon.h:69
def read(f, regex='.*', skipevents=0)
Definition: hivetimeline.py:22
uint16_t reg_num
Definition: perfmon_v2.h:39
unsigned long long uint64_t
Definition: instrset.h:143
void i_afterExecute(INamedInterface *alg)
pfmlib_input_param_t inp
unsigned short int uint16_t
Definition: instrset.h:139
uint32_t type
Definition: perfmon.h:206
os_err_t pfm_write_pmcs(int fd, pfarg_pmc_t *pmcs, int count)
Performance Monitoring Auditor that uses Perfmon2 library to monitor algorithms.
pfm_err_t pfm_dispatch_events(pfmlib_input_param_t *p, void *model_in, pfmlib_output_param_t *q, void *model_out)
T push(T...args)
pfmlib_options_t pfmlib_options
bool is_nehalem()
Definition: CPUFamily.cpp:18
STL class.
bool isFailure() const
Definition: StatusCode.h:130
uint32_t reg_random_seed
Definition: perfmon_v2.h:51
std::string first_alg_name
pfm_err_t pfm_find_full_event(const char *str, pfmlib_event_t *e)
uint64_t reg_long_reset
Definition: perfmon_v2.h:43
StatusCode finalize() override
#define MAX_EVENT_NAME_LENGTH
pfmlib_nhm_input_param_t nhm_params
#define NUM_PMCS
PropertyMgr & operator=(const PropertyMgr &)=delete
STL class.
#define DECLARE_COMPONENT(type)
#define PFM_VERSION_MAJOR(x)
Definition: perfmon.h:213
void pfm_bv_copy(uint64_t *d, uint64_t *j, uint16_t n)
GaudiUtils::VectorMap< const INamedInterface *, int > Map
int detect_unavail_pmcs(int fd, pfmlib_regmask_t *r_pmcs)
uint16_t ovfl_pmd
uint64_t ip
void pfm_bv_set(uint64_t *bv, uint16_t rnum)
uint32_t reg_flags
Definition: perfmon_v2.h:41
os_err_t pfm_restart(int fd)
STL class.
#define NUM_PMDS
std::string prefix
This class is used for returning status codes from appropriate routines.
Definition: StatusCode.h:50
Gaudi::Details::PropertyBase * declareProperty(const std::string &name, TYPE &value, const std::string &doc="none")
Declare a property (templated)
pfmlib_output_param_t outp
void after(StandardEventType, INamedInterface *, const StatusCode &) override
Definition: Auditor.cpp:103
static bool symbol(void *address, const char *&sym, const char *&lib, int &offset, int &liboffset)
T make_pair(T...args)
string prefix
Definition: gaudirun.py:327
#define LBPL
void after(StandardEventType evt, INamedInterface *alg, const StatusCode &sc) override
os_err_t pfm_start(int fd, pfarg_start_t *start)
pfm_err_t pfm_initialize(void)
#define BPL
unsigned int i
IInterface compliant class extending IInterface with the name() method.
unsigned int ph_ev_count
STL class.
int pfm_bv_isset(uint64_t *bv, uint16_t rnum)
T begin(T...args)
#define FMT_NAME
unsigned int start_at_event
static void process_smpl_buf(pfm_dfl_smpl_hdr_t *hdr, size_t entry_size)
os_err_t pfm_load_context(int fd, pfarg_load_t *load)
#define PFM_REGFL_RANDOM
Definition: perfmon.h:113
constexpr static const auto FAILURE
Definition: StatusCode.h:86
pfm_dfl_smpl_arg_t buf_arg
static void sigio_handler(int, siginfo_t *, void *)
os_err_t pfm_read_pmds(int fd, pfarg_pmd_t *pmds, int count)
unsigned int max_pmd
uint64_t reg_random_mask
Definition: perfmon_v2.h:50
StatusCode initialize() override
void i_beforeExecute(INamedInterface *alg)
virtual StatusCode initialize()
Definition: Auditor.cpp:64
AttribStringParser::Iterator begin(const AttribStringParser &parser)
#define MAX_NUMBER_OF_PROGRAMMABLE_COUNTERS
void i_afterInitialize(INamedInterface *alg)
void i_beforeInitialize(INamedInterface *alg)
pfarg_load_t load_arg
#define PFM_MSG_OVFL
Definition: perfmon.h:210
os_err_t pfm_stop(int fd)
virtual ~PerfMonAuditor()
void before(StandardEventType, INamedInterface *) override
The following methods are meant to be implemented by the child class...
Definition: Auditor.cpp:67
uint64_t reg_value
Definition: perfmon_v2.h:42
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:192
virtual const std::string & name() const =0
Retrieve the name of the instance.
void before(StandardEventType evt, INamedInterface *alg) override
evt
Definition: IOTest.py:94
pfm_err_t pfm_set_options(pfmlib_options_t *opt)
Base class from which all concrete auditor classes should be derived.
Definition: Auditor.h:34