Home | History | Annotate | Download | only in libpp
      1 /**
      2  * @file op_header.cpp
      3  * various free function acting on a sample file header
      4  *
      5  * @remark Copyright 2004 OProfile authors
      6  * @remark Read the file COPYING
      7  *
      8  * @author John Levon
      9  * @author Philippe Elie
     10  * @Modifications Daniel Hansel
     11  */
     12 
     13 #include <cstring>
     14 #include <iostream>
     15 #include <cstdlib>
     16 #include <iomanip>
     17 #include <set>
     18 #include <sstream>
     19 #include <cstring>
     20 
     21 #include <sys/types.h>
     22 #include <sys/stat.h>
     23 #include <fcntl.h>
     24 #include <unistd.h>
     25 
     26 #include "op_config.h"
     27 #include "op_exception.h"
     28 #include "odb.h"
     29 #include "op_cpu_type.h"
     30 #include "op_file.h"
     31 #include "op_header.h"
     32 #include "op_events.h"
     33 #include "string_manip.h"
     34 #include "format_output.h"
     35 #include "xml_utils.h"
     36 #include "cverb.h"
     37 
     38 using namespace std;
     39 
     40 extern verbose vbfd;
     41 
     42 void op_check_header(opd_header const & h1, opd_header const & h2,
     43 		     string const & filename)
     44 {
     45 	if (h1.mtime != h2.mtime) {
     46 		ostringstream os;
     47 		os << "header timestamps are different ("
     48 		   << h1.mtime << ", " << h2.mtime << ") for "
     49 		   << filename << "\n";
     50 		throw op_fatal_error(os.str());
     51 	}
     52 
     53 	if (h1.is_kernel != h2.is_kernel) {
     54 		ostringstream os;
     55 		os << "header is_kernel flags are different for "
     56 		   << filename << "\n";
     57 		throw op_fatal_error(os.str());
     58 	}
     59 
     60 	// Note that in the generated ELF file for anonymous code the vma
     61 	// of the symbol is exaclty the same vma as the code had during sampling.
     62 
     63 	// Note that we don't check CPU speed since that can vary
     64 	// freely on the same machine
     65 }
     66 
     67 
     68 namespace {
     69 
     70 set<string> warned_files;
     71 
     72 }
     73 
     74 bool is_jit_sample(string const & filename)
     75 {
     76 	// suffix for JIT sample files (see FIXME in check_mtime() below)
     77 	string suf = ".jo";
     78 
     79 	string::size_type pos;
     80 	pos = filename.rfind(suf);
     81 	// for JIT sample files do not output the warning to stderr.
     82 	if (pos != string::npos && pos == filename.size() - suf.size())
     83 		return true;
     84 	else
     85 		return false;
     86 }
     87 
     88 void check_mtime(string const & file, opd_header const & header)
     89 {
     90 	time_t const newmtime = op_get_mtime(file.c_str());
     91 
     92 	if (newmtime == header.mtime)
     93 		return;
     94 
     95 	if (warned_files.find(file) != warned_files.end())
     96 		return;
     97 
     98 	warned_files.insert(file);
     99 
    100 	// Files we couldn't get mtime of have zero mtime
    101 	if (!header.mtime) {
    102 		// FIXME: header.mtime for JIT sample files is 0. The problem could be that
    103 		//        in opd_mangling.c:opd_open_sample_file() the call of fill_header()
    104 		//        think that the JIT sample file is not a binary file.
    105 		if (is_jit_sample(file)) {
    106 			cverb << vbfd << "warning: could not check that the binary file "
    107 			      << file << " has not been modified since "
    108 			      "the profile was taken. Results may be inaccurate.\n";
    109 		} else {
    110 			cerr << "warning: could not check that the binary file "
    111 			     << file << " has not been modified since "
    112 			     "the profile was taken. Results may be inaccurate.\n";
    113 		}
    114 	} else {
    115 		static bool warned_already = false;
    116 
    117 #ifdef ANDROID
    118 		// Android symbol files may not have the same timestamp as the stripped
    119 		// files deployed to the device.  Suppress spurious warnings.
    120 		if (file.find("/symbols/") == string::npos) {
    121 #endif
    122 
    123 		cerr << "warning: the last modified time of the binary file "
    124 		     "does not match that of the sample file for " << file
    125 		     << "\n";
    126 
    127 		if (!warned_already) {
    128 			cerr << "Either this is the wrong binary or the binary "
    129 			"has been modified since the sample file was created.\n";
    130 			warned_already = true;
    131 		}
    132 
    133 #ifdef ANDROID
    134 		}
    135 #endif
    136 	}
    137 }
    138 
    139 
    140 opd_header const read_header(string const & sample_filename)
    141 {
    142 	int fd = open(sample_filename.c_str(), O_RDONLY);
    143 	if (fd < 0)
    144 		throw op_fatal_error("Can't open sample file:" +
    145 				     sample_filename);
    146 
    147 	opd_header header;
    148 	if (read(fd, &header, sizeof(header)) != sizeof(header)) {
    149 		close(fd);
    150 		throw op_fatal_error("Can't read sample file header:" +
    151 				     sample_filename);
    152 	}
    153 
    154 	if (memcmp(header.magic, OPD_MAGIC, sizeof(header.magic))) {
    155 		throw op_fatal_error("Invalid sample file, "
    156 				     "bad magic number: " +
    157 				     sample_filename);
    158 		close(fd);
    159 	}
    160 
    161 	close(fd);
    162 
    163 	return header;
    164 }
    165 
    166 
    167 namespace {
    168 
    169 string const op_print_event(op_cpu cpu_type, u32 type, u32 um, u32 count)
    170 {
    171 	string str;
    172 
    173 	if (cpu_type == CPU_TIMER_INT) {
    174 		str += "Profiling through timer interrupt";
    175 		return str;
    176 	}
    177 
    178 	struct op_event * event = op_find_event(cpu_type, type, um);
    179 
    180 	if (!event) {
    181 		event = op_find_event_any(cpu_type, type);
    182 		if (!event) {
    183 			cerr << "Could not locate event " << int(type) << endl;
    184 			str = "Unknown event";
    185 			return str;
    186 		}
    187 	}
    188 
    189 	char const * um_desc = 0;
    190 
    191 	for (size_t i = 0; i < event->unit->num; ++i) {
    192 		if (event->unit->um[i].value == um)
    193 			um_desc = event->unit->um[i].desc;
    194 	}
    195 
    196 	str += string("Counted ") + event->name;
    197 	str += string(" events (") + event->desc + ")";
    198 
    199 	if (cpu_type != CPU_RTC) {
    200 		str += " with a unit mask of 0x";
    201 
    202 		ostringstream ss;
    203 		ss << hex << setw(2) << setfill('0') << unsigned(um);
    204 		str += ss.str();
    205 
    206 		str += " (";
    207 		str += um_desc ? um_desc : "multiple flags";
    208 		str += ")";
    209 	}
    210 
    211 	str += " count " + op_lexical_cast<string>(count);
    212 	return str;
    213 }
    214 
    215 string const op_xml_print_event(op_cpu cpu_type, u32 type, u32 um, u32 count)
    216 {
    217 	string unit_mask;
    218 
    219 	if (cpu_type == CPU_TIMER_INT || cpu_type == CPU_RTC)
    220 		return xml_utils::get_timer_setup((size_t)count);
    221 
    222 	struct op_event * event = op_find_event(cpu_type, type, um);
    223 	if (!event) {
    224 		event = op_find_event_any(cpu_type, type);
    225 		if (!event) {
    226 			cerr << "Could not locate event " << int(type) << endl;
    227 			return "";
    228 		}
    229 	}
    230 
    231 	if (cpu_type != CPU_RTC) {
    232 		ostringstream str_out;
    233 		str_out << um;
    234 		unit_mask = str_out.str();
    235 	}
    236 
    237 	return xml_utils::get_event_setup(string(event->name),
    238 		(size_t)count, unit_mask);
    239 }
    240 
    241 }
    242 
    243 string const describe_header(opd_header const & header)
    244 {
    245 	op_cpu cpu = static_cast<op_cpu>(header.cpu_type);
    246 
    247 	if (want_xml)
    248 		return op_xml_print_event(cpu, header.ctr_event,
    249 	                      header.ctr_um, header.ctr_count);
    250 	else
    251 		return op_print_event(cpu, header.ctr_event,
    252 	                      header.ctr_um, header.ctr_count);
    253 }
    254 
    255 
    256 string const describe_cpu(opd_header const & header)
    257 {
    258 	op_cpu cpu = static_cast<op_cpu>(header.cpu_type);
    259 
    260 	string str;
    261 	if (want_xml) {
    262 		string cpu_name = op_get_cpu_name(cpu);
    263 
    264 		str = xml_utils::get_profile_header(cpu_name, header.cpu_speed);
    265 	} else {
    266 		str += string("CPU: ") + op_get_cpu_type_str(cpu);
    267 		str += ", speed ";
    268 
    269 		ostringstream ss;
    270 		ss << header.cpu_speed;
    271 		str += ss.str() + " MHz (estimated)";
    272 	}
    273 	return str;
    274 }
    275