1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "tracing.h" 18 19 #include <string.h> 20 21 #include <map> 22 #include <string> 23 #include <vector> 24 25 #include <android-base/file.h> 26 #include <android-base/logging.h> 27 #include <android-base/parseint.h> 28 #include <android-base/stringprintf.h> 29 #include <android-base/strings.h> 30 31 #include "environment.h" 32 #include "perf_event.h" 33 #include "utils.h" 34 35 const char TRACING_INFO_MAGIC[10] = {23, 8, 68, 't', 'r', 36 'a', 'c', 'i', 'n', 'g'}; 37 38 template <class T> 39 void AppendData(std::vector<char>& data, const T& s) { 40 const char* p = reinterpret_cast<const char*>(&s); 41 data.insert(data.end(), p, p + sizeof(T)); 42 } 43 44 static void AppendData(std::vector<char>& data, const char* s) { 45 data.insert(data.end(), s, s + strlen(s) + 1); 46 } 47 48 template <> 49 void AppendData(std::vector<char>& data, const std::string& s) { 50 data.insert(data.end(), s.c_str(), s.c_str() + s.size() + 1); 51 } 52 53 template <> 54 void MoveFromBinaryFormat(std::string& data, const char*& p) { 55 data.clear(); 56 while (*p != '\0') { 57 data.push_back(*p++); 58 } 59 p++; 60 } 61 62 static void AppendFile(std::vector<char>& data, const std::string& file, 63 uint32_t file_size_bytes = 8) { 64 if (file_size_bytes == 8) { 65 uint64_t file_size = file.size(); 66 AppendData(data, file_size); 67 } else if (file_size_bytes == 4) { 68 uint32_t file_size = file.size(); 69 AppendData(data, file_size); 70 } 71 data.insert(data.end(), file.begin(), file.end()); 72 } 73 74 static void DetachFile(const char*& p, std::string& file, 75 uint32_t file_size_bytes = 8) { 76 uint64_t file_size = ConvertBytesToValue(p, file_size_bytes); 77 p += file_size_bytes; 78 file.clear(); 79 file.insert(file.end(), p, p + file_size); 80 p += file_size; 81 } 82 83 struct TraceType { 84 std::string system; 85 std::string name; 86 }; 87 88 class TracingFile { 89 public: 90 TracingFile(); 91 bool RecordHeaderFiles(); 92 void RecordFtraceFiles(const std::vector<TraceType>& trace_types); 93 bool RecordEventFiles(const std::vector<TraceType>& trace_types); 94 bool RecordKallsymsFile(); 95 bool RecordPrintkFormatsFile(); 96 std::vector<char> BinaryFormat() const; 97 void LoadFromBinary(const std::vector<char>& data); 98 void Dump(size_t indent) const; 99 std::vector<TracingFormat> LoadTracingFormatsFromEventFiles() const; 100 const std::string& GetKallsymsFile() const { return kallsyms_file; } 101 uint32_t GetPageSize() const { return page_size; } 102 103 private: 104 bool ReadTraceFsFile(const std::string& path, std::string* content, bool report_error = true) { 105 const char* tracefs_dir = GetTraceFsDir(); 106 if (tracefs_dir == nullptr) { 107 if (report_error) { 108 LOG(ERROR) << "tracefs doesn't exist"; 109 } 110 return false; 111 } 112 std::string full_path = tracefs_dir + path; 113 if (!android::base::ReadFileToString(full_path, content)) { 114 if (report_error) { 115 PLOG(ERROR) << "failed to read " << full_path; 116 } 117 return false; 118 } 119 return true; 120 } 121 122 char magic[10]; 123 std::string version; 124 char endian; 125 uint8_t size_of_long; 126 uint32_t page_size; 127 std::string header_page_file; 128 std::string header_event_file; 129 130 std::vector<std::string> ftrace_format_files; 131 // pair of system, format_file_data. 132 std::vector<std::pair<std::string, std::string>> event_format_files; 133 134 std::string kallsyms_file; 135 std::string printk_formats_file; 136 }; 137 138 TracingFile::TracingFile() { 139 memcpy(magic, TRACING_INFO_MAGIC, sizeof(TRACING_INFO_MAGIC)); 140 version = "0.5"; 141 endian = 0; 142 size_of_long = static_cast<int>(sizeof(long)); // NOLINT(google-runtime-int) 143 page_size = static_cast<uint32_t>(::GetPageSize()); 144 } 145 146 bool TracingFile::RecordHeaderFiles() { 147 return ReadTraceFsFile("/events/header_page", &header_page_file) && 148 ReadTraceFsFile("/events/header_event", &header_event_file); 149 } 150 151 void TracingFile::RecordFtraceFiles(const std::vector<TraceType>& trace_types) { 152 for (const auto& type : trace_types) { 153 std::string format_data; 154 if (ReadTraceFsFile("/events/ftrace/" + type.name + "/format", &format_data, false)) { 155 ftrace_format_files.emplace_back(std::move(format_data)); 156 } 157 } 158 } 159 160 bool TracingFile::RecordEventFiles(const std::vector<TraceType>& trace_types) { 161 for (const auto& type : trace_types) { 162 std::string format_data; 163 if (!ReadTraceFsFile("/events/" + type.system + "/" + type.name + "/format", &format_data)) { 164 return false; 165 } 166 event_format_files.emplace_back(type.system, std::move(format_data)); 167 } 168 return true; 169 } 170 171 bool TracingFile::RecordPrintkFormatsFile() { 172 return ReadTraceFsFile("/printk_formats", &printk_formats_file); 173 } 174 175 std::vector<char> TracingFile::BinaryFormat() const { 176 std::vector<char> ret; 177 ret.insert(ret.end(), magic, magic + sizeof(magic)); 178 AppendData(ret, version); 179 ret.push_back(endian); 180 AppendData(ret, size_of_long); 181 AppendData(ret, page_size); 182 AppendData(ret, "header_page"); 183 AppendFile(ret, header_page_file); 184 AppendData(ret, "header_event"); 185 AppendFile(ret, header_event_file); 186 int count = static_cast<int>(ftrace_format_files.size()); 187 AppendData(ret, count); 188 for (const auto& format : ftrace_format_files) { 189 AppendFile(ret, format); 190 } 191 count = static_cast<int>(event_format_files.size()); 192 AppendData(ret, count); 193 for (const auto& pair : event_format_files) { 194 AppendData(ret, pair.first); 195 AppendData(ret, 1); 196 AppendFile(ret, pair.second); 197 } 198 AppendFile(ret, kallsyms_file, 4); 199 AppendFile(ret, printk_formats_file, 4); 200 return ret; 201 } 202 203 void TracingFile::LoadFromBinary(const std::vector<char>& data) { 204 const char* p = data.data(); 205 const char* end = data.data() + data.size(); 206 CHECK(memcmp(p, magic, sizeof(magic)) == 0); 207 p += sizeof(magic); 208 MoveFromBinaryFormat(version, p); 209 MoveFromBinaryFormat(endian, p); 210 MoveFromBinaryFormat(size_of_long, p); 211 MoveFromBinaryFormat(page_size, p); 212 std::string filename; 213 MoveFromBinaryFormat(filename, p); 214 CHECK_EQ(filename, "header_page"); 215 DetachFile(p, header_page_file); 216 MoveFromBinaryFormat(filename, p); 217 CHECK_EQ(filename, "header_event"); 218 DetachFile(p, header_event_file); 219 uint32_t count; 220 MoveFromBinaryFormat(count, p); 221 ftrace_format_files.resize(count); 222 for (uint32_t i = 0; i < count; ++i) { 223 DetachFile(p, ftrace_format_files[i]); 224 } 225 MoveFromBinaryFormat(count, p); 226 event_format_files.clear(); 227 for (uint32_t i = 0; i < count; ++i) { 228 std::string system; 229 MoveFromBinaryFormat(system, p); 230 uint32_t count_in_system; 231 MoveFromBinaryFormat(count_in_system, p); 232 for (uint32_t i = 0; i < count_in_system; ++i) { 233 std::string format; 234 DetachFile(p, format); 235 event_format_files.push_back(std::make_pair(system, std::move(format))); 236 } 237 } 238 DetachFile(p, kallsyms_file, 4); 239 DetachFile(p, printk_formats_file, 4); 240 CHECK_EQ(p, end); 241 } 242 243 void TracingFile::Dump(size_t indent) const { 244 PrintIndented(indent, "tracing data:\n"); 245 PrintIndented(indent + 1, "magic: "); 246 for (size_t i = 0; i < 3u; ++i) { 247 printf("0x%x ", magic[i]); 248 } 249 for (size_t i = 3; i < sizeof(magic); ++i) { 250 printf("%c", magic[i]); 251 } 252 printf("\n"); 253 PrintIndented(indent + 1, "version: %s\n", version.c_str()); 254 PrintIndented(indent + 1, "endian: %d\n", endian); 255 PrintIndented(indent + 1, "header_page:\n%s\n\n", header_page_file.c_str()); 256 PrintIndented(indent + 1, "header_event:\n%s\n\n", header_event_file.c_str()); 257 for (size_t i = 0; i < ftrace_format_files.size(); ++i) { 258 PrintIndented(indent + 1, "ftrace format file %zu/%zu:\n%s\n\n", i + 1, 259 ftrace_format_files.size(), ftrace_format_files[i].c_str()); 260 } 261 for (size_t i = 0; i < event_format_files.size(); ++i) { 262 PrintIndented(indent + 1, "event format file %zu/%zu %s:\n%s\n\n", i + 1, 263 event_format_files.size(), 264 event_format_files[i].first.c_str(), 265 event_format_files[i].second.c_str()); 266 } 267 PrintIndented(indent + 1, "kallsyms:\n%s\n\n", kallsyms_file.c_str()); 268 PrintIndented(indent + 1, "printk_formats:\n%s\n\n", 269 printk_formats_file.c_str()); 270 } 271 272 enum class FormatParsingState { 273 READ_NAME, 274 READ_ID, 275 READ_FIELDS, 276 READ_PRINTFMT, 277 }; 278 279 // Parse lines like: field:char comm[16]; offset:8; size:16; signed:1; 280 static TracingField ParseTracingField(const std::string& s) { 281 TracingField field; 282 size_t start = 0; 283 std::string name; 284 std::string value; 285 for (size_t i = 0; i < s.size(); ++i) { 286 if (!isspace(s[i]) && (i == 0 || isspace(s[i - 1]))) { 287 start = i; 288 } else if (s[i] == ':') { 289 name = s.substr(start, i - start); 290 start = i + 1; 291 } else if (s[i] == ';') { 292 value = s.substr(start, i - start); 293 if (name == "field") { 294 // Parse value with brackets like "comm[16]", or just a field name. 295 size_t left_bracket_pos = value.find('['); 296 if (left_bracket_pos == std::string::npos) { 297 field.name = value; 298 field.elem_count = 1; 299 } else { 300 field.name = value.substr(0, left_bracket_pos); 301 field.elem_count = 1; 302 size_t right_bracket_pos = value.find(']', left_bracket_pos); 303 if (right_bracket_pos != std::string::npos) { 304 size_t len = right_bracket_pos - left_bracket_pos - 1; 305 size_t elem_count; 306 // Array size may not be a number, like field:u32 rates[IEEE80211_NUM_BANDS]. 307 if (android::base::ParseUint(value.substr(left_bracket_pos + 1, len), &elem_count)) { 308 field.elem_count = elem_count; 309 } 310 } 311 } 312 } else if (name == "offset") { 313 field.offset = 314 static_cast<size_t>(strtoull(value.c_str(), nullptr, 10)); 315 } else if (name == "size") { 316 size_t size = static_cast<size_t>(strtoull(value.c_str(), nullptr, 10)); 317 CHECK_EQ(size % field.elem_count, 0u); 318 field.elem_size = size / field.elem_count; 319 } else if (name == "signed") { 320 int is_signed = static_cast<int>(strtoull(value.c_str(), nullptr, 10)); 321 field.is_signed = (is_signed == 1); 322 } 323 } 324 } 325 return field; 326 } 327 328 std::vector<TracingFormat> TracingFile::LoadTracingFormatsFromEventFiles() 329 const { 330 std::vector<TracingFormat> formats; 331 for (const auto& pair : event_format_files) { 332 TracingFormat format; 333 format.system_name = pair.first; 334 std::vector<std::string> strs = android::base::Split(pair.second, "\n"); 335 FormatParsingState state = FormatParsingState::READ_NAME; 336 for (const auto& s : strs) { 337 if (state == FormatParsingState::READ_NAME) { 338 size_t pos = s.find("name:"); 339 if (pos != std::string::npos) { 340 format.name = android::base::Trim(s.substr(pos + strlen("name:"))); 341 state = FormatParsingState::READ_ID; 342 } 343 } else if (state == FormatParsingState::READ_ID) { 344 size_t pos = s.find("ID:"); 345 if (pos != std::string::npos) { 346 format.id = 347 strtoull(s.substr(pos + strlen("ID:")).c_str(), nullptr, 10); 348 state = FormatParsingState::READ_FIELDS; 349 } 350 } else if (state == FormatParsingState::READ_FIELDS) { 351 size_t pos = s.find("field:"); 352 if (pos != std::string::npos) { 353 TracingField field = ParseTracingField(s); 354 format.fields.push_back(field); 355 } 356 } 357 } 358 formats.push_back(format); 359 } 360 return formats; 361 } 362 363 Tracing::Tracing(const std::vector<char>& data) { 364 tracing_file_ = new TracingFile; 365 tracing_file_->LoadFromBinary(data); 366 } 367 368 Tracing::~Tracing() { delete tracing_file_; } 369 370 void Tracing::Dump(size_t indent) { tracing_file_->Dump(indent); } 371 372 TracingFormat Tracing::GetTracingFormatHavingId(uint64_t trace_event_id) { 373 if (tracing_formats_.empty()) { 374 tracing_formats_ = tracing_file_->LoadTracingFormatsFromEventFiles(); 375 } 376 for (const auto& format : tracing_formats_) { 377 if (format.id == trace_event_id) { 378 return format; 379 } 380 } 381 LOG(FATAL) << "no tracing format for id " << trace_event_id; 382 return TracingFormat(); 383 } 384 385 std::string Tracing::GetTracingEventNameHavingId(uint64_t trace_event_id) { 386 if (tracing_formats_.empty()) { 387 tracing_formats_ = tracing_file_->LoadTracingFormatsFromEventFiles(); 388 } 389 for (const auto& format : tracing_formats_) { 390 if (format.id == trace_event_id) { 391 return android::base::StringPrintf("%s:%s", format.system_name.c_str(), 392 format.name.c_str()); 393 } 394 } 395 return ""; 396 } 397 398 const std::string& Tracing::GetKallsyms() const { 399 return tracing_file_->GetKallsymsFile(); 400 } 401 402 uint32_t Tracing::GetPageSize() const { return tracing_file_->GetPageSize(); } 403 404 bool GetTracingData(const std::vector<const EventType*>& event_types, 405 std::vector<char>* data) { 406 data->clear(); 407 std::vector<TraceType> trace_types; 408 for (const auto& type : event_types) { 409 CHECK_EQ(static_cast<uint32_t>(PERF_TYPE_TRACEPOINT), type->type); 410 size_t pos = type->name.find(':'); 411 TraceType trace_type; 412 trace_type.system = type->name.substr(0, pos); 413 trace_type.name = type->name.substr(pos + 1); 414 trace_types.push_back(trace_type); 415 } 416 TracingFile tracing_file; 417 if (!tracing_file.RecordHeaderFiles()) { 418 return false; 419 } 420 tracing_file.RecordFtraceFiles(trace_types); 421 if (!tracing_file.RecordEventFiles(trace_types)) { 422 return false; 423 } 424 // Don't record /proc/kallsyms here, as it will be contained in 425 // KernelSymbolRecord. 426 if (!tracing_file.RecordPrintkFormatsFile()) { 427 return false; 428 } 429 *data = tracing_file.BinaryFormat(); 430 return true; 431 } 432