Mike Dodd | 8cfa702 | 2010-11-17 11:12:26 -0800 | [diff] [blame] | 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 | |
Jeff Brown | 220b7ff | 2011-05-03 15:33:24 -0700 | [diff] [blame] | 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 | |
Mike Dodd | 8cfa702 | 2010-11-17 11:12:26 -0800 | [diff] [blame] | 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 | } |
Jeff Brown | 220b7ff | 2011-05-03 15:33:24 -0700 | [diff] [blame] | 132 | |
| 133 | #ifdef ANDROID |
| 134 | } |
| 135 | #endif |
Mike Dodd | 8cfa702 | 2010-11-17 11:12:26 -0800 | [diff] [blame] | 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 | } |