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