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 <vector> 26 27#include <android-base/file.h> 28#include <android-base/logging.h> 29 30#include "perf_event.h" 31#include "record.h" 32#include "utils.h" 33 34using namespace PerfFileFormat; 35 36std::unique_ptr<RecordFileWriter> RecordFileWriter::CreateInstance(const std::string& filename) { 37 // Remove old perf.data to avoid file ownership problems. 38 std::string err; 39 if (!android::base::RemoveFileIfExists(filename, &err)) { 40 LOG(ERROR) << "failed to remove file " << filename << ": " << err; 41 return nullptr; 42 } 43 FILE* fp = fopen(filename.c_str(), "web+"); 44 if (fp == nullptr) { 45 PLOG(ERROR) << "failed to open record file '" << filename << "'"; 46 return nullptr; 47 } 48 49 return std::unique_ptr<RecordFileWriter>(new RecordFileWriter(filename, fp)); 50} 51 52RecordFileWriter::RecordFileWriter(const std::string& filename, FILE* fp) 53 : filename_(filename), 54 record_fp_(fp), 55 attr_section_offset_(0), 56 attr_section_size_(0), 57 data_section_offset_(0), 58 data_section_size_(0), 59 feature_count_(0), 60 current_feature_index_(0) { 61} 62 63RecordFileWriter::~RecordFileWriter() { 64 if (record_fp_ != nullptr) { 65 Close(); 66 } 67} 68 69bool RecordFileWriter::WriteAttrSection(const std::vector<AttrWithId>& attr_ids) { 70 if (attr_ids.empty()) { 71 return false; 72 } 73 74 // Skip file header part. 75 if (fseek(record_fp_, sizeof(FileHeader), SEEK_SET) == -1) { 76 return false; 77 } 78 79 // Write id section. 80 long id_section_offset = ftell(record_fp_); 81 if (id_section_offset == -1) { 82 return false; 83 } 84 for (auto& attr_id : attr_ids) { 85 if (!Write(attr_id.ids.data(), attr_id.ids.size() * sizeof(uint64_t))) { 86 return false; 87 } 88 } 89 90 // Write attr section. 91 long attr_section_offset = ftell(record_fp_); 92 if (attr_section_offset == -1) { 93 return false; 94 } 95 for (auto& attr_id : attr_ids) { 96 FileAttr file_attr; 97 file_attr.attr = *attr_id.attr; 98 file_attr.ids.offset = id_section_offset; 99 file_attr.ids.size = attr_id.ids.size() * sizeof(uint64_t); 100 id_section_offset += file_attr.ids.size; 101 if (!Write(&file_attr, sizeof(file_attr))) { 102 return false; 103 } 104 } 105 106 long data_section_offset = ftell(record_fp_); 107 if (data_section_offset == -1) { 108 return false; 109 } 110 111 attr_section_offset_ = attr_section_offset; 112 attr_section_size_ = data_section_offset - attr_section_offset; 113 data_section_offset_ = data_section_offset; 114 115 // Save event_attr for use when reading records. 116 event_attr_ = *attr_ids[0].attr; 117 return true; 118} 119 120bool RecordFileWriter::WriteData(const void* buf, size_t len) { 121 if (!Write(buf, len)) { 122 return false; 123 } 124 data_section_size_ += len; 125 return true; 126} 127 128bool RecordFileWriter::Write(const void* buf, size_t len) { 129 if (fwrite(buf, len, 1, record_fp_) != 1) { 130 PLOG(ERROR) << "failed to write to record file '" << filename_ << "'"; 131 return false; 132 } 133 return true; 134} 135 136bool RecordFileWriter::SeekFileEnd(uint64_t* file_end) { 137 if (fseek(record_fp_, 0, SEEK_END) == -1) { 138 PLOG(ERROR) << "fseek() failed"; 139 return false; 140 } 141 long offset = ftell(record_fp_); 142 if (offset == -1) { 143 PLOG(ERROR) << "ftell() failed"; 144 return false; 145 } 146 *file_end = static_cast<uint64_t>(offset); 147 return true; 148} 149 150bool RecordFileWriter::WriteFeatureHeader(size_t feature_count) { 151 feature_count_ = feature_count; 152 current_feature_index_ = 0; 153 uint64_t feature_header_size = feature_count * sizeof(SectionDesc); 154 155 // Reserve enough space in the record file for the feature header. 156 std::vector<unsigned char> zero_data(feature_header_size); 157 if (fseek(record_fp_, data_section_offset_ + data_section_size_, SEEK_SET) == -1) { 158 PLOG(ERROR) << "fseek() failed"; 159 return false; 160 } 161 return Write(zero_data.data(), zero_data.size()); 162} 163 164bool RecordFileWriter::WriteBuildIdFeature(const std::vector<BuildIdRecord>& build_id_records) { 165 uint64_t start_offset; 166 if (!WriteFeatureBegin(&start_offset)) { 167 return false; 168 } 169 for (auto& record : build_id_records) { 170 std::vector<char> data = record.BinaryFormat(); 171 if (!Write(data.data(), data.size())) { 172 return false; 173 } 174 } 175 return WriteFeatureEnd(FEAT_BUILD_ID, start_offset); 176} 177 178bool RecordFileWriter::WriteFeatureString(int feature, const std::string& s) { 179 uint64_t start_offset; 180 if (!WriteFeatureBegin(&start_offset)) { 181 return false; 182 } 183 uint32_t len = static_cast<uint32_t>(ALIGN(s.size() + 1, 64)); 184 if (!Write(&len, sizeof(len))) { 185 return false; 186 } 187 std::vector<char> v(len, '\0'); 188 std::copy(s.begin(), s.end(), v.begin()); 189 if (!Write(v.data(), v.size())) { 190 return false; 191 } 192 return WriteFeatureEnd(feature, start_offset); 193} 194 195bool RecordFileWriter::WriteCmdlineFeature(const std::vector<std::string>& cmdline) { 196 uint64_t start_offset; 197 if (!WriteFeatureBegin(&start_offset)) { 198 return false; 199 } 200 uint32_t arg_count = cmdline.size(); 201 if (!Write(&arg_count, sizeof(arg_count))) { 202 return false; 203 } 204 for (auto& arg : cmdline) { 205 uint32_t len = static_cast<uint32_t>(ALIGN(arg.size() + 1, 64)); 206 if (!Write(&len, sizeof(len))) { 207 return false; 208 } 209 std::vector<char> array(len, '\0'); 210 std::copy(arg.begin(), arg.end(), array.begin()); 211 if (!Write(array.data(), array.size())) { 212 return false; 213 } 214 } 215 return WriteFeatureEnd(FEAT_CMDLINE, start_offset); 216} 217 218bool RecordFileWriter::WriteBranchStackFeature() { 219 uint64_t start_offset; 220 if (!WriteFeatureBegin(&start_offset)) { 221 return false; 222 } 223 return WriteFeatureEnd(FEAT_BRANCH_STACK, start_offset); 224} 225 226bool RecordFileWriter::WriteFeatureBegin(uint64_t* start_offset) { 227 CHECK_LT(current_feature_index_, feature_count_); 228 if (!SeekFileEnd(start_offset)) { 229 return false; 230 } 231 return true; 232} 233 234bool RecordFileWriter::WriteFeatureEnd(int feature, uint64_t start_offset) { 235 uint64_t end_offset; 236 if (!SeekFileEnd(&end_offset)) { 237 return false; 238 } 239 SectionDesc desc; 240 desc.offset = start_offset; 241 desc.size = end_offset - start_offset; 242 uint64_t feature_offset = data_section_offset_ + data_section_size_; 243 if (fseek(record_fp_, feature_offset + current_feature_index_ * sizeof(SectionDesc), SEEK_SET) == 244 -1) { 245 PLOG(ERROR) << "fseek() failed"; 246 return false; 247 } 248 if (!Write(&desc, sizeof(SectionDesc))) { 249 return false; 250 } 251 ++current_feature_index_; 252 features_.push_back(feature); 253 return true; 254} 255 256bool RecordFileWriter::WriteFileHeader() { 257 FileHeader header; 258 memset(&header, 0, sizeof(header)); 259 memcpy(header.magic, PERF_MAGIC, sizeof(header.magic)); 260 header.header_size = sizeof(header); 261 header.attr_size = sizeof(FileAttr); 262 header.attrs.offset = attr_section_offset_; 263 header.attrs.size = attr_section_size_; 264 header.data.offset = data_section_offset_; 265 header.data.size = data_section_size_; 266 for (auto& feature : features_) { 267 int i = feature / 8; 268 int j = feature % 8; 269 header.features[i] |= (1 << j); 270 } 271 272 if (fseek(record_fp_, 0, SEEK_SET) == -1) { 273 return false; 274 } 275 if (!Write(&header, sizeof(header))) { 276 return false; 277 } 278 return true; 279} 280 281bool RecordFileWriter::Close() { 282 CHECK(record_fp_ != nullptr); 283 bool result = true; 284 285 // Write file header. We gather enough information to write file header only after 286 // writing data section and feature section. 287 if (!WriteFileHeader()) { 288 result = false; 289 } 290 291 if (fclose(record_fp_) != 0) { 292 PLOG(ERROR) << "failed to close record file '" << filename_ << "'"; 293 result = false; 294 } 295 record_fp_ = nullptr; 296 return result; 297} 298