xref: /aosp_15_r20/system/extras/simpleperf/record_file_writer.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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 <unistd.h>
22 
23 #include <algorithm>
24 #include <set>
25 #include <string>
26 #include <unordered_map>
27 #include <vector>
28 
29 #include <android-base/file.h>
30 #include <android-base/logging.h>
31 
32 #include "dso.h"
33 #include "event_attr.h"
34 #include "perf_event.h"
35 #include "record.h"
36 #include "system/extras/simpleperf/record_file.pb.h"
37 #include "utils.h"
38 
39 namespace simpleperf {
40 
41 using namespace PerfFileFormat;
42 
CreateInstance(const std::string & filename)43 std::unique_ptr<RecordFileWriter> RecordFileWriter::CreateInstance(const std::string& filename) {
44   // Remove old perf.data to avoid file ownership problems.
45   std::string err;
46   if (!android::base::RemoveFileIfExists(filename, &err)) {
47     LOG(ERROR) << "failed to remove file " << filename << ": " << err;
48     return nullptr;
49   }
50   FILE* fp = fopen(filename.c_str(), "web+");
51   if (fp == nullptr) {
52     PLOG(ERROR) << "failed to open record file '" << filename << "'";
53     return nullptr;
54   }
55 
56   return std::unique_ptr<RecordFileWriter>(new RecordFileWriter(filename, fp, true));
57 }
58 
RecordFileWriter(const std::string & filename,FILE * fp,bool own_fp)59 RecordFileWriter::RecordFileWriter(const std::string& filename, FILE* fp, bool own_fp)
60     : filename_(filename),
61       record_fp_(fp),
62       own_fp_(own_fp),
63       attr_section_offset_(0),
64       attr_section_size_(0),
65       data_section_offset_(0),
66       data_section_size_(0),
67       feature_section_offset_(0),
68       feature_count_(0) {}
69 
~RecordFileWriter()70 RecordFileWriter::~RecordFileWriter() {
71   if (record_fp_ != nullptr && own_fp_) {
72     fclose(record_fp_);
73     unlink(filename_.c_str());
74   }
75 }
76 
SetCompressionLevel(size_t compression_level)77 bool RecordFileWriter::SetCompressionLevel(size_t compression_level) {
78   compressor_ = CreateZstdCompressor(compression_level);
79   return compressor_ != nullptr;
80 }
81 
WriteAttrSection(const EventAttrIds & attr_ids)82 bool RecordFileWriter::WriteAttrSection(const EventAttrIds& attr_ids) {
83   if (attr_ids.empty()) {
84     return false;
85   }
86 
87   // Skip file header part.
88   if (fseek(record_fp_, sizeof(FileHeader), SEEK_SET) == -1) {
89     return false;
90   }
91 
92   // Write id section.
93   uint64_t id_section_offset;
94   if (!GetFilePos(&id_section_offset)) {
95     return false;
96   }
97   for (auto& attr_id : attr_ids) {
98     if (!Write(attr_id.ids.data(), attr_id.ids.size() * sizeof(uint64_t))) {
99       return false;
100     }
101   }
102 
103   // Write attr section.
104   uint64_t attr_section_offset;
105   if (!GetFilePos(&attr_section_offset)) {
106     return false;
107   }
108   for (auto& attr_id : attr_ids) {
109     FileAttr file_attr;
110     file_attr.attr = attr_id.attr;
111     file_attr.ids.offset = id_section_offset;
112     file_attr.ids.size = attr_id.ids.size() * sizeof(uint64_t);
113     id_section_offset += file_attr.ids.size;
114     if (!Write(&file_attr, sizeof(file_attr))) {
115       return false;
116     }
117   }
118 
119   uint64_t data_section_offset;
120   if (!GetFilePos(&data_section_offset)) {
121     return false;
122   }
123 
124   attr_section_offset_ = attr_section_offset;
125   attr_section_size_ = data_section_offset - attr_section_offset;
126   data_section_offset_ = data_section_offset;
127 
128   // Save event_attr for use when reading records.
129   event_attr_ = attr_ids[0].attr;
130   return true;
131 }
132 
WriteRecord(const Record & record)133 bool RecordFileWriter::WriteRecord(const Record& record) {
134   auto write_data = [&](const char* buf, size_t len) {
135     if (compressor_) {
136       return compressor_->AddInputData(buf, len);
137     }
138     return WriteData(buf, len);
139   };
140 
141   // linux-tools-perf only accepts records with size <= 65535 bytes. To make
142   // perf.data generated by simpleperf be able to be parsed by linux-tools-perf,
143   // Split simpleperf custom records which are > 65535 into a bunch of
144   // RECORD_SPLIT records, followed by a RECORD_SPLIT_END record.
145   constexpr uint32_t RECORD_SIZE_LIMIT = 65535;
146   if (record.size() <= RECORD_SIZE_LIMIT) {
147     bool result = true;
148     if (record.type() == PERF_RECORD_AUXTRACE) {
149       result = WriteAuxTraceRecord(static_cast<const AuxTraceRecord&>(record));
150     } else {
151       result = write_data(record.Binary(), record.size());
152     }
153     if (!result) {
154       return false;
155     }
156   } else {
157     CHECK_GT(record.type(), SIMPLE_PERF_RECORD_TYPE_START);
158     const char* p = record.Binary();
159     uint32_t left_bytes = static_cast<uint32_t>(record.size());
160     RecordHeader header;
161     header.type = SIMPLE_PERF_RECORD_SPLIT;
162     char header_buf[Record::header_size()];
163     char* header_p;
164     while (left_bytes > 0) {
165       uint32_t bytes_to_write = std::min(RECORD_SIZE_LIMIT - Record::header_size(), left_bytes);
166       header.size = bytes_to_write + Record::header_size();
167       header_p = header_buf;
168       header.MoveToBinaryFormat(header_p);
169       if (!write_data(header_buf, Record::header_size())) {
170         return false;
171       }
172       if (!write_data(p, bytes_to_write)) {
173         return false;
174       }
175       p += bytes_to_write;
176       left_bytes -= bytes_to_write;
177     }
178     header.type = SIMPLE_PERF_RECORD_SPLIT_END;
179     header.size = Record::header_size();
180     header_p = header_buf;
181     header.MoveToBinaryFormat(header_p);
182     if (!write_data(header_buf, Record::header_size())) {
183       return false;
184     }
185   }
186 
187   if (compressor_) {
188     return WriteCompressorOutput(false, true);
189   }
190   return true;
191 }
192 
WriteAuxTraceRecord(const AuxTraceRecord & r)193 bool RecordFileWriter::WriteAuxTraceRecord(const AuxTraceRecord& r) {
194   if (compressor_) {
195     // For auxtrace record:
196     // 1. Write PERF_RECORD_AUXTRACE (not compressed)
197     // 2. Write compressed aux data (compressed separately)
198     if (!WriteCompressorOutput(true, true)) {
199       return false;
200     }
201     auxtrace_record_offsets_.emplace_back(data_section_offset_ + data_section_size_);
202     // TODO: Try using a separate compressor with dictionary for ETM data.
203     if (!compressor_->AddInputData(r.location.addr, r.data->aux_size) ||
204         !compressor_->FlushOutputData()) {
205       return false;
206     }
207     std::string_view compressed_data = compressor_->GetOutputData();
208     AuxTraceRecord new_r(compressed_data.size(), r.data->offset, r.data->idx, r.data->tid,
209                          r.data->cpu);
210     if (!WriteData(new_r.Binary(), new_r.size()) ||
211         !WriteData(compressed_data.data(), compressed_data.size())) {
212       return false;
213     }
214     compressor_->ConsumeOutputData(compressed_data.size());
215     return true;
216   }
217   auxtrace_record_offsets_.emplace_back(data_section_offset_ + data_section_size_);
218   return WriteData(r.Binary(), r.size()) && WriteData(r.location.addr, r.data->aux_size);
219 }
220 
FinishWritingDataSection()221 bool RecordFileWriter::FinishWritingDataSection() {
222   if (compressor_) {
223     return WriteCompressorOutput(true, true);
224   }
225   return true;
226 }
227 
WriteCompressorOutput(bool flush,bool data_section)228 bool RecordFileWriter::WriteCompressorOutput(bool flush, bool data_section) {
229   if (flush && !compressor_->FlushOutputData()) {
230     return false;
231   }
232   std::string_view output = compressor_->GetOutputData();
233   if (!output.empty()) {
234     constexpr size_t COMPRESSED_RECORD_MAX_SIZE = (1 << 16) - sizeof(perf_event_header) - 8;
235     const char* p = output.data();
236     size_t left_size = output.size();
237     while (left_size >= COMPRESSED_RECORD_MAX_SIZE) {
238       if (!WriteCompressRecord(p, COMPRESSED_RECORD_MAX_SIZE, data_section)) {
239         return false;
240       }
241       p += COMPRESSED_RECORD_MAX_SIZE;
242       left_size -= COMPRESSED_RECORD_MAX_SIZE;
243     }
244     if (left_size > 0 && flush) {
245       if (!WriteCompressRecord(p, left_size, data_section)) {
246         return false;
247       }
248       left_size = 0;
249     }
250     compressor_->ConsumeOutputData(output.size() - left_size);
251   }
252   return true;
253 }
254 
WriteCompressRecord(const char * data,size_t size,bool data_section)255 bool RecordFileWriter::WriteCompressRecord(const char* data, size_t size, bool data_section) {
256   perf_event_header header = {PERF_RECORD_COMPRESSED, 0,
257                               static_cast<uint16_t>(size + sizeof(perf_event_header))};
258   if (data_section) {
259     return WriteData(&header, sizeof(header)) && WriteData(data, size);
260   }
261   return Write(&header, sizeof(header)) && Write(data, size);
262 }
263 
WriteData(const void * buf,size_t len)264 bool RecordFileWriter::WriteData(const void* buf, size_t len) {
265   if (!Write(buf, len)) {
266     return false;
267   }
268   data_section_size_ += len;
269   return true;
270 }
271 
Write(const void * buf,size_t len)272 bool RecordFileWriter::Write(const void* buf, size_t len) {
273   if (len != 0u && fwrite(buf, len, 1, record_fp_) != 1) {
274     PLOG(ERROR) << "failed to write to record file '" << filename_ << "'";
275     return false;
276   }
277   return true;
278 }
279 
Read(void * buf,size_t len)280 bool RecordFileWriter::Read(void* buf, size_t len) {
281   if (len != 0u && fread(buf, len, 1, record_fp_) != 1) {
282     PLOG(ERROR) << "failed to read record file '" << filename_ << "'";
283     return false;
284   }
285   return true;
286 }
287 
ReadDataSection(const std::function<void (const Record *)> & callback)288 bool RecordFileWriter::ReadDataSection(const std::function<void(const Record*)>& callback) {
289   if (fseek(record_fp_, data_section_offset_, SEEK_SET) == -1) {
290     PLOG(ERROR) << "fseek() failed";
291     return false;
292   }
293   std::vector<char> record_buf(512);
294   std::unique_ptr<Decompressor> decompressor;
295   uint64_t read_pos = 0;
296   while (read_pos < data_section_size_) {
297     if (!Read(record_buf.data(), Record::header_size())) {
298       return false;
299     }
300     RecordHeader header;
301     if (!header.Parse(record_buf.data())) {
302       return false;
303     }
304     if (record_buf.size() < header.size) {
305       record_buf.resize(header.size);
306     }
307     if (!Read(record_buf.data() + Record::header_size(), header.size - Record::header_size())) {
308       return false;
309     }
310     read_pos += header.size;
311 
312     if (header.type == PERF_RECORD_COMPRESSED) {
313       if (!decompressor) {
314         decompressor = CreateZstdDecompressor();
315         if (!decompressor) {
316           return false;
317         }
318       }
319       if (!decompressor->AddInputData(record_buf.data() + Record::header_size(),
320                                       header.size - Record::header_size())) {
321         return false;
322       }
323       if (!ReadFromDecompressor(*decompressor, callback)) {
324         return false;
325       }
326     } else {
327       std::unique_ptr<Record> r = ReadRecordFromBuffer(event_attr_, header.type, record_buf.data(),
328                                                        record_buf.data() + header.size);
329       CHECK(r);
330       if (r->type() == PERF_RECORD_AUXTRACE) {
331         auto auxtrace = static_cast<AuxTraceRecord*>(r.get());
332         auxtrace->location.file_offset = data_section_offset_ + read_pos;
333         if (fseek(record_fp_, auxtrace->data->aux_size, SEEK_CUR) != 0) {
334           PLOG(ERROR) << "fseek() failed";
335           return false;
336         }
337         read_pos += auxtrace->data->aux_size;
338       }
339       callback(r.get());
340     }
341   }
342   return true;
343 }
344 
ReadFromDecompressor(Decompressor & decompressor,const std::function<void (const Record *)> & callback)345 bool RecordFileWriter::ReadFromDecompressor(Decompressor& decompressor,
346                                             const std::function<void(const Record*)>& callback) {
347   std::string_view output = decompressor.GetOutputData();
348   char* p = const_cast<char*>(output.data());
349   size_t left_size = output.size();
350   while (left_size >= sizeof(perf_event_header)) {
351     auto header = reinterpret_cast<const perf_event_header*>(p);
352     if (header->size > left_size) {
353       break;
354     }
355     std::unique_ptr<Record> r =
356         ReadRecordFromBuffer(event_attr_, header->type, p, p + header->size);
357     if (!r) {
358       return false;
359     }
360     callback(r.get());
361     p += header->size;
362     left_size -= header->size;
363   }
364   decompressor.ConsumeOutputData(output.size() - left_size);
365   return true;
366 }
367 
GetFilePos(uint64_t * file_pos)368 bool RecordFileWriter::GetFilePos(uint64_t* file_pos) {
369   off_t offset = ftello(record_fp_);
370   if (offset == -1) {
371     PLOG(ERROR) << "ftello() failed";
372     return false;
373   }
374   *file_pos = static_cast<uint64_t>(offset);
375   return true;
376 }
377 
BeginWriteFeatures(size_t feature_count)378 bool RecordFileWriter::BeginWriteFeatures(size_t feature_count) {
379   feature_section_offset_ = data_section_offset_ + data_section_size_;
380   feature_count_ = feature_count;
381   uint64_t feature_header_size = feature_count * sizeof(SectionDesc);
382 
383   // Reserve enough space in the record file for the feature header.
384   std::vector<unsigned char> zero_data(feature_header_size);
385   if (fseek(record_fp_, feature_section_offset_, SEEK_SET) == -1) {
386     PLOG(ERROR) << "fseek() failed";
387     return false;
388   }
389   return Write(zero_data.data(), zero_data.size());
390 }
391 
WriteBuildIdFeature(const std::vector<BuildIdRecord> & build_id_records)392 bool RecordFileWriter::WriteBuildIdFeature(const std::vector<BuildIdRecord>& build_id_records) {
393   if (!WriteFeatureBegin(FEAT_BUILD_ID)) {
394     return false;
395   }
396   for (auto& record : build_id_records) {
397     if (!Write(record.Binary(), record.size())) {
398       return false;
399     }
400   }
401   return WriteFeatureEnd(FEAT_BUILD_ID);
402 }
403 
WriteStringWithLength(const std::string & s)404 bool RecordFileWriter::WriteStringWithLength(const std::string& s) {
405   uint32_t len = static_cast<uint32_t>(Align(s.size() + 1, 64));
406   if (!Write(&len, sizeof(len))) {
407     return false;
408   }
409   if (!Write(&s[0], s.size() + 1)) {
410     return false;
411   }
412   size_t pad_size = Align(s.size() + 1, 64) - s.size() - 1;
413   if (pad_size > 0u) {
414     char align_buf[pad_size];
415     memset(align_buf, '\0', pad_size);
416     if (!Write(align_buf, pad_size)) {
417       return false;
418     }
419   }
420   return true;
421 }
422 
WriteFeatureString(int feature,const std::string & s)423 bool RecordFileWriter::WriteFeatureString(int feature, const std::string& s) {
424   if (!WriteFeatureBegin(feature)) {
425     return false;
426   }
427   if (!WriteStringWithLength(s)) {
428     return false;
429   }
430   return WriteFeatureEnd(feature);
431 }
432 
WriteCmdlineFeature(const std::vector<std::string> & cmdline)433 bool RecordFileWriter::WriteCmdlineFeature(const std::vector<std::string>& cmdline) {
434   if (!WriteFeatureBegin(FEAT_CMDLINE)) {
435     return false;
436   }
437   uint32_t arg_count = cmdline.size();
438   if (!Write(&arg_count, sizeof(arg_count))) {
439     return false;
440   }
441   for (auto& arg : cmdline) {
442     if (!WriteStringWithLength(arg)) {
443       return false;
444     }
445   }
446   return WriteFeatureEnd(FEAT_CMDLINE);
447 }
448 
WriteBranchStackFeature()449 bool RecordFileWriter::WriteBranchStackFeature() {
450   if (!WriteFeatureBegin(FEAT_BRANCH_STACK)) {
451     return false;
452   }
453   return WriteFeatureEnd(FEAT_BRANCH_STACK);
454 }
455 
WriteAuxTraceFeature(const std::vector<uint64_t> & auxtrace_offset)456 bool RecordFileWriter::WriteAuxTraceFeature(const std::vector<uint64_t>& auxtrace_offset) {
457   std::vector<uint64_t> data;
458   for (auto offset : auxtrace_offset) {
459     data.push_back(offset);
460     data.push_back(AuxTraceRecord::Size());
461   }
462   return WriteFeature(FEAT_AUXTRACE, reinterpret_cast<char*>(data.data()),
463                       data.size() * sizeof(uint64_t));
464 }
465 
WriteFileFeatures(const std::vector<Dso * > & dsos)466 bool RecordFileWriter::WriteFileFeatures(const std::vector<Dso*>& dsos) {
467   for (Dso* dso : dsos) {
468     // Always want to dump dex file offsets for DSO_DEX_FILE type.
469     if (!dso->HasDumpId() && dso->type() != DSO_DEX_FILE) {
470       continue;
471     }
472     FileFeature file;
473     file.path = dso->Path();
474     file.type = dso->type();
475     dso->GetMinExecutableVaddr(&file.min_vaddr, &file.file_offset_of_min_vaddr);
476 
477     // Dumping all symbols in hit files takes too much space, so only dump
478     // needed symbols.
479     const std::vector<Symbol>& symbols = dso->GetSymbols();
480     for (const auto& sym : symbols) {
481       if (sym.HasDumpId()) {
482         file.symbol_ptrs.emplace_back(&sym);
483       }
484     }
485     std::sort(file.symbol_ptrs.begin(), file.symbol_ptrs.end(), Symbol::CompareByAddr);
486 
487     if (const auto dex_file_offsets = dso->DexFileOffsets(); dex_file_offsets != nullptr) {
488       file.dex_file_offsets = *dex_file_offsets;
489     }
490     if (!WriteFileFeature(file)) {
491       return false;
492     }
493   }
494   return true;
495 }
496 
WriteFileFeature(const FileFeature & file)497 bool RecordFileWriter::WriteFileFeature(const FileFeature& file) {
498   proto::FileFeature proto_file;
499   proto_file.set_path(file.path);
500   proto_file.set_type(static_cast<uint32_t>(file.type));
501   proto_file.set_min_vaddr(file.min_vaddr);
502   auto write_symbol = [&](const Symbol& symbol) {
503     proto::FileFeature::Symbol* proto_symbol = proto_file.add_symbol();
504     proto_symbol->set_vaddr(symbol.addr);
505     proto_symbol->set_len(symbol.len);
506     // Store demangled names for rust symbols. Because simpleperf on windows host doesn't know
507     // how to demangle them.
508     if (strncmp(symbol.Name(), "_R", 2) == 0) {
509       proto_symbol->set_name(symbol.DemangledName());
510     } else {
511       proto_symbol->set_name(symbol.Name());
512     }
513   };
514   for (const Symbol& symbol : file.symbols) {
515     write_symbol(symbol);
516   }
517   for (const Symbol* symbol_ptr : file.symbol_ptrs) {
518     write_symbol(*symbol_ptr);
519   }
520   if (file.type == DSO_DEX_FILE) {
521     proto::FileFeature::DexFile* proto_dex_file = proto_file.mutable_dex_file();
522     proto_dex_file->mutable_dex_file_offset()->Add(file.dex_file_offsets.begin(),
523                                                    file.dex_file_offsets.end());
524   } else if (file.type == DSO_ELF_FILE) {
525     proto::FileFeature::ElfFile* proto_elf_file = proto_file.mutable_elf_file();
526     proto_elf_file->set_file_offset_of_min_vaddr(file.file_offset_of_min_vaddr);
527   } else if (file.type == DSO_KERNEL_MODULE) {
528     proto::FileFeature::KernelModule* proto_kernel_module = proto_file.mutable_kernel_module();
529     proto_kernel_module->set_memory_offset_of_min_vaddr(file.file_offset_of_min_vaddr);
530   }
531   std::string s;
532   if (!proto_file.SerializeToString(&s)) {
533     LOG(ERROR) << "SerializeToString() failed";
534     return false;
535   }
536   uint32_t msg_size = s.size();
537   return WriteFeatureBegin(FEAT_FILE2) && Write(&msg_size, sizeof(uint32_t)) &&
538          Write(s.data(), s.size()) && WriteFeatureEnd(FEAT_FILE2);
539 }
540 
WriteMetaInfoFeature(const std::unordered_map<std::string,std::string> & info_map)541 bool RecordFileWriter::WriteMetaInfoFeature(
542     const std::unordered_map<std::string, std::string>& info_map) {
543   uint32_t size = 0u;
544   for (auto& pair : info_map) {
545     size += pair.first.size() + 1;
546     size += pair.second.size() + 1;
547   }
548   std::vector<char> buf(size);
549   char* p = buf.data();
550   for (auto& pair : info_map) {
551     MoveToBinaryFormat(pair.first.c_str(), pair.first.size() + 1, p);
552     MoveToBinaryFormat(pair.second.c_str(), pair.second.size() + 1, p);
553   }
554   return WriteFeature(FEAT_META_INFO, buf.data(), buf.size());
555 }
556 
WriteDebugUnwindFeature(const DebugUnwindFeature & debug_unwind)557 bool RecordFileWriter::WriteDebugUnwindFeature(const DebugUnwindFeature& debug_unwind) {
558   GOOGLE_PROTOBUF_VERIFY_VERSION;
559   proto::DebugUnwindFeature proto_debug_unwind;
560   for (auto& file : debug_unwind) {
561     auto proto_file = proto_debug_unwind.add_file();
562     proto_file->set_path(file.path);
563     proto_file->set_size(file.size);
564   }
565   std::string s;
566   if (!proto_debug_unwind.SerializeToString(&s)) {
567     LOG(ERROR) << "SerializeToString() failed";
568     return false;
569   }
570   return WriteFeature(FEAT_DEBUG_UNWIND, s.data(), s.size());
571 }
572 
WriteInitMapFeature(const char * data,size_t size)573 bool RecordFileWriter::WriteInitMapFeature(const char* data, size_t size) {
574   if (!WriteFeatureBegin(FEAT_INIT_MAP)) {
575     return false;
576   }
577   if (compressor_) {
578     if (!compressor_->AddInputData(data, size) || !WriteCompressorOutput(false, false)) {
579       return false;
580     }
581   } else {
582     if (!Write(data, size)) {
583       return false;
584     }
585   }
586   return WriteFeatureEnd(FEAT_INIT_MAP);
587 }
588 
FinishWritingInitMapFeature()589 bool RecordFileWriter::FinishWritingInitMapFeature() {
590   if (compressor_) {
591     return WriteFeatureBegin(FEAT_INIT_MAP) && WriteCompressorOutput(true, false) &&
592            WriteFeatureEnd(FEAT_INIT_MAP);
593   }
594   return true;
595 }
596 
WriteFeature(int feature,const char * data,size_t size)597 bool RecordFileWriter::WriteFeature(int feature, const char* data, size_t size) {
598   return WriteFeatureBegin(feature) && Write(data, size) && WriteFeatureEnd(feature);
599 }
600 
WriteFeatureBegin(int feature)601 bool RecordFileWriter::WriteFeatureBegin(int feature) {
602   auto it = features_.find(feature);
603   if (it == features_.end()) {
604     CHECK_LT(features_.size(), feature_count_);
605     auto& sec = features_[feature];
606     if (!GetFilePos(&sec.offset)) {
607       return false;
608     }
609     // Ensure each feature section starts at a 8-byte aligned location.
610     // This is not needed for the current RecordFileReader implementation, but is helpful if we
611     // switch to a mapped file reader. So it's nice to have. But it's nice to have.
612     if (sec.offset & 7) {
613       std::vector<char> zero_data(8 - (sec.offset & 7), '\0');
614       if (!Write(zero_data.data(), zero_data.size())) {
615         return false;
616       }
617       sec.offset += zero_data.size();
618     }
619     sec.size = 0;
620   }
621   return true;
622 }
623 
WriteFeatureEnd(int feature)624 bool RecordFileWriter::WriteFeatureEnd(int feature) {
625   auto it = features_.find(feature);
626   if (it == features_.end()) {
627     return false;
628   }
629   uint64_t offset;
630   if (!GetFilePos(&offset)) {
631     return false;
632   }
633   it->second.size = offset - it->second.offset;
634   return true;
635 }
636 
EndWriteFeatures()637 bool RecordFileWriter::EndWriteFeatures() {
638   // Used features (features_.size()) should be <= allocated feature space.
639   CHECK_LE(features_.size(), feature_count_);
640   if (fseek(record_fp_, feature_section_offset_, SEEK_SET) == -1) {
641     PLOG(ERROR) << "fseek() failed";
642     return false;
643   }
644   for (const auto& pair : features_) {
645     if (!Write(&pair.second, sizeof(SectionDesc))) {
646       return false;
647     }
648   }
649   return true;
650 }
651 
WriteFileHeader()652 bool RecordFileWriter::WriteFileHeader() {
653   FileHeader header;
654   memset(&header, 0, sizeof(header));
655   memcpy(header.magic, PERF_MAGIC, sizeof(header.magic));
656   header.header_size = sizeof(header);
657   header.attr_size = sizeof(FileAttr);
658   header.attrs.offset = attr_section_offset_;
659   header.attrs.size = attr_section_size_;
660   header.data.offset = data_section_offset_;
661   header.data.size = data_section_size_;
662   for (const auto& pair : features_) {
663     int i = pair.first / 8;
664     int j = pair.first % 8;
665     header.features[i] |= (1 << j);
666   }
667 
668   if (fseek(record_fp_, 0, SEEK_SET) == -1) {
669     return false;
670   }
671   if (!Write(&header, sizeof(header))) {
672     return false;
673   }
674   return true;
675 }
676 
Close()677 bool RecordFileWriter::Close() {
678   CHECK(record_fp_ != nullptr);
679   bool result = true;
680 
681   // Write file header. We gather enough information to write file header only after
682   // writing data section and feature section.
683   if (!WriteFileHeader()) {
684     result = false;
685   }
686 
687   if (own_fp_ && fclose(record_fp_) != 0) {
688     PLOG(ERROR) << "failed to close record file '" << filename_ << "'";
689     result = false;
690   }
691   record_fp_ = nullptr;
692   return result;
693 }
694 
695 }  // namespace simpleperf
696