1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "perf_file_writer.h"
16
17 #include <cinttypes>
18 #include <cstdlib>
19 #include <unistd.h>
20
21 #include "utilities.h"
22 using namespace std::chrono;
23 namespace OHOS {
24 namespace Developtools {
25 namespace HiPerf {
~PerfFileWriter()26 PerfFileWriter::~PerfFileWriter()
27 {
28 // if file was not closed properly, remove it before exit
29 if (fp_ != nullptr) {
30 fclose(fp_);
31 fp_ = nullptr;
32 if (remove(fileName_.c_str()) != 0) {
33 HLOGE("fail to remove file(%s).", fileName_.c_str());
34 }
35 }
36 }
37
Open(const std::string & fileName,bool compressData)38 bool PerfFileWriter::Open(const std::string &fileName, bool compressData)
39 {
40 // check file existence, if exist, remove it
41 if (access(fileName.c_str(), F_OK) == 0) {
42 // file exists
43 if (remove(fileName.c_str()) != 0) {
44 char errInfo[ERRINFOLEN] = { 0 };
45 strerror_r(errno, errInfo, ERRINFOLEN);
46 printf("can't remove exist file(%s). %d:%s\n", fileName.c_str(), errno,
47 errInfo);
48 return false;
49 }
50 }
51 std::string resolvedPath = CanonicalizeSpecPath(fileName.c_str());
52 fp_ = fopen(resolvedPath.c_str(), "web+");
53 if (fp_ == nullptr) {
54 char errInfo[ERRINFOLEN] = { 0 };
55 strerror_r(errno, errInfo, ERRINFOLEN);
56 printf("can't create file(%s). %d:%s\n", fileName.c_str(), errno, errInfo);
57 return false;
58 }
59
60 fileName_ = fileName;
61 compressData_ = compressData;
62 attrSection_.offset = 0;
63 attrSection_.size = 0;
64 dataSection_ = attrSection_;
65 header_.size = sizeof(header_);
66 fileBuffer_.resize(WRITER_BUFFER_SIZE);
67 if (setvbuf(fp_, fileBuffer_.data(), _IOFBF, WRITER_BUFFER_SIZE) != 0) {
68 HLOGD("setvbuf failed");
69 }
70
71 return true;
72 }
73
Close()74 bool PerfFileWriter::Close()
75 {
76 HLOG_ASSERT(fp_ != nullptr);
77 bool rc = true;
78
79 if (!WriteHeader()) {
80 rc = false;
81 }
82 if (!WriteFeatureData()) {
83 rc = false;
84 }
85
86 if (fclose(fp_) != 0) {
87 HLOGD("fail to close fp ");
88 rc = false;
89 }
90 fp_ = nullptr;
91
92 // compress and rename .perf.data to gzip file
93 if (compressData_) {
94 std::string gzName = fileName_ + ".gz";
95 if (CompressFile(fileName_, gzName)) {
96 if (remove(fileName_.c_str()) != 0) {
97 char errInfo[ERRINFOLEN] = { 0 };
98 strerror_r(errno, errInfo, ERRINFOLEN);
99 printf("can't remove file(%s). %d:%s\n",
100 fileName_.c_str(), errno, errInfo);
101 }
102 if (rename(gzName.c_str(), fileName_.c_str()) != 0) {
103 char errInfo[ERRINFOLEN] = { 0 };
104 strerror_r(errno, errInfo, ERRINFOLEN);
105 printf("can't rename file(%s) to (%s). %d:%s\n", gzName.c_str(), fileName_.c_str(),
106 errno, errInfo);
107 }
108 } else {
109 char errInfo[ERRINFOLEN] = { 0 };
110 strerror_r(errno, errInfo, ERRINFOLEN);
111 printf("failed to compress file(%s). %d:%s\n", fileName_.c_str(), errno,
112 errInfo);
113 }
114 }
115
116 return rc;
117 }
118
WriteRecord(const PerfEventRecord & record)119 bool PerfFileWriter::WriteRecord(const PerfEventRecord &record)
120 {
121 if (!isWritingRecord) {
122 HLOGV("need write <attrs> first");
123 return false;
124 }
125
126 HLOGV("write '%s'", record.GetName().c_str());
127
128 if (record.GetSize() > RECORD_SIZE_LIMIT) {
129 HLOGD("%s record size exceed limit", record.GetName().c_str());
130 return false;
131 }
132 // signal 7 (SIGBUS), code 1 (BUS_ADRALN), fault addr 0xb64eb195
133 static std::vector<u8> buf(RECORD_SIZE_LIMIT);
134
135 if (!record.GetBinary(buf)) {
136 return false;
137 }
138
139 if (!Write(buf.data(), record.GetSize())) {
140 return false;
141 }
142 dataSection_.size += record.GetSize();
143
144 ++recordCount_;
145
146 return true;
147 }
148
ReadDataSection(ProcessRecordCB & callback)149 bool PerfFileWriter::ReadDataSection(ProcessRecordCB &callback)
150 {
151 HLOG_ASSERT(fp_ != nullptr);
152 if (fseek(fp_, dataSection_.offset, SEEK_SET) != 0) {
153 HLOGE("fseek() failed");
154 return false;
155 }
156 HLOGD("dataSection_ at offset %" PRIu64 " + %" PRIu64 "", dataSection_.offset,
157 dataSection_.size);
158
159 return ReadRecords(callback);
160 }
161
ReadRecords(ProcessRecordCB & callback)162 bool PerfFileWriter::ReadRecords(ProcessRecordCB &callback)
163 {
164 // record size can not exceed 64K
165 HIPERF_BUF_ALIGN uint8_t buf[RECORD_SIZE_LIMIT];
166 // diff with reader
167 uint64_t remainingSize = dataSection_.size;
168 size_t recordNumber = 0;
169 while (remainingSize > 0) {
170 if (remainingSize < sizeof(perf_event_header)) {
171 HLOGW("not enough sizeof(perf_event_header).");
172 return false;
173 } else if (Read(buf, sizeof(perf_event_header)) <= 0) {
174 HLOGW("read perf_event_header failed.");
175 return false;
176 } else {
177 perf_event_header *header = reinterpret_cast<perf_event_header *>(buf);
178 HLOG_ASSERT(header->size < sizeof(buf));
179 if (remainingSize >= header->size) {
180 size_t headerSize = sizeof(perf_event_header);
181 if (Read(buf + headerSize, header->size - headerSize)) {
182 uint8_t *data = buf;
183 auto record = GetPerfEventRecord(static_cast<perf_event_type>(header->type),
184 data, defaultEventAttr_);
185 // skip unknown record
186 if (record == nullptr)
187 return true;
188 remainingSize -= header->size;
189 // call callback to process, then destroy record
190 callback(std::move(record));
191 recordNumber++;
192 }
193 } else {
194 HLOGW("not enough header->size.");
195 }
196 }
197 }
198 HLOGD("read back %zu records", recordNumber);
199 return true;
200 }
201
Read(void * buf,size_t len)202 bool PerfFileWriter::Read(void *buf, size_t len)
203 {
204 HLOG_ASSERT(buf != nullptr);
205 HLOG_ASSERT(fp_ != nullptr);
206 HLOG_ASSERT(len > 0);
207
208 if (fread(buf, len, 1, fp_) != 1) {
209 HLOGD("failed to read file");
210 return false;
211 }
212 return true;
213 }
214
GetDataSize() const215 uint64_t PerfFileWriter::GetDataSize() const
216 {
217 return dataSection_.size;
218 }
219
GetRecordCount() const220 uint PerfFileWriter::GetRecordCount() const
221 {
222 return recordCount_;
223 }
224
GetFilePos(uint64_t & pos) const225 bool PerfFileWriter::GetFilePos(uint64_t &pos) const
226 {
227 off_t offset = ftello(fp_);
228 if (offset == -1) {
229 HLOGD("RecordFileWriter ftello fail");
230 return false;
231 }
232 pos = static_cast<uint64_t>(offset);
233 return true;
234 }
235
Write(const void * buf,size_t len)236 bool PerfFileWriter::Write(const void *buf, size_t len)
237 {
238 #ifdef HIPERF_DEBUG_TIME
239 const auto startTime = steady_clock::now();
240 #endif
241 if (len != 0u && fwrite(buf, len, 1, fp_) != 1) {
242 HLOGD("PerfFileWriter fwrite fail ");
243 return false;
244 }
245 #ifdef HIPERF_DEBUG_TIME
246 writeTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
247 #endif
248 return true;
249 }
250
WriteAttrAndId(const std::vector<AttrWithId> & attrIds)251 bool PerfFileWriter::WriteAttrAndId(const std::vector<AttrWithId> &attrIds)
252 {
253 if (attrIds.empty()) {
254 return false;
255 }
256
257 // Skip file header part.
258 if (fp_ == nullptr) {
259 return false;
260 } else if (fseek(fp_, header_.size, SEEK_SET) == -1) {
261 return false;
262 }
263
264 // Write id section.
265 uint64_t idSectionOffset;
266 if (!GetFilePos(idSectionOffset)) {
267 return false;
268 }
269
270 HLOGD("attrIds %zu", attrIds.size());
271 for (auto &attrId : attrIds) {
272 HLOGD(" attrIds ids %zu", attrId.ids.size());
273 if (!Write(attrId.ids.data(), attrId.ids.size() * sizeof(uint64_t))) {
274 return false;
275 }
276 }
277
278 // Write attr section.
279 uint64_t attrSectionOffset;
280 if (!GetFilePos(attrSectionOffset)) {
281 return false;
282 }
283 for (auto &attrId : attrIds) {
284 perf_file_attr fileAttr;
285 fileAttr.attr = attrId.attr;
286 fileAttr.ids.offset = idSectionOffset;
287 fileAttr.ids.size = attrId.ids.size() * sizeof(uint64_t);
288 idSectionOffset += fileAttr.ids.size;
289
290 if (!Write(&fileAttr, sizeof(fileAttr))) {
291 return false;
292 }
293 }
294
295 uint64_t dataSectionOffset;
296 if (!GetFilePos(dataSectionOffset)) {
297 return false;
298 }
299
300 attrSection_.offset = attrSectionOffset;
301 attrSection_.size = dataSectionOffset - attrSectionOffset;
302 dataSection_.offset = dataSectionOffset;
303
304 defaultEventAttr_ = attrIds[0].attr;
305
306 isWritingRecord = true;
307
308 return true;
309 }
310
LeftLessRight(const std::unique_ptr<PerfFileSection> & l,const std::unique_ptr<PerfFileSection> & r)311 static bool LeftLessRight(const std::unique_ptr<PerfFileSection> &l,
312 const std::unique_ptr<PerfFileSection> &r)
313 {
314 return l->featureId_ < r->featureId_;
315 }
316 // to write perf_file_header to file
317 // can only be called after all attr and records has been written.
WriteHeader()318 bool PerfFileWriter::WriteHeader()
319 {
320 header_.attrSize = sizeof(perf_file_attr);
321 header_.attrs = attrSection_;
322 header_.data = dataSection_;
323
324 // ignore event_types
325 header_.eventTypes.size = 0;
326 header_.eventTypes.offset = 0;
327
328 // save header
329 if (fseek(fp_, 0, SEEK_SET) == -1) {
330 HLOGD("fseek return error ");
331 return false;
332 }
333 if (!Write(&header_, sizeof(header_))) {
334 return false;
335 }
336 return true;
337 }
338
WriteFeatureData()339 bool PerfFileWriter::WriteFeatureData()
340 {
341 long featureOffset = 0;
342 if (fseek(fp_, 0, SEEK_END) != 0) {
343 HLOGD("fseek SEEK_END return error ");
344 return false;
345 }
346 if ((featureOffset = ftell(fp_)) == -1) {
347 HLOGD("ftell return error ");
348 return false;
349 }
350
351 for (size_t i = 0; i < sizeof(header_.features); i++) {
352 if (header_.features[i] != 0) {
353 HLOGV(" features['%zu'] '0x%x'", i, header_.features[i]);
354 }
355 }
356
357 unsigned long contentOffset = featureOffset + featureSections_.size() * sizeof(perf_file_section);
358 HLOGV("features start at file '0x%lx' content at '0x%lx'", featureOffset, contentOffset);
359
360 // reorder
361 std::sort(featureSections_.begin(), featureSections_.end(), LeftLessRight);
362 // save features head
363 int i = 0;
364 for (auto &featureSection : featureSections_) {
365 featureSection->header.offset = contentOffset;
366 featureSection->header.size = featureSection->GetSize();
367 contentOffset += featureSection->header.size;
368 HLOGV("save features[%d] head offset '0x%" PRIx64 "' size '0x%" PRIx64 "'", i,
369 featureSection->header.offset, featureSection->header.size);
370 i++;
371
372 // write features heads
373 if (!Write(&featureSection->header, sizeof(featureSection->header))) {
374 return false;
375 }
376 }
377 long offset = ftell(fp_);
378 if (offset < 0) {
379 return false;
380 }
381 HLOGV("features data at file '0x%lx'", offset);
382
383 i = 0;
384 for (auto &featureSection : featureSections_) {
385 std::vector<char> buf(featureSection->header.size);
386 featureSection->GetBinary(&buf[0], featureSection->header.size);
387 HLOGV("save features[%d] content size '0x%" PRIx64 "'", i, featureSection->header.size);
388 i++;
389
390 // write features content
391 if (!Write(&buf[0], featureSection->header.size)) {
392 HLOGE("write failed %" PRIu64 ".", featureSection->header.size);
393 return false;
394 }
395 }
396
397 return true;
398 }
399
AddNrCpusFeature(FEATURE feature,uint32_t nrCpusAvailable,uint32_t nrCpusOnline)400 bool PerfFileWriter::AddNrCpusFeature(FEATURE feature, uint32_t nrCpusAvailable,
401 uint32_t nrCpusOnline)
402 {
403 HLOGV("add feature '%s': nrCpusAvailable %u, nrCpusOnline %u",
404 PerfFileSection::GetFeatureName(FEATURE::NRCPUS).c_str(), nrCpusAvailable, nrCpusOnline);
405 featureSections_.emplace_back(
406 std::make_unique<PerfFileSectionNrCpus>(feature, nrCpusAvailable, nrCpusOnline));
407
408 // update header feature bits
409 header_.features[(int)FEATURE::NRCPUS / BITS_IN_BYTE] |=
410 1 << ((int)FEATURE::NRCPUS % BITS_IN_BYTE); // bit
411 return true;
412 }
413
AddEventDescFeature(FEATURE feature,const std::vector<AttrWithId> & eventDesces)414 bool PerfFileWriter::AddEventDescFeature(FEATURE feature,
415 const std::vector<AttrWithId> &eventDesces)
416 {
417 HLOGV("add feature '%s' %zu", PerfFileSection::GetFeatureName(FEATURE::EVENT_DESC).c_str(),
418 eventDesces.size());
419 featureSections_.emplace_back(std::make_unique<PerfFileSectionEventDesc>(feature, eventDesces));
420
421 header_.features[(int)FEATURE::EVENT_DESC / BITS_IN_BYTE] |=
422 1 << ((int)FEATURE::EVENT_DESC % BITS_IN_BYTE); // bit
423 return true;
424 }
425
AddStringFeature(FEATURE feature,std::string string)426 bool PerfFileWriter::AddStringFeature(FEATURE feature, std::string string)
427 {
428 HLOGV("add feature '%s' string '%s'", PerfFileSection::GetFeatureName(feature).c_str(),
429 string.c_str());
430 featureSections_.emplace_back(std::make_unique<PerfFileSectionString>(feature, string));
431
432 // update header feature bits
433 header_.features[(int)feature / BITS_IN_BYTE] |= 1 << ((int)feature % BITS_IN_BYTE); // bit
434 return true;
435 }
436
AddBoolFeature(FEATURE feature)437 bool PerfFileWriter::AddBoolFeature(FEATURE feature)
438 {
439 // same as u64, just use 1 as value
440 return AddU64Feature(feature, 1u);
441 }
442
AddU64Feature(FEATURE feature,uint64_t v)443 bool PerfFileWriter::AddU64Feature(FEATURE feature, uint64_t v)
444 {
445 HLOGV("add feature '%s' uint64_t '%" PRIu64 "'",
446 PerfFileSection::GetFeatureName(feature).c_str(), v);
447 featureSections_.emplace_back(std::make_unique<PerfFileSectionU64>(feature, v));
448
449 // update header feature bits
450 header_.features[(int)feature / BITS_IN_BYTE] |= 1 << ((int)feature % BITS_IN_BYTE); // bit
451 return true;
452 }
453
AddSymbolsFeature(const std::vector<std::unique_ptr<SymbolsFile>> & symbolsFiles)454 bool PerfFileWriter::AddSymbolsFeature(
455 const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)
456 {
457 const FEATURE feature = FEATURE::HIPERF_FILES_SYMBOL;
458 HLOGV("add feature symbolsFiles %zu", symbolsFiles.size());
459 std::vector<SymbolFileStruct> symbolFileStructs {};
460 for (auto &symbolsFile : symbolsFiles) {
461 if (symbolsFile->SymbolsLoaded()) {
462 auto &symbolsFileStruct = symbolFileStructs.emplace_back();
463 symbolsFile->ExportSymbolToFileFormat(symbolsFileStruct);
464 }
465 }
466 featureSections_.emplace_back(
467 std::make_unique<PerfFileSectionSymbolsFiles>(feature, symbolFileStructs));
468 // update header feature bits
469 header_.features[(int)feature / BITS_IN_BYTE] |= 1 << ((int)feature % BITS_IN_BYTE); // bit
470 return true;
471 }
472 } // namespace HiPerf
473 } // namespace Developtools
474 } // namespace OHOS
475