1 /* 2 * Copyright (C) 2015 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 "record.h" 18 19 #include <inttypes.h> 20 #include <unordered_map> 21 22 #include <base/logging.h> 23 #include <base/stringprintf.h> 24 25 #include "environment.h" 26 #include "utils.h" 27 28 static std::string RecordTypeToString(int record_type) { 29 static std::unordered_map<int, std::string> record_type_names = { 30 {PERF_RECORD_MMAP, "mmap"}, 31 {PERF_RECORD_LOST, "lost"}, 32 {PERF_RECORD_COMM, "comm"}, 33 {PERF_RECORD_EXIT, "exit"}, 34 {PERF_RECORD_THROTTLE, "throttle"}, 35 {PERF_RECORD_UNTHROTTLE, "unthrottle"}, 36 {PERF_RECORD_FORK, "fork"}, 37 {PERF_RECORD_READ, "read"}, 38 {PERF_RECORD_SAMPLE, "sample"}, 39 {PERF_RECORD_BUILD_ID, "build_id"}, 40 }; 41 42 auto it = record_type_names.find(record_type); 43 if (it != record_type_names.end()) { 44 return it->second; 45 } 46 return android::base::StringPrintf("unknown(%d)", record_type); 47 } 48 49 template <class T> 50 void MoveFromBinaryFormat(T& data, const char*& p) { 51 data = *reinterpret_cast<const T*>(p); 52 p += sizeof(T); 53 } 54 55 template <class T> 56 void MoveToBinaryFormat(const T& data, char*& p) { 57 *reinterpret_cast<T*>(p) = data; 58 p += sizeof(T); 59 } 60 61 SampleId::SampleId() { 62 memset(this, 0, sizeof(SampleId)); 63 } 64 65 // Return sample_id size in binary format. 66 size_t SampleId::CreateContent(const perf_event_attr& attr) { 67 sample_id_all = attr.sample_id_all; 68 sample_type = attr.sample_type; 69 // Other data are not necessary. TODO: Set missing SampleId data. 70 size_t size = 0; 71 if (sample_id_all) { 72 if (sample_type & PERF_SAMPLE_TID) { 73 size += sizeof(PerfSampleTidType); 74 } 75 if (sample_type & PERF_SAMPLE_TIME) { 76 size += sizeof(PerfSampleTimeType); 77 } 78 if (sample_type & PERF_SAMPLE_ID) { 79 size += sizeof(PerfSampleIdType); 80 } 81 if (sample_type & PERF_SAMPLE_STREAM_ID) { 82 size += sizeof(PerfSampleStreamIdType); 83 } 84 if (sample_type & PERF_SAMPLE_CPU) { 85 size += sizeof(PerfSampleCpuType); 86 } 87 } 88 return size; 89 } 90 91 void SampleId::ReadFromBinaryFormat(const perf_event_attr& attr, const char* p, const char* end) { 92 sample_id_all = attr.sample_id_all; 93 sample_type = attr.sample_type; 94 if (sample_id_all) { 95 if (sample_type & PERF_SAMPLE_TID) { 96 MoveFromBinaryFormat(tid_data, p); 97 } 98 if (sample_type & PERF_SAMPLE_TIME) { 99 MoveFromBinaryFormat(time_data, p); 100 } 101 if (sample_type & PERF_SAMPLE_ID) { 102 MoveFromBinaryFormat(id_data, p); 103 } 104 if (sample_type & PERF_SAMPLE_STREAM_ID) { 105 MoveFromBinaryFormat(stream_id_data, p); 106 } 107 if (sample_type & PERF_SAMPLE_CPU) { 108 MoveFromBinaryFormat(cpu_data, p); 109 } 110 // TODO: Add parsing of PERF_SAMPLE_IDENTIFIER. 111 } 112 CHECK_LE(p, end); 113 if (p < end) { 114 LOG(DEBUG) << "Record SampleId part has " << end - p << " bytes left\n"; 115 } 116 } 117 118 void SampleId::WriteToBinaryFormat(char*& p) const { 119 if (sample_id_all) { 120 if (sample_type & PERF_SAMPLE_TID) { 121 MoveToBinaryFormat(tid_data, p); 122 } 123 if (sample_type & PERF_SAMPLE_TIME) { 124 MoveToBinaryFormat(time_data, p); 125 } 126 if (sample_type & PERF_SAMPLE_ID) { 127 MoveToBinaryFormat(id_data, p); 128 } 129 if (sample_type & PERF_SAMPLE_STREAM_ID) { 130 MoveToBinaryFormat(stream_id_data, p); 131 } 132 if (sample_type & PERF_SAMPLE_CPU) { 133 MoveToBinaryFormat(cpu_data, p); 134 } 135 } 136 } 137 138 void SampleId::Dump(size_t indent) const { 139 if (sample_id_all) { 140 if (sample_type & PERF_SAMPLE_TID) { 141 PrintIndented(indent, "sample_id: pid %u, tid %u\n", tid_data.pid, tid_data.tid); 142 } 143 if (sample_type & PERF_SAMPLE_TIME) { 144 PrintIndented(indent, "sample_id: time %" PRId64 "\n", time_data.time); 145 } 146 if (sample_type & PERF_SAMPLE_ID) { 147 PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", id_data.id); 148 } 149 if (sample_type & PERF_SAMPLE_STREAM_ID) { 150 PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", stream_id_data.stream_id); 151 } 152 if (sample_type & PERF_SAMPLE_CPU) { 153 PrintIndented(indent, "sample_id: cpu %u, res %u\n", cpu_data.cpu, cpu_data.res); 154 } 155 } 156 } 157 158 Record::Record() { 159 memset(&header, 0, sizeof(header)); 160 } 161 162 Record::Record(const perf_event_header* pheader) { 163 header = *pheader; 164 } 165 166 void Record::Dump(size_t indent) const { 167 PrintIndented(indent, "record %s: type %u, misc %u, size %u\n", 168 RecordTypeToString(header.type).c_str(), header.type, header.misc, header.size); 169 DumpData(indent + 1); 170 sample_id.Dump(indent + 1); 171 } 172 173 MmapRecord::MmapRecord(const perf_event_attr& attr, const perf_event_header* pheader) 174 : Record(pheader) { 175 const char* p = reinterpret_cast<const char*>(pheader + 1); 176 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size; 177 MoveFromBinaryFormat(data, p); 178 filename = p; 179 p += ALIGN(filename.size() + 1, 8); 180 CHECK_LE(p, end); 181 sample_id.ReadFromBinaryFormat(attr, p, end); 182 } 183 184 void MmapRecord::DumpData(size_t indent) const { 185 PrintIndented(indent, "pid %u, tid %u, addr %p, len 0x%" PRIx64 "\n", data.pid, data.tid, 186 reinterpret_cast<void*>(data.addr), data.len); 187 PrintIndented(indent, "pgoff 0x%" PRIx64 ", filename %s\n", data.pgoff, filename.c_str()); 188 } 189 190 std::vector<char> MmapRecord::BinaryFormat() const { 191 std::vector<char> buf(header.size); 192 char* p = buf.data(); 193 MoveToBinaryFormat(header, p); 194 MoveToBinaryFormat(data, p); 195 strcpy(p, filename.c_str()); 196 p += ALIGN(filename.size() + 1, 8); 197 sample_id.WriteToBinaryFormat(p); 198 return buf; 199 } 200 201 CommRecord::CommRecord(const perf_event_attr& attr, const perf_event_header* pheader) 202 : Record(pheader) { 203 const char* p = reinterpret_cast<const char*>(pheader + 1); 204 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size; 205 MoveFromBinaryFormat(data, p); 206 comm = p; 207 p += ALIGN(strlen(p) + 1, 8); 208 CHECK_LE(p, end); 209 sample_id.ReadFromBinaryFormat(attr, p, end); 210 } 211 212 void CommRecord::DumpData(size_t indent) const { 213 PrintIndented(indent, "pid %u, tid %u, comm %s\n", data.pid, data.tid, comm.c_str()); 214 } 215 216 std::vector<char> CommRecord::BinaryFormat() const { 217 std::vector<char> buf(header.size); 218 char* p = buf.data(); 219 MoveToBinaryFormat(header, p); 220 MoveToBinaryFormat(data, p); 221 strcpy(p, comm.c_str()); 222 p += ALIGN(comm.size() + 1, 8); 223 sample_id.WriteToBinaryFormat(p); 224 return buf; 225 } 226 227 ExitRecord::ExitRecord(const perf_event_attr& attr, const perf_event_header* pheader) 228 : Record(pheader) { 229 const char* p = reinterpret_cast<const char*>(pheader + 1); 230 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size; 231 MoveFromBinaryFormat(data, p); 232 CHECK_LE(p, end); 233 sample_id.ReadFromBinaryFormat(attr, p, end); 234 } 235 236 void ExitRecord::DumpData(size_t indent) const { 237 PrintIndented(indent, "pid %u, ppid %u, tid %u, ptid %u\n", data.pid, data.ppid, data.tid, 238 data.ptid); 239 } 240 241 SampleRecord::SampleRecord(const perf_event_attr& attr, const perf_event_header* pheader) 242 : Record(pheader) { 243 const char* p = reinterpret_cast<const char*>(pheader + 1); 244 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size; 245 sample_type = attr.sample_type; 246 247 if (sample_type & PERF_SAMPLE_IP) { 248 MoveFromBinaryFormat(ip_data, p); 249 } 250 if (sample_type & PERF_SAMPLE_TID) { 251 MoveFromBinaryFormat(tid_data, p); 252 } 253 if (sample_type & PERF_SAMPLE_TIME) { 254 MoveFromBinaryFormat(time_data, p); 255 } 256 if (sample_type & PERF_SAMPLE_ADDR) { 257 MoveFromBinaryFormat(addr_data, p); 258 } 259 if (sample_type & PERF_SAMPLE_ID) { 260 MoveFromBinaryFormat(id_data, p); 261 } 262 if (sample_type & PERF_SAMPLE_STREAM_ID) { 263 MoveFromBinaryFormat(stream_id_data, p); 264 } 265 if (sample_type & PERF_SAMPLE_CPU) { 266 MoveFromBinaryFormat(cpu_data, p); 267 } 268 if (sample_type & PERF_SAMPLE_PERIOD) { 269 MoveFromBinaryFormat(period_data, p); 270 } 271 // TODO: Add parsing of other PERF_SAMPLE_*. 272 CHECK_LE(p, end); 273 if (p < end) { 274 LOG(DEBUG) << "Record has " << end - p << " bytes left\n"; 275 } 276 } 277 278 void SampleRecord::DumpData(size_t indent) const { 279 PrintIndented(indent, "sample_type: 0x%" PRIx64 "\n", sample_type); 280 if (sample_type & PERF_SAMPLE_IP) { 281 PrintIndented(indent, "ip %p\n", reinterpret_cast<void*>(ip_data.ip)); 282 } 283 if (sample_type & PERF_SAMPLE_TID) { 284 PrintIndented(indent, "pid %u, tid %u\n", tid_data.pid, tid_data.tid); 285 } 286 if (sample_type & PERF_SAMPLE_TIME) { 287 PrintIndented(indent, "time %" PRId64 "\n", time_data.time); 288 } 289 if (sample_type & PERF_SAMPLE_ADDR) { 290 PrintIndented(indent, "addr %p\n", reinterpret_cast<void*>(addr_data.addr)); 291 } 292 if (sample_type & PERF_SAMPLE_ID) { 293 PrintIndented(indent, "id %" PRId64 "\n", id_data.id); 294 } 295 if (sample_type & PERF_SAMPLE_STREAM_ID) { 296 PrintIndented(indent, "stream_id %" PRId64 "\n", stream_id_data.stream_id); 297 } 298 if (sample_type & PERF_SAMPLE_CPU) { 299 PrintIndented(indent, "cpu %u, res %u\n", cpu_data.cpu, cpu_data.res); 300 } 301 if (sample_type & PERF_SAMPLE_PERIOD) { 302 PrintIndented(indent, "period %" PRId64 "\n", period_data.period); 303 } 304 } 305 306 BuildIdRecord::BuildIdRecord(const perf_event_header* pheader) : Record(pheader) { 307 const char* p = reinterpret_cast<const char*>(pheader + 1); 308 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size; 309 MoveFromBinaryFormat(pid, p); 310 std::copy_n(p, build_id.size(), build_id.begin()); 311 p += ALIGN(build_id.size(), 8); 312 filename = p; 313 p += ALIGN(filename.size() + 1, 64); 314 CHECK_EQ(p, end); 315 } 316 317 void BuildIdRecord::DumpData(size_t indent) const { 318 PrintIndented(indent, "pid %u\n", pid); 319 PrintIndented(indent, "build_id 0x"); 320 for (auto& c : build_id) { 321 printf("%02x", c); 322 } 323 printf("\n"); 324 PrintIndented(indent, "filename %s\n", filename.c_str()); 325 } 326 327 std::vector<char> BuildIdRecord::BinaryFormat() const { 328 std::vector<char> buf(header.size); 329 char* p = buf.data(); 330 MoveToBinaryFormat(header, p); 331 MoveToBinaryFormat(pid, p); 332 memcpy(p, build_id.data(), build_id.size()); 333 p += ALIGN(build_id.size(), 8); 334 strcpy(p, filename.c_str()); 335 p += ALIGN(filename.size() + 1, 64); 336 return buf; 337 } 338 339 std::unique_ptr<const Record> ReadRecordFromBuffer(const perf_event_attr& attr, 340 const perf_event_header* pheader) { 341 switch (pheader->type) { 342 case PERF_RECORD_MMAP: 343 return std::unique_ptr<const Record>(new MmapRecord(attr, pheader)); 344 case PERF_RECORD_COMM: 345 return std::unique_ptr<const Record>(new CommRecord(attr, pheader)); 346 case PERF_RECORD_EXIT: 347 return std::unique_ptr<const Record>(new ExitRecord(attr, pheader)); 348 case PERF_RECORD_SAMPLE: 349 return std::unique_ptr<const Record>(new SampleRecord(attr, pheader)); 350 default: 351 return std::unique_ptr<const Record>(new Record(pheader)); 352 } 353 } 354 355 MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid, 356 uint64_t addr, uint64_t len, uint64_t pgoff, 357 const std::string& filename) { 358 MmapRecord record; 359 record.header.type = PERF_RECORD_MMAP; 360 record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER); 361 record.data.pid = pid; 362 record.data.tid = tid; 363 record.data.addr = addr; 364 record.data.len = len; 365 record.data.pgoff = pgoff; 366 record.filename = filename; 367 size_t sample_id_size = record.sample_id.CreateContent(attr); 368 record.header.size = sizeof(record.header) + sizeof(record.data) + 369 ALIGN(record.filename.size() + 1, 8) + sample_id_size; 370 return record; 371 } 372 373 CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, 374 const std::string& comm) { 375 CommRecord record; 376 record.header.type = PERF_RECORD_COMM; 377 record.header.misc = 0; 378 record.data.pid = pid; 379 record.data.tid = tid; 380 record.comm = comm; 381 size_t sample_id_size = record.sample_id.CreateContent(attr); 382 record.header.size = sizeof(record.header) + sizeof(record.data) + 383 ALIGN(record.comm.size() + 1, 8) + sample_id_size; 384 return record; 385 } 386 387 BuildIdRecord CreateBuildIdRecord(bool in_kernel, pid_t pid, const BuildId& build_id, 388 const std::string& filename) { 389 BuildIdRecord record; 390 record.header.type = PERF_RECORD_BUILD_ID; 391 record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER); 392 record.pid = pid; 393 record.build_id = build_id; 394 record.filename = filename; 395 record.header.size = sizeof(record.header) + sizeof(record.pid) + 396 ALIGN(record.build_id.size(), 8) + ALIGN(filename.size() + 1, 64); 397 return record; 398 } 399