| /** |
| * @file ocount_counter.cpp |
| * Functions and classes for ocount tool. |
| * |
| * @remark Copyright 2013 OProfile authors |
| * @remark Read the file COPYING |
| * |
| * Created on: May 22, 2013 |
| * @author Maynard Johnson |
| * (C) Copyright IBM Corp. 2013 |
| * |
| */ |
| #include <time.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <dirent.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <signal.h> |
| |
| #include <iostream> |
| #include <sstream> |
| #include <stdexcept> |
| |
| #include "ocount_counter.h" |
| #include "op_pe_utils.h" |
| #include "operf_event.h" |
| #include "cverb.h" |
| |
| extern verbose vdebug; |
| extern bool use_cpu_minus_one; |
| extern char * app_name; |
| |
| using namespace std; |
| |
| |
| ocount_counter::ocount_counter(operf_event_t & evt, bool enable_on_exec, |
| bool inherit) |
| { |
| memset(&attr, 0, sizeof(attr)); |
| attr.size = sizeof(attr); |
| attr.type = PERF_TYPE_RAW; |
| attr.config = evt.evt_code; |
| attr.inherit = inherit ? 1 : 0; |
| attr.enable_on_exec = enable_on_exec ? 1 : 0; |
| attr.disabled = attr.enable_on_exec; |
| attr.exclude_idle = 0; |
| attr.exclude_kernel = evt.no_kernel; |
| attr.exclude_user = evt.no_user; |
| attr.exclude_hv = evt.no_hv; |
| // This format allows us to tell user percent of time an event was scheduled |
| // when multiplexing has been done by the kernel. |
| attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
| PERF_FORMAT_TOTAL_TIME_RUNNING; |
| event_name = evt.name; |
| fd = cpu = pid = -1; |
| } |
| |
| ocount_counter::~ocount_counter() { |
| } |
| |
| #include <stdio.h> |
| int ocount_counter::perf_event_open(pid_t _pid, int _cpu) |
| { |
| fd = op_perf_event_open(&attr, _pid, _cpu, -1, 0); |
| if (fd < 0) { |
| int ret = -1; |
| cverb << vdebug << "perf_event_open failed: " << strerror(errno) << endl; |
| if (errno == EBUSY) { |
| cerr << "The performance monitoring hardware reports EBUSY. Is another profiling tool in use?" << endl |
| << "On some architectures, tools such as oprofile and perf being used in system-wide " |
| << "mode can cause this problem." << endl; |
| ret = OP_PERF_HANDLED_ERROR; |
| } else if (errno == ESRCH) { |
| cerr << "!!!! No samples collected !!!" << endl; |
| cerr << "The target program/command ended before profiling was started." << endl; |
| ret = OP_PERF_HANDLED_ERROR; |
| } else { |
| cerr << "perf_event_open failed with " << strerror(errno) << endl; |
| } |
| return ret; |
| } |
| pid = _pid; |
| cpu = _cpu; |
| |
| cverb << vdebug << "perf_event_open returning fd " << fd << endl; |
| return fd; |
| } |
| |
| int ocount_counter::read_count_data(ocount_accum_t * count_data) |
| { |
| size_t len = 3 * sizeof(u64); |
| char * buf = (char *)count_data; |
| |
| while (len) { |
| int ret = read(fd, buf, len); |
| |
| if (ret <= 0) |
| return ret; |
| |
| len -= ret; |
| buf += ret; |
| } |
| |
| return 0; |
| } |
| |
| ocount_record::ocount_record(enum op_runmode _runmode, std::vector<operf_event_t> & _evts, |
| bool _with_time_interval) |
| { |
| runmode = _runmode; |
| with_time_interval = _with_time_interval; |
| evts = _evts; |
| valid = false; |
| system_wide = false; |
| tasks_are_threads = false; |
| num_cpus = 0; |
| app_pid = -1; |
| } |
| |
| bool ocount_record::start_counting_app_process(pid_t _pid) |
| { |
| if (valid) { |
| cerr << "ocount internal error: ocount_record already initialized" << endl; |
| return false; |
| } |
| if (runmode != OP_START_APP) { |
| cerr << "ocount internal error: Current run mode " << runmode << " is incompatible with " |
| "starting app." << endl; |
| return false; |
| } |
| app_pid = _pid; |
| setup(); |
| return true; |
| } |
| |
| /* |
| * There are separate ocount options for counting events for a set of processes ("--process-list") |
| * or a set of threads ("--thread-list"). This function is used for passing the set of either |
| * processes or threads to ocount_record, along with a boolean argument to indicate whether or not |
| * the set of passed tasks are threads. If they are threads, we set up perf_event_open to NOT |
| * do "inherit". |
| */ |
| bool ocount_record::start_counting_tasklist(std::vector<pid_t> _tasks, bool _are_threads) |
| { |
| if (valid) { |
| cerr << "ocount internal error: ocount_record already initialized" << endl; |
| return false; |
| } |
| tasks_are_threads = _are_threads; |
| specified_tasks = _tasks; |
| if (tasks_are_threads) { |
| if (runmode != OP_THREADLIST) { |
| cerr << "ocount internal error: Current run mode " << runmode << " is incompatible with " |
| "--thread-list option." << endl; |
| return false; |
| } |
| } else { |
| if (runmode != OP_PROCLIST) { |
| cerr << "ocount internal error: Current run mode " << runmode << " is incompatible with " |
| "--process-list option." << endl; |
| return false; |
| } |
| } |
| setup(); |
| if (tasks_to_count.empty()) { |
| cerr << "No valid tasks to monitor -- quitting." << endl; |
| return false; |
| } |
| return true; |
| } |
| |
| bool ocount_record::start_counting_cpulist(std::vector<int> _cpus) |
| { |
| if (valid) { |
| cerr << "ocount internal error: ocount_record already initialized" << endl; |
| return false; |
| } |
| if (runmode != OP_CPULIST) { |
| cerr << "ocount internal error: Current run mode " << runmode << " is incompatible with " |
| "--cpu-list option." << endl; |
| return false; |
| } |
| specified_cpus = _cpus; |
| setup(); |
| return true; |
| } |
| |
| bool ocount_record::start_counting_syswide(void) |
| { |
| if (valid) { |
| cerr << "ocount internal error: ocount_record already initialized" << endl; |
| return false; |
| } |
| if (runmode != OP_SYSWIDE) { |
| cerr << "ocount internal error: Current run mode " << runmode << " is incompatible with " |
| "--system-wide option." << endl; |
| return false; |
| } |
| system_wide = true; |
| setup(); |
| return true; |
| } |
| |
| int ocount_record::do_counting_per_task(void) |
| { |
| string err_msg; |
| int rc = 0; |
| |
| for (set<pid_t>::iterator it = tasks_to_count.begin(); it != tasks_to_count.end(); it++) { |
| pid_t the_pid = *it; |
| bool inherit = are_tasks_processes(); |
| cverb << vdebug << "calling perf_event_open for task " << the_pid << endl; |
| for (unsigned event = 0; event < evts.size(); event++) { |
| ocount_accum_t count_data = {0ULL, 0ULL, 0ULL}; |
| accum_counts.push_back(count_data); |
| prev_accum_counts.push_back(0ULL); |
| ocount_counter op_ctr(ocount_counter(evts[event], false, inherit)); |
| if ((rc = op_ctr.perf_event_open(the_pid, -1)) < 0) { |
| err_msg = "Internal Error. Perf event setup failed."; |
| goto out; |
| } else { |
| rc = 0; |
| } |
| perfCounters.push_back(op_ctr); |
| } |
| } |
| out: |
| if (rc && rc != OP_PERF_HANDLED_ERROR) |
| throw runtime_error(err_msg); |
| return rc; |
| } |
| |
| int ocount_record::do_counting_per_cpu(void) |
| { |
| string err_msg; |
| int rc = 0; |
| |
| /* We'll do this sanity check here, but we also do it at the front-end where user |
| * args are being validated. If we wait until we get here, the invalid CPU argument |
| * becomes an ugly thrown exception. |
| */ |
| set<int> available_cpus = op_pe_utils::op_get_available_cpus(num_cpus); |
| if (runmode == OP_CPULIST) { |
| size_t k; |
| for (k = 0; k < specified_cpus.size(); k++) { |
| if (available_cpus.find(specified_cpus[k]) == available_cpus.end()) { |
| ostringstream err_msg_ostr; |
| err_msg_ostr << "Specified CPU " << specified_cpus[k] << " is not valid"; |
| err_msg = err_msg_ostr.str(); |
| rc = -1; |
| goto out; |
| } else { |
| cpus_to_count.insert(specified_cpus[k]); |
| } |
| } |
| } else { |
| cpus_to_count = available_cpus; |
| } |
| |
| for (set<pid_t>::iterator it = cpus_to_count.begin(); it != cpus_to_count.end(); it++) { |
| int the_cpu = *it; |
| cverb << vdebug << "calling perf_event_open for cpu " << the_cpu << endl; |
| for (unsigned event = 0; event < evts.size(); event++) { |
| ocount_accum_t count_data = {0ULL, 0ULL, 0ULL}; |
| accum_counts.push_back(count_data); |
| prev_accum_counts.push_back(0ULL); |
| ocount_counter op_ctr(ocount_counter(evts[event], false, true)); |
| if ((rc = op_ctr.perf_event_open(-1, the_cpu)) < 0) { |
| err_msg = "Internal Error. Perf event setup failed."; |
| goto out; |
| } else { |
| rc = 0; |
| } |
| perfCounters.push_back(op_ctr); |
| } |
| } |
| out: |
| if (rc && rc != OP_PERF_HANDLED_ERROR) |
| throw runtime_error(err_msg); |
| return rc; |
| } |
| |
| void ocount_record::setup() |
| { |
| int rc = 0; |
| string err_msg; |
| |
| if (!specified_tasks.empty()) { |
| if ((rc = get_process_info(specified_tasks)) < 0) { |
| if (rc == OP_PERF_HANDLED_ERROR) |
| return; |
| else |
| throw runtime_error("Unexpected error in ocount_record setup"); |
| } |
| } |
| |
| /* To set up to count events for an existing thread group, we need call perf_event_open |
| * for each thread, and we need to pass cpu=-1 on the syscall. |
| */ |
| use_cpu_minus_one = use_cpu_minus_one ? true : ((system_wide || (runmode == OP_CPULIST)) ? false : true); |
| num_cpus = use_cpu_minus_one ? 1 : sysconf(_SC_NPROCESSORS_ONLN); |
| if (num_cpus < 1) { |
| char int_str[256]; |
| sprintf(int_str, "Number of online CPUs is %d; cannot continue", num_cpus); |
| throw runtime_error(int_str); |
| } |
| if (system_wide || (runmode == OP_CPULIST)) { |
| rc = do_counting_per_cpu(); |
| } else if (!specified_tasks.empty()) { |
| rc = do_counting_per_task(); |
| } else { |
| cverb << vdebug << "calling perf_event_open for pid " << app_pid << endl; |
| for (unsigned event = 0; event < evts.size(); event++) { |
| ocount_accum_t count_data = {0ULL, 0ULL, 0ULL}; |
| accum_counts.push_back(count_data); |
| prev_accum_counts.push_back(0ULL); |
| ocount_counter op_ctr(ocount_counter(evts[event], true, true)); |
| if ((rc = op_ctr.perf_event_open(app_pid, -1)) < 0) { |
| err_msg = "Internal Error. Perf event setup failed."; |
| goto error; |
| } else { |
| rc = 0; |
| } |
| perfCounters.push_back(op_ctr); |
| } |
| } |
| if (!rc) { |
| cverb << vdebug << "perf counter setup complete" << endl; |
| // Set bit to indicate we're set to go. |
| valid = true; |
| // Now that all events are programmed to start counting, init the start time |
| struct timespec tspec; |
| clock_gettime(CLOCK_MONOTONIC, &tspec); |
| start_time = tspec.tv_sec * 1000000000ULL + tspec.tv_nsec; |
| |
| return; |
| } |
| |
| error: |
| if (rc != OP_PERF_HANDLED_ERROR) |
| throw runtime_error(err_msg); |
| } |
| |
| void ocount_record::output_short_results(ostream & out, bool use_separation, bool scaled) |
| { |
| size_t num_iterations = use_separation ? perfCounters.size() : evts.size(); |
| out << endl; |
| for (size_t num = 0; num < num_iterations; num++) { |
| ostringstream count_str; |
| ocount_accum_t tmp_accum; |
| double fraction_time_running; |
| if (use_separation) { |
| if (cpus_to_count.size()) { |
| out << perfCounters[num].get_cpu(); |
| } else { |
| out << perfCounters[num].get_pid(); |
| } |
| out << "," << perfCounters[num].get_event_name() << ","; |
| |
| errno = 0; |
| cverb << vdebug << "Reading counter data for event " << perfCounters[num].get_event_name() << endl; |
| if (perfCounters[num].read_count_data(&tmp_accum) < 0) { |
| string err_msg = "Internal error: read of perfCounter fd failed with "; |
| err_msg += errno ? strerror(errno) : "unknown error"; |
| throw runtime_error(err_msg); |
| } |
| fraction_time_running = scaled ? (double)tmp_accum.running_time/tmp_accum.enabled_time : 1; |
| if (with_time_interval) { |
| u64 save_prev = prev_accum_counts[num]; |
| prev_accum_counts[num] = tmp_accum.count; |
| tmp_accum.count -= save_prev; |
| } |
| u64 scaled_count = tmp_accum.count ? tmp_accum.count/fraction_time_running : 0; |
| out << dec << scaled_count << ","; |
| } else { |
| fraction_time_running = scaled ? (double)accum_counts[num].running_time/accum_counts[num].enabled_time : 1; |
| u64 scaled_count = accum_counts[num].count ? accum_counts[num].count/fraction_time_running : 0; |
| out << perfCounters[num].get_event_name() << "," << dec << scaled_count << ","; |
| } |
| ostringstream strm_tmp; |
| if (use_separation) { |
| if (!tmp_accum.enabled_time) { |
| out << 0 << endl; |
| } else { |
| strm_tmp.precision(2); |
| strm_tmp << fixed << fraction_time_running * 100 |
| << endl; |
| out << strm_tmp.str(); |
| } |
| } else { |
| if (!accum_counts[num].enabled_time) { |
| out << "Event not counted" << endl; |
| } else { |
| strm_tmp.precision(2); |
| strm_tmp << fixed << fraction_time_running * 100 |
| << endl; |
| out << strm_tmp.str(); |
| } |
| } |
| } |
| } |
| |
| void ocount_record::output_long_results(ostream & out, bool use_separation, |
| int longest_event_name, bool scaled, u64 time_enabled) |
| { |
| #define COUNT_COLUMN_WIDTH 25 |
| #define SEPARATION_ELEMENT_COLUMN_WIDTH 10 |
| char space_padding[64], temp[64]; |
| char const * cpu, * task, * scaling; |
| u64 num_seconds_enabled = time_enabled/1000000000; |
| unsigned int num_minutes_enabled = num_seconds_enabled/60; |
| cpu = "CPU"; |
| task = "Task ID"; |
| scaling = scaled ? "(scaled) " : "(actual) "; |
| |
| // We put 8 spaces between the end of the event name and beginning of the second column |
| unsigned int begin_second_col = longest_event_name + 8; |
| unsigned int num_pads = begin_second_col - strlen("Event"); |
| memset(space_padding, ' ', 64); |
| strncpy(temp, space_padding, num_pads); |
| temp[num_pads] = '\0'; |
| out << endl; |
| if (!with_time_interval) { |
| ostringstream strm; |
| strm << "Events were actively counted for "; |
| if (num_minutes_enabled) { |
| strm << " "; |
| strm << num_minutes_enabled; |
| if (num_minutes_enabled > 1) |
| strm << " minutes and "; |
| else |
| strm << " minute and "; |
| strm << num_seconds_enabled % 60; |
| strm << " seconds."; |
| } else { |
| if (num_seconds_enabled) { |
| // Show 1/10's of seconds |
| strm.precision(1); |
| strm << fixed << (double)time_enabled/1000000000; |
| strm << " seconds."; |
| } else { |
| // Show full nanoseconds |
| strm << time_enabled << " nanoseconds."; |
| } |
| } |
| out << strm.str() << endl; |
| } |
| out << "Event counts " << scaling; |
| if (app_name) |
| out << "for " << app_name << ":"; |
| else if (system_wide) |
| out << "for the whole system:"; |
| else if (!cpus_to_count.empty()) |
| out << "for the specified CPU(s):"; |
| else if (tasks_are_threads) |
| out << "for the specified thread(s):"; |
| else |
| out << "for the specified process(es):"; |
| out << endl; |
| |
| out << "\tEvent" << temp; |
| if (use_separation) { |
| if (cpus_to_count.size()) { |
| out << cpu; |
| num_pads = SEPARATION_ELEMENT_COLUMN_WIDTH - strlen(cpu); |
| } else { |
| out << task; |
| num_pads = SEPARATION_ELEMENT_COLUMN_WIDTH - strlen(task); |
| |
| } |
| strncpy(temp, space_padding, num_pads); |
| temp[num_pads] = '\0'; |
| out << temp; |
| } |
| out << "Count"; |
| num_pads = COUNT_COLUMN_WIDTH - strlen("Count"); |
| strncpy(temp, space_padding, num_pads); |
| temp[num_pads] = '\0'; |
| out << temp << "% time counted" << endl; |
| |
| /* If counting per-cpu or per-thread, I refer generically to cpu or thread values |
| * as "elements of separation". We will have one ocount_counter object per element of |
| * separation per event. So if we're counting 2 events for 4 processes (or threads), |
| * we'll have 2x4 (8) ocount_counter objects. |
| * |
| * If 'use_separation' is true, then we need to print individual counts for |
| * each element of separation for each event; otherwise, we print aggregated counts |
| * for each event. |
| */ |
| size_t num_iterations = use_separation ? perfCounters.size() : evts.size(); |
| for (size_t num = 0; num < num_iterations; num++) { |
| double fraction_time_running; |
| out << "\t" << perfCounters[num].get_event_name(); |
| num_pads = begin_second_col - perfCounters[num].get_event_name().length(); |
| strncpy(temp, space_padding, num_pads); |
| temp[num_pads] = '\0'; |
| out << temp; |
| |
| ostringstream count_str; |
| ocount_accum_t tmp_accum; |
| if (use_separation) { |
| ostringstream separation_element_str; |
| strncpy(temp, space_padding, num_pads); |
| temp[num_pads] = '\0'; |
| if (cpus_to_count.size()) { |
| separation_element_str << dec << perfCounters[num].get_cpu(); |
| out << perfCounters[num].get_cpu(); |
| } else { |
| separation_element_str << dec << perfCounters[num].get_pid(); |
| out << perfCounters[num].get_pid(); |
| } |
| num_pads = SEPARATION_ELEMENT_COLUMN_WIDTH - separation_element_str.str().length(); |
| strncpy(temp, space_padding, num_pads); |
| temp[num_pads] = '\0'; |
| out << temp; |
| |
| errno = 0; |
| cverb << vdebug << "Reading counter data for event " << perfCounters[num].get_event_name() << endl; |
| if (perfCounters[num].read_count_data(&tmp_accum) < 0) { |
| string err_msg = "Internal error: read of perfCounter fd failed with "; |
| err_msg += errno ? strerror(errno) : "unknown error"; |
| throw runtime_error(err_msg); |
| } |
| fraction_time_running = scaled ? (double)tmp_accum.running_time/tmp_accum.enabled_time : 1; |
| |
| if (with_time_interval) { |
| u64 save_prev = prev_accum_counts[num]; |
| prev_accum_counts[num] = tmp_accum.count; |
| tmp_accum.count -= save_prev; |
| } |
| u64 scaled_count = tmp_accum.count ? tmp_accum.count/fraction_time_running : 0; |
| count_str << dec << scaled_count; |
| } else { |
| fraction_time_running = scaled ? (double)accum_counts[num].running_time/accum_counts[num].enabled_time : 1; |
| u64 scaled_count = accum_counts[num].count ? accum_counts[num].count/fraction_time_running : 0; |
| count_str << dec << scaled_count; |
| } |
| string count = count_str.str(); |
| for (int i = count.size() - 3; i > 0; i-=3) { |
| count.insert(i, 1, ','); |
| } |
| out << count; |
| num_pads = COUNT_COLUMN_WIDTH - count.size(); |
| strncpy(temp, space_padding, num_pads); |
| temp[num_pads] = '\0'; |
| out << temp; |
| ostringstream strm_tmp; |
| if (use_separation) { |
| if (!tmp_accum.enabled_time) { |
| out << "Event not counted" << endl; |
| } else { |
| strm_tmp.precision(2); |
| strm_tmp << fixed << fraction_time_running * 100 |
| << endl; |
| out << strm_tmp.str(); |
| } |
| } else { |
| if (!accum_counts[num].enabled_time) { |
| out << "Event not counted" << endl; |
| } else { |
| strm_tmp.precision(2); |
| strm_tmp << fixed << fraction_time_running * 100 |
| << endl; |
| out << strm_tmp.str(); |
| } |
| } |
| } |
| } |
| |
| void ocount_record::output_results(ostream & out, bool use_separation, bool short_format) |
| { |
| size_t longest_event_name = 0; |
| u64 time_enabled = 0ULL; |
| bool scaled = false; |
| |
| for (unsigned long evt_num = 0; evt_num < evts.size(); evt_num++) { |
| if (strlen(evts[evt_num].name) > longest_event_name) |
| longest_event_name = strlen(evts[evt_num].name); |
| } |
| |
| if (with_time_interval) { |
| // reset the accum count values |
| for (size_t i = 0; i < evts.size(); i++) { |
| ocount_accum_t accum = accum_counts[i]; |
| accum.count = 0ULL; |
| accum_counts[i] = accum; |
| } |
| } |
| |
| /* We need to inspect all of the count data now to ascertain if scaling |
| * is required, so we also collect aggregated counts into the accum_counts |
| * vector (if needed). |
| */ |
| for (unsigned long ocounter = 0; ocounter < perfCounters.size(); ocounter++) { |
| ocount_accum_t tmp_accum; |
| int evt_key = ocounter % evts.size(); |
| errno = 0; |
| cverb << vdebug << "Reading counter data for event " << evts[evt_key].name << endl; |
| if (perfCounters[ocounter].read_count_data(&tmp_accum) < 0) { |
| string err_msg = "Internal error: read of perfCounter fd failed with "; |
| err_msg += errno ? strerror(errno) : "unknown error"; |
| throw runtime_error(err_msg); |
| } |
| if (!use_separation) { |
| ocount_accum_t real_accum = accum_counts[evt_key]; |
| real_accum.count += tmp_accum.count; |
| real_accum.enabled_time += tmp_accum.enabled_time; |
| real_accum.running_time += tmp_accum.running_time; |
| accum_counts[evt_key] = real_accum; |
| } |
| if (tmp_accum.enabled_time != tmp_accum.running_time) { |
| if (((double)(tmp_accum.enabled_time - tmp_accum.running_time)/tmp_accum.enabled_time) > 0.01) |
| scaled = true; |
| } |
| } |
| |
| if (with_time_interval && !use_separation) { |
| for (size_t i = 0; i < evts.size(); i++) { |
| u64 save_prev = prev_accum_counts[i]; |
| ocount_accum_t real_accum = accum_counts[i]; |
| prev_accum_counts[i] = real_accum.count; |
| real_accum.count -= save_prev; |
| accum_counts[i] = real_accum; |
| } |
| } |
| struct timespec tspec; |
| clock_gettime(CLOCK_MONOTONIC, &tspec); |
| time_enabled = (tspec.tv_sec * 1000000000ULL + tspec.tv_nsec) - start_time; |
| |
| |
| if (short_format) |
| output_short_results(out, use_separation, scaled); |
| else |
| output_long_results(out, use_separation, longest_event_name, scaled, time_enabled); |
| } |
| |
| int ocount_record::_get_one_process_info(pid_t pid) |
| { |
| char fname[PATH_MAX]; |
| DIR *tids; |
| struct dirent dirent, *next; |
| int ret = 0; |
| |
| add_process(pid); |
| if (are_tasks_processes()) { |
| snprintf(fname, sizeof(fname), "/proc/%d/task", pid); |
| tids = opendir(fname); |
| if (tids == NULL) { |
| // process must have exited |
| ret = -1; |
| cverb << vdebug << "Process " << pid << " apparently exited while " |
| << "process info was being collected"<< endl; |
| goto out; |
| } |
| |
| while (!readdir_r(tids, &dirent, &next) && next) { |
| char *end; |
| pid = strtol(dirent.d_name, &end, 10); |
| if (*end) |
| continue; |
| add_process(pid); |
| } |
| closedir(tids); |
| } |
| |
| out: |
| return ret; |
| } |
| |
| /* Obtain process information for one or more active process, where the user has |
| * either passed in a set of processes via the --process-list option or has specified |
| * --system_wide. |
| */ |
| int ocount_record::get_process_info(const vector<pid_t> & _procs) |
| { |
| int ret = 0; |
| if (cverb << vdebug) |
| cout << "op_get_process_info" << endl; |
| for (size_t i = 0; i < _procs.size(); i++) { |
| errno = 0; |
| if (kill(_procs[i], 0) < 0) { |
| if (errno == EPERM) { |
| string errmsg = "You do not have permission to monitor "; |
| errmsg += are_tasks_processes() ? "process " : "thread "; |
| cerr << errmsg << _procs[i] << endl; |
| ret = OP_PERF_HANDLED_ERROR; |
| } |
| break; |
| } |
| if ((ret = _get_one_process_info(_procs[i])) < 0) |
| break; |
| } |
| return ret; |
| } |