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_file.h" 18 19 #include <fcntl.h> 20 #include <string.h> 21 #include <sys/mman.h> 22 #include <unistd.h> 23 #include <set> 24 #include <vector> 25 26 #include <base/logging.h> 27 28 #include "event_fd.h" 29 #include "perf_event.h" 30 #include "record.h" 31 #include "utils.h" 32 33 using namespace PerfFileFormat; 34 35 std::unique_ptr<RecordFileWriter> RecordFileWriter::CreateInstance( 36 const std::string& filename, const perf_event_attr& event_attr, 37 const std::vector<std::unique_ptr<EventFd>>& event_fds) { 38 FILE* fp = fopen(filename.c_str(), "web+"); 39 if (fp == nullptr) { 40 PLOG(ERROR) << "failed to open record file '" << filename << "'"; 41 return nullptr; 42 } 43 44 auto writer = std::unique_ptr<RecordFileWriter>(new RecordFileWriter(filename, fp)); 45 if (!writer->WriteAttrSection(event_attr, event_fds)) { 46 return nullptr; 47 } 48 return writer; 49 } 50 51 RecordFileWriter::RecordFileWriter(const std::string& filename, FILE* fp) 52 : filename_(filename), 53 record_fp_(fp), 54 attr_section_offset_(0), 55 attr_section_size_(0), 56 data_section_offset_(0), 57 data_section_size_(0), 58 feature_count_(0), 59 current_feature_index_(0) { 60 } 61 62 RecordFileWriter::~RecordFileWriter() { 63 if (record_fp_ != nullptr) { 64 Close(); 65 } 66 } 67 68 bool RecordFileWriter::WriteAttrSection(const perf_event_attr& event_attr, 69 const std::vector<std::unique_ptr<EventFd>>& event_fds) { 70 // Skip file header part. 71 if (fseek(record_fp_, sizeof(FileHeader), SEEK_SET) == -1) { 72 return false; 73 } 74 75 // Write id section. 76 std::vector<uint64_t> ids; 77 for (auto& event_fd : event_fds) { 78 ids.push_back(event_fd->Id()); 79 } 80 long id_section_offset = ftell(record_fp_); 81 if (id_section_offset == -1) { 82 return false; 83 } 84 if (!Write(ids.data(), ids.size() * sizeof(uint64_t))) { 85 return false; 86 } 87 88 // Write attr section. 89 FileAttr attr; 90 attr.attr = event_attr; 91 attr.ids.offset = id_section_offset; 92 attr.ids.size = ids.size() * sizeof(uint64_t); 93 94 long attr_section_offset = ftell(record_fp_); 95 if (attr_section_offset == -1) { 96 return false; 97 } 98 if (!Write(&attr, sizeof(attr))) { 99 return false; 100 } 101 102 long data_section_offset = ftell(record_fp_); 103 if (data_section_offset == -1) { 104 return false; 105 } 106 107 attr_section_offset_ = attr_section_offset; 108 attr_section_size_ = sizeof(attr); 109 data_section_offset_ = data_section_offset; 110 111 // Save event_attr for use when reading records. 112 event_attr_ = event_attr; 113 return true; 114 } 115 116 bool RecordFileWriter::WriteData(const void* buf, size_t len) { 117 if (!Write(buf, len)) { 118 return false; 119 } 120 data_section_size_ += len; 121 return true; 122 } 123 124 bool RecordFileWriter::Write(const void* buf, size_t len) { 125 if (fwrite(buf, len, 1, record_fp_) != 1) { 126 PLOG(ERROR) << "failed to write to record file '" << filename_ << "'"; 127 return false; 128 } 129 return true; 130 } 131 132 void RecordFileWriter::GetHitModulesInBuffer(const char* p, const char* end, 133 std::vector<std::string>* hit_kernel_modules, 134 std::vector<std::string>* hit_user_files) { 135 std::vector<std::unique_ptr<const Record>> kernel_mmaps; 136 std::vector<std::unique_ptr<const Record>> user_mmaps; 137 std::set<std::string> hit_kernel_set; 138 std::set<std::string> hit_user_set; 139 140 while (p < end) { 141 auto header = reinterpret_cast<const perf_event_header*>(p); 142 CHECK_LE(p + header->size, end); 143 p += header->size; 144 std::unique_ptr<const Record> record = ReadRecordFromBuffer(event_attr_, header); 145 CHECK(record != nullptr); 146 if (record->header.type == PERF_RECORD_MMAP) { 147 if (record->header.misc & PERF_RECORD_MISC_KERNEL) { 148 kernel_mmaps.push_back(std::move(record)); 149 } else { 150 user_mmaps.push_back(std::move(record)); 151 } 152 } else if (record->header.type == PERF_RECORD_SAMPLE) { 153 auto& r = *static_cast<const SampleRecord*>(record.get()); 154 if (!(r.sample_type & PERF_SAMPLE_IP) || !(r.sample_type & PERF_SAMPLE_TID)) { 155 continue; 156 } 157 uint32_t pid = r.tid_data.pid; 158 uint64_t ip = r.ip_data.ip; 159 if (r.header.misc & PERF_RECORD_MISC_KERNEL) { 160 // Loop from back to front, because new MmapRecords are inserted at the end of the mmaps, 161 // and we want to match the newest one. 162 for (auto it = kernel_mmaps.rbegin(); it != kernel_mmaps.rend(); ++it) { 163 auto& m_record = *reinterpret_cast<const MmapRecord*>(it->get()); 164 if (ip >= m_record.data.addr && ip < m_record.data.addr + m_record.data.len) { 165 hit_kernel_set.insert(m_record.filename); 166 break; 167 } 168 } 169 } else { 170 for (auto it = user_mmaps.rbegin(); it != user_mmaps.rend(); ++it) { 171 auto& m_record = *reinterpret_cast<const MmapRecord*>(it->get()); 172 if (pid == m_record.data.pid && ip >= m_record.data.addr && 173 ip < m_record.data.addr + m_record.data.len) { 174 hit_user_set.insert(m_record.filename); 175 break; 176 } 177 } 178 } 179 } 180 } 181 hit_kernel_modules->clear(); 182 hit_kernel_modules->insert(hit_kernel_modules->begin(), hit_kernel_set.begin(), 183 hit_kernel_set.end()); 184 hit_user_files->clear(); 185 hit_user_files->insert(hit_user_files->begin(), hit_user_set.begin(), hit_user_set.end()); 186 } 187 188 bool RecordFileWriter::GetHitModules(std::vector<std::string>* hit_kernel_modules, 189 std::vector<std::string>* hit_user_files) { 190 if (fflush(record_fp_) != 0) { 191 PLOG(ERROR) << "fflush() failed"; 192 return false; 193 } 194 if (fseek(record_fp_, 0, SEEK_END) == -1) { 195 PLOG(ERROR) << "fseek() failed"; 196 return false; 197 } 198 long file_size = ftell(record_fp_); 199 if (file_size == -1) { 200 PLOG(ERROR) << "ftell() failed"; 201 return false; 202 } 203 size_t mmap_len = file_size; 204 void* mmap_addr = mmap(nullptr, mmap_len, PROT_READ, MAP_SHARED, fileno(record_fp_), 0); 205 if (mmap_addr == MAP_FAILED) { 206 PLOG(ERROR) << "mmap() failed"; 207 return false; 208 } 209 const char* data_section_p = reinterpret_cast<const char*>(mmap_addr) + data_section_offset_; 210 const char* data_section_end = data_section_p + data_section_size_; 211 GetHitModulesInBuffer(data_section_p, data_section_end, hit_kernel_modules, hit_user_files); 212 213 if (munmap(mmap_addr, mmap_len) == -1) { 214 PLOG(ERROR) << "munmap() failed"; 215 return false; 216 } 217 return true; 218 } 219 220 bool RecordFileWriter::WriteFeatureHeader(size_t feature_count) { 221 feature_count_ = feature_count; 222 current_feature_index_ = 0; 223 uint64_t feature_header_size = feature_count * sizeof(SectionDesc); 224 225 // Reserve enough space in the record file for the feature header. 226 std::vector<unsigned char> zero_data(feature_header_size); 227 if (fseek(record_fp_, data_section_offset_ + data_section_size_, SEEK_SET) == -1) { 228 PLOG(ERROR) << "fseek() failed"; 229 return false; 230 } 231 return Write(zero_data.data(), zero_data.size()); 232 } 233 234 bool RecordFileWriter::WriteBuildIdFeature(const std::vector<BuildIdRecord>& build_id_records) { 235 if (current_feature_index_ >= feature_count_) { 236 return false; 237 } 238 // Always write features at the end of the file. 239 if (fseek(record_fp_, 0, SEEK_END) == -1) { 240 PLOG(ERROR) << "fseek() failed"; 241 return false; 242 } 243 long section_start = ftell(record_fp_); 244 if (section_start == -1) { 245 PLOG(ERROR) << "ftell() failed"; 246 return false; 247 } 248 for (auto& record : build_id_records) { 249 std::vector<char> data = record.BinaryFormat(); 250 if (!Write(data.data(), data.size())) { 251 return false; 252 } 253 } 254 long section_end = ftell(record_fp_); 255 if (section_end == -1) { 256 return false; 257 } 258 259 // Write feature section descriptor for build_id feature. 260 SectionDesc desc; 261 desc.offset = section_start; 262 desc.size = section_end - section_start; 263 uint64_t feature_offset = data_section_offset_ + data_section_size_; 264 if (fseek(record_fp_, feature_offset + current_feature_index_ * sizeof(SectionDesc), SEEK_SET) == 265 -1) { 266 PLOG(ERROR) << "fseek() failed"; 267 return false; 268 } 269 if (fwrite(&desc, sizeof(SectionDesc), 1, record_fp_) != 1) { 270 PLOG(ERROR) << "fwrite() failed"; 271 return false; 272 } 273 ++current_feature_index_; 274 features_.push_back(FEAT_BUILD_ID); 275 return true; 276 } 277 278 bool RecordFileWriter::WriteFileHeader() { 279 FileHeader header; 280 memset(&header, 0, sizeof(header)); 281 memcpy(header.magic, PERF_MAGIC, sizeof(header.magic)); 282 header.header_size = sizeof(header); 283 header.attr_size = sizeof(FileAttr); 284 header.attrs.offset = attr_section_offset_; 285 header.attrs.size = attr_section_size_; 286 header.data.offset = data_section_offset_; 287 header.data.size = data_section_size_; 288 for (auto& feature : features_) { 289 int i = feature / 8; 290 int j = feature % 8; 291 header.features[i] |= (1 << j); 292 } 293 294 if (fseek(record_fp_, 0, SEEK_SET) == -1) { 295 return false; 296 } 297 if (!Write(&header, sizeof(header))) { 298 return false; 299 } 300 return true; 301 } 302 303 bool RecordFileWriter::Close() { 304 CHECK(record_fp_ != nullptr); 305 bool result = true; 306 307 // Write file header. We gather enough information to write file header only after 308 // writing data section and feature section. 309 if (!WriteFileHeader()) { 310 result = false; 311 } 312 313 if (fclose(record_fp_) != 0) { 314 PLOG(ERROR) << "failed to close record file '" << filename_ << "'"; 315 result = false; 316 } 317 record_fp_ = nullptr; 318 return result; 319 } 320 321 std::unique_ptr<RecordFileReader> RecordFileReader::CreateInstance(const std::string& filename) { 322 int fd = open(filename.c_str(), O_RDONLY | O_CLOEXEC); 323 if (fd == -1) { 324 PLOG(ERROR) << "failed to open record file '" << filename << "'"; 325 return nullptr; 326 } 327 auto reader = std::unique_ptr<RecordFileReader>(new RecordFileReader(filename, fd)); 328 if (!reader->MmapFile()) { 329 return nullptr; 330 } 331 return reader; 332 } 333 334 RecordFileReader::RecordFileReader(const std::string& filename, int fd) 335 : filename_(filename), record_fd_(fd), mmap_addr_(nullptr), mmap_len_(0) { 336 } 337 338 RecordFileReader::~RecordFileReader() { 339 if (record_fd_ != -1) { 340 Close(); 341 } 342 } 343 344 bool RecordFileReader::Close() { 345 bool result = true; 346 if (munmap(const_cast<char*>(mmap_addr_), mmap_len_) == -1) { 347 PLOG(ERROR) << "failed to munmap() record file '" << filename_ << "'"; 348 result = false; 349 } 350 if (close(record_fd_) == -1) { 351 PLOG(ERROR) << "failed to close record file '" << filename_ << "'"; 352 result = false; 353 } 354 record_fd_ = -1; 355 return result; 356 } 357 358 bool RecordFileReader::MmapFile() { 359 off64_t file_size = lseek64(record_fd_, 0, SEEK_END); 360 if (file_size == -1) { 361 return false; 362 } 363 size_t mmap_len = file_size; 364 void* mmap_addr = mmap(nullptr, mmap_len, PROT_READ, MAP_SHARED, record_fd_, 0); 365 if (mmap_addr == MAP_FAILED) { 366 PLOG(ERROR) << "failed to mmap() record file '" << filename_ << "'"; 367 return false; 368 } 369 370 mmap_addr_ = reinterpret_cast<const char*>(mmap_addr); 371 mmap_len_ = mmap_len; 372 return true; 373 } 374 375 const FileHeader* RecordFileReader::FileHeader() { 376 return reinterpret_cast<const struct FileHeader*>(mmap_addr_); 377 } 378 379 std::vector<const FileAttr*> RecordFileReader::AttrSection() { 380 std::vector<const FileAttr*> result; 381 const struct FileHeader* header = FileHeader(); 382 size_t attr_count = header->attrs.size / header->attr_size; 383 const FileAttr* attr = reinterpret_cast<const FileAttr*>(mmap_addr_ + header->attrs.offset); 384 for (size_t i = 0; i < attr_count; ++i) { 385 result.push_back(attr++); 386 } 387 return result; 388 } 389 390 std::vector<uint64_t> RecordFileReader::IdsForAttr(const FileAttr* attr) { 391 std::vector<uint64_t> result; 392 size_t id_count = attr->ids.size / sizeof(uint64_t); 393 const uint64_t* id = reinterpret_cast<const uint64_t*>(mmap_addr_ + attr->ids.offset); 394 for (size_t i = 0; i < id_count; ++i) { 395 result.push_back(*id++); 396 } 397 return result; 398 } 399 400 std::vector<std::unique_ptr<const Record>> RecordFileReader::DataSection() { 401 std::vector<std::unique_ptr<const Record>> result; 402 const struct FileHeader* header = FileHeader(); 403 auto file_attrs = AttrSection(); 404 CHECK(file_attrs.size() > 0); 405 perf_event_attr attr = file_attrs[0]->attr; 406 407 const char* end = mmap_addr_ + header->data.offset + header->data.size; 408 const char* p = mmap_addr_ + header->data.offset; 409 while (p < end) { 410 const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p); 411 if (p + header->size <= end) { 412 result.push_back(std::move(ReadRecordFromBuffer(attr, header))); 413 } 414 p += header->size; 415 } 416 return result; 417 } 418 419 std::vector<SectionDesc> RecordFileReader::FeatureSectionDescriptors() { 420 std::vector<SectionDesc> result; 421 const struct FileHeader* header = FileHeader(); 422 size_t feature_count = 0; 423 for (size_t i = 0; i < sizeof(header->features); ++i) { 424 for (size_t j = 0; j < 8; ++j) { 425 if (header->features[i] & (1 << j)) { 426 ++feature_count; 427 } 428 } 429 } 430 uint64_t feature_section_offset = header->data.offset + header->data.size; 431 const SectionDesc* p = reinterpret_cast<const SectionDesc*>(mmap_addr_ + feature_section_offset); 432 for (size_t i = 0; i < feature_count; ++i) { 433 result.push_back(*p++); 434 } 435 return result; 436 } 437