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