1 /*
2 * Copyright (c) 2021 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
20 #include "logging.h"
21
22 using CharPtr = std::unique_ptr<char>::pointer;
23 using ConstCharPtr = std::unique_ptr<const char>::pointer;
24
25 namespace {
26 const int MB_TO_BYTE = 1024 * 1024;
27 const int MIN_BYTE = 200;
28 } // namespace
29
TraceFileWriter(const std::string & path,bool splitFile,uint32_t singleFileMaxSizeMb)30 TraceFileWriter::TraceFileWriter(const std::string& path, bool splitFile, uint32_t singleFileMaxSizeMb)
31 : path_(path), writeBytes_(0)
32 {
33 isSplitFile_ = splitFile;
34 singleFileMaxSize_ = (singleFileMaxSizeMb < MIN_BYTE) ? (MIN_BYTE * MB_TO_BYTE) :
35 (singleFileMaxSizeMb * MB_TO_BYTE);
36 oldPath_ = path;
37 fileNum_ = 1;
38
39 WriteHeader();
40 Flush();
41 }
42
~TraceFileWriter()43 TraceFileWriter::~TraceFileWriter()
44 {
45 Flush();
46 if (stream_.is_open()) {
47 stream_.close();
48 }
49 }
50
Path() const51 std::string TraceFileWriter::Path() const
52 {
53 return path_;
54 }
55
SetPluginConfig(const void * data,size_t size)56 bool TraceFileWriter::SetPluginConfig(const void* data, size_t size)
57 {
58 if (isSplitFile_) {
59 std::vector<char> configVec;
60 auto configData = reinterpret_cast<ConstCharPtr>(data);
61 configVec.insert(configVec.end(), configData, configData + size);
62 pluginConfigsData_.push_back(std::move(configVec));
63 }
64
65 Write(data, size);
66 return true;
67 }
68
SetTimeStamp()69 void TraceFileWriter::SetTimeStamp()
70 {
71 constexpr uint64_t nanoSeconds = 1000000000;
72 struct timespec ts;
73 clock_gettime(CLOCK_BOOTTIME, &ts);
74 header_.data_.boottime = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
75 static_cast<uint64_t>(ts.tv_nsec);
76 clock_gettime(CLOCK_REALTIME, &ts);
77 header_.data_.realtime = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
78 static_cast<uint64_t>(ts.tv_nsec);
79 clock_gettime(CLOCK_REALTIME_COARSE, &ts);
80 header_.data_.realtimeCoarse = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
81 static_cast<uint64_t>(ts.tv_nsec);
82 clock_gettime(CLOCK_MONOTONIC, &ts);
83 header_.data_.monotonic = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
84 static_cast<uint64_t>(ts.tv_nsec);
85 clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
86 header_.data_.monotonicCoarse = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
87 static_cast<uint64_t>(ts.tv_nsec);
88 clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
89 header_.data_.monotonicRaw = static_cast<uint64_t>(ts.tv_sec) * nanoSeconds +
90 static_cast<uint64_t>(ts.tv_nsec);
91 }
92
GetCurrentTime()93 static std::string GetCurrentTime()
94 {
95 const int usMs = 1000;
96 struct timeval tv;
97 gettimeofday(&tv, NULL);
98 return std::to_string(tv.tv_sec * usMs + tv.tv_usec / usMs);
99 }
100
WriteHeader()101 bool TraceFileWriter::WriteHeader()
102 {
103 if (isSplitFile_) {
104 std::string timeStr = GetCurrentTime();
105 int pos = static_cast<int>(oldPath_.find_last_of('.'));
106 if (pos != 0) {
107 path_ = oldPath_.substr(0, pos) + "_" + timeStr + "_" + std::to_string(fileNum_) +
108 oldPath_.substr(pos, oldPath_.size());
109 } else {
110 path_ = oldPath_ + "_" + timeStr + "_" + std::to_string(fileNum_);
111 }
112 }
113
114 stream_.open(path_, std::ios_base::out | std::ios_base::binary);
115 CHECK_TRUE(stream_.is_open(), false, "open %s failed, %d!", path_.c_str(), errno);
116
117 // write initial header, makes file write position move forward
118 helper_ = {};
119 header_ = {};
120 stream_.write(reinterpret_cast<CharPtr>(&header_), sizeof(header_));
121 CHECK_TRUE(stream_, false, "write initial header to %s failed!", path_.c_str());
122 dataSize_ = header_.HEADER_SIZE;
123 HILOG_INFO(LOG_CORE, "write file(%s) header end", path_.c_str());
124 return true;
125 }
126
Write(const void * data,size_t size)127 long TraceFileWriter::Write(const void* data, size_t size)
128 {
129 uint32_t dataLen = size;
130 CHECK_TRUE(stream_.is_open(), 0, "binary file %s not open or open failed!", path_.c_str());
131
132 // write 4B data length.
133 stream_.write(reinterpret_cast<CharPtr>(&dataLen), sizeof(dataLen));
134 CHECK_TRUE(stream_, 0, "binary file %s write raw buffer size failed!", path_.c_str());
135 CHECK_TRUE(helper_.AddSegment(reinterpret_cast<uint8_t*>(&dataLen), sizeof(dataLen)),
136 0, "Add payload for size %u FAILED!", dataLen);
137
138 // write data bytes
139 stream_.write(reinterpret_cast<ConstCharPtr>(data), size);
140 CHECK_TRUE(stream_, 0, "binary file %s write raw buffer data failed!", path_.c_str());
141 CHECK_TRUE(helper_.AddSegment(reinterpret_cast<uint8_t*>(const_cast<void*>(data)), size),
142 0, "Add payload for data bytes %zu FAILED!", size);
143
144 long nbytes = sizeof(dataLen) + size;
145 writeBytes_ += nbytes;
146 ++writeCount_;
147 return nbytes;
148 }
149
IsSplitFile(uint32_t size)150 bool TraceFileWriter::IsSplitFile(uint32_t size)
151 {
152 dataSize_ += sizeof(uint32_t) + size;
153 if (dataSize_ >= singleFileMaxSize_) {
154 HILOG_INFO(LOG_CORE, "need to split the file(%s), data size:%d, size: %d, singleFileMaxSize_:%d",
155 path_.c_str(), dataSize_, size, singleFileMaxSize_);
156
157 // update old file header
158 Finish();
159 if (stream_.is_open()) {
160 stream_.close();
161 }
162 fileNum_++;
163
164 // write header of the new file
165 WriteHeader();
166 // write the plugin config of the new file
167 for (size_t i = 0; i < pluginConfigsData_.size(); i++) {
168 Write(pluginConfigsData_[i].data(), pluginConfigsData_[i].size());
169 }
170 Flush();
171 return true;
172 }
173 return false;
174 }
175
Write(const MessageLite & message)176 long TraceFileWriter::Write(const MessageLite& message)
177 {
178 auto size = message.ByteSizeLong();
179 if (isSplitFile_ && !isStop_) {
180 if (IsSplitFile(size)) {
181 return -1;
182 }
183 }
184
185 // serialize message to bytes array
186 std::vector<char> msgData(size);
187 CHECK_TRUE(message.SerializeToArray(msgData.data(), msgData.size()), 0, "SerializeToArray failed!");
188
189 return Write(msgData.data(), msgData.size());
190 }
191
Finish()192 bool TraceFileWriter::Finish()
193 {
194 // update header info
195 helper_.Update(header_);
196
197 // move write position to begin of file
198 CHECK_TRUE(stream_.is_open(), false, "binary file %s not open or open failed!", path_.c_str());
199 stream_.seekp(0);
200 CHECK_TRUE(stream_, false, "seek write position to head for %s failed!", path_.c_str());
201
202 SetTimeStamp(); // add timestamp in header
203
204 // write final header
205 stream_.write(reinterpret_cast<CharPtr>(&header_), sizeof(header_));
206 CHECK_TRUE(stream_, false, "write final header to %s failed!", path_.c_str());
207 CHECK_TRUE(stream_.flush(), false, "binary file %s flush failed!", path_.c_str());
208 return true;
209 }
210
Flush()211 bool TraceFileWriter::Flush()
212 {
213 CHECK_TRUE(stream_.is_open(), false, "binary file %s not open or open failed!", path_.c_str());
214 CHECK_TRUE(stream_.flush(), false, "binary file %s flush failed!", path_.c_str());
215 HILOG_INFO(LOG_CORE, "flush: %s, bytes: %" PRIu64 ", count: %" PRIu64, path_.c_str(), writeBytes_, writeCount_);
216 return true;
217 }
218
SetStopSplitFile(bool isStop)219 void TraceFileWriter::SetStopSplitFile(bool isStop)
220 {
221 isStop_ = isStop;
222 }