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
36using namespace std;
37using namespace options;
38
39namespace {
40
41size_t nr_events;
42
43scoped_ptr<profile_container> samples;
44
45/// how opannotate was invoked
46string cmdline;
47
48/// empty annotation fill string
49string annotation_fill;
50
51/// string used as start / end comment to annotate source
52string const begin_comment("/* ");
53string const in_comment(" * ");
54string const end_comment(" */");
55
56/// field width for the sample count
57unsigned int const count_width = 6;
58
59string 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
76symbol_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
90void 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
137string 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///
163int 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
260string 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
281bool 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
290void 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
375void 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
394void 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	child_reader reader("objdump", args);
421	if (reader.error()) {
422		cerr << "An error occur during the execution of objdump:\n\n";
423		cerr << reader.error_str() << endl;
424		return;
425	}
426
427	// Read each output line from objdump and store in a list.
428	string str;
429	while (reader.getline(str))
430		asm_lines.push_back(str);
431
432	output_objdump_str_list(symbols, app_name, asm_lines);
433
434	// objdump always returns SUCCESS so we must rely on the stderr state
435	// of objdump. If objdump error message is cryptic our own error
436	// message will be probably also cryptic
437	ostringstream std_err;
438	ostringstream std_out;
439	reader.get_data(std_out, std_err);
440	if (std_err.str().length()) {
441		cerr << "An error occur during the execution of objdump:\n\n";
442		cerr << std_err.str() << endl;
443		return ;
444	}
445
446	// force error code to be acquired
447	reader.terminate_process();
448
449	// required because if objdump stop by signal all above things suceeed
450	// (signal error message are not output through stdout/stderr)
451	if (reader.error()) {
452		cerr << "An error occur during the execution of objdump:\n\n";
453		cerr << reader.error_str() << endl;
454		return;
455	}
456}
457
458
459void output_objdump_asm(symbol_collection const & symbols,
460			string const & app_name)
461{
462	image_error error;
463	string image =
464		classes.extra_found_images.find_image_path(app_name, error,
465							   true);
466
467	// this is only an optimisation, we can either filter output by
468	// directly calling objdump and rely on the symbol filtering or
469	// we can call objdump with the right parameter to just disassemble
470	// the needed part. This is a real win only when calling objdump
471	// a medium number of times, I dunno if the used threshold is optimal
472	// but it is a conservative value.
473	size_t const max_objdump_exec = 50;
474	if (symbols.size() <= max_objdump_exec || error != image_ok) {
475		symbol_collection::const_iterator cit = symbols.begin();
476		symbol_collection::const_iterator end = symbols.end();
477		for (; cit != end; ++cit) {
478			bfd_vma start = (*cit)->sample.vma;
479			bfd_vma end  = start + (*cit)->size;
480			do_one_output_objdump(symbols, image, app_name,
481					      start, end);
482		}
483	} else {
484		do_one_output_objdump(symbols, image,
485				      app_name, 0, ~bfd_vma(0));
486	}
487}
488
489
490bool output_asm(string const & app_name)
491{
492	profile_container::symbol_choice choice;
493	choice.threshold = options::threshold;
494	choice.image_name = app_name;
495	choice.match_image = true;
496	symbol_collection symbols = samples->select_symbols(choice);
497
498	if (!symbols.empty()) {
499		sort_options options;
500		options.add_sort_option(sort_options::sample);
501		options.sort(symbols, false, false);
502
503		output_info(cout);
504
505		output_objdump_asm(symbols, app_name);
506
507		return true;
508	}
509
510	return false;
511}
512
513
514string const source_line_annotation(debug_name_id filename, size_t linenr)
515{
516	string str;
517
518	count_array_t counts = samples->samples_count(filename, linenr);
519	if (!counts.zero()) {
520		str += count_str(counts, samples->samples_count());
521		for (size_t i = 1; i < nr_events; ++i)
522			str += "  ";
523		str += " :";
524	} else {
525		str = annotation_fill;
526	}
527
528	return str;
529}
530
531
532string source_symbol_annotation(debug_name_id filename, size_t linenr)
533{
534	symbol_collection const symbols = samples->find_symbol(filename, linenr);
535
536	if (symbols.empty())
537		return string();
538
539	string str = " " + begin_comment;
540
541	count_array_t counts;
542	for (size_t i = 0; i < symbols.size(); ++i) {
543		str += symbol_names.demangle(symbols[i]->name);
544		if (symbols.size() == 1)
545			str += " total: ";
546		else
547			str += " ";
548		str += count_str(symbols[i]->sample.counts,
549		          samples->samples_count());
550		if (symbols.size() != 1)
551			str += ", ";
552
553		counts += symbols[i]->sample.counts;
554	}
555
556	if (symbols.size() > 1)
557		str += "total: " + count_str(counts, samples->samples_count());
558	str += end_comment;
559
560	return str;
561}
562
563
564void output_per_file_info(ostream & out, debug_name_id filename,
565			  count_array_t const & total_file_count)
566{
567	out << begin_comment << '\n'
568	     << in_comment << "Total samples for file : "
569	     << '"' << debug_names.name(filename) << '"'
570	     << '\n';
571	out << in_comment << '\n' << in_comment
572	    << count_str(total_file_count, samples->samples_count())
573	    << '\n';
574	out << end_comment << '\n' << '\n';
575}
576
577
578string const line0_info(debug_name_id filename)
579{
580	string annotation = source_line_annotation(filename, 0);
581	if (trim(annotation, " \t:").empty())
582		return string();
583
584	string str = "<credited to line zero> ";
585	str += annotation;
586	return str;
587}
588
589
590void do_output_one_file(ostream & out, istream & in, debug_name_id filename,
591                        bool header)
592{
593	count_array_t count = samples->samples_count(filename);
594
595	if (header) {
596		output_per_file_info(out, filename, count);
597		out << line0_info(filename) << '\n';
598	}
599
600
601	if (in) {
602		string str;
603
604		for (size_t linenr = 1 ; getline(in, str) ; ++linenr) {
605			out << source_line_annotation(filename, linenr) << str
606			    << source_symbol_annotation(filename, linenr)
607			    << '\n';
608		}
609
610	} else {
611		// source is not available but we can at least output all the
612		// symbols belonging to this file. This make more visible the
613		// problem of having less samples for a given file than the
614		// sum of all symbols samples for this file due to inlining
615		symbol_collection const symbols = samples->select_symbols(filename);
616		for (size_t i = 0; i < symbols.size(); ++i)
617			out << symbol_annotation(symbols[i]) << endl;
618	}
619
620	if (!header) {
621		output_per_file_info(out, filename, count);
622		out << line0_info(filename) << '\n';
623	}
624}
625
626
627void output_one_file(istream & in, debug_name_id filename,
628                     string const & source)
629{
630	if (output_dir.empty()) {
631		do_output_one_file(cout, in, filename, true);
632		return;
633	}
634
635	string const out_file = op_realpath(output_dir + source);
636
637	/* Just because you're paranoid doesn't mean they're not out to
638	 * get you ...
639	 *
640	 * This is just a lame final safety check. If we found the
641	 * source, then "source" should be canonical already, and
642	 * can't escape from the output dir. We can't use op_realpath()
643	 * alone as that needs the file to exist already.
644	 *
645	 * Let's not complain again if we couldn't find the file anyway.
646	 */
647	if (out_file.find("/../") != string::npos) {
648		if (in) {
649			cerr << "refusing to create non-canonical filename "
650			     << out_file  << endl;
651		}
652		return;
653	} else if (!is_prefix(out_file, output_dir)) {
654		if (in) {
655			cerr << "refusing to create file " << out_file
656			     << " outside of output directory " << output_dir
657			     << endl;
658		}
659		return;
660	}
661
662	if (is_files_identical(out_file, source)) {
663		cerr << "input and output files are identical: "
664		     << out_file << endl;
665		return;
666	}
667
668	if (create_path(out_file.c_str())) {
669		cerr << "unable to create file: "
670		     << '"' << op_dirname(out_file) << '"' << endl;
671		return;
672	}
673
674	ofstream out(out_file.c_str());
675	if (!out) {
676		cerr << "unable to open output file "
677		     << '"' << out_file << '"' << endl;
678	} else {
679		do_output_one_file(out, in, filename, false);
680		output_info(out);
681	}
682}
683
684
685/* Locate a source file from debug info, which may be relative */
686string const locate_source_file(debug_name_id filename_id)
687{
688	string const origfile = debug_names.name(filename_id);
689	string file = origfile;
690
691	if (file.empty())
692		return file;
693
694	/* Allow absolute paths to be relocated to a different directory */
695	if (file[0] == '/') {
696		vector<string>::const_iterator cit = base_dirs.begin();
697		vector<string>::const_iterator end = base_dirs.end();
698		for (; cit != end; ++cit) {
699			string path = op_realpath(*cit);
700
701			if (is_prefix(file, path)) {
702				file = file.substr(path.length());
703				break;
704			}
705		}
706	}
707
708	vector<string>::const_iterator cit = search_dirs.begin();
709	vector<string>::const_iterator end = search_dirs.end();
710
711	for (; cit != end; ++cit) {
712		string const absfile = op_realpath(*cit + "/" + file);
713
714		if (op_file_readable(absfile))
715			return absfile;
716	}
717
718	/* We didn't find a relocated absolute file, or a relative file,
719	 * assume the original is correct, accounting for the
720	 * possibility it's relative the cwd
721	 */
722	return op_realpath(origfile);
723}
724
725
726void output_source(path_filter const & filter)
727{
728	bool const separate_file = !output_dir.empty();
729
730	if (!separate_file)
731		output_info(cout);
732
733	vector<debug_name_id> filenames =
734		samples->select_filename(options::threshold);
735
736	for (size_t i = 0 ; i < filenames.size() ; ++i) {
737		string const & source = locate_source_file(filenames[i]);
738
739		if (!filter.match(source))
740			continue;
741
742		ifstream in(source.c_str());
743
744		// it is common to have empty filename due to the lack
745		// of debug info (eg _init function) so warn only
746		// if the filename is non empty. The case: no debug
747		// info at all has already been checked.
748		if (!in && source.length()) {
749			cerr << "opannotate (warning): unable to open for "
750			     "reading: " << source << endl;
751		}
752
753		if (source.length())
754			output_one_file(in, filenames[i], source);
755	}
756}
757
758
759bool annotate_source(list<string> const & images)
760{
761	annotation_fill = get_annotation_fill();
762
763	if (!output_dir.empty()) {
764
765		if (create_path(output_dir.c_str())) {
766			cerr << "unable to create " << output_dir
767			     << " directory: " << endl;
768			return false;
769		}
770
771		// Make sure we have an absolute path.
772		output_dir = op_realpath(output_dir);
773		if (output_dir.length() &&
774		    output_dir[output_dir.length() - 1] != '/')
775			output_dir += '/';
776
777		/* Don't let the user stomp on their sources */
778		if (output_dir == "/") {
779			cerr << "Output path of / would over-write the "
780				"source files" << endl;
781			return false;
782		}
783	}
784
785	if (assembly) {
786		bool some_output = false;
787
788		list<string>::const_iterator it = images.begin();
789		list<string>::const_iterator const end = images.end();
790
791		for (; it != end; ++it) {
792			if (output_asm(*it))
793				some_output = true;
794		}
795
796		if (!some_output) {
797			// It's the only case we must care since we know the
798			// selected image set is not empty
799			cerr << "selected image set doesn't contain any of "
800			     << "the selected symbol\n";
801		}
802	} else {
803		output_source(file_filter);
804	}
805
806	return true;
807}
808
809
810int opannotate(options::spec const & spec)
811{
812	handle_options(spec);
813
814	nr_events = classes.v.size();
815
816	samples.reset(new profile_container(true, true,
817					    classes.extra_found_images));
818
819	list<string> images;
820
821	list<inverted_profile> iprofiles = invert_profiles(classes);
822
823	report_image_errors(iprofiles, classes.extra_found_images);
824
825	list<inverted_profile>::iterator it = iprofiles.begin();
826	list<inverted_profile>::iterator const end = iprofiles.end();
827
828	bool debug_info = false;
829	for (; it != end; ++it) {
830		bool tmp = false;
831		populate_for_image(*samples, *it,
832				   options::symbol_filter, &tmp);
833		images.push_back(it->image);
834		if (tmp)
835			debug_info = true;
836	}
837
838	if (!debug_info && !options::assembly) {
839		cerr << "opannotate (warning): no debug information available for binary "
840		     << it->image << ", and --assembly not requested\n";
841	}
842
843	annotate_source(images);
844
845	return 0;
846}
847
848} // anonymous namespace
849
850
851int main(int argc, char const * argv[])
852{
853	// set the invocation, for the file headers later
854	for (int i = 0 ; i < argc ; ++i)
855		cmdline += string(argv[i]) + " ";
856
857	return run_pp_tool(argc, argv, opannotate);
858}
859