]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gprofng: 31123 improvements to hardware event implementation
authorVladimir Mezentsev <vladimir.mezentsev@oracle.com>
Tue, 9 Jan 2024 06:00:24 +0000 (22:00 -0800)
committerVladimir Mezentsev <vladimir.mezentsev@oracle.com>
Wed, 10 Jan 2024 17:31:08 +0000 (09:31 -0800)
Our hardware counter profiling is based on perf_event_open().
Our HWC tables are absent for new machines.
I have added HWC tables for the following events: PERF_TYPE_HARDWARE,
PERF_TYPE_SOFTWARE, PERF_TYPE_HW_CACHE. Other events require additional fixes.

Did a little cleaning: marked the symbols as static, used Stringbuilder,
created a function to read /proc/cpuinfo.

gprofng/ChangeLog
2024-01-08  Vladimir Mezentsev  <vladimir.mezentsev@oracle.com>

PR gprofng/31123
* common/core_pcbe.c: Mark the symbols as static. Add events_generic[].
* common/hwc_cpus.h: Declare a new function read_cpuinfo.
* common/hwcdrv.c: Add a new parameter in init_perf_event().
* common/hwcentry.h: Add use_perf_event_type in Hwcentry.
* common/hwcfuncs.c (process_data_descriptor): Read use_perf_event_type,
type, config.
* common/hwctable.c: Add a new HWC table generic_list[].
* common/opteron_pcbe.c (opt_pcbe_init): Accept AMD machines.
* src/collctrl.cc: Use StringBuilder in Coll_Ctrl::build_data_desc().
Add a new function read_cpuinfo.

gprofng/common/core_pcbe.c
gprofng/common/hwc_cpus.h
gprofng/common/hwcdrv.c
gprofng/common/hwcentry.h
gprofng/common/hwcfuncs.c
gprofng/common/hwctable.c
gprofng/common/opteron_pcbe.c
gprofng/src/collctrl.cc

index 14c4268ac9cbfc471670c06dff1c82fe536795be..25bf484d7f2f44789cce249493b079d22d396bd2 100644 (file)
@@ -2597,102 +2597,95 @@ struct events_table_t
 
 static const struct events_table_t *events_table = NULL;
 
