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 
CreateInstance(const std::string & filename)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 
RecordFileWriter(const std::string & filename,FILE * fp)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 
~RecordFileWriter()65 RecordFileWriter::~RecordFileWriter() {
66   if (record_fp_ != nullptr) {
67     Close();
68   }
69 }
70 
WriteAttrSection(const std::vector<EventAttrWithId> & attr_ids)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 
WriteRecord(const Record & record)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 
WriteData(const void * buf,size_t len)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 
Write(const void * buf,size_t len)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 
Read(void * buf,size_t len)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 
ReadDataSection(const std::function<void (const Record *)> & callback)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 
GetFilePos(uint64_t * file_pos)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 
BeginWriteFeatures(size_t feature_count)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 
WriteBuildIdFeature(const std::vector<BuildIdRecord> & build_id_records)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 
WriteStringWithLength(const std::string & s)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 
WriteFeatureString(int feature,const std::string & s)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 
WriteCmdlineFeature(const std::vector<std::string> & cmdline)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 
WriteBranchStackFeature()290 bool RecordFileWriter::WriteBranchStackFeature() {
291   if (!WriteFeatureBegin(FEAT_BRANCH_STACK)) {
292     return false;
293   }
294   return WriteFeatureEnd(FEAT_BRANCH_STACK);
295 }
296 
WriteFileFeature(const std::string & file_path,uint32_t file_type,uint64_t min_vaddr,const std::vector<const Symbol * > & symbols)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 
WriteFeatureBegin(int feature)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 
WriteFeatureEnd(int feature)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 
EndWriteFeatures()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 
WriteFileHeader()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 
Close()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