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 <string>
     25 #include <unordered_map>
     26 #include <vector>
     27 
     28 #include <android-base/file.h>
     29 #include <android-base/logging.h>
     30 
     31 #include "event_attr.h"
     32 #include "perf_event.h"
     33 #include "record.h"
     34 #include "utils.h"
     35 
     36 using namespace PerfFileFormat;
     37 
     38 std::unique_ptr<RecordFileWriter> RecordFileWriter::CreateInstance(const std::string& filename) {
     39   // Remove old perf.data to avoid file ownership problems.
     40   std::string err;
     41   if (!android::base::RemoveFileIfExists(filename, &err)) {
     42     LOG(ERROR) << "failed to remove file " << filename << ": " << err;
     43     return nullptr;
     44   }
     45   FILE* fp = fopen(filename.c_str(), "web+");
     46   if (fp == nullptr) {
     47     PLOG(ERROR) << "failed to open record file '" << filename << "'";
     48     return nullptr;
     49   }
     50 
     51   return std::unique_ptr<RecordFileWriter>(new RecordFileWriter(filename, fp));
     52 }
     53 
     54 RecordFileWriter::RecordFileWriter(const std::string& filename, FILE* fp)
     55     : filename_(filename),
     56       record_fp_(fp),
     57       attr_section_offset_(0),
     58       attr_section_size_(0),
     59       data_section_offset_(0),
     60       data_section_size_(0),
     61       feature_section_offset_(0),
     62       feature_count_(0) {
     63 }
     64 
     65 RecordFileWriter::~RecordFileWriter() {
     66   if (record_fp_ != nullptr) {
     67     Close();
     68   }
     69 }
     70 
     71 bool RecordFileWriter::WriteAttrSection(const std::vector<EventAttrWithId>& attr_ids) {
     72   if (attr_ids.empty()) {
     73     return false;
     74   }
     75 
     76   // Skip file header part.
     77   if (fseek(record_fp_, sizeof(FileHeader), SEEK_SET) == -1) {
     78     return false;
     79   }
     80 
     81   // Write id section.
     82   uint64_t id_section_offset;
     83   if (!GetFilePos(&id_section_offset)) {
     84     return false;
     85   }
     86   for (auto& attr_id : attr_ids) {
     87     if (!Write(attr_id.ids.data(), attr_id.ids.size() * sizeof(uint64_t))) {
     88       return false;
     89     }
     90   }
     91 
     92   // Write attr section.
     93   uint64_t attr_section_offset;
     94   if (!GetFilePos(&attr_section_offset)) {
     95     return false;
     96   }
     97   for (auto& attr_id : attr_ids) {
     98     FileAttr file_attr;
     99     file_attr.attr = *attr_id.attr;
    100     file_attr.ids.offset = id_section_offset;
    101     file_attr.ids.size = attr_id.ids.size() * sizeof(uint64_t);
    102     id_section_offset += file_attr.ids.size;
    103     if (!Write(&file_attr, sizeof(file_attr))) {
    104       return false;
    105     }
    106   }
    107 
    108   uint64_t data_section_offset;
    109   if (!GetFilePos(&data_section_offset)) {
    110     return false;
    111   }
    112 
    113   attr_section_offset_ = attr_section_offset;
    114   attr_section_size_ = data_section_offset - attr_section_offset;
    115   data_section_offset_ = data_section_offset;
    116 
    117   // Save event_attr for use when reading records.
    118   event_attr_ = *attr_ids[0].attr;
    119   return true;
    120 }
    121 
    122 bool RecordFileWriter::WriteRecord(const Record& record) {
    123   // linux-tools-perf only accepts records with size <= 65535 bytes. To make
    124   // perf.data generated by simpleperf be able to be parsed by linux-tools-perf,
    125   // Split simpleperf custom records which are > 65535 into a bunch of
    126   // RECORD_SPLIT records, followed by a RECORD_SPLIT_END record.
    127   constexpr uint32_t RECORD_SIZE_LIMIT = 65535;
    128   if (record.size() <= RECORD_SIZE_LIMIT) {
    129     WriteData(record.Binary(), record.size());
    130     return true;
    131   }
    132   CHECK_GT(record.type(), SIMPLE_PERF_RECORD_TYPE_START);
    133   const char* p = record.Binary();
    134   uint32_t left_bytes = static_cast<uint32_t>(record.size());
    135   RecordHeader header;
    136   header.type = SIMPLE_PERF_RECORD_SPLIT;
    137   char header_buf[Record::header_size()];
    138   char* header_p;
    139   while (left_bytes > 0) {
    140     uint32_t bytes_to_write = std::min(RECORD_SIZE_LIMIT - Record::header_size(), left_bytes);
    141     header.size = bytes_to_write + Record::header_size();
    142     header_p = header_buf;
    143     header.MoveToBinaryFormat(header_p);
    144     if (!WriteData(header_buf, Record::header_size())) {
    145       return false;
    146     }
    147     if (!WriteData(p, bytes_to_write)) {
    148       return false;
    149     }
    150     p += bytes_to_write;
    151     left_bytes -= bytes_to_write;
    152   }
    153   header.type = SIMPLE_PERF_RECORD_SPLIT_END;
    154   header.size = Record::header_size();
    155   header_p = header_buf;
    156   header.MoveToBinaryFormat(header_p);
    157   return WriteData(header_buf, Record::header_size());
    158 }
    159 
    160 bool RecordFileWriter::WriteData(const void* buf, size_t len) {
    161   if (!Write(buf, len)) {
    162     return false;
    163   }
    164   data_section_size_ += len;
    165   return true;
    166 }
    167 
    168 bool RecordFileWriter::Write(const void* buf, size_t len) {
    169   if (fwrite(buf, len, 1, record_fp_) != 1) {
    170     PLOG(ERROR) << "failed to write to record file '" << filename_ << "'";
    171     return false;
    172   }
    173   return true;
    174 }
    175 
    176 bool RecordFileWriter::Read(void* buf, size_t len) {
    177   if (len != 0u && fread(buf, len, 1, record_fp_) != 1) {
    178     PLOG(ERROR) << "failed to read record file '" << filename_ << "'";
    179     return false;
    180   }
    181   return true;
    182 }
    183 
    184 bool RecordFileWriter::ReadDataSection(const std::function<void(const Record*)>& callback) {
    185   if (fseek(record_fp_, data_section_offset_, SEEK_SET) == -1) {
    186     PLOG(ERROR) << "fseek() failed";
    187     return false;
    188   }
    189   std::vector<char> record_buf(512);
    190   uint64_t read_pos = 0;
    191   while (read_pos < data_section_size_) {
    192     if (!Read(record_buf.data(), Record::header_size())) {
    193       return false;
    194     }
    195     RecordHeader header(record_buf.data());
    196     if (record_buf.size() < header.size) {
    197       record_buf.resize(header.size);
    198     }
    199     if (!Read(record_buf.data() + Record::header_size(), header.size - Record::header_size())) {
    200       return false;
    201     }
    202     read_pos += header.size;
    203     std::unique_ptr<Record> r = ReadRecordFromBuffer(event_attr_, header.type, record_buf.data());
    204     callback(r.get());
    205   }
    206   return true;
    207 }
    208 
    209 bool RecordFileWriter::GetFilePos(uint64_t* file_pos) {
    210   off_t offset = ftello(record_fp_);
    211   if (offset == -1) {
    212     PLOG(ERROR) << "ftello() failed";
    213     return false;
    214   }
    215   *file_pos = static_cast<uint64_t>(offset);
    216   return true;
    217 }
    218 
    219 bool RecordFileWriter::BeginWriteFeatures(size_t feature_count) {
    220   feature_section_offset_ = data_section_offset_ + data_section_size_;
    221   feature_count_ = feature_count;
    222   uint64_t feature_header_size = feature_count * sizeof(SectionDesc);
    223 
    224   // Reserve enough space in the record file for the feature header.
    225   std::vector<unsigned char> zero_data(feature_header_size);
    226   if (fseek(record_fp_, feature_section_offset_, SEEK_SET) == -1) {
    227     PLOG(ERROR) << "fseek() failed";
    228     return false;
    229   }
    230   return Write(zero_data.data(), zero_data.size());
    231 }
    232 
    233 bool RecordFileWriter::WriteBuildIdFeature(const std::vector<BuildIdRecord>& build_id_records) {
    234   if (!WriteFeatureBegin(FEAT_BUILD_ID)) {
    235     return false;
    236   }
    237   for (auto& record : build_id_records) {
    238     if (!Write(record.Binary(), record.size())) {
    239       return false;
    240     }
    241   }
    242   return WriteFeatureEnd(FEAT_BUILD_ID);
    243 }
    244 
    245 bool RecordFileWriter::WriteStringWithLength(const std::string& s) {
    246   uint32_t len = static_cast<uint32_t>(Align(s.size() + 1, 64));
    247   if (!Write(&len, sizeof(len))) {
    248     return false;
    249   }
    250   if (!Write(&s[0], s.size() + 1)) {
    251     return false;
    252   }
    253   size_t pad_size = Align(s.size() + 1, 64) - s.size() - 1;
    254   if (pad_size > 0u) {
    255     char align_buf[pad_size];
    256     memset(align_buf, '\0', pad_size);
    257     if (!Write(align_buf, pad_size)) {
    258       return false;
    259     }
    260   }
    261   return true;
    262 }
    263 
    264 bool RecordFileWriter::WriteFeatureString(int feature, const std::string& s) {
    265   if (!WriteFeatureBegin(feature)) {
    266     return false;
    267   }
    268   if (!WriteStringWithLength(s)) {
    269     return false;
    270   }
    271   return WriteFeatureEnd(feature);
    272 }
    273 
    274 bool RecordFileWriter::WriteCmdlineFeature(const std::vector<std::string>& cmdline) {
    275   if (!WriteFeatureBegin(FEAT_CMDLINE)) {
    276     return false;
    277   }
    278   uint32_t arg_count = cmdline.size();
    279   if (!Write(&arg_count, sizeof(arg_count))) {
    280     return false;
    281   }
    282   for (auto& arg : cmdline) {
    283     if (!WriteStringWithLength(arg)) {
    284       return false;
    285     }
    286   }
    287   return WriteFeatureEnd(FEAT_CMDLINE);
    288 }
    289 
    290 bool RecordFileWriter::WriteBranchStackFeature() {
    291   if (!WriteFeatureBegin(FEAT_BRANCH_STACK)) {
    292     return false;
    293   }
    294   return WriteFeatureEnd(FEAT_BRANCH_STACK);
    295 }
    296 
    297 bool RecordFileWriter::WriteFileFeature(const std::string& file_path,
    298                                         uint32_t file_type,
    299                                         uint64_t min_vaddr,
    300                                         const std::vector<const Symbol*>& symbols) {
    301   uint32_t size = file_path.size() + 1 + sizeof(uint32_t) * 2 +
    302       sizeof(uint64_t) + symbols.size() * (sizeof(uint64_t) + sizeof(uint32_t));
    303   for (const auto& symbol : symbols) {
    304     size += strlen(symbol->Name()) + 1;
    305   }
    306   std::vector<char> buf(sizeof(uint32_t) + size);
    307   char* p = buf.data();
    308   MoveToBinaryFormat(size, p);
    309   MoveToBinaryFormat(file_path.c_str(), file_path.size() + 1, p);
    310   MoveToBinaryFormat(file_type, p);
    311   MoveToBinaryFormat(min_vaddr, p);
    312   uint32_t symbol_count = static_cast<uint32_t>(symbols.size());
    313   MoveToBinaryFormat(symbol_count, p);
    314   for (const auto& symbol : symbols) {
    315     MoveToBinaryFormat(symbol->addr, p);
    316     uint32_t len = symbol->len;
    317     MoveToBinaryFormat(len, p);
    318     MoveToBinaryFormat(symbol->Name(), strlen(symbol->Name()) + 1, p);
    319   }
    320   CHECK_EQ(buf.size(), static_cast<size_t>(p - buf.data()));
    321 
    322   if (!WriteFeatureBegin(FEAT_FILE)) {
    323     return false;
    324   }
    325   if (!Write(buf.data(), buf.size())) {
    326     return false;
    327   }
    328   return WriteFeatureEnd(FEAT_FILE);
    329 }
    330 
    331 bool RecordFileWriter::WriteFeatureBegin(int feature) {
    332   auto it = features_.find(feature);
    333   if (it == features_.end()) {
    334     CHECK_LT(features_.size(), feature_count_);
    335     auto& sec = features_[feature];
    336     if (!GetFilePos(&sec.offset)) {
    337       return false;
    338     }
    339     sec.size = 0;
    340   }
    341   return true;
    342 }
    343 
    344 bool RecordFileWriter::WriteFeatureEnd(int feature) {
    345   auto it = features_.find(feature);
    346   if (it == features_.end()) {
    347     return false;
    348   }
    349   uint64_t offset;
    350   if (!GetFilePos(&offset)) {
    351     return false;
    352   }
    353   it->second.size = offset - it->second.offset;
    354   return true;
    355 }
    356 
    357 bool RecordFileWriter::EndWriteFeatures() {
    358   // Used features (features_.size()) should be <= allocated feature space.
    359   CHECK_LE(features_.size(), feature_count_);
    360   if (fseek(record_fp_, feature_section_offset_, SEEK_SET) == -1) {
    361     PLOG(ERROR) << "fseek() failed";
    362     return false;
    363   }
    364   for (const auto& pair : features_) {
    365     if (!Write(&pair.second, sizeof(SectionDesc))) {
    366       return false;
    367     }
    368   }
    369   return true;
    370 }
    371 
    372 bool RecordFileWriter::WriteFileHeader() {
    373   FileHeader header;
    374   memset(&header, 0, sizeof(header));
    375   memcpy(header.magic, PERF_MAGIC, sizeof(header.magic));
    376   header.header_size = sizeof(header);
    377   header.attr_size = sizeof(FileAttr);
    378   header.attrs.offset = attr_section_offset_;
    379   header.attrs.size = attr_section_size_;
    380   header.data.offset = data_section_offset_;
    381   header.data.size = data_section_size_;
    382   for (const auto& pair : features_) {
    383     int i = pair.first / 8;
    384     int j = pair.first % 8;
    385     header.features[i] |= (1 << j);
    386   }
    387 
    388   if (fseek(record_fp_, 0, SEEK_SET) == -1) {
    389     return false;
    390   }
    391   if (!Write(&header, sizeof(header))) {
    392     return false;
    393   }
    394   return true;
    395 }
    396 
    397 bool RecordFileWriter::Close() {
    398   CHECK(record_fp_ != nullptr);
    399   bool result = true;
    400 
    401   // Write file header. We gather enough information to write file header only after
    402   // writing data section and feature section.
    403   if (!WriteFileHeader()) {
    404     result = false;
    405   }
    406 
    407   if (fclose(record_fp_) != 0) {
    408     PLOG(ERROR) << "failed to close record file '" << filename_ << "'";
    409     result = false;
    410   }
    411   record_fp_ = nullptr;
    412   return result;
    413 }
    414