1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. All rights reserved.
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 "trace_file_writer.h"
16
17 #include <cinttypes>
18 #include <fcntl.h>
19 #include <memory>
20 #include <sys/mman.h>
21 #include <sys/statvfs.h>
22 #include <unistd.h>
23 #include <cstdio>
24
25 #include "common.h"
26 #ifdef LITE_PROTO
27 #include "common_types_lite.pb.h"
28 #else
29 #include "common_types.pb.h"
30 #endif
31 #include "logging.h"
32
33 using CharPtr = std::unique_ptr<char>::pointer;
34 using ConstCharPtr = std::unique_ptr<const char>::pointer;
35
36 namespace {
37 constexpr int MB_TO_BYTE = (1024 * 1024);
38 constexpr int GB_TO_BYTE = (1024 * 1024 * 1024);
39 constexpr int SPLIT_FILE_MIN_SIZE = 200; // split file min size
40 constexpr int SPLIT_FILE_DEFAULT_NUM = 10; // split file default num
41 constexpr size_t DEFULT_PAGES = 64 * 256; // 64M
42 const int PIECE_HEAD_LEN = 4;
43 } // namespace
44
TraceFileWriter(const std::string & path)45 TraceFileWriter::TraceFileWriter(const std::string& path) : TraceFileWriter(path, false, 0, 0) {}
46
TraceFileWriter(int32_t fd)47 TraceFileWriter::TraceFileWriter(int32_t fd) : fd_(fd)
48 {
49 if (write(fd_, &header_, sizeof(header_)) != sizeof(header_)) {
50 PROFILER_LOG_ERROR(LOG_CORE, "write initial header failed!, error: %s", strerror(errno));
51 }
52 (void)FlushStream();
53 fileWriteLength_ = sizeof(header_);
54 mapOffset_ = sizeof(header_);
55 messageWriteOffset_ = PIECE_HEAD_LEN;
56 pageSize_ = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
57 fileLength_ = DEFULT_PAGES * pageSize_;
58
59 if (ftruncate(fd_, fileLength_) != 0) {
60 PROFILER_LOG_ERROR(LOG_CORE, "ftruncate file(%zu) failed, error: %s", fileLength_, strerror(errno));
61 return;
62 }
63
64 fileMapAddr_ = mmap(nullptr, fileLength_, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd_, 0);
65 if (fileMapAddr_ == MAP_FAILED) {
66 PROFILER_LOG_ERROR(LOG_CORE, "mmap file(%d) failed, error: %s", fd_, strerror(errno));
67 return;
68 }
69
70 writeCtx_.write = this;
71 writeCtx_.ctx.getMemory = [](RandomWriteCtx* ctx, uint32_t size, uint8_t** memory, uint32_t* offset) -> bool {
72 TraceFileWriterCtx* writeCtx = reinterpret_cast<TraceFileWriterCtx*>(ctx);
73 return writeCtx->write->GetMemory(size, memory, offset);
74 };
75 writeCtx_.ctx.seek = [](RandomWriteCtx* ctx, uint32_t offset) -> bool {
76 TraceFileWriterCtx* writeCtx = reinterpret_cast<TraceFileWriterCtx*>(ctx);
77 return writeCtx->write->Seek(offset);
78 };
79 }
80
TraceFileWriter(const std::string & path,bool splitFile,uint32_t splitFileMaxSizeMb,uint32_t splitFileMaxNum)81 TraceFileWriter::TraceFileWriter(const std::string& path, bool splitFile, uint32_t splitFileMaxSizeMb,
82 uint32_t splitFileMaxNum) : path_(path), isSplitFile_(splitFile)
83 {
84 splitFileMaxSize_ = (splitFileMaxSizeMb < SPLIT_FILE_MIN_SIZE) ? (SPLIT_FILE_MIN_SIZE * MB_TO_BYTE) :
85 (splitFileMaxSizeMb * MB_TO_BYTE);
86 splitFileMaxNum_ = (splitFileMaxNum == 0) ? SPLIT_FILE_DEFAULT_NUM : splitFileMaxNum;
87 oldPath_ = path;
88 fileNum_ = 1;
89
90 WriteHeader();
91 (void)FlushStream();
92 }
93
~TraceFileWriter()94 TraceFileWriter::~TraceFileWriter()
95 {
96 (void)FlushStream();
97 if (stream_.is_open()) {
98 stream_.close();
99 }
100 if (fileMapAddr_ != MMAP_FAILED) {
101 munmap(fileMapAddr_, fileLength_);
102 }
103 }
104
Path() const105 std::string TraceFileWriter::Path() const
106 {
107 return path_;
108 }
109
SetPluginConfig(const void * data,size_t size)110 bool TraceFileWriter::SetPluginConfig(const void* data, size_t size)
111 {
112 if (isSplitFile_) {
113 std::vector<char> configVec;
114 auto configData = reinterpret_cast<ConstCharPtr>(data);
115 configVec.insert(configVec.end(), configData, configData + size);
116 pluginConfigsData_.push_back(std::move(configVec));
117 }
118
119 Write(data, size);
120 return true;
121 }
122
123 #ifdef LITE_PROTO
WriteStandalonePluginData(const std::string & pluginName,const std::string & data,const std::string & pluginVersion)124 void TraceFileWriter::WriteStandalonePluginData(
125 const std::string &pluginName, const std::string &data,
126 const std::string &pluginVersion)
127 {
128 LITE::ProfilerPluginData pluginData;
129 pluginData.set_name(pluginName);
130 pluginData.set_data(data);
131 if (!pluginVersion.empty()) {
132 pluginData.set_version(pluginVersion);
133 pluginData.set_status(0);
134
135 struct timespec ts = { 0, 0 };
136 clock_gettime(CLOCK_REALTIME, &ts);
137 pluginData.set_tv_sec(ts.tv_sec);
138 pluginData.set_tv_nsec(ts.tv_nsec);
139 pluginData.set_clock_id(LITE::ProfilerPluginData::CLOCKID_REALTIME);
140 }
141
142 std::vector<char> msgData(pluginData.ByteSizeLong());
143 if (pluginData.SerializeToArray(msgData.data(), msgData.size()) <= 0) {
144 PROFILER_LOG_WARN(LOG_CORE, "%s StandalonePluginData SerializeToArray failed!", pluginName.c_str());
145 }
146
147 Write(msgData.data(), msgData.size());
148 }
149 #endif
150
SetTimeStamp()151 void TraceFileWriter::SetTimeStamp()
152 {
153 header_.data_.boottime = headerDataTime_.boottime;
154 header_.data_.realtime = headerDataTime_.realtime;
155 header_.data_.realtimeCoarse = headerDataTime_.realtimeCoarse;
156 header_.data_.monotonic = headerDataTime_.monotonic;
157 header_.data_.monotonicCoarse = headerDataTime_.monotonicCoarse;
158 header_.data_.monotonicRaw = headerDataTime_.monotonicRaw;
159 header_.data_.durationNs = headerDataTime_.durationNs;
160 }
161
SetTimeSource()162 void TraceFileWriter::SetTimeSource()
163 {
164 constexpr uint64_t nanoSeconds = 1000000000;
165 struct timespec ts;
166 clock_gettime(CLOCK_BOOTTIME, &ts);
167 headerDataTime_.boottime = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
168 static_cast<uint64_t>(ts.tv_nsec);
169 clock_gettime(CLOCK_REALTIME, &ts);
170 headerDataTime_.realtime = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
171 static_cast<uint64_t>(ts.tv_nsec);
172 clock_gettime(CLOCK_REALTIME_COARSE, &ts);
173 headerDataTime_.realtimeCoarse = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
174 static_cast<uint64_t>(ts.tv_nsec);
175 clock_gettime(CLOCK_MONOTONIC, &ts);
176 headerDataTime_.monotonic = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
177 static_cast<uint64_t>(ts.tv_nsec);
178 clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
179 headerDataTime_.monotonicCoarse = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
180 static_cast<uint64_t>(ts.tv_nsec);
181 clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
182 headerDataTime_.monotonicRaw = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
183 static_cast<uint64_t>(ts.tv_nsec);
184 }
185
SetDurationTime()186 void TraceFileWriter::SetDurationTime()
187 {
188 struct timespec ts;
189 clock_gettime(CLOCK_BOOTTIME, &ts);
190 constexpr uint64_t nanoSeconds = 1000000000;
191 auto currBoottime = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds + static_cast<uint64_t>(ts.tv_nsec);
192 headerDataTime_.durationNs = currBoottime - headerDataTime_.boottime;
193 }
194
WriteHeader()195 bool TraceFileWriter::WriteHeader()
196 {
197 LogDiskUsage();
198 if (isSplitFile_) {
199 std::string timeStr = COMMON::GetTimeStr();
200 std::size_t pos = oldPath_.find_last_of('.');
201 if (pos != std::string::npos) {
202 path_ = oldPath_.substr(0, pos) + "_" + timeStr + "_" + std::to_string(fileNum_) +
203 oldPath_.substr(pos, oldPath_.size());
204 } else {
205 path_ = oldPath_ + "_" + timeStr + "_" + std::to_string(fileNum_);
206 }
207 splitFilePaths_.push(path_);
208 DeleteOldSplitFile();
209 }
210
211 stream_ = std::ofstream(path_, std::ios_base::out | std::ios_base::binary);
212 CHECK_TRUE(stream_.is_open(), false, "open %s failed, %d!", path_.c_str(), errno);
213
214 // write initial header, makes file write position move forward
215 helper_ = {};
216 header_ = {};
217 stream_.write(reinterpret_cast<CharPtr>(&header_), sizeof(header_));
218 CHECK_TRUE(stream_, false, "write initial header to %s failed!", path_.c_str());
219 dataSize_ = header_.HEADER_SIZE;
220 PROFILER_LOG_INFO(LOG_CORE, "write file(%s) header end", path_.c_str());
221 return true;
222 }
223
224 // delete first split file if split file num over max
DeleteOldSplitFile()225 void TraceFileWriter::DeleteOldSplitFile()
226 {
227 if (splitFilePaths_.size() <= splitFileMaxNum_) {
228 PROFILER_LOG_INFO(LOG_CORE, "splitFilePaths_ size %zu, no need to delete.", splitFilePaths_.size());
229 return;
230 }
231
232 std::string splitFilePath = splitFilePaths_.front();
233 int ret = unlink(splitFilePath.c_str());
234 PROFILER_LOG_INFO(LOG_CORE, "DeleteOldSplitFile remove %s return %d. ", splitFilePath.c_str(), ret);
235 splitFilePaths_.pop();
236 }
237
Write(const void * data,size_t size)238 long TraceFileWriter::Write(const void* data, size_t size)
239 {
240 if (isSplitFile_ && !isStop_) {
241 if (IsSplitFile(size)) {
242 return -1;
243 }
244 }
245
246 uint32_t dataLen = size;
247 CHECK_TRUE(stream_.is_open(), 0, "binary file %s not open or open failed!", path_.c_str());
248
249 // write 4B data length.
250 stream_.write(reinterpret_cast<CharPtr>(&dataLen), sizeof(dataLen));
251 CHECK_TRUE(stream_, 0, "binary file %s write raw buffer size failed!", path_.c_str());
252 CHECK_TRUE(helper_.AddSegment(reinterpret_cast<uint8_t*>(&dataLen), sizeof(dataLen)),
253 0, "Add payload for size %u FAILED!", dataLen);
254
255 // write data bytes
256 stream_.write(reinterpret_cast<ConstCharPtr>(data), size);
257 CHECK_TRUE(stream_, 0, "binary file %s write raw buffer data failed!", path_.c_str());
258
259 CHECK_TRUE(helper_.AddSegment(reinterpret_cast<uint8_t*>(const_cast<void*>(data)), size),
260 0, "Add payload for data bytes %zu FAILED!", size);
261
262 long nbytes = sizeof(dataLen) + size;
263 writeBytes_ += nbytes;
264 ++writeCount_;
265 return nbytes;
266 }
267
WriteStandalonePluginFile(const std::string & file,const std::string & name,const std::string & version,DataType type)268 long TraceFileWriter::WriteStandalonePluginFile(const std::string &file,
269 const std::string &name,
270 const std::string &version,
271 DataType type)
272 {
273 CHECK_TRUE(stream_.is_open(), 0, "binary file %s not open or open failed!", path_.c_str());
274
275 std::ifstream fsFile {}; // read data from file
276 fsFile.open(file, std::ios_base::in | std::ios_base::binary);
277 if (!fsFile.good()) {
278 PROFILER_LOG_ERROR(LOG_CORE, "open file(%s) failed: %d", file.c_str(), fsFile.rdstate());
279 return 0;
280 }
281
282 TraceFileHeader header {};
283 fsFile.seekg(0, std::ios_base::end);
284 uint64_t fileSize = static_cast<uint64_t>(fsFile.tellg());
285 header.data_.length += fileSize;
286 size_t size = name.size();
287 if (size > 0) {
288 if (size > PLUGIN_MODULE_NAME_MAX) {
289 PROFILER_LOG_ERROR(LOG_CORE, "standalonePluginName(%s) size(%zu) is greater than %d!",
290 name.c_str(), size, PLUGIN_MODULE_NAME_MAX);
291 } else if (strncpy_s(header.data_.standalonePluginName, PLUGIN_MODULE_NAME_MAX, name.c_str(), size) != EOK) {
292 PROFILER_LOG_ERROR(LOG_CORE, "strncpy_s standalonePluginName(%s) error!", name.c_str());
293 }
294 }
295 size = version.size();
296 if (size > 0) {
297 if (size > PLUGIN_MODULE_VERSION_MAX) {
298 PROFILER_LOG_ERROR(LOG_CORE, "pluginVersion(%s) size(%zu) is greater than %d!",
299 version.c_str(), size, PLUGIN_MODULE_VERSION_MAX);
300 } else if (strncpy_s(header.data_.pluginVersion, PLUGIN_MODULE_VERSION_MAX, version.c_str(), size) != EOK) {
301 PROFILER_LOG_ERROR(LOG_CORE, "strncpy_s pluginVersion(%s) error!", version.c_str());
302 }
303 }
304 header.data_.dataType = type;
305 stream_.write(reinterpret_cast<char*>(&header), sizeof(header));
306
307 constexpr uint64_t readBufSize = 4 * 1024 * 1024;
308 std::vector<char> readBuf(readBufSize);
309 uint64_t readSize = 0;
310 fsFile.seekg(0);
311 while ((readSize = std::min(readBufSize, fileSize)) > 0) {
312 fsFile.read(readBuf.data(), readSize);
313 stream_.write(readBuf.data(), readSize);
314
315 fileSize -= readSize;
316 writeBytes_ += readSize;
317 ++writeCount_;
318 }
319
320 fsFile.close();
321 return fileSize;
322 }
323
IsSplitFile(uint32_t size)324 bool TraceFileWriter::IsSplitFile(uint32_t size)
325 {
326 dataSize_ += sizeof(uint32_t) + size;
327 if (dataSize_ >= splitFileMaxSize_) {
328 PROFILER_LOG_INFO(LOG_CORE, "need to split the file(%s), data size:%d, size: %d, splitFileMaxSize_:%d",
329 path_.c_str(), dataSize_, size, splitFileMaxSize_);
330
331 // update old file header
332 SetDurationTime();
333 Finish();
334 if (stream_.is_open()) {
335 stream_.close();
336 }
337 fileNum_++;
338
339 // write header of the new file
340 if (!WriteHeader()) {
341 return false;
342 }
343 SetTimeSource();
344
345 // write the plugin config of the new file
346 for (size_t i = 0; i < pluginConfigsData_.size(); i++) {
347 Write(pluginConfigsData_[i].data(), pluginConfigsData_[i].size());
348 }
349 Flush();
350 return true;
351 }
352 return false;
353 }
354
Write(const MessageLite & message)355 long TraceFileWriter::Write(const MessageLite& message)
356 {
357 auto size = message.ByteSizeLong();
358 if (isSplitFile_ && !isStop_) {
359 if (IsSplitFile(size)) {
360 return -1;
361 }
362 }
363
364 // serialize message to bytes array
365 std::vector<char> msgData(size);
366 CHECK_TRUE(message.SerializeToArray(msgData.data(), msgData.size()), 0, "SerializeToArray failed!");
367
368 return Write(msgData.data(), msgData.size());
369 }
370
Finish()371 bool TraceFileWriter::Finish()
372 {
373 // update header info
374 helper_.Update(header_);
375 SetTimeStamp(); // add timestamp in header
376
377 if (fd_ != -1) {
378 if (lseek(fd_, 0, SEEK_SET) == -1) {
379 return false;
380 }
381 auto ret = write(fd_, &header_, sizeof(header_));
382 CHECK_TRUE(ret == sizeof(header_), false, "write initial header failed!, error: %s", strerror(errno));
383 CHECK_TRUE(ftruncate(fd_, fileWriteLength_) == 0, false, "ftruncate(%u) failed, error: %s",
384 fileWriteLength_, strerror(errno));
385 } else {
386 long long filePos = stream_.tellp();
387 if (filePos == -1) { // -1 :file not open or error
388 return false;
389 }
390 // move write position to begin of file
391 CHECK_TRUE(stream_.is_open(), false, "binary file %s not open or open failed!", path_.c_str());
392 stream_.seekp(0);
393 CHECK_TRUE(stream_, false, "seek write position to head for %s failed!", path_.c_str());
394
395 // write final header
396 stream_.write(reinterpret_cast<CharPtr>(&header_), sizeof(header_));
397 stream_.seekp(filePos);
398 CHECK_TRUE(stream_, false, "write final header to %s failed!", path_.c_str());
399 CHECK_TRUE(stream_.flush(), false, "binary file %s flush failed!", path_.c_str());
400 PROFILER_LOG_DEBUG(LOG_CORE, "Finish: %s, bytes: %" PRIu64 ", count: %" PRIu64, path_.c_str(), writeBytes_,
401 writeCount_);
402 }
403 return true;
404 }
405
Flush()406 bool TraceFileWriter::Flush()
407 {
408 return FlushStream();
409 }
410
FlushStream()411 bool TraceFileWriter::FlushStream()
412 {
413 if (fd_ == -1) {
414 CHECK_TRUE(stream_.is_open(), false, "binary file %s not open or open failed!", path_.c_str());
415 CHECK_TRUE(stream_.flush(), false, "binary file %s flush failed!", path_.c_str());
416 }
417 PROFILER_LOG_DEBUG(LOG_CORE, "flush: %s, bytes: %" PRIu64 ", count: %" PRIu64, path_.c_str(),
418 writeBytes_, writeCount_);
419 return true;
420 }
421
SetStopSplitFile(bool isStop)422 void TraceFileWriter::SetStopSplitFile(bool isStop)
423 {
424 isStop_ = isStop;
425 }
426
LogDiskUsage()427 void TraceFileWriter::LogDiskUsage()
428 {
429 std::string diskPath = "/data/local/tmp/";
430 std::string::size_type pos = oldPath_.find_last_of('/');
431 if (pos != std::string::npos) {
432 diskPath = oldPath_.substr(0, pos);
433 }
434
435 struct statvfs diskInfo;
436 int ret = statvfs(diskPath.c_str(), &diskInfo);
437 if (ret != 0) {
438 std::string errorMsg = COMMON::GetErrorMsg();
439 PROFILER_LOG_ERROR(LOG_CORE, "LogDiskUsage() return %d, path:%s, msg:%s",
440 ret, diskPath.c_str(), errorMsg.c_str());
441 return;
442 }
443
444 unsigned long long freeSize = static_cast<unsigned long long>(diskInfo.f_bsize) *
445 static_cast<unsigned long long>(diskInfo.f_bfree);
446 unsigned long long totalSize = static_cast<unsigned long long>(diskInfo.f_bsize) *
447 static_cast<unsigned long long>(diskInfo.f_blocks);
448 float freePercent = 0;
449 if (totalSize != 0) {
450 freePercent = static_cast<float>(freeSize) / static_cast<float>(totalSize);
451 }
452 uint32_t freeSizeGb = freeSize / GB_TO_BYTE;
453 // 100: in terms of percentage
454 PROFILER_LOG_INFO(LOG_CORE, "LogDiskUsage() freePercent:%.1f, freeSizeGb:%u", freePercent * 100, freeSizeGb);
455 }
456
RemapFile()457 bool TraceFileWriter::RemapFile()
458 {
459 if (munmap(fileMapAddr_, fileLength_) != 0) {
460 PROFILER_LOG_ERROR(LOG_CORE, "munmap file(%zu) failed, error: %s", fileLength_, strerror(errno));
461 return false;
462 }
463
464 // file size increased by DEFULT_PAGES * pageSize_(64M)
465 size_t newLength = fileLength_ + DEFULT_PAGES * pageSize_;
466 if (ftruncate(fd_, newLength) != 0) {
467 PROFILER_LOG_ERROR(LOG_CORE, "ftruncate file(%zu) failed, error: %s", newLength, strerror(errno));
468 return false;
469 }
470
471 size_t remapPos = fileWriteLength_ & ~(pageSize_ - 1);
472 fileMapAddr_ = mmap(nullptr, newLength, PROT_WRITE | PROT_READ, MAP_SHARED | MAP_POPULATE, fd_, remapPos);
473 if (fileMapAddr_ == MAP_FAILED) {
474 PROFILER_LOG_ERROR(LOG_CORE, "remap(%zu:%zu) data file failed, error: %s", newLength, remapPos,
475 strerror(errno));
476 return false;
477 }
478
479 mapOffset_ = fileWriteLength_ - remapPos;
480 fileLength_ = newLength;
481 PROFILER_LOG_INFO(LOG_CORE, "remap(%zu:%zu) data file(%zu) sucess", remapPos, mapOffset_, fileLength_);
482 return true;
483 }
484
GetMemory(uint32_t size,uint8_t ** memory,uint32_t * offset)485 bool TraceFileWriter::GetMemory(uint32_t size, uint8_t** memory, uint32_t* offset)
486 {
487 if ((fileWriteLength_ + PIECE_HEAD_LEN + messageWriteOffset_ + size) > fileLength_) {
488 if (!RemapFile()) {
489 PROFILER_LOG_ERROR(LOG_CORE, "RemapFile failed!");
490 return false;
491 }
492 }
493
494 *memory = &reinterpret_cast<uint8_t*>(fileMapAddr_)[mapOffset_ + messageWriteOffset_];
495 *offset = messageWriteOffset_;
496 return true;
497 }
498
Seek(uint32_t offset)499 bool TraceFileWriter::Seek(uint32_t offset)
500 {
501 messageWriteOffset_ = offset;
502 return true;
503 }
504
FinishReport(int32_t size)505 void TraceFileWriter::FinishReport(int32_t size)
506 {
507 auto realSize = PIECE_HEAD_LEN + size;
508 CHECK_TRUE(helper_.AddSegment(nullptr, realSize), NO_RETVAL, "AddSegment(%d) failed, error: %s",
509 realSize, strerror(errno));
510
511 // write data length
512 *(reinterpret_cast<int*>((&reinterpret_cast<uint8_t*>(fileMapAddr_)[mapOffset_]))) = size;
513 mapOffset_ += static_cast<uint64_t>(realSize);
514 fileWriteLength_ += static_cast<uint64_t>(realSize);
515 writeBytes_ += static_cast<uint64_t>(realSize);
516 ++writeCount_;
517 }
518
ResetPos()519 void TraceFileWriter::ResetPos()
520 {
521 messageWriteOffset_ = PIECE_HEAD_LEN;
522 }
523