-const struct events_table_t events_fam6_mod23[] = {
+static const struct events_table_t events_fam6_mod23[] = {
   ARCH_EVENTS
   EVENTS_FAM6_MOD23
   NT_END
 };
 
-const struct events_table_t events_fam6_mod28[] = {
+static const struct events_table_t events_fam6_mod28[] = {
   ARCH_EVENTS
   EVENTS_FAM6_MOD28
   NT_END
 };
 
-const struct events_table_t events_fam6_mod26[] = {
+static const struct events_table_t events_fam6_mod26[] = {
   ARCH_EVENTS
   EVENTS_FAM6_MOD26
   NT_END
 };
 
-const struct events_table_t events_fam6_mod46[] = {
+static const struct events_table_t events_fam6_mod46[] = {
   ARCH_EVENTS
   EVENTS_FAM6_MOD26
   EVENTS_FAM6_MOD46_ONLY
   NT_END
 };
 
-const struct events_table_t events_fam6_mod37[] = {
+static const struct events_table_t events_fam6_mod37[] = {
   ARCH_EVENTS
   EVENTS_FAM6_MOD37
   EVENTS_FAM6_MOD37_ALSO
   NT_END
 };
 
-const struct events_table_t events_fam6_mod47[] = {
+static const struct events_table_t events_fam6_mod47[] = {
   ARCH_EVENTS
   EVENTS_FAM6_MOD37
   NT_END
 };
 
-const struct events_table_t events_fam6_mod42[] = {
+static const struct events_table_t events_fam6_mod42[] = {
   ARCH_EVENTS
   EVENTS_FAM6_MOD42
   EVENTS_FAM6_MOD42_ONLY
   NT_END
 };
 
-const struct events_table_t events_fam6_mod45[] = {
+static const struct events_table_t events_fam6_mod45[] = {
   ARCH_EVENTS
   EVENTS_FAM6_MOD42
   EVENTS_FAM6_MOD45_ONLY
   NT_END
 };
 
-const struct events_table_t events_fam6_mod58[] = {
+static const struct events_table_t events_fam6_mod58[] = {
   ARCH_EVENTS
   EVENTS_FAM6_MOD58
   NT_END
 };
 
-const struct events_table_t events_fam6_mod62[] = {
+static const struct events_table_t events_fam6_mod62[] = {
   ARCH_EVENTS
   EVENTS_FAM6_MOD58
   EVENTS_FAM6_MOD62_ONLY
   NT_END
 };
 
-const struct events_table_t events_fam6_mod60[] = {
+static const struct events_table_t events_fam6_mod60[] = {
   ARCH_EVENTS
   EVENTS_FAM6_MOD60
   NT_END
 };
 
-const struct events_table_t events_fam6_mod61[] = {
+static const struct events_table_t events_fam6_mod61[] = {
   ARCH_EVENTS
   EVENTS_FAM6_MOD61
   NT_END
 };
 
-const struct events_table_t events_fam6_mod78[] = {
+static const struct events_table_t events_fam6_mod78[] = {
   ARCH_EVENTS
   EVENTS_FAM6_MOD78
   NT_END
 };
 
-const struct events_table_t events_fam6_unknown[] = {
+static const struct events_table_t events_fam6_unknown[] = {
   ARCH_EVENTS
   NT_END
 };
 
-const struct events_table_t events_fam_arm[] = {
-//     ARCH_EVENTS
-//    *eventnum = pevent->eventselect;
-//    *eventnum |= (pevent->unitmask << PERFCTR_UMASK_SHIFT);
-//    *eventnum |= (pevent->attrs << 16);
-//    *eventnum |= (pevent->cmask << 24);
-// eventselect, unitmask, supported_counters, name, cmask, attrs, msr_offset
-
+const struct events_table_t events_generic[] = {
 // Hardware event
 #define HWE(nm, id)     { id, 0, C_ALL, nm, PERF_TYPE_HARDWARE, 0, 0 },
   HWE("branch-instructions",    PERF_COUNT_HW_BRANCH_INSTRUCTIONS)
@@ -2741,13 +2734,20 @@ core_pcbe_init (void)
 {
   switch (cpuid_getvendor ())
     {
+    case X86_VENDOR_AMD:
+      snprintf (core_impl_name, sizeof (core_impl_name), "%s", X86_VENDORSTR_AMD);
+      events_table = events_generic;
+      num_gpc = 4;
+      num_ffc = 0;
+      total_pmc = num_gpc + num_ffc;
+      return 0;
     case ARM_CPU_IMP_ARM:
     case ARM_CPU_IMP_BRCM:
     case ARM_CPU_IMP_CAVIUM:
     case ARM_CPU_IMP_APM:
     case ARM_CPU_IMP_QCOM:
       snprintf (core_impl_name, sizeof (core_impl_name), "%s", AARCH64_VENDORSTR_ARM);
-      events_table = events_fam_arm;
+      events_table = events_generic;
       num_gpc = 4;  // MEZ: a real implementation is needed
       num_ffc = 0;
       total_pmc = num_gpc + num_ffc;
index 634aa4f592327430a9bb9d3694b8bb80f118d494..34896d6fb464589e416852cea3903a0940802446 100644 (file)
 #ifndef __HWC_CPUS_H
 #define __HWC_CPUS_H
 
+typedef struct
+{
+  int cpu_cnt;
+  int cpu_clk_freq;
+  int cpu_model;
+  int cpu_family;
+  int cpu_vendor;
+  char *cpu_vendorstr;
+  char *cpu_modelstr;
+} cpu_info_t;
+
+extern cpu_info_t *read_cpuinfo();
+
 #define MAX_PICS    20 /* Max # of HW ctrs that can be enabled simultaneously */
 
   /* type for specifying CPU register number */
 #define CPC_AMD_FAM_10H         2501 /* Barcelona, Shanghai... */
 #define CPC_AMD_FAM_11H         2502 /* Griffin... */
 #define CPC_AMD_FAM_15H         2503
+#define CPC_AMD_Authentic       2504
+  
 #define CPC_KPROF               3003 // OBSOLETE (To support 12.3 and earlier)
 #define CPC_FOX                 3004 /* pseudo-chip */
 
@@ -191,6 +206,7 @@ enum {
   {CPC_ULTRA2                , "UltraSPARC I&II"}, \
   {CPC_ULTRA1                , "UltraSPARC I&II"}, \
   {ARM_CPU_IMP_APM           , AARCH64_VENDORSTR_ARM}, \
+  {CPC_AMD_Authentic         , "AuthenticAMD"}, \
   {0, NULL}
   /* init like this:
      static libcpc2_cpu_lookup_t cpu_table[]={LIBCPC2_CPU_LOOKUP_LIST};
index 29a0e17f7997a9153c2dcad78975015f7b597191..2d549b0d6a5105faa0554943f4b8df631343bf24 100644 (file)
@@ -675,26 +675,21 @@ dump_perf_event_attr (struct perf_event_attr *at)
 }
 
 static void
-init_perf_event (struct perf_event_attr *hw, uint64_t event, uint64_t period)
+init_perf_event (struct perf_event_attr *hw, uint64_t event, uint64_t period,
+                Hwcentry *hwce)
 {
   memset (hw, 0, sizeof (struct perf_event_attr));
-  hw->size = sizeof (struct perf_event_attr); // fwd/bwd compat
-
-#if defined(__i386__) || defined(__x86_64)
-  //note: Nehalem/Westmere OFFCORE_RESPONSE in upper 32 bits
-  hw->config = event;
-  hw->type = PERF_TYPE_RAW;     // hw/sw/trace/raw...
-#elif defined(__aarch64__)
-  hw->type = (event >> 24) & 7;
-  hw->config = event & 0xff;
-#elif defined(sparc)
-  //SPARC needs to be shifted up 16 bits
-  hw->config = (event & 0xFFFF) << 16;  // uint64_t event
-  uint64_t regs = (event >> 20) & 0xf;  // see sparc_pcbe.c
-  hw->config |= regs << 4;  // for M8, supported PICs need to be placed at bits [7:4]
-  hw->type = PERF_TYPE_RAW; // hw/sw/trace/raw...
-#endif
-
+  hw->size = sizeof (struct perf_event_attr);
+  if (hwce && hwce->use_perf_event_type)
+    {
+      hw->config = hwce->config;
+      hw->type = hwce->type;
+    }
+  else
+    { // backward compatibility. The old interface had no 'hwce' argument.
+      hw->config = event;
+      hw->type = PERF_TYPE_RAW; // hw/sw/trace/raw...
+    }
   hw->sample_period = period;
   hw->sample_type = PERF_SAMPLE_IP |
          // PERF_SAMPLE_TID            |
@@ -858,7 +853,7 @@ hdrv_pcl_internal_open ()
   perf_event_def_t tmp_event_def;
   memset (&tmp_event_def, 0, sizeof (tmp_event_def));
   struct perf_event_attr *pe_attr = &tmp_event_def.hw;
-  init_perf_event (pe_attr, 0, 0);
+  init_perf_event (pe_attr, 0, 0, NULL);
   pe_attr->type = PERF_TYPE_HARDWARE; // specify abstracted HW event
   pe_attr->config = PERF_COUNT_HW_INSTRUCTIONS; // specify abstracted insts
   int hwc_fd = perf_event_open (pe_attr,
@@ -1283,7 +1278,7 @@ hwcdrv_create_counters (unsigned hwcdef_cnt, Hwcentry *hwcdef)
       glb_event_def->min_time = hwcdef[idx].min_time;
       glb_event_def->name = strdup (hwcdef[idx].name); // memory leak??? very minor
       init_perf_event (&glb_event_def->hw, glb_event_def->eventsel,
-                      glb_event_def->counter_preload);
+                      glb_event_def->counter_preload, hwcdef + idx);
       TprintfT (DBG_LT1, "hwcdrv: create_counters: pic=%u name='%s' interval=%lld"
                "(min_time=%lld): reg_num=0x%x eventsel=0x%llx ireset=%lld usr=%lld sys=%lld\n",
                idx, hwcdef[idx].int_name, (long long) glb_event_def->counter_preload,
index 739bc4eaf6846cc4df92ec89140d84448af274da..a35a363e693863232345d258ba62444b00e0a53e 100644 (file)
@@ -112,11 +112,12 @@ extern "C"
     int timecvt;        /* multiplier to convert metric to time, 0 if N/A */
     ABST_type memop;    /* type of backtracking allowed */
     char *short_desc;   /* optional one-liner description, or NULL */
-    int type;           /* Type of perf_event_attr */
-    long long config;   /* perf_event_type -specific configuration */
     /* the fields above this line are expected, in order, by the tables in hwctable.c */
     /* ================================================== */
     /* the fields below this line are more flexible */
+    unsigned int use_perf_event_type : 16; /* Set 1 to use two fields below */
+    unsigned int type : 16; /* Type of perf_event_attr */
+    long long config;   /* perf_event_type -specific configuration */
     int sort_order;     /* "tag" to associate experiment record with HWC def */
     regno_t *reg_list;  /* if not NULL, legal values for <reg_num> field above */
     /* Note: reg_list will be terminated by REGNO_ANY */
index 3c44ab6e5639f5b3b6db7d154c3d097f032fbca7..86d6935b321a5ee3be212d1a76d4a6ce16979500 100644 (file)
@@ -259,18 +259,11 @@ process_data_descriptor (const char *defstring)
 
   clear_hwcdefs ();
   if (!defstring || !strlen (defstring))
-    {
-      err = HWCFUNCS_ERROR_HWCARGS;
-      goto ext_hw_install_end;
-    }
+    return HWCFUNCS_ERROR_HWCARGS;
   ds = strdup (defstring);
   if (!ds)
-    {
-      err = HWCFUNCS_ERROR_HWCINIT;
-      goto ext_hw_install_end;
-    }
+    return HWCFUNCS_ERROR_HWCINIT;
   dsp = ds;
-
   for (idx = 0; idx < MAX_PICS && *dsp; idx++)
     {
       char *name = NULL;
@@ -281,13 +274,33 @@ process_data_descriptor (const char *defstring)
       int timecvt = 0;
       unsigned sort_order = (unsigned) - 1;
 
+      // Read use_perf_event_type, type, config
+      hwcdef[idx].use_perf_event_type = (int) strtol (dsp, &dsp, 0);
+      if (*dsp++ != ':')
+       {
+         err = HWCFUNCS_ERROR_HWCARGS;
+         break;
+       }
+      hwcdef[idx].type = (int) strtol (dsp, &dsp, 0);
+      if (*dsp++ != ':')
+       {
+         err = HWCFUNCS_ERROR_HWCARGS;
+         break;
+       }
+      hwcdef[idx].config = strtol (dsp, &dsp, 0);
+      if (*dsp++ != ':')
+       {
+         err = HWCFUNCS_ERROR_HWCARGS;
+         break;
+       }
+
       /* name */
       name = dsp;
       dsp = strchr (dsp, ':');
       if (dsp == NULL)
        {
          err = HWCFUNCS_ERROR_HWCARGS;
-         goto ext_hw_install_end;
+         break;
        }
       *dsp++ = (char) 0;
 
@@ -297,7 +310,7 @@ process_data_descriptor (const char *defstring)
       if (dsp == NULL)
        {
          err = HWCFUNCS_ERROR_HWCARGS;
-         goto ext_hw_install_end;
+         break;
        }
       *dsp++ = (char) 0;
 
@@ -306,12 +319,12 @@ process_data_descriptor (const char *defstring)
       if (*dsp++ != ':')
        {
          err = HWCFUNCS_ERROR_HWCARGS;
-         goto ext_hw_install_end;
+         break;
        }
       if (reg < 0 && reg != -1)
        {
          err = HWCFUNCS_ERROR_HWCARGS;
-         goto ext_hw_install_end;
+         break;
        }
       if (reg >= 0)
        hwcdef[idx].reg_num = reg;
@@ -321,21 +334,16 @@ process_data_descriptor (const char *defstring)
       if (*dsp++ != ':')
        {
          err = HWCFUNCS_ERROR_HWCARGS;
-         goto ext_hw_install_end;
+         break;
        }
       if (interval < 0)
        {
          err = HWCFUNCS_ERROR_HWCARGS;
-         goto ext_hw_install_end;
+         break;
        }
       hwcdef[idx].val = interval;
 
       /* min_time */
-      /*
-       * This is a new field.
-       * An old launcher (dbx, etc.) would not include it.
-       * Detect the presence of the field by the char 'm'.
-       */
       if (*dsp == 'm')
        {
          long long tmp_ll = 0;
@@ -344,12 +352,12 @@ process_data_descriptor (const char *defstring)
          if (*dsp++ != ':')
            {
              err = HWCFUNCS_ERROR_HWCARGS;
-             goto ext_hw_install_end;
+             break;
            }
          if (tmp_ll < 0)
            {
              err = HWCFUNCS_ERROR_HWCARGS;
-             goto ext_hw_install_end;
+             break;
            }
          hwcdef[idx].min_time = tmp_ll;
        }
@@ -361,7 +369,7 @@ process_data_descriptor (const char *defstring)
       if (*dsp++ != ':')
        {
          err = HWCFUNCS_ERROR_HWCARGS;
-         goto ext_hw_install_end;
+         break;
        }
       hwcdef[idx].sort_order = sort_order;
 
@@ -370,7 +378,7 @@ process_data_descriptor (const char *defstring)
       if (*dsp++ != ':')
        {
          err = HWCFUNCS_ERROR_HWCARGS;
-         goto ext_hw_install_end;
+         break;
        }
       hwcdef[idx].timecvt = timecvt;
 
@@ -379,7 +387,7 @@ process_data_descriptor (const char *defstring)
       if (*dsp != 0 && *dsp++ != ',')
        {
          err = HWCFUNCS_ERROR_HWCARGS;
-         goto ext_hw_install_end;
+         break;
        }
       hwcdef[idx].memop = memop;
       if (*name)
@@ -394,27 +402,11 @@ process_data_descriptor (const char *defstring)
     }
 
   if (*dsp)
-    {
-      TprintfT (DBG_LT0, "hwcfuncs: ERROR: process_data_descriptor(): "
-               "ctr string had some trailing garbage:"
-               " '%s'\n", dsp);
-      err = HWCFUNCS_ERROR_HWCARGS;
-      goto ext_hw_install_end;
-    }
-  free (ds);
-  hwcdef_cnt = idx;
-  return 0;
-
-ext_hw_install_end:
-  if (dsp && *dsp)
-    {
-      TprintfT (DBG_LT0, "hwcfuncs: ERROR: process_data_descriptor(): "
-               " syntax error just before:"
-               " '%s;\n", dsp);
-      logerr (GTXT ("Data descriptor syntax error near `%s'\n"), dsp);
-    }
+    err = HWCFUNCS_ERROR_HWCARGS;
+  if (err != 0)
+    logerr (GTXT ("Data descriptor syntax error near `%s'\n"), dsp);
   else
-    logerr (GTXT ("Data descriptor syntax error\n"));
+    hwcdef_cnt = idx;
   free (ds);
   return err;
 }
index 2a3b59aa7f975c103e9bcbe16cb8808ceede11f4..d0735132facf608edeafa1b00e58fe52b2feb887 100644 (file)
@@ -23,9 +23,9 @@
 #include <errno.h>
 #include <string.h>
 #include <limits.h>
+#include <linux/perf_event.h>
 
 #include "hwcdrv.h"
-#include "hwcfuncs.h"
 
 /* TprintfT(<level>,...) definitions.  Adjust per module as needed */
 #define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings
@@ -2367,52 +2367,87 @@ static Hwcentry amd_15h[] = {
   {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE}
 };
 
-#define USE_ARM_REF_CYCLES \
-    {"usr_time","cycles",                 REGNO_ANY, STXT("User CPU"),   PRELOADS_85, 1, ABST_NONE}, \
-    {"sys_time","cycles~system=1~user=0", REGNO_ANY, STXT("System CPU"), PRELOADS_85, 1, ABST_NONE}, \
+#define INIT_HWC(nm, mtr, cfg, ty) .name = (nm), .metric = (mtr), \
+    .config = (cfg), .type = ty, .use_perf_event_type = 1, \
+    .val = PRELOAD_DEF, .reg_num = REGNO_ANY
+#define HWE(nm, mtr, cfg) INIT_HWC(nm, mtr, cfg, PERF_TYPE_HARDWARE)
+#define SWE(nm, mtr, cfg) INIT_HWC(nm, mtr, cfg, PERF_TYPE_SOFTWARE)
+#define HWCE(nm, mtr, id, op, res) \
+    INIT_HWC(nm, mtr, (id) | ((op) << 8) | ((res) << 16), PERF_TYPE_HW_CACHE)
 
-static Hwcentry        armlist[] = {
-  USE_ARM_REF_CYCLES
+static Hwcentry        generic_list[] = {
 // Hardware event:
-  {"branch-instructions",     NULL, REGNO_ANY, STXT("Branch-instructions"), PRELOADS_35, 0, ABST_NONE},
-  {"branch-misses",           NULL, REGNO_ANY, STXT("Branch-misses"), PRELOADS_35, 0, ABST_NONE},
-  {"bus-cycles",              NULL, REGNO_ANY, STXT("Bus Cycles"), PRELOADS_35, 1, ABST_NONE},
-  {"cache-misses",            NULL, REGNO_ANY, STXT("Cache-misses"), PRELOADS_35, 0, ABST_NONE},
-  {"cache-references",        NULL, REGNO_ANY, STXT("Cache-references"), PRELOADS_35, 0, ABST_NONE},
-  {"cycles",                  NULL, REGNO_ANY, STXT("CPU Cycles"), PRELOADS_85, 1, ABST_NONE},
-  {"insts",         "instructions", REGNO_ANY, STXT("Instructions Executed"), PRELOADS_75, 0, ABST_NONE},
-  {"ref-cycles",              NULL, REGNO_ANY, STXT("Total Cycles"), PRELOADS_85, 1, ABST_NONE},
-  {"stalled-cycles-backend",  NULL, REGNO_ANY, STXT("Stalled Cycles during issue."), PRELOADS_85, 1, ABST_NONE},
-  {"stalled-cycles-frontend", NULL, REGNO_ANY, STXT("Stalled Cycles during retirement."), PRELOADS_85, 1, ABST_NONE},
-
+  { HWE("usr_time", STXT("User CPU"), PERF_COUNT_HW_CPU_CYCLES), .timecvt = 1,
+    .int_name = "cycles" },
+  { HWE("sys_time", STXT("System CPU"), PERF_COUNT_HW_CPU_CYCLES), .timecvt = 1,
+    .int_name = "cycles~system=1~user=0" },
+  { HWE("branch-instructions", STXT("Branch-instructions"),
+       PERF_COUNT_HW_BRANCH_INSTRUCTIONS) },
+  { HWE("branch-misses", STXT("Branch-misses"), PERF_COUNT_HW_BRANCH_MISSES) },
+  { HWE("bus-cycles", STXT("Bus Cycles"), PERF_COUNT_HW_BUS_CYCLES),
+         .timecvt = 1 },
+  { HWE("cache-misses", STXT("Cache-misses"), PERF_COUNT_HW_CACHE_MISSES) },
+  { HWE("cache-references", STXT("Cache-references"),
+       PERF_COUNT_HW_CACHE_REFERENCES) },
+  { HWE("cycles", STXT("CPU Cycles"), PERF_COUNT_HW_CPU_CYCLES), .timecvt = 1 },
+  { HWE("insts", STXT("Instructions Executed"), PERF_COUNT_HW_INSTRUCTIONS),
+         .int_name = "instructions" },
+  { HWE("ref-cycles", STXT("Total Cycles"), PERF_COUNT_HW_REF_CPU_CYCLES),
+         .timecvt = 1 },
+  { HWE("stalled-cycles-backend", STXT("Stalled Cycles during issue."),
+       PERF_COUNT_HW_STALLED_CYCLES_BACKEND), .timecvt = 1 },
+  { HWE("stalled-cycles-frontend", STXT("Stalled Cycles during retirement."),
+       PERF_COUNT_HW_STALLED_CYCLES_FRONTEND), .timecvt = 1 },
 // Software event:
-  {"alignment-faults",        NULL, REGNO_ANY, STXT("Alignment Faults"), PRELOADS_85, 0, ABST_NONE},
-  {"context-switches",        NULL, REGNO_ANY, STXT("Context Switches"), PRELOADS_85, 0, ABST_NONE},
-  {"cpu-clock",               NULL, REGNO_ANY, STXT("CPU Clock"), PRELOADS_85, 1, ABST_NONE},
-  {"cpu-migrations",          NULL, REGNO_ANY, STXT("CPU Migrations"), PRELOADS_85, 0, ABST_NONE},
-  {"emulation-faults",        NULL, REGNO_ANY, STXT("Emulation Faults"), PRELOADS_85, 0, ABST_NONE},
-  {"major-faults",            NULL, REGNO_ANY, STXT("Major Page Faults"), PRELOADS_85, 0, ABST_NONE},
-  {"minor-faults",            NULL, REGNO_ANY, STXT("Minor Page Faults"), PRELOADS_85, 0, ABST_NONE},
-  {"page-faults",             NULL, REGNO_ANY, STXT("Page Faults"), PRELOADS_85, 0, ABST_NONE},
-  {"task-clock",              NULL, REGNO_ANY, STXT("Clock Count Specific"), PRELOADS_85, 1, ABST_NONE},
-
+  { SWE("alignment-faults", STXT("Alignment Faults"),
+       PERF_COUNT_SW_ALIGNMENT_FAULTS) },
+  { SWE("context-switches", STXT("Context Switches"),
+       PERF_COUNT_SW_CONTEXT_SWITCHES) },
+  { SWE("cpu-clock", STXT("CPU Clock"), PERF_COUNT_SW_CPU_CLOCK),
+         .timecvt = 1 },
+  { SWE("cpu-migrations", STXT("CPU Migrations"),
+       PERF_COUNT_SW_CPU_MIGRATIONS) },
+  { SWE("emulation-faults", STXT("Emulation Faults"),
+       PERF_COUNT_SW_EMULATION_FAULTS) },
+  { SWE("major-faults", STXT("Major Page Faults"),
+       PERF_COUNT_SW_PAGE_FAULTS_MAJ) },
+  { SWE("minor-faults", STXT("Minor Page Faults"),
+       PERF_COUNT_SW_PAGE_FAULTS_MIN) },
+  { SWE("page-faults", STXT("Page Faults"), PERF_COUNT_SW_PAGE_FAULTS) },
+  { SWE("task-clock", STXT("Clock Count Specific"), PERF_COUNT_SW_TASK_CLOCK),
+         .timecvt = 1 },
 // Hardware cache event
-  {"L1-dcache-load-misses",   NULL, REGNO_ANY, STXT("L1 D-cache Load Misses"), PRELOADS_35, 0, ABST_NONE},
-  {"L1-dcache-loads",         NULL, REGNO_ANY, STXT("L1 D-cache Loads"), PRELOADS_35, 0, ABST_NONE},
-  {"L1-dcache-store-misses",  NULL, REGNO_ANY, STXT("L1 D-cache Store Misses"), PRELOADS_35, 0, ABST_NONE},
-  {"L1-dcache-stores",        NULL, REGNO_ANY, STXT("L1 D-cache Store Stores"), PRELOADS_35, 0, ABST_NONE},
-  {"L1-icache-load-misses",   NULL, REGNO_ANY, STXT("L1 Instructions Load Misses"), PRELOADS_35, 0, ABST_NONE},
-  {"L1-icache-load-misses",   NULL, REGNO_ANY, STXT("L1 Instructions Loads"), PRELOADS_35, 0, ABST_NONE},
-  {"dTLB-load-misses",        NULL, REGNO_ANY, STXT("D-TLB Load Misses"), PRELOADS_35, 0, ABST_NONE},
-  {"dTLB-loads",              NULL, REGNO_ANY, STXT("D-TLB Loads"), PRELOADS_35, 0, ABST_NONE},
-  {"iTLB-load-misses",        NULL, REGNO_ANY, STXT("The Instruction TLB Load Misses"), PRELOADS_35, 0, ABST_NONE},
-  {"iTLB-loads",              NULL, REGNO_ANY, STXT("The Instruction TLB Loads"), PRELOADS_35, 0, ABST_NONE},
-
-  {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE}
-};
+  { HWCE("L1-dcache-load-misses", STXT("L1 D-cache Load Misses"),
+        PERF_COUNT_HW_CACHE_L1D,
+        PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_MISS) },
+  { HWCE("L1-dcache-loads", STXT("L1 D-cache Loads"),
+        PERF_COUNT_HW_CACHE_L1D,
+        PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_ACCESS) },
+  { HWCE("L1-dcache-store-misses", STXT("L1 D-cache Store Misses"),
+        PERF_COUNT_HW_CACHE_L1D,
+        PERF_COUNT_HW_CACHE_RESULT_MISS, PERF_COUNT_HW_CACHE_RESULT_ACCESS) },
+  { HWCE("L1-dcache-stores", STXT("L1 D-cache Store Stores"),
+        PERF_COUNT_HW_CACHE_L1D,
+        PERF_COUNT_HW_CACHE_OP_WRITE, PERF_COUNT_HW_CACHE_RESULT_ACCESS) },
+  { HWCE("L1-icache-load-misses", STXT("L1 Instructions Load Misses"),
+        PERF_COUNT_HW_CACHE_L1I,
+        PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_MISS) },
+  { HWCE("L1-icache-load-misses", STXT("L1 Instructions Loads"),
+        PERF_COUNT_HW_CACHE_L1I,
+        PERF_COUNT_HW_CACHE_OP_READ,  PERF_COUNT_HW_CACHE_RESULT_ACCESS) },
+  { HWCE("dTLB-load-misses", STXT("D-TLB Load Misses"),
+        PERF_COUNT_HW_CACHE_DTLB,
+        PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_MISS) },
+  { HWCE("dTLB-loads", STXT("D-TLB Loads"),
+        PERF_COUNT_HW_CACHE_DTLB,
+        PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_ACCESS) },
+  { HWCE("iTLB-load-misses", STXT("The Instruction TLB Load Misses"),
+        PERF_COUNT_HW_CACHE_ITLB,
+        PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_MISS) },
+  { HWCE("iTLB-loads", STXT("The Instruction TLB Loads"),
+        PERF_COUNT_HW_CACHE_ITLB,
+        PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_ACCESS) },
 
-static Hwcentry unknownlist[] =
-       /*  used for unrecognized CPU type */{
   {NULL, NULL, 0, NULL, 0, 0, 0, 0, ABST_NONE}
 };
 
@@ -2485,8 +2520,9 @@ static cpu_list_t cputabs[] = {
   {CPC_SPARC64_X, usfuji_X_list, {"insts,,cycles,,dcstall", 0}},
   {CPC_SPARC64_XII, usfuji_XII_list, {"insts,,cycles,,dcstall", 0}},
   {CPC_KPROF, kproflist, {NULL}}, // OBSOLETE (To support 12.3 and earlier, TBR)
-  {ARM_CPU_IMP_APM, armlist, {"insts,,cycles", 0}},
-  {0, unknownlist, {NULL}} /* processor is unknown, but experiment is allowed */
+  {ARM_CPU_IMP_APM, generic_list, {"insts,,cycles", 0}},
+  {CPC_AMD_Authentic, generic_list, {"insts,,cycles", 0}},
+  {0, generic_list, {"insts,,cycles", 0}},
 };
 
 /*---------------------------------------------------------------------------*/
index 4104a136df31bf124715270c25943bdca843ec33..0f1815d42e85840e664da1a309fc04ed2f0f72cf 100644 (file)
@@ -315,30 +315,25 @@ static int
 opt_pcbe_init (void)
 {
   amd_family = cpuid_getfamily ();
-  /*
-   * Make sure this really _is_ an Opteron or Athlon 64 system. The kernel
-   * loads this module based on its name in the module directory, but it
-   * could have been renamed.
-   */
-  if (cpuid_getvendor () != X86_VENDOR_AMD
-      || (amd_family != OPTERON_FAMILY && amd_family != AMD_FAMILY_10H))
-    return (-1);
+  if (cpuid_getvendor () != X86_VENDOR_AMD)
+    return -1;
 
   /*
    * Figure out processor revision here and assign appropriate
    * event configuration.
    */
-  if (amd_family == OPTERON_FAMILY)
+  switch (amd_family)
     {
+    case OPTERON_FAMILY:
       amd_events = opt_events_rev_E;
       amd_generic_events = opt_generic_events;
-    }
-  else
-    {
+      break;
+    case AMD_FAMILY_10H:
       amd_events = family_10h_events;
       amd_generic_events = family_10h_generic_events;
+      break;
     }
-  return (0);
+  return 0;
 }
 
 static uint_t
index 703344cd2c18d92ed33edfb70818862ecba169df..ebf888c5a2023285806030dfbb183aa6d1ae8fb4 100644 (file)
@@ -39,7 +39,7 @@
 #include "libiberty.h"
 #include "collctrl.h"
 #include "hwcdrv.h"
-//#include "hwcfuncs.h"
+#include "StringBuilder.h"
 
 #define SP_GROUP_HEADER     "#analyzer experiment group"
 #define DD_MAXPATHLEN       (MAXPATHLEN * 4) /* large, to build up data descriptor */
@@ -55,7 +55,84 @@ extern const char *strsignal (int);
 #define _SC_CPUID_MAX       517
 #endif
 
-const char *get_fstype (char *);
+static const char *get_fstype (char *);
+static cpu_info_t cpu_info;
+
+static void
+read_str (char *from, char **to)
+{
+  if (*to != NULL)
+    return;
+  for (char *s = from; *s; s++)
+    if (*s != ':' && *s != '\t' && *s != ' ')
+      {
+       for (int i = ((int) strlen (s)) - 1; i >= 0; i--)
+         {
+           if (s[i] != '\n' && s[i] != ' ' && s[i] != '\t')
+             {
+               *to = strndup(s, i + 1);
+               return;
+             }
+         }
+       return; // string is empty
+      }
+}
+
+static int
+read_int (char *from)
+{
+  char *val = strchr (from, ':');
+  if (val)
+    return atoi (val + 1);
+  return 0;
+}
+
+cpu_info_t *
+read_cpuinfo()
+{
+  static int inited = 0;
+  if (inited)
+    return &cpu_info;
+  inited = 1;
+
+#if defined(__aarch64__)
+  asm volatile("mrs %0, cntfrq_el0" : "=r" (cpu_info.cpu_clk_freq));
+#endif
+
+  // Read /proc/cpuinfo to get CPU info and clock rate
+  FILE *procf = fopen ("/proc/cpuinfo", "r");
+  if (procf != NULL)
+    {
+      char temp[1024];
+      while (fgets (temp, (int) sizeof (temp), procf) != NULL)
+       {
+         if (strncmp (temp, "processor", 9) == 0)
+           cpu_info.cpu_cnt++;
+         else if (strncmp (temp, "cpu MHz", 7) == 0)
+           cpu_info.cpu_clk_freq = read_int (temp + 9);
+         else if (strncmp (temp, "cpu family", 10) == 0)
+           cpu_info.cpu_family = read_int (temp + 10);
+         else if (strncmp (temp, "vendor_id", 9) == 0)
+           {
+             if (cpu_info.cpu_vendorstr == NULL)
+               read_str (temp + 9, &cpu_info.cpu_vendorstr);
+           }
+         else if (strncmp (temp, "model name", 10) == 0)
+           {
+             if (cpu_info.cpu_modelstr == NULL)
+               read_str (temp + 10, &cpu_info.cpu_modelstr);
+           }
+         else if (strncmp (temp, "model", 5) == 0)
+           cpu_info.cpu_model = read_int (temp + 5);
+         else if (strncmp (temp, "CPU implementer", 15) == 0)
+           cpu_info.cpu_family = read_int (temp + 15);
+         else if (strncmp (temp, "CPU architecture", 16) == 0)
+           cpu_info.cpu_model = read_int (temp + 16);
+       }
+      fclose (procf);
+    }
+  return &cpu_info;
+}
 
 Coll_Ctrl::Coll_Ctrl (int _interactive, bool _defHWC, bool _kernelHWC)
 {
@@ -81,59 +158,9 @@ Coll_Ctrl::Coll_Ctrl (int _interactive, bool _defHWC, bool _kernelHWC)
       /* add 2048 to count, since on some systems CPUID does not start at zero */
       ncpumax = ncpus + 2048;
     }
-  ncpus = 0;
-  cpu_clk_freq = 0;
-
-  // On Linux, read /proc/cpuinfo to get CPU count and clock rate
-  // Note that parsing is different on SPARC and x86
-#if defined(sparc)
-  FILE *procf = fopen ("/proc/cpuinfo", "r");
-  if (procf != NULL)
-    {
-      char temp[1024];
-      while (fgets (temp, (int) sizeof (temp), procf) != NULL)
-       {
-         if (strncmp (temp, "Cpu", 3) == 0 && temp[3] != '\0'
-             && strncmp ((strchr (temp + 1, 'C')) ? strchr (temp + 1, 'C')
-                         : (temp + 4), "ClkTck", 6) == 0)
-           {
-             ncpus++;
-             char *val = strchr (temp, ':');
-             if (val)
-               {
-                 unsigned long long freq;
-                 sscanf (val + 2, "%llx", &freq);
-                 cpu_clk_freq = (unsigned int) (((double) freq) / 1000000.0 + 0.5);
-               }
-             else
-               cpu_clk_freq = 0;
-           }
-       }
-      fclose (procf);
-    }
-
-#elif defined(__aarch64__)
-  asm volatile("mrs %0, cntfrq_el0" : "=r" (cpu_clk_freq));
-
-#else
-  FILE *procf = fopen ("/proc/cpuinfo", "r");
-  if (procf != NULL)
-    {
-      char temp[1024];
-      while (fgets (temp, (int) sizeof (temp), procf) != NULL)
-       {
-         // x86 Linux
-         if (strncmp (temp, "processor", 9) == 0)
-           ncpus++;
-         else if (strncmp (temp, "cpu MHz", 7) == 0)
-           {
-             char *val = strchr (temp, ':');
-             cpu_clk_freq = val ? atoi (val + 1) : 0;
-           }
-       }
-      fclose (procf);
-    }
-#endif
+  cpu_info_t *cpu_p = read_cpuinfo();
+  ncpus = cpu_p->cpu_cnt;
+  cpu_clk_freq = cpu_p->cpu_clk_freq;
 
   /* check resolution of system clock */
   sys_resolution = sysconf (_SC_CLK_TCK);
@@ -1720,78 +1747,62 @@ Coll_Ctrl::set_size_limit (const char *string)
 void
 Coll_Ctrl::build_data_desc ()
 {
-  char spec[DD_MAXPATHLEN];
-  spec[0] = 0;
+  StringBuilder sb;
 
   // Put sample sig before clock profiling. Dbx uses PROF
   // for that purpose and we want it to be processed first.
   if (project_home)
-    snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "P:%s;", project_home);
+    sb.appendf ("P:%s;", project_home);
   if (sample_sig != 0)
-    snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "g:%d;", sample_sig);
+    sb.appendf ("g:%d;", sample_sig);
   if (pauseresume_sig != 0)
-    snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "d:%d%s;", pauseresume_sig,
-             (pauseresume_pause == 1 ? "p" : ""));
+    sb.appendf ("d:%d%s;", pauseresume_sig, pauseresume_pause == 1 ? "p" : "");
   if (clkprof_enabled == 1)
-    snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "p:%d;", clkprof_timer);
+    sb.appendf ("p:%d;", clkprof_timer);
   if (synctrace_enabled == 1)
-    snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "s:%d,%d;", synctrace_thresh, synctrace_scope);
+    sb.appendf ("s:%d,%d;", synctrace_thresh, synctrace_scope);
   if (heaptrace_enabled == 1)
-    snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "H:%d;", heaptrace_checkenabled);
+    sb.appendf ("H:%d;", heaptrace_checkenabled);
   if (iotrace_enabled == 1)
-    snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "i:;");
+    sb.append ("i:;");
   if (hwcprof_enabled_cnt > 0)
     {
-      snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "h:%s",
-               (hwcprof_default == true) ? "*" : "");
+      sb.appendf ("h:%s", (hwcprof_default == true) ? "*" : "");
       for (int ii = 0; ii < hwcprof_enabled_cnt; ii++)
        {
-         /* min_time is a "new" field.
-          *
-          * To help process_data_descriptor() in hwcfuncs.c parse
-          * the HWC portion of this string -- specifically, to
-          * recognize min_time when it's present and skip over
-          * when it's not -- we prepend 'm' to the min_time value.
-          *
-          * When we no longer worry about, say, an old dbx
-          * writing this string and a new libcollector looking for
-          * the min_time field, the 'm' character can be
-          * removed and process_data_descriptor() simplified.
-          */
-         hrtime_t min_time = hwctr[ii].min_time;
+         Hwcentry *h = hwctr + ii;
+         hrtime_t min_time = h->min_time;
          if (min_time == HWCTIME_TBD)
            // user did not specify any value for overflow rate
-           min_time = hwctr[ii].min_time_default;
-         snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec),
-                   "%s%s:%s:%d:%d:m%lld:%d:%d:0x%x", ii ? "," : "",
-                   strcmp (hwctr[ii].name, hwctr[ii].int_name) ? hwctr[ii].name : "",
-                   hwctr[ii].int_name, hwctr[ii].reg_num, hwctr[ii].val,
-                   min_time, ii, /*tag*/ hwctr[ii].timecvt, hwctr[ii].memop);
+           min_time = h->min_time_default;
+         if (ii > 0)
+           sb.append (',');
+         sb.appendf ("%d:%d:%lld:%s:%s:%lld:%d:m%lld:%d:%d:0x%x",
+                   h->use_perf_event_type, h->type, (long long) h->config,
+                   strcmp (h->name, h->int_name) ? h->name : "",
+                   h->int_name, (long long) h->reg_num, h->val,
+                   (long long) min_time, ii, /*tag*/ h->timecvt, h->memop);
        }
-      snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), ";");
+      sb.append (";");
     }
-  if ((time_run != 0) || (start_delay != 0))
+  if (time_run != 0 || start_delay != 0)
     {
       if (start_delay != 0)
-       snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "t:%d:%d;", start_delay, time_run);
+       sb.appendf ("t:%d:%d;", start_delay, time_run);
       else
-       snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "t:%d;", time_run);
+       sb.appendf ("t:%d;", time_run);
     }
   if (sample_period != 0)
-    snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "S:%d;",
-             sample_period);
+    sb.appendf ("S:%d;", sample_period);
   if (size_limit != 0)
-    snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "L:%d;",
-             size_limit);
+    sb.appendf ("L:%d;", size_limit);
   if (java_mode != 0)
-    snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "j:%d;", (int) java_mode);
+    sb.appendf ("j:%d;", (int) java_mode);
   if (follow_mode != FOLLOW_NONE)
-    snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "F:%d;", (int) follow_mode);
-  snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "a:%s;", archive_mode);
-  if (strlen (spec) + 1 >= sizeof (spec))
-    abort ();
+    sb.appendf ("F:%d;", (int) follow_mode);
+  sb.appendf ("a:%s;", archive_mode);
   free (data_desc);
-  data_desc = strdup (spec);
+  data_desc = sb.toString ();
 }
 
 char *