• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }