| /** |
| * @file op_pe_utils.cpp |
| * General utility functions for tools using Linux Performance Events Subsystem. |
| * |
| * @remark Copyright 2013 OProfile authors |
| * @remark Read the file COPYING |
| * |
| * Created on: May 21, 2013 |
| * @author Maynard Johnson |
| * (C) Copyright IBM Corp. 2013 |
| * |
| */ |
| |
| #include <linux/perf_event.h> |
| #include <dirent.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <asm/unistd.h> |
| #include <string.h> |
| #include <signal.h> |
| |
| #include <iostream> |
| #include <set> |
| #include <stdexcept> |
| #include <sstream> |
| #include <string> |
| |
| #include "config.h" |
| // HAVE_LIBPFM is defined in config.h |
| #ifdef HAVE_LIBPFM |
| #include <perfmon/pfmlib.h> |
| #endif |
| #include "op_config.h" |
| #include "op_types.h" |
| #include "op_pe_utils.h" |
| #include "operf_event.h" |
| #include "op_libiberty.h" |
| #include "cverb.h" |
| #include "op_string.h" |
| #include "op_netburst.h" |
| #include "op_events.h" |
| |
| |
| extern verbose vdebug; |
| extern std::vector<operf_event_t> events; |
| extern op_cpu cpu_type; |
| |
| |
| using namespace std; |
| |
| static int _op_get_next_online_cpu(DIR * dir, struct dirent *entry) |
| { |
| #define OFFLINE 0x30 |
| unsigned int cpu_num; |
| char cpu_online_pathname[40]; |
| int res; |
| FILE * online; |
| again: |
| do { |
| entry = readdir(dir); |
| if (!entry) |
| return -1; |
| } while (entry->d_type != DT_DIR); |
| |
| res = sscanf(entry->d_name, "cpu%u", &cpu_num); |
| if (res <= 0) |
| goto again; |
| |
| errno = 0; |
| snprintf(cpu_online_pathname, 40, "/sys/devices/system/cpu/cpu%u/online", cpu_num); |
| if ((online = fopen(cpu_online_pathname, "r")) == NULL) { |
| cerr << "Unable to open " << cpu_online_pathname << endl; |
| if (errno) |
| cerr << strerror(errno) << endl; |
| return -1; |
| } |
| res = fgetc(online); |
| fclose(online); |
| if (res == OFFLINE) |
| goto again; |
| else |
| return cpu_num; |
| } |
| |
| // Global functions |
| |
| int op_pe_utils::op_get_sys_value(const char * filename) |
| { |
| char str[10]; |
| int _val = -999; |
| FILE * fp = fopen(filename, "r"); |
| if (fp == NULL) |
| return _val; |
| if (fgets(str, 9, fp)) |
| sscanf(str, "%d", &_val); |
| fclose(fp); |
| return _val; |
| } |
| |
| int op_pe_utils::op_get_cpu_for_perf_events_cap(void) |
| { |
| int retval; |
| string err_msg; |
| char cpus_online[257]; |
| FILE * online_cpus; |
| DIR *dir = NULL; |
| |
| int total_cpus = sysconf(_SC_NPROCESSORS_ONLN); |
| if (!total_cpus) { |
| err_msg = "Internal Error (1): Number of online cpus cannot be determined."; |
| retval = -1; |
| goto error; |
| } |
| |
| online_cpus = fopen("/sys/devices/system/cpu/online", "r"); |
| if (!online_cpus) { |
| err_msg = "Internal Error (2): Number of online cpus cannot be determined."; |
| retval = -1; |
| goto error; |
| } |
| memset(cpus_online, 0, sizeof(cpus_online)); |
| |
| if ( fgets(cpus_online, sizeof(cpus_online), online_cpus) == NULL) { |
| fclose(online_cpus); |
| err_msg = "Internal Error (3): Number of online cpus cannot be determined."; |
| retval = -1; |
| goto error; |
| } |
| |
| if (!cpus_online[0]) { |
| fclose(online_cpus); |
| err_msg = "Internal Error (4): Number of online cpus cannot be determined."; |
| retval = -1; |
| goto error; |
| |
| } |
| if (index(cpus_online, ',') || cpus_online[0] != '0') { |
| // A comma in cpus_online implies a gap, which in turn implies that not all |
| // CPUs are online. |
| if ((dir = opendir("/sys/devices/system/cpu")) == NULL) { |
| fclose(online_cpus); |
| err_msg = "Internal Error (5): Number of online cpus cannot be determined."; |
| retval = -1; |
| goto error; |
| } else { |
| struct dirent *entry = NULL; |
| retval = _op_get_next_online_cpu(dir, entry); |
| closedir(dir); |
| } |
| } else { |
| // All CPUs are available, so we just arbitrarily choose CPU 0. |
| retval = 0; |
| } |
| fclose(online_cpus); |
| error: |
| return retval; |
| } |
| |
| int op_pe_utils::op_check_perf_events_cap(bool use_cpu_minus_one) |
| { |
| /* If perf_events syscall is not implemented, the syscall below will fail |
| * with ENOSYS (38). If implemented, but the processor type on which this |
| * program is running is not supported by perf_events, the syscall returns |
| * ENOENT (2). |
| */ |
| struct perf_event_attr attr; |
| pid_t pid ; |
| int cpu_to_try = use_cpu_minus_one ? -1 : op_get_cpu_for_perf_events_cap(); |
| errno = 0; |
| memset(&attr, 0, sizeof(attr)); |
| attr.size = sizeof(attr); |
| attr.sample_type = PERF_SAMPLE_IP; |
| |
| pid = getpid(); |
| syscall(__NR_perf_event_open, &attr, pid, cpu_to_try, -1, 0); |
| return errno; |
| } |
| |
| static const char * appname; |
| static int find_app_file_in_dir(const struct dirent * d) |
| { |
| if (!strcmp(d->d_name, appname)) |
| return 1; |
| else |
| return 0; |
| } |
| |
| static char full_pathname[PATH_MAX]; |
| static int _get_PATH_based_pathname(const char * app_name) |
| { |
| int retval = -1; |
| |
| char * real_path = getenv("PATH"); |
| char * path = (char *) xstrdup(real_path); |
| char * segment = strtok(path, ":"); |
| appname = app_name; |
| while (segment) { |
| struct dirent ** namelist; |
| int rc = scandir(segment, &namelist, find_app_file_in_dir, NULL); |
| if (rc < 0) { |
| if (errno != ENOENT) { |
| cerr << strerror(errno) << endl; |
| cerr << app_name << " cannot be found in your PATH." << endl; |
| break; |
| } |
| } else if (rc == 1) { |
| size_t applen = strlen(app_name); |
| size_t dirlen = strlen(segment); |
| |
| if (applen + dirlen + 2 > PATH_MAX) { |
| cerr << "Path segment " << segment |
| << " prepended to the passed app name is too long" |
| << endl; |
| retval = -1; |
| break; |
| } |
| |
| if (!strcmp(segment, ".")) { |
| if (getcwd(full_pathname, PATH_MAX) == NULL) { |
| retval = -1; |
| cerr << "getcwd [3] failed when processing <cur-dir>/" << app_name << " found via PATH. Aborting." |
| << endl; |
| break; |
| } |
| } else { |
| strncpy(full_pathname, segment, dirlen); |
| } |
| strcat(full_pathname, "/"); |
| strncat(full_pathname, app_name, applen); |
| retval = 0; |
| free(namelist[0]); |
| free(namelist); |
| |
| break; |
| } |
| segment = strtok(NULL, ":"); |
| } |
| free(path); |
| return retval; |
| } |
| |
| int op_pe_utils::op_validate_app_name(char ** app, char ** save_appname) |
| { |
| int rc = 0; |
| struct stat filestat; |
| char * app_name = *app; |
| size_t len = strlen(app_name); |
| |
| if (len > (size_t) (OP_APPNAME_LEN - 1)) { |
| cerr << "app name longer than max allowed (" << OP_APPNAME_LEN |
| << " chars)\n"; |
| cerr << app_name << endl; |
| rc = -1; |
| goto out; |
| } |
| |
| if (index(app_name, '/') == app_name) { |
| // Full pathname of app was specified, starting with "/". |
| strncpy(full_pathname, app_name, len); |
| } else if ((app_name[0] == '.') && (app_name[1] == '/')) { |
| // Passed app is in current directory; e.g., "./myApp" |
| if (getcwd(full_pathname, PATH_MAX) == NULL) { |
| rc = -1; |
| cerr << "getcwd [1] failed when trying to find app name " << app_name << ". Aborting." |
| << endl; |
| goto out; |
| } |
| strcat(full_pathname, "/"); |
| if ((strlen(full_pathname) + strlen(app_name + 2) + 1) > PATH_MAX) { |
| rc = -1; |
| cerr << "Length of current dir (" << full_pathname << ") and app name (" |
| << (app_name + 2) << ") exceeds max allowed (" << PATH_MAX << "). Aborting." |
| << endl; |
| goto out; |
| } |
| strcat(full_pathname, (app_name + 2)); |
| } else if (index(app_name, '/')) { |
| // Passed app is in a subdirectory of cur dir; e.g., "test-stuff/myApp" |
| if (getcwd(full_pathname, PATH_MAX) == NULL) { |
| rc = -1; |
| cerr << "getcwd [2] failed when trying to find app name " << app_name << ". Aborting." |
| << endl; |
| goto out; |
| } |
| strcat(full_pathname, "/"); |
| strcat(full_pathname, app_name); |
| } else { |
| // Passed app name, at this point, MUST be found in PATH |
| rc = _get_PATH_based_pathname(app_name); |
| } |
| |
| if (rc) { |
| cerr << "Problem finding app name " << app_name << ". Aborting." |
| << endl; |
| goto out; |
| } |
| *save_appname = app_name; |
| *app = full_pathname; |
| if (stat(*app, &filestat)) { |
| char msg[OP_APPNAME_LEN + 50]; |
| snprintf(msg, OP_APPNAME_LEN + 50, "Non-existent app name \"%s\"", |
| *app); |
| perror(msg); |
| rc = -1; |
| } |
| |
| out: return rc; |
| } |
| static int _get_next_online_cpu(DIR * dir, struct dirent *entry) |
| { |
| #define OFFLINE 0x30 |
| unsigned int cpu_num; |
| char cpu_online_pathname[40]; |
| int res; |
| FILE * online; |
| again: |
| do { |
| entry = readdir(dir); |
| if (!entry) |
| return -1; |
| } while (entry->d_type != DT_DIR); |
| |
| res = sscanf(entry->d_name, "cpu%u", &cpu_num); |
| if (res <= 0) |
| goto again; |
| |
| errno = 0; |
| snprintf(cpu_online_pathname, 40, "/sys/devices/system/cpu/cpu%u/online", cpu_num); |
| if ((online = fopen(cpu_online_pathname, "r")) == NULL) { |
| cerr << "Unable to open " << cpu_online_pathname << endl; |
| if (errno) |
| cerr << strerror(errno) << endl; |
| return -1; |
| } |
| res = fgetc(online); |
| fclose(online); |
| if (res == OFFLINE) |
| goto again; |
| else |
| return cpu_num; |
| } |
| |
| |
| set<int> op_pe_utils::op_get_available_cpus(int max_num_cpus) |
| { |
| struct dirent *entry = NULL; |
| int rc = 0; |
| bool all_cpus_avail = true; |
| DIR *dir = NULL; |
| string err_msg; |
| char cpus_online[257]; |
| set<int> available_cpus; |
| FILE * online_cpus = fopen("/sys/devices/system/cpu/online", "r"); |
| |
| if (max_num_cpus == -1) { |
| if (online_cpus) |
| fclose(online_cpus); |
| return available_cpus; |
| } |
| |
| if (!online_cpus) { |
| err_msg = "Internal Error: Number of online cpus cannot be determined."; |
| rc = -1; |
| goto out; |
| } |
| memset(cpus_online, 0, sizeof(cpus_online)); |
| fgets(cpus_online, sizeof(cpus_online), online_cpus); |
| if (!cpus_online[0]) { |
| fclose(online_cpus); |
| err_msg = "Internal Error: Number of online cpus cannot be determined."; |
| rc = -1; |
| goto out; |
| |
| } |
| if (index(cpus_online, ',') || cpus_online[0] != '0') { |
| all_cpus_avail = false; |
| if ((dir = opendir("/sys/devices/system/cpu")) == NULL) { |
| fclose(online_cpus); |
| err_msg = "Internal Error: Number of online cpus cannot be determined."; |
| rc = -1; |
| goto out; |
| } |
| } |
| fclose(online_cpus); |
| |
| for (int cpu = 0; cpu < max_num_cpus; cpu++) { |
| int real_cpu; |
| if (all_cpus_avail) { |
| available_cpus.insert(cpu); |
| } else { |
| real_cpu = _get_next_online_cpu(dir, entry); |
| if (real_cpu < 0) { |
| err_msg = "Internal Error: Number of online cpus cannot be determined."; |
| rc = -1; |
| goto out; |
| } |
| available_cpus.insert(real_cpu); |
| } |
| } |
| out: |
| if (dir) |
| closedir(dir); |
| if (rc) |
| throw runtime_error(err_msg); |
| return available_cpus; |
| |
| } |
| |
| |
| static void _get_event_code(operf_event_t * event, op_cpu cpu_type) |
| { |
| FILE * fp; |
| char oprof_event_code[9]; |
| string command; |
| u64 base_code, config; |
| char buf[20]; |
| if ((snprintf(buf, 20, "%lu", event->count)) < 0) { |
| cerr << "Error parsing event count of " << event->count << endl; |
| exit(EXIT_FAILURE); |
| } |
| base_code = config = 0ULL; |
| |
| command = OP_BINDIR; |
| command += "ophelp "; |
| command += event->name; |
| |
| fp = popen(command.c_str(), "r"); |
| if (fp == NULL) { |
| cerr << "Unable to execute ophelp to get info for event " |
| << event->name << endl; |
| exit(EXIT_FAILURE); |
| } |
| if (fgets(oprof_event_code, sizeof(oprof_event_code), fp) == NULL) { |
| pclose(fp); |
| cerr << "Unable to find info for event " |
| << event->name << endl; |
| exit(EXIT_FAILURE); |
| } |
| |
| pclose(fp); |
| |
| base_code = strtoull(oprof_event_code, (char **) NULL, 10); |
| |
| |
| #if defined(__i386__) || defined(__x86_64__) |
| // Setup EventSelct[11:8] field for AMD |
| char mask[12]; |
| const char * vendor_AMD = "AuthenticAMD"; |
| if (op_is_cpu_vendor((char *)vendor_AMD)) { |
| config = base_code & 0xF00ULL; |
| config = config << 32; |
| } |
| |
| // Setup EventSelct[7:0] field |
| config |= base_code & 0xFFULL; |
| |
| // Setup unitmask field |
| handle_named_um: |
| if (event->um_name[0]) { |
| command = OP_BINDIR; |
| command += "ophelp "; |
| command += "--extra-mask "; |
| command += event->name; |
| command += ":"; |
| command += buf; |
| command += ":"; |
| command += event->um_name; |
| fp = popen(command.c_str(), "r"); |
| if (fp == NULL) { |
| cerr << "Unable to execute ophelp to get info for event " |
| << event->name << endl; |
| exit(EXIT_FAILURE); |
| } |
| if (fgets(mask, sizeof(mask), fp) == NULL) { |
| pclose(fp); |
| cerr << "Unable to find unit mask info for " << event->um_name << " for event " |
| << event->name << endl; |
| exit(EXIT_FAILURE); |
| } |
| pclose(fp); |
| // FIXME: The mask value here is the extra bits from the named unit mask. It's not |
| // ideal to put that value into the UM's mask, since that's what will show up in |
| // opreport. It would be better if we could somehow have the unit mask name that the |
| // user passed to us show up in opreort. |
| event->evt_um = strtoull(mask, (char **) NULL, 10); |
| /* A value >= EXTRA_MIN_VAL returned by 'ophelp --extra-mask' is interpreted as a |
| * valid extra value; otherwise we interpret it as a simple unit mask value |
| * for a named unit mask with EXTRA_NONE. |
| */ |
| if (event->evt_um >= EXTRA_MIN_VAL) |
| config |= event->evt_um; |
| else |
| config |= ((event->evt_um & 0xFFULL) << 8); |
| } else if (!event->evt_um) { |
| char * endptr; |
| command.clear(); |
| command = OP_BINDIR; |
| command += "ophelp "; |
| command += "--unit-mask "; |
| command += event->name; |
| command += ":"; |
| command += buf; |
| fp = popen(command.c_str(), "r"); |
| if (fp == NULL) { |
| cerr << "Unable to execute ophelp to get unit mask for event " |
| << event->name << endl; |
| exit(EXIT_FAILURE); |
| } |
| if (fgets(mask, sizeof(mask), fp) == NULL) { |
| pclose(fp); |
| cerr << "Unable to find unit mask info for event " << event->name << endl; |
| exit(EXIT_FAILURE); |
| } |
| pclose(fp); |
| event->evt_um = strtoull(mask, &endptr, 10); |
| if ((endptr >= mask) && |
| (endptr <= (mask + strlen(mask) - 1))) { |
| // Must be a default named unit mask |
| strncpy(event->um_name, mask, OP_MAX_UM_NAME_LEN); |
| goto handle_named_um; |
| } |
| config |= ((event->evt_um & 0xFFULL) << 8); |
| } else { |
| config |= ((event->evt_um & 0xFFULL) << 8); |
| } |
| #else |
| config = base_code; |
| #endif |
| |
| event->op_evt_code = base_code; |
| if (cpu_type == CPU_P4 || cpu_type == CPU_P4_HT2) { |
| if (op_netburst_get_perf_encoding(event->name, event->evt_um, 1, 1, &config)) { |
| cerr << "Unable to get event encoding for " << event->name << endl; |
| exit(EXIT_FAILURE); |
| } |
| } |
| event->evt_code = config; |
| } |
| |
| #if PPC64_ARCH |
| /* All ppc64 events (except CYCLES) have a _GRP<n> suffix. This is |
| * because the legacy opcontrol profiler can only profile events in |
| * the same group (i.e., having the same _GRP<n> suffix). But operf |
| * can multiplex events, so we should allow the user to pass event |
| * names without the _GRP<n> suffix. |
| * |
| * If event name is not CYCLES or does not have a _GRP<n> suffix, |
| * we'll call ophelp and scan the list of events, searching for one |
| * that matches up to the _GRP<n> suffix. If we don't find a match, |
| * then we'll exit with the expected error message for invalid event name. |
| */ |
| static string _handle_powerpc_event_spec(string event_spec) |
| { |
| FILE * fp; |
| char line[MAX_INPUT]; |
| size_t grp_pos; |
| string evt, err_msg; |
| size_t evt_name_len; |
| bool first_non_cyc_evt_found = false; |
| bool event_found = false; |
| char event_name[OP_MAX_EVT_NAME_LEN], * remaining_evt_spec, * colon_start; |
| string cmd = OP_BINDIR; |
| cmd += "/ophelp"; |
| |
| colon_start = (char *)index(event_spec.c_str(), ':'); |
| if (colon_start) |
| evt_name_len = colon_start - event_spec.c_str(); |
| else |
| evt_name_len = event_spec.length(); |
| strncpy(event_name, event_spec.c_str(), evt_name_len); |
| event_name[evt_name_len] = '\0'; |
| remaining_evt_spec = colon_start ? |
| ((char *)event_spec.c_str() + strlen(event_name) + 1) |
| : NULL; |
| if (!strcmp("CYCLES", event_name)) { |
| event_found = true; |
| goto out; |
| } |
| |
| evt = event_name; |
| // Need to make sure the event name truly has a _GRP<n> suffix. |
| grp_pos = evt.rfind("_GRP"); |
| if ((grp_pos != string::npos) && ((evt = evt.substr(grp_pos, string::npos))).length() > 4) { |
| char * end; |
| strtoul(evt.substr(4, string::npos).c_str(), &end, 0); |
| if (end && (*end == '\0')) { |
| // Valid group number found after _GRP, so we can skip to the end. |
| event_found = true; |
| goto out; |
| } |
| } |
| |
| // If we get here, it implies the user passed a non-CYCLES event without a GRP suffix. |
| // Lets try to find a valid suffix for it. |
| fp = popen(cmd.c_str(), "r"); |
| if (fp == NULL) { |
| cerr << "Unable to execute ophelp to get info for event " |
| << event_spec << endl; |
| exit(EXIT_FAILURE); |
| } |
| |
| err_msg = "Cannot find event "; |
| while (fgets(line, MAX_INPUT, fp)) { |
| if (!first_non_cyc_evt_found) { |
| if (!strncmp(line, "PM_", 3)) |
| first_non_cyc_evt_found = true; |
| else |
| continue; |
| } |
| if (line[0] == ' ' || line[0] == '\t') |
| continue; |
| if (!strncmp(line, event_name, evt_name_len)) { |
| // Found a potential match. Check if it's a perfect match. |
| string save_event_name = event_name; |
| size_t full_evt_len = index(line, ':') - line; |
| memset(event_name, '\0', OP_MAX_EVT_NAME_LEN); |
| strncpy(event_name, line, full_evt_len); |
| string candidate = event_name; |
| if (candidate.rfind("_GRP") == evt_name_len) { |
| event_found = true; |
| break; |
| } else { |
| memset(event_name, '\0', OP_MAX_EVT_NAME_LEN); |
| strncpy(event_name, save_event_name.c_str(), evt_name_len); |
| } |
| } |
| } |
| pclose(fp); |
| |
| out: |
| if (!event_found) { |
| cerr << err_msg << event_name << endl; |
| cerr << "Error retrieving info for event " |
| << event_spec << endl; |
| exit(EXIT_FAILURE); |
| } |
| ostringstream ret_strm; |
| if (remaining_evt_spec) |
| ret_strm << event_name << ":" << remaining_evt_spec; |
| else |
| ret_strm << event_name; |
| return ret_strm.str(); |
| } |
| |
| |
| /* Some architectures (e.g., ppc64) do not use the same event value (code) for oprofile |
| * and for perf_events. The operf-record process requires event values that perf_events |
| * understands, but the operf-read process requires oprofile event values. The purpose of |
| * the following method is to map the operf-record event value to a value that |
| * opreport can understand. |
| */ |
| |
| extern op_cpu cpu_type; |
| #define NIL_CODE ~0U |
| |
| #if HAVE_LIBPFM3 |
| static bool _get_codes_for_match(unsigned int pfm_idx, const char name[], |
| vector<operf_event_t> * evt_vec) |
| { |
| unsigned int num_events = evt_vec->size(); |
| int tmp_code, ret; |
| char evt_name[OP_MAX_EVT_NAME_LEN]; |
| unsigned int events_converted = 0; |
| for (unsigned int i = 0; i < num_events; i++) { |
| operf_event_t event = (*evt_vec)[i]; |
| if (event.evt_code != NIL_CODE) { |
| events_converted++; |
| continue; |
| } |
| memset(evt_name, 0, OP_MAX_EVT_NAME_LEN); |
| if (!strcmp(event.name, "CYCLES")) { |
| strcpy(evt_name ,"PM_CYC") ; |
| } else if (strstr(event.name, "_GRP")) { |
| string str = event.name; |
| strncpy(evt_name, event.name, str.rfind("_GRP")); |
| } else { |
| strncpy(evt_name, event.name, strlen(event.name)); |
| } |
| if (strncmp(name, evt_name, OP_MAX_EVT_NAME_LEN)) |
| continue; |
| ret = pfm_get_event_code(pfm_idx, &tmp_code); |
| if (ret != PFMLIB_SUCCESS) { |
| string evt_name_str = event.name; |
| string msg = "libpfm cannot find event code for " + evt_name_str + |
| "; cannot continue"; |
| throw runtime_error(msg); |
| } |
| event.evt_code = tmp_code; |
| (*evt_vec)[i] = event; |
| events_converted++; |
| cverb << vdebug << "Successfully converted " << event.name << " to perf_event code " |
| << hex << tmp_code << endl; |
| } |
| return (events_converted == num_events); |
| } |
| #else |
| static bool _op_get_event_codes(vector<operf_event_t> * evt_vec) |
| { |
| int ret, i; |
| unsigned int num_events = evt_vec->size(); |
| char evt_name[OP_MAX_EVT_NAME_LEN]; |
| unsigned int events_converted = 0; |
| u64 code[1]; |
| |
| typedef struct { |
| u64 *codes; |
| char **fstr; |
| size_t size; |
| int count; |
| int idx; |
| } pfm_raw_pmu_encode_t; |
| |
| pfm_raw_pmu_encode_t raw; |
| raw.codes = code; |
| raw.count = 1; |
| raw.fstr = NULL; |
| |
| if (pfm_initialize() != PFM_SUCCESS) |
| throw runtime_error("Unable to initialize libpfm; cannot continue"); |
| |
| for (unsigned int i = 0; i < num_events; i++) { |
| operf_event_t event = (*evt_vec)[i]; |
| if (event.evt_code != NIL_CODE) { |
| events_converted++; |
| continue; |
| } |
| memset(evt_name, 0, OP_MAX_EVT_NAME_LEN); |
| if (!strcmp(event.name, "CYCLES")) { |
| strcpy(evt_name ,"PM_CYC") ; |
| } else if (strstr(event.name, "_GRP")) { |
| string str = event.name; |
| strncpy(evt_name, event.name, str.rfind("_GRP")); |
| } else { |
| strncpy(evt_name, event.name, strlen(event.name)); |
| } |
| |
| memset(&raw, 0, sizeof(raw)); |
| ret = pfm_get_os_event_encoding(evt_name, PFM_PLM3, PFM_OS_NONE, &raw); |
| if (ret != PFM_SUCCESS) { |
| string evt_name_str = event.name; |
| string msg = "libpfm cannot find event code for " + evt_name_str + |
| "; cannot continue"; |
| throw runtime_error(msg); |
| } |
| |
| event.evt_code = raw.codes[0]; |
| (*evt_vec)[i] = event; |
| events_converted++; |
| cverb << vdebug << "Successfully converted " << event.name << " to perf_event code " |
| << hex << event.evt_code << endl; |
| } |
| return (events_converted == num_events); |
| } |
| #endif |
| |
| static bool convert_event_vals(vector<operf_event_t> * evt_vec) |
| { |
| unsigned int i, count; |
| char name[256]; |
| int ret; |
| for (unsigned int i = 0; i < evt_vec->size(); i++) { |
| operf_event_t event = (*evt_vec)[i]; |
| if (cpu_type == CPU_PPC64_POWER7) { |
| if (!strncmp(event.name, "PM_RUN_CYC", strlen("PM_RUN_CYC"))) { |
| event.evt_code = 0x600f4; |
| } else if (!strncmp(event.name, "PM_RUN_INST_CMPL", strlen("PM_RUN_INST_CMPL"))) { |
| event.evt_code = 0x500fa; |
| } else { |
| event.evt_code = NIL_CODE; |
| } |
| } else { |
| event.evt_code = NIL_CODE; |
| } |
| (*evt_vec)[i] = event; |
| } |
| |
| #if HAVE_LIBPFM3 |
| if (pfm_initialize() != PFMLIB_SUCCESS) |
| throw runtime_error("Unable to initialize libpfm; cannot continue"); |
| |
| ret = pfm_get_num_events(&count); |
| if (ret != PFMLIB_SUCCESS) |
| throw runtime_error("Unable to use libpfm to obtain event code; cannot continue"); |
| for(i =0 ; i < count; i++) |
| { |
| ret = pfm_get_event_name(i, name, 256); |
| if (ret != PFMLIB_SUCCESS) |
| continue; |
| if (_get_codes_for_match(i, name, evt_vec)) |
| break; |
| } |
| return (i != count); |
| #else |
| return _op_get_event_codes(evt_vec); |
| #endif |
| } |
| |
| #endif // PPC64_ARCH |
| |
| |
| |
| void op_pe_utils::op_process_events_list(vector<string> & passed_evts) |
| { |
| string cmd = OP_BINDIR; |
| |
| if (passed_evts.size() > OP_MAX_EVENTS) { |
| cerr << "Number of events specified is greater than allowed maximum of " |
| << OP_MAX_EVENTS << "." << endl; |
| exit(EXIT_FAILURE); |
| } |
| cmd += "/ophelp --check-events --ignore-count "; |
| for (unsigned int i = 0; i < passed_evts.size(); i++) { |
| FILE * fp; |
| string full_cmd = cmd; |
| string event_spec = passed_evts[i]; |
| |
| #if PPC64_ARCH |
| // Starting with CPU_PPC64_ARCH_V1, ppc64 events files are formatted like |
| // other architectures, so no special handling is needed. |
| if (cpu_type < CPU_PPC64_ARCH_V1) |
| event_spec = _handle_powerpc_event_spec(event_spec); |
| #endif |
| |
| full_cmd += event_spec; |
| fp = popen(full_cmd.c_str(), "r"); |
| if (fp == NULL) { |
| cerr << "Unable to execute ophelp to get info for event " |
| << event_spec << endl; |
| exit(EXIT_FAILURE); |
| } |
| if (fgetc(fp) == EOF) { |
| pclose(fp); |
| cerr << "Error retrieving info for event " |
| << event_spec << endl; |
| exit(EXIT_FAILURE); |
| } |
| pclose(fp); |
| char * event_str = op_xstrndup(event_spec.c_str(), event_spec.length()); |
| operf_event_t event; |
| strncpy(event.name, strtok(event_str, ":"), OP_MAX_EVT_NAME_LEN - 1); |
| /* Event name is required in the event spec in order for |
| * 'ophelp --check-events --ignore-count' to pass. But since unit mask |
| * and domain control bits are optional, we need to ensure the result of |
| * strtok is valid. |
| */ |
| char * info; |
| #define _OP_UM 1 |
| #define _OP_KERNEL 2 |
| #define _OP_USER 3 |
| int place = _OP_UM; |
| char * endptr = NULL; |
| event.evt_um = 0UL; |
| event.count = 0UL; |
| event.no_kernel = 0; |
| event.no_user = 0; |
| event.throttled = false; |
| memset(event.um_name, '\0', OP_MAX_UM_NAME_LEN); |
| while ((info = strtok(NULL, ":"))) { |
| switch (place) { |
| case _OP_UM: |
| event.evt_um = strtoul(info, &endptr, 0); |
| // If any of the UM part is not a number, then we |
| // consider the entire part a string. |
| if (*endptr) { |
| event.evt_um = 0; |
| strncpy(event.um_name, info, OP_MAX_UM_NAME_LEN - 1); |
| } |
| break; |
| case _OP_KERNEL: |
| if (atoi(info) == 0) |
| event.no_kernel = 1; |
| break; |
| case _OP_USER: |
| if (atoi(info) == 0) |
| event.no_user = 1; |
| break; |
| } |
| place++; |
| } |
| free(event_str); |
| _get_event_code(&event, cpu_type); |
| events.push_back(event); |
| } |
| #if PPC64_ARCH |
| { |
| /* For ppc64 architecture processors prior to the introduction of |
| * architected_events_v1, the oprofile event code needs to be converted |
| * to the appropriate event code to pass to the perf_event_open syscall. |
| * But as of the introduction of architected_events_v1, the events |
| * file contains the necessary event code information, so this conversion |
| * step is no longer needed. |
| */ |
| |
| using namespace op_pe_utils; |
| if ((cpu_type < CPU_PPC64_ARCH_V1) && !convert_event_vals(&events)) { |
| cerr << "Unable to convert all oprofile event values to perf_event values" << endl; |
| exit(EXIT_FAILURE); |
| } |
| } |
| #endif |
| } |
| |
| void op_pe_utils::op_get_default_event(void) |
| { |
| operf_event_t dft_evt; |
| struct op_default_event_descr descr; |
| vector<operf_event_t> tmp_events; |
| |
| |
| op_default_event(cpu_type, &descr); |
| if (descr.name[0] == '\0') { |
| cerr << "Unable to find default event" << endl; |
| exit(EXIT_FAILURE); |
| } |
| |
| memset(&dft_evt, 0, sizeof(dft_evt)); |
| dft_evt.count = descr.count; |
| dft_evt.evt_um = descr.um; |
| strncpy(dft_evt.name, descr.name, OP_MAX_EVT_NAME_LEN - 1); |
| _get_event_code(&dft_evt, cpu_type); |
| events.push_back(dft_evt); |
| |
| #if PPC64_ARCH |
| { |
| /* This section of code is for architectures such as ppc[64] for which |
| * the oprofile event code needs to be converted to the appropriate event |
| * code to pass to the perf_event_open syscall. |
| */ |
| |
| using namespace op_pe_utils; |
| if ((cpu_type < CPU_PPC64_ARCH_V1) && !convert_event_vals(&events)) { |
| cerr << "Unable to convert all oprofile event values to perf_event values" << endl; |
| exit(EXIT_FAILURE); |
| } |
| } |
| #endif |
| } |