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