1 /*
2 * Copyright (c) 2021-2023 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 "trace_file_writer.h"
16
17 #include <cinttypes>
18 #include <memory>
19 #include <sys/statvfs.h>
20 #include <unistd.h>
21
22 #include "common.h"
23 #include "logging.h"
24
25 using CharPtr = std::unique_ptr<char>::pointer;
26 using ConstCharPtr = std::unique_ptr<const char>::pointer;
27
28 namespace {
29 constexpr int MB_TO_BYTE = (1024 * 1024);
30 constexpr int GB_TO_BYTE = (1024 * 1024 * 1024);
31 constexpr int SPLIT_FILE_MIN_SIZE = 200; // split file min size
32 constexpr int SPLIT_FILE_DEFAULT_NUM = 10; // split file default num
33 } // namespace
34
TraceFileWriter(const std::string & path)35 TraceFileWriter::TraceFileWriter(const std::string& path) : TraceFileWriter(path, false, 0, 0)
36 {
37 }
38
TraceFileWriter(const std::string & path,bool splitFile,uint32_t splitFileMaxSizeMb,uint32_t splitFileMaxNum)39 TraceFileWriter::TraceFileWriter(const std::string& path, bool splitFile, uint32_t splitFileMaxSizeMb,
40 uint32_t splitFileMaxNum) : path_(path), isSplitFile_(splitFile)
41 {
42 splitFileMaxSize_ = (splitFileMaxSizeMb < SPLIT_FILE_MIN_SIZE) ? (SPLIT_FILE_MIN_SIZE * MB_TO_BYTE) :
43 (splitFileMaxSizeMb * MB_TO_BYTE);
44 splitFileMaxNum_ = (splitFileMaxNum == 0) ? SPLIT_FILE_DEFAULT_NUM : splitFileMaxNum;
45 oldPath_ = path;
46 fileNum_ = 1;
47
48 WriteHeader();
49 (void)FlushStream();
50 }
51
~TraceFileWriter()52 TraceFileWriter::~TraceFileWriter()
53 {
54 (void)FlushStream();
55 if (stream_.is_open()) {
56 stream_.close();
57 }
58 }
59
Path() const60 std::string TraceFileWriter::Path() const
61 {
62 return path_;
63 }
64
SetPluginConfig(const void * data,size_t size)65 bool TraceFileWriter::SetPluginConfig(const void* data, size_t size)
66 {
67 if (isSplitFile_) {
68 std::vector<char> configVec;
69 auto configData = reinterpret_cast<ConstCharPtr>(data);
70 configVec.insert(configVec.end(), configData, configData + size);
71 pluginConfigsData_.push_back(std::move(configVec));
72 }
73
74 Write(data, size);
75 return true;
76 }
77
SetTimeStamp()78 void TraceFileWriter::SetTimeStamp()
79 {
80 constexpr uint64_t nanoSeconds = 1000000000;
81 struct timespec ts;
82 clock_gettime(CLOCK_BOOTTIME, &ts);
83 header_.data_.boottime = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
84 static_cast<uint64_t>(ts.tv_nsec);
85 clock_gettime(CLOCK_REALTIME, &ts);
86 header_.data_.realtime = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
87 static_cast<uint64_t>(ts.tv_nsec);
88 clock_gettime(CLOCK_REALTIME_COARSE, &ts);
89 header_.data_.realtimeCoarse = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
90 static_cast<uint64_t>(ts.tv_nsec);
91 clock_gettime(CLOCK_MONOTONIC, &ts);
92 header_.data_.monotonic = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
93 static_cast<uint64_t>(ts.tv_nsec);
94 clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
95 header_.data_.monotonicCoarse = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
96 static_cast<uint64_t>(ts.tv_nsec);
97 clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
98 header_.data_.monotonicRaw = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
99 static_cast<uint64_t>(ts.tv_nsec);
100 }
101
WriteHeader()102 bool TraceFileWriter::WriteHeader()
103 {
104 LogDiskUsage();
105 if (isSplitFile_) {
106 std::string timeStr = COMMON::GetTimeStr();
107 std::size_t pos = oldPath_.find_last_of('.');
108 if (pos != std::string::npos) {
109 path_ = oldPath_.substr(0, pos) + "_" + timeStr + "_" + std::to_string(fileNum_) +
110 oldPath_.substr(pos, oldPath_.size());
111 } else {
112 path_ = oldPath_ + "_" + timeStr + "_" + std::to_string(fileNum_);
113 }
114 splitFilePaths_.push(path_);
115 DeleteOldSplitFile();
116 }
117
118 stream_.open(path_, std::ios_base::out | std::ios_base::binary);
119 CHECK_TRUE(stream_.is_open(), false, "open %s failed, %d!", path_.c_str(), errno);
120
121 // write initial header, makes file write position move forward
122 helper_ = {};
123 header_ = {};
124 stream_.write(reinterpret_cast<CharPtr>(&header_), sizeof(header_));
125 CHECK_TRUE(stream_, false, "write initial header to %s failed!", path_.c_str());
126 dataSize_ = header_.HEADER_SIZE;
127 HILOG_INFO(LOG_CORE, "write file(%s) header end", path_.c_str());
128 return true;
129 }
130
131 // delete first split file if split file num over max
DeleteOldSplitFile()132 void TraceFileWriter::DeleteOldSplitFile()
133 {
134 if (splitFilePaths_.size() <= splitFileMaxNum_) {
135 HILOG_INFO(LOG_CORE, "splitFilePaths_ size %zu, no need to delete.", splitFilePaths_.size());
136 return;
137 }
138
139 std::string splitFilePath = splitFilePaths_.front();
140 int ret = unlink(splitFilePath.c_str());
141 HILOG_INFO(LOG_CORE, "DeleteOldSplitFile remove %s return %d. ", splitFilePath.c_str(), ret);
142 splitFilePaths_.pop();
143 }
144
Write(const void * data,size_t size)145 long TraceFileWriter::Write(const void* data, size_t size)
146 {
147 if (isSplitFile_ && !isStop_) {
148 if (IsSplitFile(size)) {
149 return -1;
150 }
151 }
152
153 uint32_t dataLen = size;
154 CHECK_TRUE(stream_.is_open(), 0, "binary file %s not open or open failed!", path_.c_str());
155
156 // write 4B data length.
157 stream_.write(reinterpret_cast<CharPtr>(&dataLen), sizeof(dataLen));
158 CHECK_TRUE(stream_, 0, "binary file %s write raw buffer size failed!", path_.c_str());
159 CHECK_TRUE(helper_.AddSegment(reinterpret_cast<uint8_t*>(&dataLen), sizeof(dataLen)),
160 0, "Add payload for size %u FAILED!", dataLen);
161
162 // write data bytes
163 stream_.write(reinterpret_cast<ConstCharPtr>(data), size);
164 CHECK_TRUE(stream_, 0, "binary file %s write raw buffer data failed!", path_.c_str());
165 CHECK_TRUE(helper_.AddSegment(reinterpret_cast<uint8_t*>(const_cast<void*>(data)), size),
166 0, "Add payload for data bytes %zu FAILED!", size);
167
168 long nbytes = sizeof(dataLen) + size;
169 writeBytes_ += nbytes;
170 ++writeCount_;
171 return nbytes;
172 }
173
IsSplitFile(uint32_t size)174 bool TraceFileWriter::IsSplitFile(uint32_t size)
175 {
176 dataSize_ += sizeof(uint32_t) + size;
177 if (dataSize_ >= splitFileMaxSize_) {
178 HILOG_INFO(LOG_CORE, "need to split the file(%s), data size:%d, size: %d, splitFileMaxSize_:%d",
179 path_.c_str(), dataSize_, size, splitFileMaxSize_);
180
181 // update old file header
182 Finish();
183 if (stream_.is_open()) {
184 stream_.close();
185 }
186 fileNum_++;
187
188 // write header of the new file
189 if (!WriteHeader()) {
190 return false;
191 }
192
193 // write the plugin config of the new file
194 for (size_t i = 0; i < pluginConfigsData_.size(); i++) {
195 Write(pluginConfigsData_[i].data(), pluginConfigsData_[i].size());
196 }
197 Flush();
198 return true;
199 }
200 return false;
201 }
202
Write(const MessageLite & message)203 long TraceFileWriter::Write(const MessageLite& message)
204 {
205 auto size = message.ByteSizeLong();
206 if (isSplitFile_ && !isStop_) {
207 if (IsSplitFile(size)) {
208 return -1;
209 }
210 }
211
212 // serialize message to bytes array
213 std::vector<char> msgData(size);
214 CHECK_TRUE(message.SerializeToArray(msgData.data(), msgData.size()), 0, "SerializeToArray failed!");
215
216 return Write(msgData.data(), msgData.size());
217 }
218
Finish()219 bool TraceFileWriter::Finish()
220 {
221 // update header info
222 helper_.Update(header_);
223
224 // move write position to begin of file
225 CHECK_TRUE(stream_.is_open(), false, "binary file %s not open or open failed!", path_.c_str());
226 stream_.seekp(0);
227 CHECK_TRUE(stream_, false, "seek write position to head for %s failed!", path_.c_str());
228
229 SetTimeStamp(); // add timestamp in header
230
231 // write final header
232 stream_.write(reinterpret_cast<CharPtr>(&header_), sizeof(header_));
233 CHECK_TRUE(stream_, false, "write final header to %s failed!", path_.c_str());
234 CHECK_TRUE(stream_.flush(), false, "binary file %s flush failed!", path_.c_str());
235 return true;
236 }
237
Flush()238 bool TraceFileWriter::Flush()
239 {
240 return FlushStream();
241 }
242
FlushStream()243 bool TraceFileWriter::FlushStream()
244 {
245 CHECK_TRUE(stream_.is_open(), false, "binary file %s not open or open failed!", path_.c_str());
246 CHECK_TRUE(stream_.flush(), false, "binary file %s flush failed!", path_.c_str());
247 HILOG_INFO(LOG_CORE, "flush: %s, bytes: %" PRIu64 ", count: %" PRIu64, path_.c_str(), writeBytes_, writeCount_);
248 return true;
249 }
250
SetStopSplitFile(bool isStop)251 void TraceFileWriter::SetStopSplitFile(bool isStop)
252 {
253 isStop_ = isStop;
254 }
255
LogDiskUsage()256 void TraceFileWriter::LogDiskUsage()
257 {
258 std::string diskPath = "/data/local/tmp/";
259 std::string::size_type pos = oldPath_.find_last_of('/');
260 if (pos != std::string::npos) {
261 diskPath = oldPath_.substr(0, pos);
262 }
263
264 struct statvfs diskInfo;
265 int ret = statvfs(diskPath.c_str(), &diskInfo);
266 if (ret != 0) {
267 std::string errorMsg = COMMON::GetErrorMsg();
268 HILOG_ERROR(LOG_CORE, "LogDiskUsage() return %d, path:%s, msg:%s", ret, diskPath.c_str(), errorMsg.c_str());
269 return;
270 }
271
272 unsigned long long freeSize = static_cast<unsigned long long>(diskInfo.f_bsize) *
273 static_cast<unsigned long long>(diskInfo.f_bfree);
274 unsigned long long totalSize = static_cast<unsigned long long>(diskInfo.f_bsize) *
275 static_cast<unsigned long long>(diskInfo.f_blocks);
276 float freePercent = static_cast<float>(freeSize) / static_cast<float>(totalSize);
277 uint32_t freeSizeGb = freeSize / GB_TO_BYTE;
278 // 100: in terms of percentage
279 HILOG_INFO(LOG_CORE, "LogDiskUsage() freePercent:%.1f, freeSizeGb:%u", freePercent * 100, freeSizeGb);
280 }