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 
CreateInstance(const std::string & filename,const perf_event_attr & event_attr,const std::vector<std::unique_ptr<EventFd>> & event_fds)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 
RecordFileWriter(const std::string & filename,FILE * fp)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 
~RecordFileWriter()62 RecordFileWriter::~RecordFileWriter() {
63   if (record_fp_ != nullptr) {
64     Close();
65   }
66 }
67 
WriteAttrSection(const perf_event_attr & event_attr,const std::vector<std::unique_ptr<EventFd>> & event_fds)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 
WriteData(const void * buf,size_t len)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 
Write(const void * buf,size_t len)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 
GetHitModulesInBuffer(const char * p,const char * end,std::vector<std::string> * hit_kernel_modules,std::vector<std::string> * hit_user_files)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 
GetHitModules(std::vector<std::string> * hit_kernel_modules,std::vector<std::string> * hit_user_files)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 
WriteFeatureHeader(size_t feature_count)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 
WriteBuildIdFeature(const std::vector<BuildIdRecord> & build_id_records)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 
WriteFileHeader()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 
Close()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 
CreateInstance(const std::string & filename)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 
RecordFileReader(const std::string & filename,int fd)334 RecordFileReader::RecordFileReader(const std::string& filename, int fd)
335     : filename_(filename), record_fd_(fd), mmap_addr_(nullptr), mmap_len_(0) {
336 }
337 
~RecordFileReader()338 RecordFileReader::~RecordFileReader() {
339   if (record_fd_ != -1) {
340     Close();
341   }
342 }
343 
Close()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 
MmapFile()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 
FileHeader()375 const FileHeader* RecordFileReader::FileHeader() {
376   return reinterpret_cast<const struct FileHeader*>(mmap_addr_);
377 }
378 
AttrSection()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 
IdsForAttr(const FileAttr * attr)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 
DataSection()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 
FeatureSectionDescriptors()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