oprofile 0.9.6

Copy in the rest of the oprofile 0.9.6 tree so we have a source
copy to match the prebuilt binaries that are checked into
external/.

Change-Id: Iaac327571d5d583594a4194973bf256569061048
diff --git a/pp/opreport.cpp b/pp/opreport.cpp
new file mode 100644
index 0000000..327043c
--- /dev/null
+++ b/pp/opreport.cpp
@@ -0,0 +1,591 @@
+/**
+ * @file opreport.cpp
+ * Implement opreport utility
+ *
+ * @remark Copyright 2003 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include <iostream>
+#include <iomanip>
+#include <vector>
+#include <algorithm>
+#include <sstream>
+#include <numeric>
+
+#include "op_exception.h"
+#include "stream_util.h"
+#include "string_manip.h"
+#include "file_manip.h"
+#include "opreport_options.h"
+#include "op_header.h"
+#include "profile.h"
+#include "populate.h"
+#include "arrange_profiles.h"
+#include "profile_container.h"
+#include "callgraph_container.h"
+#include "diff_container.h"
+#include "symbol_sort.h"
+#include "format_output.h"
+#include "xml_utils.h"
+#include "image_errors.h"
+
+using namespace std;
+
+namespace {
+
+static size_t nr_classes;
+
+/// storage for a merged file summary
+struct summary {
+	count_array_t counts;
+	string lib_image;
+
+	bool operator<(summary const & rhs) const {
+		return options::reverse_sort
+		    ? counts[0] < rhs.counts[0] : rhs.counts[0] < counts[0];
+	}
+
+	/// add a set of files to a summary
+	count_type add_files(list<profile_sample_files> const & files,
+	                     size_t pclass);
+};
+
+
+count_type summary::
+add_files(list<profile_sample_files> const & files, size_t pclass)
+{
+	count_type subtotal = 0;
+
+	list<profile_sample_files>::const_iterator it = files.begin();
+	list<profile_sample_files>::const_iterator const end = files.end();
+
+	for (; it != end; ++it) {
+		count_type count = profile_t::sample_count(it->sample_filename);
+		counts[pclass] += count;
+		subtotal += count;
+
+		if (!it->cg_files.empty()) {
+			throw op_runtime_error("opreport.cpp::add_files(): "
+			       "unxpected non empty cg file set");
+		}
+	}
+
+	return subtotal;
+}
+
+
+/**
+ * Summary of an application: a set of image summaries
+ * for one application, i.e. an application image and all
+ * dependent images such as libraries.
+ */
+struct app_summary {
+	/// total count of us and all dependents
+	count_array_t counts;
+	/// the main image
+	string image;
+	/// our dependent images
+	vector<summary> deps;
+
+	/// construct and fill in the data
+	count_type add_profile(profile_set const & profile, size_t pclass);
+
+	bool operator<(app_summary const & rhs) const {
+		return options::reverse_sort 
+		    ? counts[0] < rhs.counts[0] : rhs.counts[0] < counts[0];
+	}
+
+private:
+	/// find a matching summary (including main app summary)
+	summary & find_summary(string const & image);
+};
+
+
+summary & app_summary::find_summary(string const & image)
+{
+	vector<summary>::iterator sit = deps.begin();
+	vector<summary>::iterator const send = deps.end();
+	for (; sit != send; ++sit) {
+		if (sit->lib_image == image)
+			return *sit;
+	}
+
+	summary summ;
+	summ.lib_image = image;
+	deps.push_back(summ);
+	return deps.back();
+}
+
+
+count_type app_summary::add_profile(profile_set const & profile,
+                                size_t pclass)
+{
+	count_type group_total = 0;
+
+	// first the main image
+	summary & summ = find_summary(profile.image);
+	count_type app_count = summ.add_files(profile.files, pclass);
+	counts[pclass] += app_count;
+	group_total += app_count;
+
+	// now all dependent images if any
+	list<profile_dep_set>::const_iterator it = profile.deps.begin();
+	list<profile_dep_set>::const_iterator const end = profile.deps.end();
+
+	for (; it != end; ++it) {
+		summary & summ = find_summary(it->lib_image);
+		count_type lib_count = summ.add_files(it->files, pclass);
+		counts[pclass] += lib_count;
+		group_total += lib_count;
+	}
+
+	return group_total;
+}
+
+
+/// all the summaries
+struct summary_container {
+	summary_container(vector<profile_class> const & pclasses);
+
+	/// all map summaries
+	vector<app_summary> apps;
+	/// total count of samples for all summaries
+	count_array_t total_counts;
+};
+
+
+summary_container::
+summary_container(vector<profile_class> const & pclasses)
+{
+	typedef map<string, app_summary> app_map_t;
+	app_map_t app_map;
+
+	for (size_t i = 0; i < pclasses.size(); ++i) {
+		list<profile_set>::const_iterator it
+			= pclasses[i].profiles.begin();
+		list<profile_set>::const_iterator const end
+			= pclasses[i].profiles.end();
+
+		for (; it != end; ++it) {
+			app_map_t::iterator ait = app_map.find(it->image);
+			if (ait == app_map.end()) {
+				app_summary app;
+				app.image = it->image;
+				total_counts[i] += app.add_profile(*it, i);
+				app_map[app.image] = app;
+			} else {
+				total_counts[i]
+					+= ait->second.add_profile(*it, i);
+			}
+		}
+	}
+
+	app_map_t::const_iterator it = app_map.begin();
+	app_map_t::const_iterator const end = app_map.end();
+
+	for (; it != end; ++it)
+		apps.push_back(it->second);
+
+	// sort by count
+	stable_sort(apps.begin(), apps.end());
+	vector<app_summary>::iterator ait = apps.begin();
+	vector<app_summary>::iterator const aend = apps.end();
+	for (; ait != aend; ++ait)
+		stable_sort(ait->deps.begin(), ait->deps.end());
+}
+
+
+void output_header()
+{
+	if (!options::show_header)
+		return;
+
+	cout << classes.cpuinfo << endl;
+	if (!classes.event.empty())
+		cout << classes.event << endl;
+
+	for (vector<profile_class>::size_type i = 0;
+	     i < classes.v.size(); ++i) {
+		cout << classes.v[i].longname << endl;
+	}
+}
+
+
+string get_filename(string const & filename)
+{
+	return options::long_filenames ? filename : op_basename(filename);
+}
+
+
+/// Output a count and a percentage
+void output_count(count_type total_count, count_type count)
+{
+	cout << setw(9) << count << ' ';
+	double ratio = op_ratio(count, total_count);
+	cout << format_percent(ratio * 100, percent_int_width,
+			      percent_fract_width) << ' ';
+}
+
+
+void output_col_headers(bool indent)
+{
+	if (!options::show_header)
+		return;
+
+	if (indent)
+		cout << '\t';
+
+	size_t colwidth = 9 + 1 + percent_width;
+
+	for (size_t i = 0; i < classes.v.size(); ++i) {
+		string name = classes.v[i].name;
+		if (name.length() > colwidth)
+			name = name.substr(0, colwidth - 3)
+				+ "...";
+		io_state state(cout);
+		// gcc 2.95 doesn't know right io manipulator
+		cout.setf(ios::right, ios::adjustfield);
+		// gcc 2.95 doesn't honor setw() for std::string
+		cout << setw(colwidth) << name.c_str();
+		cout << '|';
+	}
+	cout << '\n';
+
+	if (indent)
+		cout << '\t';
+
+	for (size_t i = 0; i < classes.v.size(); ++i) {
+		cout << "  samples| ";
+		io_state state(cout);
+		// gcc 2.95 doesn't know right io manipulator
+		cout.setf(ios::right, ios::adjustfield);
+		cout << setw(percent_width) << "%|";
+	}
+
+	cout << '\n';
+
+	if (indent)
+		cout << '\t';
+
+	for (size_t i = 0; i < classes.v.size(); ++i) {
+		cout << "-----------";
+		string str(percent_width, '-');
+		cout << str;
+	}
+
+	cout << '\n';
+}
+
+
+void
+output_deps(summary_container const & summaries,
+	    app_summary const & app)
+{
+	// the app summary itself is *always* present
+	// (perhaps with zero counts) so this test
+	// is correct
+	if (app.deps.size() == 1)
+		return;
+
+	output_col_headers(true);
+
+	for (size_t j = 0 ; j < app.deps.size(); ++j) {
+		summary const & summ = app.deps[j];
+
+		if (summ.counts.zero())
+			continue;
+
+		cout << '\t';
+
+		for (size_t i = 0; i < nr_classes; ++i) {
+			count_type tot_count = options::global_percent
+				? summaries.total_counts[i] : app.counts[i];
+
+			output_count(tot_count, summ.counts[i]);
+		}
+
+		cout << get_filename(summ.lib_image);
+		cout << '\n';
+	}
+}
+
+
+/**
+ * Display all the given summary information
+ */
+void output_summaries(summary_container const & summaries)
+{
+	output_col_headers(false);
+
+	for (size_t i = 0; i < summaries.apps.size(); ++i) {
+		app_summary const & app = summaries.apps[i];
+
+		if ((app.counts[0] * 100.0) / summaries.total_counts[0]
+		    < options::threshold) {
+			continue;
+		}
+
+		for (size_t j = 0; j < nr_classes; ++j)
+			output_count(summaries.total_counts[j], app.counts[j]);
+
+		cout << get_filename(app.image) << '\n';
+
+		output_deps(summaries, app);
+	}
+}
+
+
+format_flags get_format_flags(column_flags const & cf)
+{
+	format_flags flags(ff_none);
+	flags = format_flags(flags | ff_nr_samples);
+	flags = format_flags(flags | ff_percent | ff_symb_name);
+
+	if (options::show_address)
+		flags = format_flags(flags | ff_vma);
+
+	if (options::debug_info)
+		flags = format_flags(flags | ff_linenr_info);
+
+	if (options::accumulated) {
+		flags = format_flags(flags | ff_nr_samples_cumulated);
+		flags = format_flags(flags | ff_percent_cumulated);
+	}
+
+	if (classes2.v.size())
+		flags = format_flags(flags | ff_diff);
+
+	if (cf & cf_image_name)
+		flags = format_flags(flags | ff_image_name);
+
+	return flags;
+}
+
+
+void output_symbols(profile_container const & pc, bool multiple_apps)
+{
+	profile_container::symbol_choice choice;
+	choice.threshold = options::threshold;
+	symbol_collection symbols = pc.select_symbols(choice);
+	options::sort_by.sort(symbols, options::reverse_sort,
+	                      options::long_filenames);
+	format_output::formatter * out;
+	format_output::xml_formatter * xml_out = 0;
+	format_output::opreport_formatter * text_out = 0;
+
+	if (options::xml) {
+		xml_out = new format_output::xml_formatter(&pc, symbols,
+			pc.extra_found_images, options::symbol_filter);
+		xml_out->show_details(options::details);
+		out = xml_out;
+		// for XML always output long filenames
+		out->show_long_filenames(true);
+	} else {
+		text_out = new format_output::opreport_formatter(pc);
+		text_out->show_details(options::details);
+		out = text_out;
+		out->show_long_filenames(options::long_filenames);
+	}
+
+	out->set_nr_classes(nr_classes);
+	out->show_header(options::show_header);
+	out->vma_format_64bit(choice.hints & cf_64bit_vma);
+	out->show_global_percent(options::global_percent);
+
+	format_flags flags = get_format_flags(choice.hints);
+	if (multiple_apps)
+		flags = format_flags(flags | ff_app_name);
+
+	out->add_format(flags);
+
+	if (options::xml) {
+		xml_support = new xml_utils(xml_out, symbols, nr_classes,
+			pc.extra_found_images);
+		xml_out->output(cout);
+	} else {
+		text_out->output(cout, symbols);
+	}
+}
+
+
+void output_diff_symbols(profile_container const & pc1,
+                         profile_container const & pc2, bool multiple_apps)
+{
+	diff_container dc(pc1, pc2);
+
+	profile_container::symbol_choice choice;
+	choice.threshold = options::threshold;
+
+	diff_collection symbols = dc.get_symbols(choice);
+
+	format_flags flags = get_format_flags(choice.hints);
+	if (multiple_apps)
+		flags = format_flags(flags | ff_app_name);
+
+	// With diff profile we output only filename coming from the first
+	// profile session, internally we use only name derived from the sample
+	// filename so image name can match.
+	format_output::diff_formatter out(dc, pc1.extra_found_images);
+
+	out.set_nr_classes(nr_classes);
+	out.show_long_filenames(options::long_filenames);
+	out.show_header(options::show_header);
+	out.show_global_percent(options::global_percent);
+	out.vma_format_64bit(choice.hints & cf_64bit_vma);
+	out.add_format(flags);
+
+	options::sort_by.sort(symbols, options::reverse_sort,
+	                      options::long_filenames);
+
+	out.output(cout, symbols);
+}
+
+
+void output_cg_symbols(callgraph_container const & cg, bool multiple_apps)
+{
+	column_flags output_hints = cg.output_hint();
+
+	symbol_collection symbols = cg.get_symbols();
+
+	options::sort_by.sort(symbols, options::reverse_sort,
+	                      options::long_filenames);
+
+	format_output::formatter * out;
+	format_output::xml_cg_formatter * xml_out = 0;
+	format_output::cg_formatter * text_out = 0;
+
+	if (options::xml) {
+		xml_out = new format_output::xml_cg_formatter(cg, symbols,
+			options::symbol_filter);
+		xml_out->show_details(options::details);
+		out = xml_out;
+		// for XML always output long filenames
+		out->show_long_filenames(true);
+	} else {
+		text_out = new format_output::cg_formatter(cg);
+		out = text_out;
+		out->show_long_filenames(options::long_filenames);
+	}
+
+	out->set_nr_classes(nr_classes);
+	out->show_header(options::show_header);
+	out->vma_format_64bit(output_hints & cf_64bit_vma);
+	out->show_global_percent(options::global_percent);
+
+	format_flags flags = get_format_flags(output_hints);
+	if (multiple_apps)
+		flags = format_flags(flags | ff_app_name);
+
+	out->add_format(flags);
+
+	if (options::xml) {
+		xml_support = new xml_utils(xml_out, symbols, nr_classes,
+			cg.extra_found_images);
+		xml_out->output(cout);
+	} else {
+		text_out->output(cout, symbols);
+	}
+
+}
+
+
+int opreport(options::spec const & spec)
+{
+	want_xml = options::xml;
+
+	handle_options(spec);
+
+	nr_classes = classes.v.size();
+
+	if (!options::symbols && !options::xml) {
+		summary_container summaries(classes.v);
+		output_header();
+		output_summaries(summaries);
+		return 0;
+	}
+
+	bool multiple_apps = false;
+
+	for (size_t i = 0; i < classes.v.size(); ++i) {
+		if (classes.v[i].profiles.size() > 1)
+			multiple_apps = true;
+	}
+
+	list<inverted_profile> iprofiles = invert_profiles(classes);
+
+	report_image_errors(iprofiles, classes.extra_found_images);
+
+	if (options::xml) {
+		xml_utils::output_xml_header(options::command_options,
+		                             classes.cpuinfo, classes.event);
+	} else {
+		output_header();
+	}
+
+	if (classes2.v.size()) {
+		for (size_t i = 0; i < classes2.v.size(); ++i) {
+			if (classes2.v[i].profiles.size() > 1)
+				multiple_apps |= true;
+		}
+
+		profile_container pc1(options::debug_info, options::details,
+				      classes.extra_found_images);
+
+		list<inverted_profile>::iterator it = iprofiles.begin();
+		list<inverted_profile>::iterator const end = iprofiles.end();
+
+		for (; it != end; ++it)
+			populate_for_image(pc1, *it,
+					   options::symbol_filter, 0);
+
+		list<inverted_profile> iprofiles2 = invert_profiles(classes2);
+
+		report_image_errors(iprofiles2, classes2.extra_found_images);
+
+		profile_container pc2(options::debug_info, options::details,
+				      classes2.extra_found_images);
+
+		list<inverted_profile>::iterator it2 = iprofiles2.begin();
+		list<inverted_profile>::iterator const end2 = iprofiles2.end();
+
+		for (; it2 != end2; ++it2)
+			populate_for_image(pc2, *it2,
+					   options::symbol_filter, 0);
+
+		output_diff_symbols(pc1, pc2, multiple_apps);
+	} else if (options::callgraph) {
+		callgraph_container cg_container;
+		cg_container.populate(iprofiles, classes.extra_found_images,
+			options::debug_info, options::threshold,
+			options::merge_by.lib, options::symbol_filter);
+
+		output_cg_symbols(cg_container, multiple_apps);
+	} else {
+		profile_container samples(options::debug_info,
+			options::details, classes.extra_found_images);
+
+		list<inverted_profile>::iterator it = iprofiles.begin();
+		list<inverted_profile>::iterator const end = iprofiles.end();
+
+		for (; it != end; ++it)
+			populate_for_image(samples, *it,
+					   options::symbol_filter, 0);
+
+		output_symbols(samples, multiple_apps);
+	}
+
+	return 0;
+}
+
+}  // anonymous namespace
+
+
+int main(int argc, char const * argv[])
+{
+	cout.tie(0);
+	return run_pp_tool(argc, argv, opreport);
+}