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
34 using namespace PerfFileFormat;
35
CreateInstance(const std::string & filename)36 std::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
RecordFileWriter(const std::string & filename,FILE * fp)52 RecordFileWriter::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
~RecordFileWriter()63 RecordFileWriter::~RecordFileWriter() {
64 if (record_fp_ != nullptr) {
65 Close();
66 }
67 }
68
WriteAttrSection(const std::vector<AttrWithId> & attr_ids)69 bool 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
WriteData(const void * buf,size_t len)120 bool 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
Write(const void * buf,size_t len)128 bool 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
SeekFileEnd(uint64_t * file_end)136 bool 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
WriteFeatureHeader(size_t feature_count)150 bool 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
WriteBuildIdFeature(const std::vector<BuildIdRecord> & build_id_records)164 bool 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
WriteFeatureString(int feature,const std::string & s)178 bool 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
WriteCmdlineFeature(const std::vector<std::string> & cmdline)195 bool 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
WriteBranchStackFeature()218 bool 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
WriteFeatureBegin(uint64_t * start_offset)226 bool 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
WriteFeatureEnd(int feature,uint64_t start_offset)234 bool 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
WriteFileHeader()256 bool 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
Close()281 bool 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