Home | History | Annotate | Download | only in libpp
      1 /**
      2  * @file arrange_profiles.cpp
      3  * Classify and process a list of candidate sample files
      4  * into merged sets and classes.
      5  *
      6  * @remark Copyright 2003 OProfile authors
      7  * @remark Read the file COPYING
      8  *
      9  * @author John Levon
     10  */
     11 
     12 #include <algorithm>
     13 #include <cstdlib>
     14 #include <iostream>
     15 #include <iterator>
     16 #include <map>
     17 #include <set>
     18 
     19 #include "string_manip.h"
     20 #include "op_header.h"
     21 #include "op_exception.h"
     22 
     23 #include "arrange_profiles.h"
     24 #include "format_output.h"
     25 #include "xml_utils.h"
     26 #include "parse_filename.h"
     27 #include "locate_images.h"
     28 
     29 using namespace std;
     30 
     31 namespace {
     32 
     33 int numeric_compare(string const & lhs, string const & rhs)
     34 {
     35 	if (lhs == "all" && rhs == "all")
     36 		return 0;
     37 	// we choose an order arbitrarily
     38 	if (lhs == "all")
     39 		return 1;
     40 	if (rhs == "all")
     41 		return -1;
     42 	unsigned int lhsval = op_lexical_cast<unsigned int>(lhs);
     43 	unsigned int rhsval = op_lexical_cast<unsigned int>(rhs);
     44 	if (lhsval == rhsval)
     45 		return 0;
     46 	if (lhsval < rhsval)
     47 		return -1;
     48 	return 1;
     49 }
     50 
     51 
     52 } // anonymous namespace
     53 
     54 
     55 // global to fix some C++ obscure corner case.
     56 bool operator<(profile_class const & lhs,
     57                profile_class const & rhs)
     58 {
     59 	profile_template const & lt = lhs.ptemplate;
     60 	profile_template const & rt = rhs.ptemplate;
     61 	int comp;
     62 
     63 	// The profile classes are used to traverse the sample data
     64 	// arrays.  We create XML elements for <process> and <thread>
     65 	// that contain the sample data that can then be divided amongst
     66 	// CPU, event, mask axes so it is more convenient to have the
     67 	// process and thread classes be the outermost nesting level of
     68 	// the sample data arrays
     69 	if (!want_xml) {
     70 		comp = numeric_compare(lt.cpu, rt.cpu);
     71 		if (comp)
     72 			return comp < 0;
     73 	}
     74 
     75 	comp = numeric_compare(lt.tgid, rt.tgid);
     76 	if (comp)
     77 		return comp < 0;
     78 
     79 	comp = numeric_compare(lt.tid, rt.tid);
     80 	if (comp)
     81 		return comp < 0;
     82 
     83 	comp = numeric_compare(lt.unitmask, rt.unitmask);
     84 	if (comp)
     85 		return comp < 0;
     86 
     87 	if (want_xml) {
     88 		if (lt.event != rt.event)
     89 			return lt.event < rt.event;
     90 		if (lt.count != rt.count)
     91 			return lt.count < rt.count;
     92 
     93 		return numeric_compare(lt.cpu, rt.cpu) < 0;
     94 	} else {
     95 		if (lt.event == rt.event)
     96 			return lt.count < rt.count;
     97 		return lt.event < rt.event;
     98 	}
     99 }
    100 
    101 namespace {
    102 
    103 struct axis_t {
    104 	string name;
    105 	string suggestion;
    106 } axes[AXIS_MAX] = {
    107 	{ "event", "specify event:, count: or unitmask: (see also --merge=unitmask)" },
    108 	{ "tgid", "specify tgid: or --merge tgid" },
    109 	{ "tid", "specify tid: or --merge tid" },
    110 	{ "cpu", "specify cpu: or --merge cpu" },
    111 };
    112 
    113 } // anonymous namespace
    114 
    115 
    116 bool profile_classes::matches(profile_classes const & classes)
    117 {
    118 	if (v.size() != classes.v.size())
    119 		return false;
    120 
    121 	axis_types const axis2 = classes.axis;
    122 
    123 	switch (axis) {
    124 		case AXIS_EVENT:
    125 			break;
    126 		case AXIS_TGID:
    127 		case AXIS_TID:
    128 			return axis2 == AXIS_TID || axis2 == AXIS_TGID;
    129 		case AXIS_CPU:
    130 			return axis2 == AXIS_CPU;
    131 		case AXIS_MAX:
    132 			return false;
    133 	}
    134 
    135 	// check that the events match (same event, count)
    136 
    137 	vector<profile_class>::const_iterator it1 = v.begin();
    138 	vector<profile_class>::const_iterator end1 = v.end();
    139 	vector<profile_class>::const_iterator it2 = classes.v.begin();
    140 
    141 	while (it1 != end1) {
    142 		if (it1->ptemplate.event != it2->ptemplate.event)
    143 			return false;
    144 		if (it1->ptemplate.count != it2->ptemplate.count)
    145 			return false;
    146 		// differing unit mask is considered comparable
    147 		++it1;
    148 		++it2;
    149 	}
    150 
    151 	return true;
    152 }
    153 
    154 namespace {
    155 
    156 typedef growable_vector<string> event_array_t;
    157 typedef growable_vector<string>::size_type event_index_t;
    158 
    159 bool new_event_index(string event, event_array_t & events, event_index_t & index)
    160 {
    161 	event_index_t sz = events.size();
    162 	for (event_index_t i = 0; i != sz; ++i) {
    163 		if (events[i] == event) {
    164 			index = i;
    165 			return false;
    166 		}
    167 	}
    168 
    169 	index = sz;
    170 	events[sz] = event;
    171 	return true;
    172 }
    173 
    174 
    175 /// We have more than one axis of classification, tell the user.
    176 void report_error(profile_classes const & classes, axis_types newaxis)
    177 {
    178 	string str = "Already displaying results for parameter ";
    179 	str += axes[classes.axis].name;
    180 	str += " with values:\n";
    181 	vector<profile_class>::const_iterator it = classes.v.begin();
    182 	vector<profile_class>::const_iterator const end = classes.v.end();
    183 
    184 	// We show error for the first conflicting axis but on this
    185 	// axis we can get only a few different it->name, we display only
    186 	// these different name.
    187 	set <string> name_seen;
    188 	size_t i = 5;
    189 	for (; it != end && i; ++it) {
    190 		if (name_seen.find(it->name) == name_seen.end()) {
    191 			name_seen.insert(it->name);
    192 			str += it->name + ",";
    193 			--i;
    194 		}
    195 	}
    196 
    197 	if (!i) {
    198 		str += " and ";
    199 		str += op_lexical_cast<string>(classes.v.size() - 5);
    200 		str += " more,";
    201 	}
    202 
    203 	str += "\nwhich conflicts with parameter ";
    204 	str += axes[newaxis].name += ".\n";
    205 	str += "Suggestion: ";
    206 	str += axes[classes.axis].suggestion;
    207 	throw op_fatal_error(str);
    208 }
    209 
    210 
    211 /**
    212  * check that two different axes are OK - this is only
    213  * allowed if they are TGID,TID and for each class,
    214  * tid == tgid
    215  */
    216 bool allow_axes(profile_classes const & classes, axis_types newaxis)
    217 {
    218 	// No previous axis - OK
    219 	if (classes.axis == AXIS_MAX)
    220 		return true;
    221 
    222 	if (classes.axis != AXIS_TID && classes.axis != AXIS_TGID)
    223 		return false;
    224 
    225 	if (newaxis != AXIS_TID && newaxis != AXIS_TGID)
    226 		return false;
    227 
    228 	vector<profile_class>::const_iterator it = classes.v.begin();
    229 	vector<profile_class>::const_iterator const end = classes.v.end();
    230 
    231 	for (; it != end; ++it) {
    232 		if (it->ptemplate.tgid != it->ptemplate.tid)
    233 			return false;
    234 	}
    235 
    236 	return true;
    237 }
    238 
    239 
    240 /// find the first sample file header in the class
    241 opd_header const get_first_header(profile_class const & pclass)
    242 {
    243 	profile_set const & profile = *(pclass.profiles.begin());
    244 
    245 	string file;
    246 
    247 	// could be only one main app, with no samples for the main image
    248 	if (profile.files.empty()) {
    249 		profile_dep_set const & dep = *(profile.deps.begin());
    250 		list<profile_sample_files> const & files = dep.files;
    251 		profile_sample_files const & sample_files = *(files.begin());
    252 		if (!sample_files.sample_filename.empty())
    253 			file = sample_files.sample_filename;
    254 		else
    255 			file = *sample_files.cg_files.begin();
    256 	} else {
    257 		profile_sample_files const & sample_files
    258 			= *(profile.files.begin());
    259 		if (!sample_files.sample_filename.empty())
    260 			file = sample_files.sample_filename;
    261 		else
    262 			file = *sample_files.cg_files.begin();
    263 	}
    264 
    265 	return read_header(file);
    266 }
    267 
    268 /// merge sample file header in the profile_sample_files
    269 void merge_header(profile_sample_files const & files, opd_header & header)
    270 {
    271 	if (!files.sample_filename.empty()) {
    272 		opd_header const temp = read_header(files.sample_filename);
    273 		header.ctr_um |=  temp.ctr_um;
    274 	}
    275 
    276 	list<string>::const_iterator it = files.cg_files.begin();
    277 	list<string>::const_iterator const end = files.cg_files.end();
    278 	for ( ; it != end; ++it) {
    279 		opd_header const temp = read_header(*it);
    280 		header.ctr_um |= temp.ctr_um;
    281 	}
    282 }
    283 
    284 /// merge sample file header in the class
    285 opd_header const get_header(profile_class const & pclass,
    286                             merge_option const & merge_by)
    287 {
    288 	opd_header header = get_first_header(pclass);
    289 
    290 	if (!merge_by.unitmask)
    291 		return header;
    292 
    293 	profile_set const & profile = *(pclass.profiles.begin());
    294 
    295 	typedef list<profile_sample_files>::const_iterator citerator;
    296 
    297 	citerator it = profile.files.begin();
    298 	citerator const end = profile.files.end();
    299 	for ( ; it != end; ++it)
    300 		merge_header(*it, header);
    301 
    302 	list<profile_dep_set>::const_iterator dep_it = profile.deps.begin();
    303 	list<profile_dep_set>::const_iterator dep_end = profile.deps.end();
    304 	for ( ; dep_it != dep_end; ++dep_it) {
    305 		citerator it = dep_it->files.begin();
    306 		citerator const end = dep_it->files.end();
    307 		for ( ; it != end; ++it)
    308 			merge_header(*it, header);
    309 	}
    310 
    311 	return header;
    312 }
    313 
    314 
    315 /// Give human-readable names to each class.
    316 void name_classes(profile_classes & classes, merge_option const & merge_by)
    317 {
    318 	opd_header header = get_header(classes.v[0], merge_by);
    319 
    320 	classes.event = describe_header(header);
    321 	classes.cpuinfo = describe_cpu(header);
    322 
    323 	// If we're splitting on event anyway, clear out the
    324 	// global event name
    325 	if (classes.axis == AXIS_EVENT)
    326 		classes.event.erase();
    327 
    328 	vector<profile_class>::iterator it = classes.v.begin();
    329 	vector<profile_class>::iterator const end = classes.v.end();
    330 
    331 	for (; it != end; ++it) {
    332 		it->name = axes[classes.axis].name + ":";
    333 		switch (classes.axis) {
    334 		case AXIS_EVENT:
    335 			it->name = it->ptemplate.event
    336 				+ ":" + it->ptemplate.count;
    337 			header = get_header(*it, merge_by);
    338 			it->longname = describe_header(header);
    339 			break;
    340 		case AXIS_TGID:
    341 			it->name += it->ptemplate.tgid;
    342 			it->longname = "Processes with a thread group ID of ";
    343 			it->longname += it->ptemplate.tgid;
    344 			break;
    345 		case AXIS_TID:
    346 			it->name += it->ptemplate.tid;
    347 			it->longname = "Processes with a thread ID of ";
    348 			it->longname += it->ptemplate.tid;
    349 			break;
    350 		case AXIS_CPU:
    351 			it->name += it->ptemplate.cpu;
    352 			it->longname = "Samples on CPU " + it->ptemplate.cpu;
    353 			break;
    354 		case AXIS_MAX:;
    355 		}
    356 	}
    357 }
    358 
    359 
    360 /**
    361  * Name and verify classes.
    362  */
    363 void identify_classes(profile_classes & classes,
    364                       merge_option const & merge_by)
    365 {
    366 	profile_template & ptemplate = classes.v[0].ptemplate;
    367 	bool changed[AXIS_MAX] = { false, };
    368 
    369 	vector<profile_class>::iterator it = classes.v.begin();
    370 	++it;
    371 	vector<profile_class>::iterator end = classes.v.end();
    372 
    373 	// only one class, name it after the event
    374 	if (it == end)
    375 		changed[AXIS_EVENT] = true;
    376 
    377 	for (; it != end; ++it) {
    378 		if (it->ptemplate.event != ptemplate.event
    379 		    ||  it->ptemplate.count != ptemplate.count
    380 		    // unit mask are mergeable
    381 		    || (!merge_by.unitmask
    382 			&& it->ptemplate.unitmask != ptemplate.unitmask))
    383 			changed[AXIS_EVENT] = true;
    384 
    385 		// we need the merge checks here because each
    386 		// template is filled in from the first non
    387 		// matching profile, so just because they differ
    388 		// doesn't mean it's the axis we care about
    389 		if (!merge_by.tgid && it->ptemplate.tgid != ptemplate.tgid)
    390 			changed[AXIS_TGID] = true;
    391 
    392 		if (!merge_by.tid && it->ptemplate.tid != ptemplate.tid)
    393 			changed[AXIS_TID] = true;
    394 
    395 		if (!merge_by.cpu && it->ptemplate.cpu != ptemplate.cpu)
    396 			changed[AXIS_CPU] = true;
    397 	}
    398 
    399 	classes.axis = AXIS_MAX;
    400 
    401 	for (size_t i = 0; i < AXIS_MAX; ++i) {
    402 		if (!changed[i])
    403 			continue;
    404 
    405 		if (!allow_axes(classes, axis_types(i)))
    406 			report_error(classes, axis_types(i));
    407 		classes.axis = axis_types(i);
    408 		/* do this early for report_error */
    409 		name_classes(classes, merge_by);
    410 	}
    411 
    412 	if (classes.axis == AXIS_MAX) {
    413 		cerr << "Internal error - no equivalence class axis" << endl;
    414 		abort();
    415 	}
    416 }
    417 
    418 void identify_xml_classes(profile_classes & classes, merge_option const & merge_by)
    419 {
    420 	opd_header header = get_header(classes.v[0], merge_by);
    421 
    422 	vector<profile_class>::iterator it = classes.v.begin();
    423 	vector<profile_class>::iterator end = classes.v.end();
    424 
    425 	event_index_t event_num;
    426 	event_index_t event_max = 0;
    427 	event_array_t event_array;
    428 	size_t nr_cpus = 0;
    429 	bool has_nonzero_mask = false;
    430 
    431 	ostringstream event_setup;
    432 
    433 	// fill in XML identifying each event, and replace event name by event_num
    434 	for (; it != end; ++it) {
    435 		string mask = it->ptemplate.unitmask;
    436 		if (mask.find_first_of("x123456789abcdefABCDEF") != string::npos)
    437 			has_nonzero_mask = true;
    438 		if (new_event_index(it->ptemplate.event, event_array, event_num)) {
    439 			// replace it->ptemplate.event with the event_num string
    440 			// this is the first time we've seen this event
    441 			header = get_header(*it, merge_by);
    442 			event_setup << describe_header(header);
    443 			event_max = event_num;
    444 		}
    445 		if (it->ptemplate.cpu != "all") {
    446 			size_t cpu = atoi(it->ptemplate.cpu.c_str());
    447 			if (cpu > nr_cpus) nr_cpus = cpu;
    448 		}
    449 
    450 		ostringstream str;
    451 		str << event_num;
    452 		it->ptemplate.event = str.str();
    453 	}
    454 	xml_utils::set_nr_cpus(++nr_cpus);
    455 	xml_utils::set_nr_events(event_max+1);
    456 	if (has_nonzero_mask)
    457 		xml_utils::set_has_nonzero_masks();
    458 	classes.event = event_setup.str();
    459 	classes.cpuinfo = describe_cpu(header);
    460 }
    461 
    462 /// construct a class template from a profile
    463 profile_template const
    464 template_from_profile(parsed_filename const & parsed,
    465                       merge_option const & merge_by)
    466 {
    467 	profile_template ptemplate;
    468 
    469 	ptemplate.event = parsed.event;
    470 	ptemplate.count = parsed.count;
    471 
    472 	if (!merge_by.unitmask)
    473 		ptemplate.unitmask = parsed.unitmask;
    474 	if (!merge_by.tgid)
    475 		ptemplate.tgid = parsed.tgid;
    476 	if (!merge_by.tid)
    477 		ptemplate.tid = parsed.tid;
    478 	if (!merge_by.cpu)
    479 		ptemplate.cpu = parsed.cpu;
    480 	return ptemplate;
    481 }
    482 
    483 
    484 /**
    485  * Find a matching class the sample file could go in, or generate
    486  * a new class if needed.
    487  * This is the heart of the merging and classification process.
    488  * The returned value is non-const reference but the ptemplate member
    489  * must be considered as const
    490  */
    491 profile_class & find_class(set<profile_class> & classes,
    492                            parsed_filename const & parsed,
    493                            merge_option const & merge_by)
    494 {
    495 	profile_class cls;
    496 	cls.ptemplate = template_from_profile(parsed, merge_by);
    497 
    498 	pair<set<profile_class>::iterator, bool> ret = classes.insert(cls);
    499 
    500 	return const_cast<profile_class &>(*ret.first);
    501 }
    502 
    503 /**
    504  * Sanity check : we can't overwrite sample_filename, if we abort here it means
    505  * we fail to detect that parsed sample filename for two distinct samples
    506  * filename must go in two distinct profile_sample_files. This assumption is
    507  * false for callgraph samples files so this function is only called for non cg
    508  * files.
    509  */
    510 void sanitize_profile_sample_files(profile_sample_files const & sample_files,
    511     parsed_filename const & parsed)
    512 {
    513 	// We can't allow to overwrite sample_filename.
    514 	if (!sample_files.sample_filename.empty()) {
    515 		ostringstream out;
    516 		out << "sanitize_profile_sample_files(): sample file "
    517 		    << "parsed twice ?\nsample_filename:\n"
    518 		    << sample_files.sample_filename << endl
    519 		    << parsed << endl;
    520 		throw op_fatal_error(out.str());
    521 	}
    522 }
    523 
    524 
    525 /**
    526  * Add a sample filename (either cg or non cg files) to this profile.
    527  */
    528 void
    529 add_to_profile_sample_files(profile_sample_files & sample_files,
    530     parsed_filename const & parsed)
    531 {
    532 	if (parsed.cg_image.empty()) {
    533 		// We can't allow to overwrite sample_filename.
    534 		sanitize_profile_sample_files(sample_files, parsed);
    535 
    536 		sample_files.sample_filename = parsed.filename;
    537 	} else {
    538 		sample_files.cg_files.push_back(parsed.filename);
    539 	}
    540 }
    541 
    542 
    543 /**
    544  * we need to fix cg filename: a callgraph filename can occur before the binary
    545  * non callgraph samples filename occur so we must search.
    546  */
    547 profile_sample_files &
    548 find_profile_sample_files(list<profile_sample_files> & files,
    549 			  parsed_filename const & parsed,
    550 			  extra_images const & extra)
    551 {
    552 	list<profile_sample_files>::iterator it;
    553 	list<profile_sample_files>::iterator const end = files.end();
    554 	for (it = files.begin(); it != end; ++it) {
    555 		if (!it->sample_filename.empty()) {
    556 			parsed_filename psample_filename =
    557 			  parse_filename(it->sample_filename, extra);
    558 			if (psample_filename.lib_image == parsed.lib_image &&
    559 			    psample_filename.image == parsed.image &&
    560 			    psample_filename.profile_spec_equal(parsed))
    561 				return *it;
    562 		}
    563 
    564 		list<string>::const_iterator cit;
    565 		list<string>::const_iterator const cend = it->cg_files.end();
    566 		for (cit = it->cg_files.begin(); cit != cend; ++cit) {
    567 			parsed_filename pcg_filename =
    568 				parse_filename(*cit, extra);
    569 			if (pcg_filename.lib_image == parsed.lib_image &&
    570 			    pcg_filename.image == parsed.image &&
    571 			    pcg_filename.profile_spec_equal(parsed))
    572 				return *it;
    573 		}
    574 	}
    575 
    576 	// not found, create a new one
    577 	files.push_back(profile_sample_files());
    578 	return files.back();
    579 }
    580 
    581 
    582 /**
    583  * Add a profile to particular profile set. If the new profile is
    584  * a dependent image, it gets added to the dep list, or just placed
    585  * on the normal list of profiles otherwise.
    586  */
    587 void
    588 add_to_profile_set(profile_set & set, parsed_filename const & parsed,
    589 		   bool merge_by_lib, extra_images const & extra)
    590 {
    591 	if (parsed.image == parsed.lib_image && !merge_by_lib) {
    592 		profile_sample_files & sample_files =
    593 			find_profile_sample_files(set.files, parsed, extra);
    594 		add_to_profile_sample_files(sample_files, parsed);
    595 		return;
    596 	}
    597 
    598 	list<profile_dep_set>::iterator it = set.deps.begin();
    599 	list<profile_dep_set>::iterator const end = set.deps.end();
    600 
    601 	for (; it != end; ++it) {
    602 		if (it->lib_image == parsed.lib_image && !merge_by_lib &&
    603 				parsed.jit_dumpfile_exists == false) {
    604 			profile_sample_files & sample_files =
    605 				find_profile_sample_files(it->files, parsed,
    606 							  extra);
    607 			add_to_profile_sample_files(sample_files, parsed);
    608 			return;
    609 		}
    610 	}
    611 
    612 	profile_dep_set depset;
    613 	depset.lib_image = parsed.lib_image;
    614 	profile_sample_files & sample_files =
    615 		find_profile_sample_files(depset.files, parsed, extra);
    616 	add_to_profile_sample_files(sample_files, parsed);
    617 	set.deps.push_back(depset);
    618 }
    619 
    620 
    621 /**
    622  * Add a profile to a particular equivalence class. The previous matching
    623  * will have ensured the profile "fits", so now it's just a matter of
    624  * finding which sample file list it needs to go on.
    625  */
    626 void add_profile(profile_class & pclass, parsed_filename const & parsed,
    627 		 bool merge_by_lib, extra_images const & extra)
    628 {
    629 	list<profile_set>::iterator it = pclass.profiles.begin();
    630 	list<profile_set>::iterator const end = pclass.profiles.end();
    631 
    632 	for (; it != end; ++it) {
    633 		if (it->image == parsed.image) {
    634 			add_to_profile_set(*it, parsed, merge_by_lib, extra);
    635 			return;
    636 		}
    637 	}
    638 
    639 	profile_set set;
    640 	set.image = parsed.image;
    641 	add_to_profile_set(set, parsed, merge_by_lib, extra);
    642 	pclass.profiles.push_back(set);
    643 }
    644 
    645 }  // anon namespace
    646 
    647 
    648 profile_classes const
    649 arrange_profiles(list<string> const & files, merge_option const & merge_by,
    650 		 extra_images const & extra)
    651 {
    652 	set<profile_class> temp_classes;
    653 
    654 	list<string>::const_iterator it = files.begin();
    655 	list<string>::const_iterator const end = files.end();
    656 
    657 	for (; it != end; ++it) {
    658 		parsed_filename parsed = parse_filename(*it, extra);
    659 
    660 		if (parsed.lib_image.empty())
    661 			parsed.lib_image = parsed.image;
    662 
    663 		// This simplifies the add of the profile later,
    664 		// if we're lib-merging, then the app_image cannot
    665 		// matter. After this, any non-dependent has
    666 		// image == lib_image
    667 		if (merge_by.lib)
    668 			parsed.image = parsed.lib_image;
    669 
    670 		profile_class & pclass =
    671 			find_class(temp_classes, parsed, merge_by);
    672 		add_profile(pclass, parsed, merge_by.lib, extra);
    673 	}
    674 
    675 	profile_classes classes;
    676 	copy(temp_classes.begin(), temp_classes.end(),
    677 	     back_inserter(classes.v));
    678 
    679 	if (classes.v.empty())
    680 		return classes;
    681 
    682 	// sort by template for nicely ordered columns
    683 	stable_sort(classes.v.begin(), classes.v.end());
    684 
    685 	if (want_xml)
    686 		identify_xml_classes(classes, merge_by);
    687 	else
    688 		identify_classes(classes, merge_by);
    689 
    690 	classes.extra_found_images = extra;
    691 
    692 	return classes;
    693 }
    694 
    695 
    696 ostream & operator<<(ostream & out, profile_sample_files const & sample_files)
    697 {
    698 	out << "sample_filename: " << sample_files.sample_filename << endl;
    699 	out << "callgraph filenames:\n";
    700 	copy(sample_files.cg_files.begin(), sample_files.cg_files.end(),
    701 	     ostream_iterator<string>(out, "\n"));
    702 	return out;
    703 }
    704 
    705 ostream & operator<<(ostream & out, profile_dep_set const & pdep_set)
    706 {
    707 	out << "lib_image: " << pdep_set.lib_image << endl;
    708 
    709 	list<profile_sample_files>::const_iterator it;
    710 	list<profile_sample_files>::const_iterator const end =
    711 		pdep_set.files.end();
    712 	size_t i = 0;
    713 	for (it = pdep_set.files.begin(); it != end; ++it)
    714 		out << "profile_sample_files #" << i++ << ":\n" << *it;
    715 
    716 	return out;
    717 }
    718 
    719 ostream & operator<<(ostream & out, profile_set const & pset)
    720 {
    721 	out << "image: " << pset.image << endl;
    722 
    723 	list<profile_sample_files>::const_iterator it;
    724 	list<profile_sample_files>::const_iterator const end =
    725 		pset.files.end();
    726 	size_t i = 0;
    727 	for (it = pset.files.begin(); it != end; ++it)
    728 		out << "profile_sample_files #" << i++ << ":\n" << *it;
    729 
    730 	list<profile_dep_set>::const_iterator cit;
    731 	list<profile_dep_set>::const_iterator const cend = pset.deps.end();
    732 	i = 0;
    733 	for (cit = pset.deps.begin(); cit != cend; ++cit)
    734 		out << "profile_dep_set #" << i++ << ":\n" << *cit;
    735 
    736 	return out;
    737 }
    738 
    739 ostream & operator<<(ostream & out, profile_template const & ptemplate)
    740 {
    741 	out << "event: " << ptemplate.event << endl
    742 	    << "count: " << ptemplate.count << endl
    743 	    << "unitmask: " << ptemplate.unitmask << endl
    744 	    << "tgid: " << ptemplate.tgid << endl
    745 	    << "tid: " << ptemplate.tid << endl
    746 	    << "cpu: " << ptemplate.cpu << endl;
    747 	return out;
    748 }
    749 
    750 ostream & operator<<(ostream & out, profile_class const & pclass)
    751 {
    752 	out << "name: " << pclass.name << endl
    753 	    << "longname: " << pclass.longname << endl
    754 	    << "ptemplate:\n" << pclass.ptemplate;
    755 
    756 	size_t i = 0;
    757 	list<profile_set>::const_iterator it;
    758 	list<profile_set>::const_iterator const end = pclass.profiles.end();
    759 	for (it = pclass.profiles.begin(); it != end; ++it)
    760 		out << "profiles_set #" << i++ << ":\n" << *it;
    761 
    762 	return out;
    763 }
    764 
    765 ostream & operator<<(ostream & out, profile_classes const & pclasses)
    766 {
    767 	out << "event: " << pclasses.event << endl
    768 	    << "cpuinfo: " << pclasses.cpuinfo << endl;
    769 
    770 	for (size_t i = 0; i < pclasses.v.size(); ++i)
    771 		out << "class #" << i << ":\n" << pclasses.v[i];
    772 
    773 	return out;
    774 }
    775 
    776 
    777 namespace {
    778 
    779 /// add the files to group of image sets
    780 void add_to_group(image_group_set & group, string const & app_image,
    781                   list<profile_sample_files> const & files)
    782 {
    783 	image_set set;
    784 	set.app_image = app_image;
    785 	set.files = files;
    786 	group.push_back(set);
    787 }
    788 
    789 
    790 typedef map<string, inverted_profile> app_map_t;
    791 
    792 
    793 inverted_profile &
    794 get_iprofile(app_map_t & app_map, string const & image, size_t nr_classes)
    795 {
    796 	app_map_t::iterator ait = app_map.find(image);
    797 	if (ait != app_map.end())
    798 		return ait->second;
    799 
    800 	inverted_profile ip;
    801 	ip.image = image;
    802 	ip.groups.resize(nr_classes);
    803 	app_map[image] = ip;
    804 	return app_map[image];
    805 }
    806 
    807 
    808 /// Pull out all the images, removing any we can't access.
    809 void
    810 verify_and_fill(app_map_t & app_map, list<inverted_profile> & plist,
    811 		extra_images const & extra)
    812 {
    813 	app_map_t::iterator it = app_map.begin();
    814 	app_map_t::iterator const end = app_map.end();
    815 
    816 	for (; it != end; ++it) {
    817 		plist.push_back(it->second);
    818 		inverted_profile & ip = plist.back();
    819 		extra.find_image_path(ip.image, ip.error, false);
    820 	}
    821 }
    822 
    823 } // anon namespace
    824 
    825 
    826 list<inverted_profile> const
    827 invert_profiles(profile_classes const & classes)
    828 {
    829 	app_map_t app_map;
    830 
    831 	size_t nr_classes = classes.v.size();
    832 
    833 	for (size_t i = 0; i < nr_classes; ++i) {
    834 		list<profile_set>::const_iterator pit
    835 			= classes.v[i].profiles.begin();
    836 		list<profile_set>::const_iterator pend
    837 			= classes.v[i].profiles.end();
    838 
    839 		for (; pit != pend; ++pit) {
    840 			// files can be empty if samples for a lib image
    841 			// but none for the main image. Deal with it here
    842 			// rather than later.
    843 			if (pit->files.size()) {
    844 				inverted_profile & ip = get_iprofile(app_map,
    845 					pit->image, nr_classes);
    846 				add_to_group(ip.groups[i], pit->image, pit->files);
    847 			}
    848 
    849 			list<profile_dep_set>::const_iterator dit
    850 				= pit->deps.begin();
    851 			list<profile_dep_set>::const_iterator const dend
    852 				= pit->deps.end();
    853 
    854 			for (;  dit != dend; ++dit) {
    855 				inverted_profile & ip = get_iprofile(app_map,
    856 					dit->lib_image, nr_classes);
    857 				add_to_group(ip.groups[i], pit->image,
    858 				             dit->files);
    859 			}
    860 		}
    861 	}
    862 
    863 	list<inverted_profile> inverted_list;
    864 
    865 	verify_and_fill(app_map, inverted_list, classes.extra_found_images);
    866 
    867 	return inverted_list;
    868 }
    869