--- a
+++ b/pe_counting/ocount.cpp
@@ -0,0 +1,817 @@
+/**
+ * @file ocount.cpp
+ * Tool for event counting using the new 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 "config.h"
+
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <set>
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/time.h>
+
+#include "op_pe_utils.h"
+#include "ocount_counter.h"
+#include "op_cpu_type.h"
+#include "op_cpufreq.h"
+#include "operf_event.h"
+#include "cverb.h"
+#include "op_libiberty.h"
+
+// Globals
+char * app_name = NULL;
+bool use_cpu_minus_one = false;
+std::vector<operf_event_t> events;
+op_cpu cpu_type;
+
+
+static char * app_name_SAVE = NULL;
+static char ** app_args = NULL;
+static bool app_started;
+static bool startApp;
+static bool stop = false;
+static std::ofstream outfile;
+static pid_t my_uid;
+static double cpu_speed;
+static ocount_record * orecord;
+static pid_t app_PID = -1;
+
+using namespace std;
+using namespace op_pe_utils;
+
+
+typedef enum END_CODE {
+	ALL_OK = 0,
+	APP_ABNORMAL_END =  1,
+	PERF_RECORD_ERROR = 2,
+	PERF_READ_ERROR   = 4,
+	PERF_BOTH_ERROR   = 8
+} end_code_t;
+
+namespace ocount_options {
+bool verbose;
+bool system_wide;
+vector<pid_t> processes;
+vector<pid_t> threads;
+vector<int> cpus;
+string outfile;
+bool separate_cpu;
+bool separate_thread;
+vector<string> evts;
+bool csv_output;
+long display_interval;
+long num_intervals;
+}
+
+
+static enum op_runmode runmode = OP_MAX_RUNMODE;
+static string runmode_options[] = { "<command> [command-args]", "--system-wide", "--cpu-list",
+                                    "--process-list", "--thread-list"
+};
+
+
+struct option long_options [] =
+{
+ {"verbose", no_argument, NULL, 'V'},
+ {"system-wide", no_argument, NULL, 's'},
+ {"cpu-list", required_argument, NULL, 'C'},
+ {"process-list", required_argument, NULL, 'p'},
+ {"thread-list", required_argument, NULL, 'r'},
+ {"events", required_argument, NULL, 'e'},
+ {"output-file", required_argument, NULL, 'f'},
+ {"separate-cpu", no_argument, NULL, 'c'},
+ {"separate-thread", no_argument, NULL, 't'},
+ {"brief-format", no_argument, NULL, 'b'},
+ {"time-interval", required_argument, NULL, 'i'},
+ {"help", no_argument, NULL, 'h'},
+ {"usage", no_argument, NULL, 'u'},
+ {"version", no_argument, NULL, 'v'},
+ {NULL, 9, NULL, 0}
+};
+
+const char * short_options = "VsC:p:r:e:f:ctbi:huv";
+
+static void cleanup(void)
+{
+	free(app_name_SAVE);
+	free(app_args);
+	events.clear();
+	if (!ocount_options::outfile.empty())
+		outfile.close();
+}
+
+
+// Signal handler for main (parent) process.
+static void op_sig_stop(int sigval __attribute__((unused)))
+{
+	// Received a signal to quit, so we need to stop the
+	// app being counted.
+	size_t dummy __attribute__ ((__unused__));
+	stop = true;
+	if (cverb << vdebug)
+		dummy = write(1, "in op_sig_stop\n", 15);
+	if (startApp)
+		kill(app_PID, SIGKILL);
+}
+
+void set_signals_for_parent(void)
+{
+	struct sigaction act;
+	sigset_t ss;
+
+	sigfillset(&ss);
+	sigprocmask(SIG_UNBLOCK, &ss, NULL);
+
+	act.sa_handler = op_sig_stop;
+	act.sa_flags = 0;
+	sigemptyset(&act.sa_mask);
+	sigaddset(&act.sa_mask, SIGINT);
+
+	if (sigaction(SIGINT, &act, NULL)) {
+		perror("ocount: install of SIGINT handler failed: ");
+		exit(EXIT_FAILURE);
+	}
+}
+
+
+static void __print_usage_and_exit(const char * extra_msg)
+{
+	if (extra_msg)
+		cerr << extra_msg << endl;
+	cerr << "usage: ocount [ options ] [ --system-wide | --pid <pid> | [ command [ args ] ] ]" << endl;
+	cerr << "See ocount man page for details." << endl;
+	exit(EXIT_FAILURE);
+}
+
+static string args_to_string(void)
+{
+	string ret;
+	char * const * ptr = app_args + 1;
+	while (*ptr != NULL) {
+		ret.append(*ptr);
+		ret += ' ';
+		ptr++;
+	}
+	return ret;
+}
+
+static int app_ready_pipe[2], start_app_pipe[2];
+
+void run_app(void)
+{
+	// ASSUMPTION: app_name is a fully-qualified pathname
+	char * app_fname = rindex(app_name, '/') + 1;
+	app_args[0] = app_fname;
+
+	string arg_str = args_to_string();
+	cverb << vdebug << "Exec args are: " << app_fname << " " << arg_str << endl;
+	// Fake an exec to warm-up the resolver
+	execvp("", app_args);
+	// signal to the parent that we're ready to exec
+	int startup = 1;
+	if (write(app_ready_pipe[1], &startup, sizeof(startup)) < 0) {
+		perror("Internal error on app_ready_pipe");
+		_exit(EXIT_FAILURE);
+	}
+
+	// wait for parent to tell us to start
+	int startme = 0;
+	if (read(start_app_pipe[0], &startme, sizeof(startme)) == -1) {
+		perror("Internal error in run_app on start_app_pipe");
+		_exit(EXIT_FAILURE);
+	}
+	if (startme != 1)
+		_exit(EXIT_SUCCESS);
+
+	cverb << vdebug << "parent says start app " << app_name << endl;
+	execvp(app_name, app_args);
+	cerr <<  "Failed to exec " << app_fname << " " << arg_str << ": " << strerror(errno) << endl;
+	/* We don't want any cleanup in the child */
+	_exit(EXIT_FAILURE);
+
+}
+
+bool start_counting(void)
+{
+	vector<pid_t> proc_list; // May contain processes or threads
+	bool procs_are_threads;
+
+	// The only process that should return from this function is the process
+	// which invoked it.  Any forked process must do _exit() rather than return().
+
+	startApp = runmode == OP_START_APP;
+
+	if (startApp) {
+		if (pipe(app_ready_pipe) < 0 || pipe(start_app_pipe) < 0) {
+			perror("Internal error: ocount-record could not create pipe");
+			_exit(EXIT_FAILURE);
+		}
+		app_PID = fork();
+		if (app_PID < 0) {
+			perror("Internal error: fork failed");
+			_exit(EXIT_FAILURE);
+		} else if (app_PID == 0) { // child process for exec'ing app
+			run_app();
+		}
+	}
+
+	// parent
+	int startup;
+	if (startApp) {
+		if (read(app_ready_pipe[0], &startup, sizeof(startup)) == -1) {
+			perror("Internal error on app_ready_pipe");
+			return false;
+		} else if (startup != 1) {
+			cerr << "app is not ready to start; exiting" << endl;
+			return false;
+		}
+		proc_list.push_back(app_PID);
+		procs_are_threads = false;
+	} else if (!ocount_options::threads.empty()) {
+		procs_are_threads = true;
+		proc_list = ocount_options::threads;
+	} else if (!ocount_options::processes.empty()) {
+		procs_are_threads = false;
+		proc_list = ocount_options::processes;
+	}
+
+	if (startApp) {
+		// Tell app_PID to start the app
+		cverb << vdebug << "telling child to start app" << endl;
+		if (write(start_app_pipe[1], &startup, sizeof(startup)) < 0) {
+			perror("Internal error on start_app_pipe");
+			return -1;
+		}
+		app_started = true;
+	}
+
+	orecord = new ocount_record(runmode, events);
+	bool ret;
+	switch (runmode) {
+	case OP_START_APP:
+		ret = orecord->start_counting_app_process(app_PID);
+		break;
+	case OP_SYSWIDE:
+		ret = orecord->start_counting_syswide();
+		break;
+	case OP_CPULIST:
+		ret = orecord->start_counting_cpulist(ocount_options::cpus);
+		break;
+	case OP_PROCLIST:
+		ret = orecord->start_counting_tasklist(ocount_options::processes, false);
+		break;
+	case OP_THREADLIST:
+		ret = orecord->start_counting_tasklist(ocount_options::threads, true);
+		break;
+	default:
+		ret = false;
+		break;   // impossible to get here, since we validate runmode prior to this point
+	}
+	if (!orecord->get_valid()) {
+		/* If valid is false, it means that one of the "known" errors has
+		 * occurred:
+		 *   - monitored process has already ended
+		 *   - passed PID was invalid
+		 *   - device or resource busy
+		 */
+		cverb << vdebug << "ocount record init failed" << endl;
+		ret = false;
+	}
+
+	return ret;
+}
+
+static void do_results(ostream & out)
+{
+	orecord->output_results(out, ocount_options::separate_cpu | ocount_options::separate_thread,
+	                        ocount_options::csv_output);
+}
+
+end_code_t _get_waitpid_status(int waitpid_status, int wait_rc)
+{
+	end_code_t rc = ALL_OK;
+	if (wait_rc < 0) {
+		if (errno == EINTR) {
+			//  Ctrl-C will only kill the monitored app.  See the op_sig_stop signal handler.
+			cverb << vdebug << "Caught ctrl-C.  Killed app process." << endl;
+		} else {
+			cerr << "waitpid for app process failed: " << strerror(errno) << endl;
+			rc = APP_ABNORMAL_END;
+		}
+	} else if (wait_rc) {
+		if (WIFEXITED(waitpid_status) && (!WEXITSTATUS(waitpid_status))) {
+			cverb << vdebug << "app process ended normally." << endl;
+		} else if (WIFEXITED(waitpid_status)) {
+			cerr << "app process exited with the following status: "
+					<< WEXITSTATUS(waitpid_status) << endl;
+			rc = APP_ABNORMAL_END;
+		}  else if (WIFSIGNALED(waitpid_status)) {
+			if (WTERMSIG(waitpid_status) != SIGKILL) {
+				cerr << "app process killed by signal "
+						<< WTERMSIG(waitpid_status) << endl;
+				rc = APP_ABNORMAL_END;
+			}
+		}
+	}
+	return rc;
+}
+
+end_code_t _wait_for_app(ostream & out)
+{
+	int wait_rc;
+	end_code_t rc = ALL_OK;
+	int waitpid_status = 0;
+
+	bool done = false;
+	cverb << vdebug << "going into waitpid on monitored app " << app_PID << endl;
+	if (ocount_options::display_interval) {
+		long number_intervals = ocount_options::num_intervals;
+		do {
+			struct timeval mytime;
+			int countdown = ocount_options::display_interval;
+			while (countdown) {
+				sleep(1);
+				if (--countdown == 0) {
+					if (gettimeofday(&mytime, NULL) < 0) {
+						cleanup();
+						perror("gettimeofday");
+						exit(EXIT_FAILURE);
+					}
+					if (!ocount_options::csv_output)
+						out << endl << "Current time (seconds since epoch): ";
+					else
+						out << endl << "t:";
+					out << dec << mytime.tv_sec;
+					do_results(out);
+				}
+				wait_rc = waitpid(app_PID, &waitpid_status, WNOHANG);
+				if (wait_rc) {
+					rc = _get_waitpid_status(waitpid_status, wait_rc);
+					done = true;
+					countdown = 0;
+				}
+			}
+			if (--number_intervals == 0) {
+				done = true;
+				kill(app_PID, SIGKILL);
+			}
+		} while (!done);
+	} else {
+		wait_rc = waitpid(app_PID, &waitpid_status, 0);
+		rc = _get_waitpid_status(waitpid_status, wait_rc);
+	}
+	return rc;
+}
+
+static end_code_t _run(ostream & out)
+{
+	end_code_t rc = ALL_OK;
+
+	// Fork processes with signals blocked.
+	sigset_t ss;
+	sigfillset(&ss);
+	sigprocmask(SIG_BLOCK, &ss, NULL);
+
+	if (!start_counting()) {
+		return PERF_RECORD_ERROR;
+	}
+	// parent continues here
+	if (startApp)
+		cverb << vdebug << "app " << app_PID << " is running" << endl;
+
+	set_signals_for_parent();
+	if (startApp) {
+		rc = _wait_for_app(out);
+	} else {
+		cout << "ocount: Press Ctl-c or 'kill -SIGINT " << getpid() << "' to stop counting" << endl;
+		if (ocount_options::display_interval) {
+			long number_intervals = ocount_options::num_intervals;
+			struct timeval mytime;
+			while (!stop) {
+				sleep(ocount_options::display_interval);
+				if (gettimeofday(&mytime, NULL) < 0) {
+					cleanup();
+					perror("gettimeofday");
+					exit(EXIT_FAILURE);
+				}
+				if (!ocount_options::csv_output)
+					out << endl << "Current time (seconds since epoch): ";
+				else
+					out << endl << "t:";
+				out << dec << mytime.tv_sec;
+				do_results(out);
+				if (--number_intervals == 0)
+					stop = true;
+			}
+		} else {
+			while (!stop)
+				sleep(1);
+		}
+	}
+	return rc;
+}
+
+static void _parse_cpu_list(void)
+{
+	char * comma_sep;
+	char * endptr;
+	char * aCpu = strtok_r(optarg, ",", &comma_sep);
+	do {
+		int tmp = strtol(aCpu, &endptr, 10);
+		if ((endptr >= aCpu) && (endptr <= (aCpu + strlen(aCpu) - 1))) {
+			// Check if user has passed a range of cpu numbers:  e.g., '3-8'
+			char * dash_sep;
+			char * ending_cpu_str, * starting_cpu_str = strtok_r(aCpu, "-", &dash_sep);
+			int starting_cpu, ending_cpu;
+			if (starting_cpu_str) {
+				ending_cpu_str = strtok_r(NULL, "-", &dash_sep);
+				if (!ending_cpu_str) {
+					__print_usage_and_exit("ocount: Invalid cpu range.");
+				}
+				starting_cpu = strtol(starting_cpu_str, &endptr, 10);
+				if ((endptr >= starting_cpu_str) &&
+						(endptr <= (starting_cpu_str + strlen(starting_cpu_str) - 1))) {
+					__print_usage_and_exit("ocount: Invalid numeric value for --cpu-list option.");
+				}
+				ending_cpu = strtol(ending_cpu_str, &endptr, 10);
+				if ((endptr >= ending_cpu_str) &&
+						(endptr <= (ending_cpu_str + strlen(ending_cpu_str) - 1))) {
+					__print_usage_and_exit("ocount: Invalid numeric value for --cpu-list option.");
+				}
+				for (int i = starting_cpu; i < ending_cpu + 1; i++)
+					ocount_options::cpus.push_back(i);
+			} else {
+				__print_usage_and_exit("ocount: Invalid numeric value for --cpu-list option.");
+			}
+		} else {
+			ocount_options::cpus.push_back(tmp);
+		}
+	} while ((aCpu = strtok_r(NULL, ",", &comma_sep)));
+}
+
+static void _parse_time_interval(void)
+{
+	char * endptr;
+	char * num_intervals, * interval = strtok(optarg, ":");
+	ocount_options::display_interval = strtol(interval, &endptr, 10);
+	if ((endptr >= interval) && (endptr <= (interval + strlen(interval) - 1))) {
+			__print_usage_and_exit("ocount: Invalid numeric value for num_seconds.");
+	}
+	// User has specified num_intervals: e.g., '-i 5:10'
+	num_intervals = strtok(NULL, ":");
+	if (num_intervals) {
+		ocount_options::num_intervals = strtol(num_intervals, &endptr, 10);
+		if ((endptr >= num_intervals) && (endptr <= (num_intervals + strlen(num_intervals) - 1)))
+				__print_usage_and_exit("ocount: Invalid numeric value for num_intervals.");
+	}
+}
+
+static int _process_ocount_and_app_args(int argc, char * const argv[])
+{
+	bool keep_trying = true;
+	int idx_of_non_options = 0;
+	setenv("POSIXLY_CORRECT", "1", 0);
+	while (keep_trying) {
+		int option_idx = 0;
+		int c = getopt_long(argc, argv, short_options, long_options, &option_idx);
+		switch (c) {
+		char * endptr;
+		char * event;
+
+		case -1:
+			if (optind != argc) {
+				idx_of_non_options = optind;
+			}
+			keep_trying = false;
+			break;
+		case '?':
+			cerr << "ocount: non-option detected at optind " << optind << endl;
+			keep_trying = false;
+			idx_of_non_options = -1;
+			break;
+		case 'V':
+			ocount_options::verbose = true;
+			break;
+		case 's':
+			ocount_options::system_wide = true;
+			break;
+		case 'C':
+			_parse_cpu_list();
+			break;
+		case 'p':
+		{
+			char * aPid = strtok(optarg, ",");
+			do {
+				ocount_options::processes.push_back(strtol(aPid, &endptr, 10));
+				if ((endptr >= aPid) && (endptr <= (aPid + strlen(aPid) - 1)))
+					__print_usage_and_exit("ocount: Invalid numeric value for --process-list option.");
+			} while ((aPid = strtok(NULL, ",")));
+			break;
+		}
+		case 'r':
+		{
+			char * aTid = strtok(optarg, ",");
+			do {
+				ocount_options::threads.push_back(strtol(aTid, &endptr, 10));
+				if ((endptr >= aTid) && (endptr <= (aTid + strlen(aTid) - 1)))
+					__print_usage_and_exit("ocount: Invalid numeric value for --thread-list option.");
+			} while ((aTid = strtok(NULL, ",")));
+			break;
+		}
+		case 'e':
+			event = strtok(optarg, ",");
+			do {
+				ocount_options::evts.push_back(event);
+			} while ((event = strtok(NULL, ",")));
+			break;
+		case 'f':
+			ocount_options::outfile = optarg;
+			break;
+		case 'c':
+			ocount_options::separate_cpu = true;
+			break;
+		case 't':
+			ocount_options::separate_thread = true;
+			break;
+		case 'b':
+			ocount_options::csv_output = true;
+			break;
+		case 'i':
+			_parse_time_interval();
+			break;
+		case 'h':
+			__print_usage_and_exit(NULL);
+			break;
+		case 'u':
+			__print_usage_and_exit(NULL);
+			break;
+		case 'v':
+			cout << argv[0] << ": " << PACKAGE << " " << VERSION << " compiled on " << __DATE__
+			     << " " << __TIME__ << endl;
+			exit(EXIT_SUCCESS);
+			break;
+		default:
+			__print_usage_and_exit("ocount: unexpected end of arg parsing");
+		}
+	}
+	return idx_of_non_options;
+}
+
+
+static enum op_runmode _get_runmode(int starting_point)
+{
+	enum op_runmode ret_rm = OP_MAX_RUNMODE;
+	for (int i = starting_point; i < OP_MAX_RUNMODE && ret_rm == OP_MAX_RUNMODE; i++) {
+		switch (i) {
+		// There is no option to check for OP_START_APP; we include a case
+		// statement here just to silence Coverity.
+		case OP_START_APP:
+			break;
+		case OP_SYSWIDE:
+			if (ocount_options::system_wide)
+				ret_rm = OP_SYSWIDE;
+			break;
+		case OP_CPULIST:
+			if (!ocount_options::cpus.empty())
+				ret_rm = OP_CPULIST;
+			break;
+		case OP_PROCLIST:
+			if (!ocount_options::processes.empty())
+				ret_rm = OP_PROCLIST;
+			break;
+		case OP_THREADLIST:
+			if (!ocount_options::threads.empty())
+				ret_rm = OP_THREADLIST;
+			break;
+		default:
+			break;
+		}
+	}
+	return ret_rm;
+}
+
+static void _validate_args(void)
+{
+	if (ocount_options::verbose && !verbose::setup("debug")) {
+		cerr << "unknown --verbose= options\n";
+		__print_usage_and_exit(NULL);
+	}
+	if (runmode == OP_START_APP) {
+		enum op_runmode conflicting_mode = OP_MAX_RUNMODE;
+		if (ocount_options::system_wide)
+			conflicting_mode = OP_SYSWIDE;
+		else if (!ocount_options::cpus.empty())
+			conflicting_mode = OP_CPULIST;
+		else if (!ocount_options::processes.empty())
+			conflicting_mode = OP_PROCLIST;
+		else if (!ocount_options::threads.empty())
+			conflicting_mode = OP_THREADLIST;
+
+		if (conflicting_mode != OP_MAX_RUNMODE) {
+			cerr << "Run mode " << runmode_options[OP_START_APP] << " is incompatible with "
+			     << runmode_options[conflicting_mode] << endl;
+			__print_usage_and_exit(NULL);
+		}
+	} else {
+		enum op_runmode rm2;
+		runmode = _get_runmode(OP_SYSWIDE);
+		if (runmode == OP_MAX_RUNMODE) {
+			__print_usage_and_exit("You must either pass in the name of a command or app to run or specify a run mode");
+		}
+		rm2 = _get_runmode(runmode + 1);
+		if (rm2 != OP_MAX_RUNMODE) {
+			cerr << "Run mode " << runmode_options[rm2] << " is incompatible with "
+			     << runmode_options[runmode] << endl;
+			__print_usage_and_exit(NULL);
+		}
+
+	}
+
+	if (ocount_options::separate_cpu && !(ocount_options::system_wide || !ocount_options::cpus.empty())) {
+		cerr << "The --separate-cpu option is only valid with --system-wide or --cpu-list." << endl;
+		__print_usage_and_exit(NULL);
+	}
+
+	if (ocount_options::separate_thread && !(!ocount_options::threads.empty() || !ocount_options::processes.empty())) {
+		cerr << "The --separate-thread option is only valid with --process_list or --thread_list." << endl;
+		__print_usage_and_exit(NULL);
+	}
+
+	if (runmode == OP_CPULIST) {
+		int num_cpus = use_cpu_minus_one ? 1 : sysconf(_SC_NPROCESSORS_ONLN);
+		if (num_cpus < 1) {
+			cerr << "System config says number of online CPUs is " << num_cpus << "; cannot continue" << endl;
+			exit(EXIT_FAILURE);
+		}
+
+		set<int> available_cpus = op_pe_utils::op_get_available_cpus(num_cpus);
+		size_t k;
+		for (k = 0; k < ocount_options::cpus.size(); k++) {
+			if (available_cpus.find(ocount_options::cpus[k]) == available_cpus.end()) {
+				cerr << "Specified CPU " << ocount_options::cpus[k] << " is not valid" << endl;
+				__print_usage_and_exit(NULL);
+			}
+		}
+	}
+}
+
+static void process_args(int argc, char * const argv[])
+{
+	int non_options_idx  = _process_ocount_and_app_args(argc, argv);
+
+	if (non_options_idx < 0) {
+		__print_usage_and_exit(NULL);
+	} else if ((non_options_idx) > 0) {
+		runmode = OP_START_APP;
+		app_name = (char *) xmalloc(strlen(argv[non_options_idx]) + 1);
+		strcpy(app_name, argv[non_options_idx]);
+		// Note 1: app_args[0] is placeholder for app_fname (filled in later).
+		// Note 2: app_args[<end>] is set to NULL (required by execvp)
+		if (non_options_idx < (argc -1)) {
+			app_args = (char **) xmalloc((sizeof *app_args) *
+			                             (argc - non_options_idx + 1));
+			for(int i = non_options_idx + 1; i < argc; i++) {
+				app_args[i - non_options_idx] = argv[i];
+			}
+			app_args[argc - non_options_idx] = NULL;
+		} else {
+			app_args = (char **) xmalloc((sizeof *app_args) * 2);
+			app_args[1] = NULL;
+		}
+		if (op_validate_app_name(&app_name, &app_name_SAVE) < 0) {
+			__print_usage_and_exit(NULL);
+		}
+	}
+	_validate_args();
+
+	/*  At this point, we know which of the three counting modes the user requested:
+	 *    - count events in named app
+	 *    - count events in app by PID
+	 *    - count events in whole system
+	 */
+
+	if (ocount_options::evts.empty()) {
+		// Use default event
+		op_pe_utils::op_get_default_event();
+	} else  {
+		op_pe_utils::op_process_events_list(ocount_options::evts);
+	}
+	cverb << vdebug << "Number of events passed is " << events.size() << endl;
+	return;
+}
+
+int main(int argc, char * const argv[])
+{
+	int rc;
+	bool get_results = true;
+	int perf_event_paranoid = op_get_sys_value("/proc/sys/kernel/perf_event_paranoid");
+
+	my_uid = geteuid();
+	rc = op_check_perf_events_cap(use_cpu_minus_one);
+	if (rc == EACCES) {
+		/* Early perf_events kernels required the cpu argument to perf_event_open
+		 * to be '-1' when setting up to monitor a single process if 1) the user is
+		 * not root; and 2) perf_event_paranoid is > 0.  An EACCES error would be
+		 * returned if passing '0' or greater for the cpu arg and the above criteria
+		 * was not met.  Unfortunately, later kernels turned this requirement around
+		 * such that the passed cpu arg must be '0' or greater when the user is not
+		 * root.
+		 *
+		 * We don't really have a good way to check whether we're running on such an
+		 * early kernel except to try the perf_event_open with different values to see
+		 * what works.
+		 */
+		if (my_uid != 0 && perf_event_paranoid > 0) {
+			use_cpu_minus_one = true;
+			rc = op_check_perf_events_cap(use_cpu_minus_one);
+		}
+	}
+	if (rc == EBUSY)
+		cerr << "Performance monitor unit is busy.  Do 'opcontrol --deinit' and try again." << endl;
+	else if (rc == ENOSYS)
+		cerr << "Your kernel does not implement a required syscall"
+		     << " for the ocount program." << endl;
+	else if (rc == ENOENT)
+		cerr << "Your kernel's Performance Events Subsystem does not support"
+		     << " your processor type." << endl;
+	else if (rc)
+		cerr << "Unexpected error running ocount: " << strerror(rc) << endl;
+
+	if (rc)
+		exit(1);
+
+	cpu_type = op_get_cpu_type();
+	cpu_speed = op_cpu_frequency();
+	process_args(argc, argv);
+
+	if ((runmode == OP_SYSWIDE || runmode == OP_CPULIST) && ((my_uid != 0) && (perf_event_paranoid > 0))) {
+		cerr << "To do ";
+		if (runmode == OP_SYSWIDE)
+			cerr << "system-wide ";
+		else
+			cerr << "cpu-list ";
+		cerr << "event counting, either you must be root or" << endl;
+		cerr << "/proc/sys/kernel/perf_event_paranoid must be set to 0 or -1." << endl;
+		cleanup();
+		exit(1);
+	}
+
+	if (cpu_type == CPU_NO_GOOD) {
+		cerr << "Unable to ascertain cpu type.  Exiting." << endl;
+		cleanup();
+		exit(1);
+	}
+
+	if (!ocount_options::outfile.empty()) {
+		outfile.open(ocount_options::outfile.c_str());
+	}
+	ostream & out = !ocount_options::outfile.empty() ? outfile : cout;
+
+	end_code_t run_result;
+	if ((run_result = _run(out))) {
+		get_results = false;
+		if (startApp && app_started && (run_result != APP_ABNORMAL_END)) {
+			int rc;
+			cverb << vdebug << "Killing monitored app . . ." << endl;
+			rc = kill(app_PID, SIGKILL);
+			if (rc) {
+				if (errno == ESRCH)
+					cverb << vdebug
+					      << "Unable to kill monitored app because it has already ended"
+					      << endl;
+				else
+					perror("Attempt to kill monitored app failed.");
+			}
+		}
+		if ((run_result == PERF_RECORD_ERROR) || (run_result == PERF_BOTH_ERROR)) {
+			cerr <<  "Error running ocount" << endl;
+		} else {
+			get_results = true;
+			cverb << vdebug << "WARNING: Results may be incomplete due to to abend of monitored app." << endl;
+		}
+	}
+	if (get_results)
+		// We don't do a final display of results if we've been doing it on an interval already.
+		if (!ocount_options::display_interval)
+			do_results(out);
+
+	cleanup();
+	return 0;
+}