• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file opannotate.cpp
3  * Implement opannotate utility
4  *
5  * @remark Copyright 2003 OProfile authors
6  * @remark Read the file COPYING
7  *
8  * @author John Levon
9  * @author Philippe Elie
10  */
11 
12 #include <iostream>
13 #include <sstream>
14 #include <algorithm>
15 #include <iomanip>
16 #include <fstream>
17 #include <utility>
18 
19 #include "op_exception.h"
20 #include "op_header.h"
21 #include "profile.h"
22 #include "populate.h"
23 #include "op_sample_file.h"
24 #include "cverb.h"
25 #include "string_manip.h"
26 #include "demangle_symbol.h"
27 #include "child_reader.h"
28 #include "op_file.h"
29 #include "file_manip.h"
30 #include "arrange_profiles.h"
31 #include "opannotate_options.h"
32 #include "profile_container.h"
33 #include "symbol_sort.h"
34 #include "image_errors.h"
35 
36 using namespace std;
37 using namespace options;
38 
39 namespace {
40 
41 size_t nr_events;
42 
43 scoped_ptr<profile_container> samples;
44 
45 /// how opannotate was invoked
46 string cmdline;
47 
48 /// empty annotation fill string
49 string annotation_fill;
50 
51 /// string used as start / end comment to annotate source
52 string const begin_comment("/* ");
53 string const in_comment(" * ");
54 string const end_comment(" */");
55 
56 /// field width for the sample count
57 unsigned int const count_width = 6;
58 
get_annotation_fill()59 string get_annotation_fill()
60 {
61 	string str;
62 
63 	for (size_t i = 0; i < nr_events; ++i) {
64 		str += string(count_width, ' ') + ' ';
65 		str += string(percent_width, ' ');
66 	}
67 
68 	for (size_t i = 1; i < nr_events; ++i)
69 		str += "  ";
70 
71 	str += " :";
72 	return str;
73 }
74 
75 
find_symbol(string const & image_name,string const & str_vma)76 symbol_entry const * find_symbol(string const & image_name,
77 				 string const & str_vma)
78 {
79 	// do not use the bfd equivalent:
80 	//  - it does not skip space at begin
81 	//  - we does not need cross architecture compile so the native
82 	// strtoull must work, assuming unsigned long long can contain a vma
83 	// and on 32/64 bits box bfd_vma is 64 bits
84 	bfd_vma vma = strtoull(str_vma.c_str(), NULL, 16);
85 
86 	return samples->find_symbol(image_name, vma);
87 }
88 
89 
output_info(ostream & out)90 void output_info(ostream & out)
91 {
92 	out << begin_comment << '\n';
93 
94 	out << in_comment << "Command line: " << cmdline << '\n'
95 	    << in_comment << '\n';
96 
97 	out << in_comment << "Interpretation of command line:" << '\n';
98 
99 	if (!assembly) {
100 		out << in_comment
101 		    << "Output annotated source file with samples" << '\n';
102 
103 		if (options::threshold != 0) {
104 			out << in_comment
105 			    << "Output files where samples count reach "
106 			    << options::threshold << "% of the samples\n";
107 		} else {
108 			out << in_comment << "Output all files" << '\n';
109 		}
110 	} else {
111 		out << in_comment
112 		    << "Output annotated assembly listing with samples"
113 		    << '\n';
114 
115 		if (!objdump_params.empty()) {
116 			out << in_comment << "Passing the following "
117 				"additional arguments to objdump ; \"";
118 			for (size_t i = 0 ; i < objdump_params.size() ; ++i)
119 				out << objdump_params[i] << " ";
120 			out << "\"" << '\n';
121 		}
122 	}
123 
124 	out << in_comment << '\n';
125 
126 	out << in_comment << classes.cpuinfo << endl;
127 	if (!classes.event.empty())
128 		out << in_comment << classes.event << endl;
129 
130 	for (size_t i = 0; i < classes.v.size(); ++i)
131 		out << in_comment << classes.v[i].longname << endl;
132 
133 	out << end_comment << '\n';
134 }
135 
136 
count_str(count_array_t const & count,count_array_t const & total)137 string count_str(count_array_t const & count,
138 		   count_array_t const & total)
139 {
140 	ostringstream os;
141 	for (size_t i = 0; i < nr_events; ++i) {
142 		os << setw(count_width) << count[i] << ' ';
143 
144 		os << format_percent(op_ratio(count[i], total[i]) * 100.0,
145 				    percent_int_width, percent_fract_width);
146 	}
147 	return os.str();
148 }
149 
150 
151 /// NOTE: This function annotates a list<string> containing output from objdump.
152 /// It uses a list iterator, and a sample_container iterator which iterates
153 /// from the beginning to the end, and compare sample address
154 /// against the instruction address on the asm line.
155 ///
156 /// There are 2 cases of annotation:
157 /// 1. If sample address matches current line address, annotate the current line.
158 /// 2. If (previous line address < sample address < current line address),
159 ///    then we annotate previous line.  This case happens when sample address
160 ///    is not aligned with the instruction address, which is seen when profile
161 ///    using the instruction fetch mode of AMD Instruction-Based Sampling (IBS).
162 ///
asm_list_annotation(symbol_entry const * last_symbol,bfd_vma last_symbol_vma,list<string>::iterator sit,sample_container::samples_iterator & samp_it,list<string> & asm_lines)163 int asm_list_annotation(symbol_entry const * last_symbol,
164 			bfd_vma last_symbol_vma,
165 			list<string>::iterator sit,
166 			sample_container::samples_iterator & samp_it,
167 			list<string> & asm_lines)
168 {
169 	int ret = 0;
170 
171 	sample_entry const * sample = NULL;
172 
173 	if (samp_it != samples->end())
174 		sample = &samp_it->second;
175 
176 	// do not use the bfd equivalent:
177 	//  - it does not skip space at begin
178 	//  - we does not need cross architecture compile so the native
179 	// strtoull must work, assuming unsigned long long can contain a vma
180 	// and on 32/64 bits box bfd_vma is 64 bits
181 	// gcc 2.91.66 workaround
182 	bfd_vma vma = strtoull((*sit).c_str(), NULL, 16);
183 
184 	if (sample
185 	    && ((sample->vma < last_symbol_vma) || (sample->vma > vma))) {
186 		*sit = annotation_fill + *sit;
187 	} else if (sample && sample->vma == vma) {
188 		// Case 1 : Sample address match current line address.
189 		string str = count_str(sample->counts, samples->samples_count());
190 
191 		// For each events
192 		for (size_t i = 1; i < nr_events; ++i)
193 			str += "  ";
194 
195 		*sit = str + " :" + *sit;
196 		if (samp_it != samples->end())
197 			++samp_it;
198 
199 	} else	if (sample && sample->vma < vma) {
200 		// Case 2 : vma of the current line is greater than vma of the sample
201 
202 		// Get the string of previous assembly line
203 		list<string>::iterator sit_prev = sit;
204 		string prev_line, prev_vma_str;
205 		string::size_type loc1 = string::npos, loc2 = string::npos;
206 		while (sit_prev != asm_lines.begin()) {
207 			--sit_prev;
208 			prev_line = *sit_prev;
209 
210 			loc1 = prev_line.find(":", 0);
211 			if (loc1 != string::npos) {
212 				loc2 = prev_line.find(":", loc1+1);
213 				if (loc2 != string::npos) {
214 					prev_vma_str = prev_line.substr(loc1+1, loc2);
215 					break;
216 				}
217 			}
218 		}
219 
220 		bfd_vma prev_vma = strtoull(prev_vma_str.c_str(), NULL, 16);
221 
222 		// Need to check if prev_vma < sample->vma
223 		if (prev_vma != 0 && prev_vma < sample->vma) {
224 			string str;
225 
226 			// Get sample for previous line.
227 			sample_entry * prev_sample = (sample_entry *)samples->
228 							find_sample(last_symbol, prev_vma);
229 			if (prev_sample) {
230 				// Aggregate sample with previous line if it already has samples
231 				prev_sample->counts += sample->counts;
232 				str = count_str(prev_sample->counts, samples->samples_count());
233 			} else {
234 				str = count_str(sample->counts, samples->samples_count());
235 			}
236 
237 			// For each events
238 			for (size_t i = 1; i < nr_events; ++i)
239 				str += "  ";
240 
241 			*sit_prev = str + " :" + prev_line.substr(loc1+1);
242 			if (samp_it != samples->end())
243 				++samp_it;
244 			ret = -1;
245 		} else {
246 			// Failed to annotate the previous line. Skip sample.
247 			*sit = annotation_fill + *sit;
248 			if (samp_it != samples->end())
249 				++samp_it;
250 		}
251 	} else {
252 		// In case sample is NULL
253 		*sit = annotation_fill + *sit;
254 	}
255 
256 	return ret;
257 }
258 
259 
symbol_annotation(symbol_entry const * symbol)260 string symbol_annotation(symbol_entry const * symbol)
261 {
262 	if (!symbol)
263 		return string();
264 
265 	string annot = count_str(symbol->sample.counts,
266 	                         samples->samples_count());
267 
268 	string const & symname = symbol_names.demangle(symbol->name);
269 
270 	string str = " ";
271 	str += begin_comment + symname + " total: ";
272 	str += count_str(symbol->sample.counts, samples->samples_count());
273 	str += end_comment;
274 	return str;
275 }
276 
277 
278 /// return true if  this line contains a symbol name in objdump formatting
279 /// symbol are on the form 08030434 <symbol_name>:  we need to be strict
280 /// here to avoid any interpretation of a source line as a symbol line
is_symbol_line(string const & str,string::size_type pos)281 bool is_symbol_line(string const & str, string::size_type pos)
282 {
283 	if (str[pos] != ' ' || str[pos + 1] != '<')
284 		return false;
285 
286 	return str[str.length() - 1] == ':';
287 }
288 
289 
annotate_objdump_str_list(string const & app_name,symbol_collection const & symbols,list<string> & asm_lines)290 void annotate_objdump_str_list(string const & app_name,
291 			       symbol_collection const & symbols,
292 			       list<string> & asm_lines)
293 {
294 	symbol_entry const * last_symbol = 0;
295 	bfd_vma last_symbol_vma = 0;
296 	int ret = 0;
297 
298 	// to filter output of symbols (filter based on command line options)
299 	bool do_output = true;
300 
301 	// We simultaneously walk the two structures (list and sample_container)
302 	// which are sorted by address. and do address comparision.
303 	list<string>::iterator sit  = asm_lines.begin();
304 	list<string>::iterator send = asm_lines.end();
305 	sample_container::samples_iterator samp_it = samples->begin();
306 
307 	for (; sit != send; (!ret? sit++: sit)) {
308 		// output of objdump is a human readable form and can contain some
309 		// ambiguity so this code is dirty. It is also optimized a little bit
310 		// so it is difficult to simplify it without breaking something ...
311 
312 		// line of interest are: "[:space:]*[:xdigit:]?[ :]", the last char of
313 		// this regexp dis-ambiguate between a symbol line and an asm line. If
314 		// source contain line of this form an ambiguity occur and we rely on
315 		// the robustness of this code.
316 		string str = *sit;
317 		size_t pos = 0;
318 		while (pos < str.length() && isspace(str[pos]))
319 			++pos;
320 
321 		if (pos == str.length() || !isxdigit(str[pos])) {
322 			if (do_output) {
323 				*sit = annotation_fill + str;
324 				continue;
325 			}
326 		}
327 
328 		while (pos < str.length() && isxdigit(str[pos]))
329 			++pos;
330 
331 		if (pos == str.length() || (!isspace(str[pos]) && str[pos] != ':')) {
332 			if (do_output) {
333 				*sit = annotation_fill + str;
334 				continue;
335 			}
336 		}
337 
338 		if (is_symbol_line(str, pos)) {
339 
340 			last_symbol = find_symbol(app_name, str);
341 			last_symbol_vma = strtoull(str.c_str(), NULL, 16);
342 
343 			// ! complexity: linear in number of symbol must use sorted
344 			// by address vector and lower_bound ?
345 			// Note this use a pointer comparison. It work because symbols
346 			// pointer are unique
347 			if (find(symbols.begin(), symbols.end(), last_symbol)
348 			    != symbols.end())
349 				do_output = true;
350 			else
351 				do_output = false;
352 
353 			if (do_output) {
354 				*sit += symbol_annotation(last_symbol);
355 
356 				// Realign the sample iterator to
357 				// the beginning of this symbols
358 				samp_it = samples->begin(last_symbol);
359 			}
360 		} else {
361 			// not a symbol, probably an asm line.
362 			if (do_output)
363 				ret = asm_list_annotation(last_symbol,
364 							  last_symbol_vma,
365 							  sit, samp_it,
366 							  asm_lines);
367 		}
368 
369 		if (!do_output)
370 			*sit = "";
371 	}
372 }
373 
374 
output_objdump_str_list(symbol_collection const & symbols,string const & app_name,list<string> & asm_lines)375 void output_objdump_str_list(symbol_collection const & symbols,
376 			string const & app_name,
377 			list<string> & asm_lines)
378 {
379 
380 	annotate_objdump_str_list(app_name, symbols, asm_lines);
381 
382 	// Printing objdump output to stdout
383 	list<string>::iterator sit  = asm_lines.begin();
384 	list<string>::iterator send = asm_lines.end();
385 	sit = asm_lines.begin();
386 	for (; sit != send; ++sit) {
387 		string str = *sit;
388 		if (str.length() != 0)
389 			cout << str << '\n';
390 	}
391 }
392 
393 
do_one_output_objdump(symbol_collection const & symbols,string const & image_name,string const & app_name,bfd_vma start,bfd_vma end)394 void do_one_output_objdump(symbol_collection const & symbols,
395 			   string const & image_name, string const & app_name,
396 			   bfd_vma start, bfd_vma end)
397 {
398 	vector<string> args;
399 	list<string> asm_lines;
400 
401 	args.push_back("-d");
402 	args.push_back("--no-show-raw-insn");
403 	if (source)
404 		args.push_back("-S");
405 
406 	if (start || end != ~(bfd_vma)0) {
407 		ostringstream arg1, arg2;
408 		arg1 << "--start-address=" << start;
409 		arg2 << "--stop-address=" << end;
410 		args.push_back(arg1.str());
411 		args.push_back(arg2.str());
412 	}
413 
414 	if (!objdump_params.empty()) {
415 		for (size_t i = 0 ; i < objdump_params.size() ; ++i)
416 			args.push_back(objdump_params[i]);
417 	}
418 
419 	args.push_back(image_name);
420 #if defined(ANDROID_TARGET_ARM)
421 	child_reader reader("arm-eabi-objdump", args);
422 #elif defined(ANDROID_TARGET_MIPS)
423 	child_reader reader("mipsel-linux-android-objdump", args);
424 #else
425 	child_reader reader("objdump", args);
426 #endif
427 	if (reader.error()) {
428 		cerr << "An error occur during the execution of objdump:\n\n";
429 		cerr << reader.error_str() << endl;
430 		return;
431 	}
432 
433 	// Read each output line from objdump and store in a list.
434 	string str;
435 	while (reader.getline(str))
436 		asm_lines.push_back(str);
437 
438 	output_objdump_str_list(symbols, app_name, asm_lines);
439 
440 	// objdump always returns SUCCESS so we must rely on the stderr state
441 	// of objdump. If objdump error message is cryptic our own error
442 	// message will be probably also cryptic
443 	ostringstream std_err;
444 	ostringstream std_out;
445 	reader.get_data(std_out, std_err);
446 	if (std_err.str().length()) {
447 		cerr << "An error occur during the execution of objdump:\n\n";
448 		cerr << std_err.str() << endl;
449 		return ;
450 	}
451 
452 	// force error code to be acquired
453 	reader.terminate_process();
454 
455 	// required because if objdump stop by signal all above things suceeed
456 	// (signal error message are not output through stdout/stderr)
457 	if (reader.error()) {
458 		cerr << "An error occur during the execution of objdump:\n\n";
459 		cerr << reader.error_str() << endl;
460 		return;
461 	}
462 }
463 
464 
output_objdump_asm(symbol_collection const & symbols,string const & app_name)465 void output_objdump_asm(symbol_collection const & symbols,
466 			string const & app_name)
467 {
468 	image_error error;
469 	string image =
470 		classes.extra_found_images.find_image_path(app_name, error,
471 							   true);
472 
473 	// this is only an optimisation, we can either filter output by
474 	// directly calling objdump and rely on the symbol filtering or
475 	// we can call objdump with the right parameter to just disassemble
476 	// the needed part. This is a real win only when calling objdump
477 	// a medium number of times, I dunno if the used threshold is optimal
478 	// but it is a conservative value.
479 	size_t const max_objdump_exec = 50;
480 	if (symbols.size() <= max_objdump_exec || error != image_ok) {
481 		symbol_collection::const_iterator cit = symbols.begin();
482 		symbol_collection::const_iterator end = symbols.end();
483 		for (; cit != end; ++cit) {
484 			bfd_vma start = (*cit)->sample.vma;
485 			bfd_vma end  = start + (*cit)->size;
486 			do_one_output_objdump(symbols, image, app_name,
487 					      start, end);
488 		}
489 	} else {
490 		do_one_output_objdump(symbols, image,
491 				      app_name, 0, ~bfd_vma(0));
492 	}
493 }
494 
495 
output_asm(string const & app_name)496 bool output_asm(string const & app_name)
497 {
498 	profile_container::symbol_choice choice;
499 	choice.threshold = options::threshold;
500 	choice.image_name = app_name;
501 	choice.match_image = true;
502 	symbol_collection symbols = samples->select_symbols(choice);
503 
504 	if (!symbols.empty()) {
505 		sort_options options;
506 		options.add_sort_option(sort_options::sample);
507 		options.sort(symbols, false, false);
508 
509 		output_info(cout);
510 
511 		output_objdump_asm(symbols, app_name);
512 
513 		return true;
514 	}
515 
516 	return false;
517 }
518 
519 
source_line_annotation(debug_name_id filename,size_t linenr)520 string const source_line_annotation(debug_name_id filename, size_t linenr)
521 {
522 	string str;
523 
524 	count_array_t counts = samples->samples_count(filename, linenr);
525 	if (!counts.zero()) {
526 		str += count_str(counts, samples->samples_count());
527 		for (size_t i = 1; i < nr_events; ++i)
528 			str += "  ";
529 		str += " :";
530 	} else {
531 		str = annotation_fill;
532 	}
533 
534 	return str;
535 }
536 
537 
source_symbol_annotation(debug_name_id filename,size_t linenr)538 string source_symbol_annotation(debug_name_id filename, size_t linenr)
539 {
540 	symbol_collection const symbols = samples->find_symbol(filename, linenr);
541 
542 	if (symbols.empty())
543 		return string();
544 
545 	string str = " " + begin_comment;
546 
547 	count_array_t counts;
548 	for (size_t i = 0; i < symbols.size(); ++i) {
549 		str += symbol_names.demangle(symbols[i]->name);
550 		if (symbols.size() == 1)
551 			str += " total: ";
552 		else
553 			str += " ";
554 		str += count_str(symbols[i]->sample.counts,
555 		          samples->samples_count());
556 		if (symbols.size() != 1)
557 			str += ", ";
558 
559 		counts += symbols[i]->sample.counts;
560 	}
561 
562 	if (symbols.size() > 1)
563 		str += "total: " + count_str(counts, samples->samples_count());
564 	str += end_comment;
565 
566 	return str;
567 }
568 
569 
output_per_file_info(ostream & out,debug_name_id filename,count_array_t const & total_file_count)570 void output_per_file_info(ostream & out, debug_name_id filename,
571 			  count_array_t const & total_file_count)
572 {
573 	out << begin_comment << '\n'
574 	     << in_comment << "Total samples for file : "
575 	     << '"' << debug_names.name(filename) << '"'
576 	     << '\n';
577 	out << in_comment << '\n' << in_comment
578 	    << count_str(total_file_count, samples->samples_count())
579 	    << '\n';
580 	out << end_comment << '\n' << '\n';
581 }
582 
583 
line0_info(debug_name_id filename)584 string const line0_info(debug_name_id filename)
585 {
586 	string annotation = source_line_annotation(filename, 0);
587 	if (trim(annotation, " \t:").empty())
588 		return string();
589 
590 	string str = "<credited to line zero> ";
591 	str += annotation;
592 	return str;
593 }
594 
595 
do_output_one_file(ostream & out,istream & in,debug_name_id filename,bool header)596 void do_output_one_file(ostream & out, istream & in, debug_name_id filename,
597                         bool header)
598 {
599 	count_array_t count = samples->samples_count(filename);
600 
601 	if (header) {
602 		output_per_file_info(out, filename, count);
603 		out << line0_info(filename) << '\n';
604 	}
605 
606 
607 	if (in) {
608 		string str;
609 
610 		for (size_t linenr = 1 ; getline(in, str) ; ++linenr) {
611 			out << source_line_annotation(filename, linenr) << str
612 			    << source_symbol_annotation(filename, linenr)
613 			    << '\n';
614 		}
615 
616 	} else {
617 		// source is not available but we can at least output all the
618 		// symbols belonging to this file. This make more visible the
619 		// problem of having less samples for a given file than the
620 		// sum of all symbols samples for this file due to inlining
621 		symbol_collection const symbols = samples->select_symbols(filename);
622 		for (size_t i = 0; i < symbols.size(); ++i)
623 			out << symbol_annotation(symbols[i]) << endl;
624 	}
625 
626 	if (!header) {
627 		output_per_file_info(out, filename, count);
628 		out << line0_info(filename) << '\n';
629 	}
630 }
631 
632 
output_one_file(istream & in,debug_name_id filename,string const & source)633 void output_one_file(istream & in, debug_name_id filename,
634                      string const & source)
635 {
636 	if (output_dir.empty()) {
637 		do_output_one_file(cout, in, filename, true);
638 		return;
639 	}
640 
641 	string const out_file = op_realpath(output_dir + source);
642 
643 	/* Just because you're paranoid doesn't mean they're not out to
644 	 * get you ...
645 	 *
646 	 * This is just a lame final safety check. If we found the
647 	 * source, then "source" should be canonical already, and
648 	 * can't escape from the output dir. We can't use op_realpath()
649 	 * alone as that needs the file to exist already.
650 	 *
651 	 * Let's not complain again if we couldn't find the file anyway.
652 	 */
653 	if (out_file.find("/../") != string::npos) {
654 		if (in) {
655 			cerr << "refusing to create non-canonical filename "
656 			     << out_file  << endl;
657 		}
658 		return;
659 	} else if (!is_prefix(out_file, output_dir)) {
660 		if (in) {
661 			cerr << "refusing to create file " << out_file
662 			     << " outside of output directory " << output_dir
663 			     << endl;
664 		}
665 		return;
666 	}
667 
668 	if (is_files_identical(out_file, source)) {
669 		cerr << "input and output files are identical: "
670 		     << out_file << endl;
671 		return;
672 	}
673 
674 	if (create_path(out_file.c_str())) {
675 		cerr << "unable to create file: "
676 		     << '"' << op_dirname(out_file) << '"' << endl;
677 		return;
678 	}
679 
680 	ofstream out(out_file.c_str());
681 	if (!out) {
682 		cerr << "unable to open output file "
683 		     << '"' << out_file << '"' << endl;
684 	} else {
685 		do_output_one_file(out, in, filename, false);
686 		output_info(out);
687 	}
688 }
689 
690 
691 /* Locate a source file from debug info, which may be relative */
locate_source_file(debug_name_id filename_id)692 string const locate_source_file(debug_name_id filename_id)
693 {
694 	string const origfile = debug_names.name(filename_id);
695 	string file = origfile;
696 
697 	if (file.empty())
698 		return file;
699 
700 	/* Allow absolute paths to be relocated to a different directory */
701 	if (file[0] == '/') {
702 		vector<string>::const_iterator cit = base_dirs.begin();
703 		vector<string>::const_iterator end = base_dirs.end();
704 		for (; cit != end; ++cit) {
705 			string path = op_realpath(*cit);
706 
707 			if (is_prefix(file, path)) {
708 				file = file.substr(path.length());
709 				break;
710 			}
711 		}
712 	}
713 
714 	vector<string>::const_iterator cit = search_dirs.begin();
715 	vector<string>::const_iterator end = search_dirs.end();
716 
717 	for (; cit != end; ++cit) {
718 		string const absfile = op_realpath(*cit + "/" + file);
719 
720 		if (op_file_readable(absfile))
721 			return absfile;
722 	}
723 
724 	/* We didn't find a relocated absolute file, or a relative file,
725 	 * assume the original is correct, accounting for the
726 	 * possibility it's relative the cwd
727 	 */
728 	return op_realpath(origfile);
729 }
730 
731 
output_source(path_filter const & filter)732 void output_source(path_filter const & filter)
733 {
734 	bool const separate_file = !output_dir.empty();
735 
736 	if (!separate_file)
737 		output_info(cout);
738 
739 	vector<debug_name_id> filenames =
740 		samples->select_filename(options::threshold);
741 
742 	for (size_t i = 0 ; i < filenames.size() ; ++i) {
743 		string const & source = locate_source_file(filenames[i]);
744 
745 		if (!filter.match(source))
746 			continue;
747 
748 		ifstream in(source.c_str());
749 
750 		// it is common to have empty filename due to the lack
751 		// of debug info (eg _init function) so warn only
752 		// if the filename is non empty. The case: no debug
753 		// info at all has already been checked.
754 		if (!in && source.length()) {
755 			cerr << "opannotate (warning): unable to open for "
756 			     "reading: " << source << endl;
757 		}
758 
759 		if (source.length())
760 			output_one_file(in, filenames[i], source);
761 	}
762 }
763 
764 
annotate_source(list<string> const & images)765 bool annotate_source(list<string> const & images)
766 {
767 	annotation_fill = get_annotation_fill();
768 
769 	if (!output_dir.empty()) {
770 
771 		if (create_path(output_dir.c_str())) {
772 			cerr << "unable to create " << output_dir
773 			     << " directory: " << endl;
774 			return false;
775 		}
776 
777 		// Make sure we have an absolute path.
778 		output_dir = op_realpath(output_dir);
779 		if (output_dir.length() &&
780 		    output_dir[output_dir.length() - 1] != '/')
781 			output_dir += '/';
782 
783 		/* Don't let the user stomp on their sources */
784 		if (output_dir == "/") {
785 			cerr << "Output path of / would over-write the "
786 				"source files" << endl;
787 			return false;
788 		}
789 	}
790 
791 	if (assembly) {
792 		bool some_output = false;
793 
794 		list<string>::const_iterator it = images.begin();
795 		list<string>::const_iterator const end = images.end();
796 
797 		for (; it != end; ++it) {
798 			if (output_asm(*it))
799 				some_output = true;
800 		}
801 
802 		if (!some_output) {
803 			// It's the only case we must care since we know the
804 			// selected image set is not empty
805 			cerr << "selected image set doesn't contain any of "
806 			     << "the selected symbol\n";
807 		}
808 	} else {
809 		output_source(file_filter);
810 	}
811 
812 	return true;
813 }
814 
815 
opannotate(options::spec const & spec)816 int opannotate(options::spec const & spec)
817 {
818 	handle_options(spec);
819 
820 	nr_events = classes.v.size();
821 
822 	samples.reset(new profile_container(true, true,
823 					    classes.extra_found_images));
824 
825 	list<string> images;
826 
827 	list<inverted_profile> iprofiles = invert_profiles(classes);
828 
829 	report_image_errors(iprofiles, classes.extra_found_images);
830 
831 	list<inverted_profile>::iterator it = iprofiles.begin();
832 	list<inverted_profile>::iterator const end = iprofiles.end();
833 
834 	bool debug_info = false;
835 	for (; it != end; ++it) {
836 		bool tmp = false;
837 		populate_for_image(*samples, *it,
838 				   options::symbol_filter, &tmp);
839 		images.push_back(it->image);
840 		if (tmp)
841 			debug_info = true;
842 	}
843 
844 	if (!debug_info && !options::assembly) {
845 		cerr << "opannotate (warning): no debug information available for binary "
846 		     << it->image << ", and --assembly not requested\n";
847 	}
848 
849 	annotate_source(images);
850 
851 	return 0;
852 }
853 
854 } // anonymous namespace
855 
856 
main(int argc,char const * argv[])857 int main(int argc, char const * argv[])
858 {
859 	// set the invocation, for the file headers later
860 	for (int i = 0 ; i < argc ; ++i)
861 		cmdline += string(argv[i]) + " ";
862 
863 	return run_pp_tool(argc, argv, opannotate);
864 }
865