Home | History | Annotate | Download | only in simpleperf
      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