1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. 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 "hilog_plugin.h"
16 #include "securec.h"
17
18 #include <fcntl.h>
19 #include <cinttypes>
20 #include <csignal>
21 #include <sstream>
22 #include <cstdio>
23 #include <sys/syscall.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 #include <unistd.h>
27 #include "common.h"
28 #include "hilog_plugin_result.pbencoder.h"
29
30 namespace {
31 using namespace OHOS::Developtools::Profiler;
32 std::atomic<uint64_t> g_id(1);
33 const int DEC_BASE = 10;
34 const int TIME_HOUR_WIDTH = 5;
35 const int TIME_SEC_WIDTH = 14;
36 const int TIME_NS_WIDTH = 24;
37 const int FILE_NAME_LEN = 15;
38 const int TIME_BUFF_LEN = 32;
39 const int PIPE_SIZE_RATIO = 8;
40 const int BYTE_BUFFER_SIZE = 1024;
41 const int BASE_YEAR = 1900;
42 const int MAX_BUFFER_LEN = 8192;
43 const std::string DEFAULT_LOG_PATH("/data/local/tmp/");
44 FileCache g_fileCache(DEFAULT_LOG_PATH);
45 const std::string BIN_COMMAND("/system/bin/hilog");
46 } // namespace
47
HilogPlugin()48 HilogPlugin::HilogPlugin() : fp_(nullptr, nullptr) {}
49
~HilogPlugin()50 HilogPlugin::~HilogPlugin()
51 {
52 PROFILER_LOG_INFO(LOG_CORE, "%s: ready!", __func__);
53 std::unique_lock<std::mutex> locker(mutex_);
54 if (running_) {
55 running_ = false;
56 if (workThread_.joinable()) {
57 workThread_.join();
58 }
59 }
60 locker.unlock();
61
62 if (protoConfig_.need_record()) {
63 g_fileCache.Close();
64 }
65 if (fp_ != nullptr) {
66 fp_.reset();
67 }
68 PROFILER_LOG_INFO(LOG_CORE, "%s: success!", __func__);
69 }
70
GetCmdArgs(const HilogConfig & protoConfig)71 std::string HilogPlugin::GetCmdArgs(const HilogConfig& protoConfig)
72 {
73 std::stringstream args;
74 args << "log_level: " << std::to_string(protoConfig.log_level()) << ", ";
75 args << "pid: " << COMMON::GetProcessNameByPid(protoConfig.pid()) << ", ";
76 args << "need_record: " << (protoConfig.need_record() ? "true" : "false") << ", ";
77 args << "need_clear: " << (protoConfig.need_clear() ? "true" : "false");
78 return args.str();
79 }
80
Start(const uint8_t * configData,uint32_t configSize)81 int HilogPlugin::Start(const uint8_t* configData, uint32_t configSize)
82 {
83 CHECK_TRUE(protoConfig_.ParseFromArray(configData, configSize) > 0, -1, "HilogPlugin: ParseFromArray failed");
84 if (protoConfig_.need_clear()) {
85 std::vector<std::string> cmdArg;
86
87 cmdArg.emplace_back(BIN_COMMAND); // exe file path
88 cmdArg.emplace_back("hilog"); // exe file name
89 cmdArg.emplace_back("-r");
90 volatile pid_t childPid = -1;
91 int pipeFds[2] = {-1, -1};
92 FILE* fp = COMMON::CustomPopen(cmdArg, "r", pipeFds, childPid);
93 CHECK_NOTNULL(fp, -1, "%s:clear hilog error", __func__);
94 COMMON::CustomPclose(fp, pipeFds, childPid);
95 }
96 InitHilogCmd();
97 fp_ = std::unique_ptr<FILE, std::function<int (FILE*)>>(
98 COMMON::CustomPopen(fullCmd_, "r", pipeFds_, childPid_, true), [this](FILE* fp) -> int {
99 return COMMON::CustomPclose(fp, pipeFds_, childPid_, true);
100 });
101
102 if (protoConfig_.need_record()) {
103 OpenLogFile();
104 }
105
106 CHECK_NOTNULL(resultWriter_, -1, "HilogPlugin: Writer is no set!!");
107 CHECK_NOTNULL(resultWriter_->write, -1, "HilogPlugin: Writer.write is no set!!");
108 CHECK_NOTNULL(resultWriter_->flush, -1, "HilogPlugin: Writer.flush is no set!!");
109 g_id = 1;
110 std::unique_lock<std::mutex> locker(mutex_);
111 running_ = true;
112 locker.unlock();
113
114 int oldPipeSize = fcntl(fileno(fp_.get()), F_GETPIPE_SZ);
115 fcntl(fileno(fp_.get()), F_SETPIPE_SZ, oldPipeSize * PIPE_SIZE_RATIO);
116 int pipeSize = fcntl(fileno(fp_.get()), F_GETPIPE_SZ);
117 PROFILER_LOG_INFO(LOG_CORE, "{fp = %d, pipeSize=%d, oldPipeSize=%d}", fileno(fp_.get()), pipeSize, oldPipeSize);
118 workThread_ = std::thread([this] { this->Run(); });
119
120 int ret = COMMON::PluginWriteToHisysevent("hilog_plugin", "sh", GetCmdArgs(protoConfig_),
121 COMMON::ErrorType::RET_SUCC, "success");
122 PROFILER_LOG_INFO(LOG_CORE, "hisysevent report hilog_plugin result:%d", ret);
123 return 0;
124 }
125
Stop()126 int HilogPlugin::Stop()
127 {
128 PROFILER_LOG_INFO(LOG_CORE, "HilogPlugin: ready stop thread!");
129 std::unique_lock<std::mutex> locker(mutex_);
130 running_ = false;
131 COMMON::CustomPUnblock(pipeFds_);
132 locker.unlock();
133 if (workThread_.joinable()) {
134 workThread_.join();
135 }
136 if (protoConfig_.need_record() && !dataBuffer_.empty()) {
137 g_fileCache.Write(dataBuffer_.data(), dataBuffer_.size());
138 dataBuffer_.erase(dataBuffer_.begin(), dataBuffer_.end());
139 }
140 PROFILER_LOG_INFO(LOG_CORE, "HilogPlugin: stop thread success!");
141 if (protoConfig_.need_record()) {
142 g_fileCache.Close();
143 }
144 fp_.reset();
145
146 PROFILER_LOG_INFO(LOG_CORE, "HilogPlugin: stop success!");
147 return 0;
148 }
149
SetWriter(WriterStruct * writer)150 int HilogPlugin::SetWriter(WriterStruct* writer)
151 {
152 resultWriter_ = writer;
153 return 0;
154 }
155
OpenLogFile()156 bool HilogPlugin::OpenLogFile()
157 {
158 char name[FILE_NAME_LEN] = {0};
159 GetDateTime(name, sizeof(name));
160 CHECK_TRUE(g_fileCache.Open(name), false, "HilogPlugin:%s failed!", __func__);
161
162 return true;
163 }
164
GetlevelCmd()165 std::string HilogPlugin::GetlevelCmd()
166 {
167 std::string levelCmd = "";
168 switch (protoConfig_.log_level()) {
169 case ERROR:
170 levelCmd = "E";
171 break;
172 case INFO:
173 levelCmd = "I";
174 break;
175 case DEBUG:
176 levelCmd = "D";
177 break;
178 case WARN:
179 levelCmd = "W";
180 break;
181 case FATAL:
182 levelCmd = "F";
183 break;
184 default:
185 break;
186 }
187
188 return levelCmd;
189 }
190
InitHilogCmd()191 void HilogPlugin::InitHilogCmd()
192 {
193 fullCmd_.emplace_back(BIN_COMMAND); // exe file path
194 fullCmd_.emplace_back("hilog"); // exe file name
195
196 if (protoConfig_.pid() > 0) {
197 fullCmd_.emplace_back("-P");
198 fullCmd_.emplace_back(std::to_string(protoConfig_.pid()));
199 }
200 if (GetlevelCmd().length() > 0) {
201 fullCmd_.emplace_back("-L");
202 fullCmd_.emplace_back(GetlevelCmd());
203 }
204
205 fullCmd_.emplace_back("--format");
206 fullCmd_.emplace_back("nsec");
207 }
208
Run(void)209 void HilogPlugin::Run(void)
210 {
211 PROFILER_LOG_INFO(LOG_CORE, "HilogPlugin::Run start!");
212 std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(MAX_BUFFER_LEN);
213
214 std::unique_ptr<HilogInfo> dataProto = nullptr;
215 std::unique_ptr<ProtoEncoder::HilogInfo> hilogInfo = nullptr;
216 if (resultWriter_->isProtobufSerialize) {
217 dataProto = std::make_unique<HilogInfo>();
218 } else {
219 hilogInfo = std::make_unique<ProtoEncoder::HilogInfo>(resultWriter_->startReport(resultWriter_));
220 }
221 time_t startTm = time(nullptr);
222 struct tm* pTime = localtime(&startTm);
223 char startTime[FILE_NAME_LEN] = {0};
224 if (pTime != nullptr) {
225 (void)snprintf_s(startTime, FILE_NAME_LEN, FILE_NAME_LEN - 1, "%02d-%02d %02d:%02d:%02d",
226 pTime->tm_mon + 1, pTime->tm_mday, pTime->tm_hour, pTime->tm_min, pTime->tm_sec);
227 }
228
229 while (running_) {
230 if (fgets(reinterpret_cast<char*>(buffer.get()), MAX_BUFFER_LEN - 1, fp_.get()) == nullptr) {
231 continue;
232 }
233
234 if ((strlen(reinterpret_cast<char*>(buffer.get())) + 1) == (MAX_BUFFER_LEN - 1)) {
235 PROFILER_LOG_ERROR(LOG_CORE,
236 "HilogPlugin:data length is greater than the MAX_BUFFER_LEN(%d)", MAX_BUFFER_LEN);
237 }
238
239 auto cptr = reinterpret_cast<char*>(buffer.get());
240 std::string curTime = cptr;
241 curTime = curTime.substr(0, TIME_SEC_WIDTH);
242 if (curTime < startTime) {
243 continue;
244 }
245 if (resultWriter_->isProtobufSerialize) {
246 ParseLogLineData(cptr, strlen(cptr), dataProto.get());
247
248 if (dataProto->ByteSizeLong() >= BYTE_BUFFER_SIZE) {
249 FlushData(dataProto.get());
250 dataProto.reset();
251 dataProto = std::make_unique<HilogInfo>();
252 }
253 } else {
254 ParseLogLineData(cptr, strlen(cptr), hilogInfo.get());
255
256 if (hilogInfo->Size() >= BYTE_BUFFER_SIZE) {
257 FlushDataOptimize(hilogInfo.get());
258 hilogInfo.reset();
259 hilogInfo = std::make_unique<ProtoEncoder::HilogInfo>(resultWriter_->startReport(resultWriter_));
260 }
261 }
262
263 if (protoConfig_.need_record() && dataBuffer_.size() >= BYTE_BUFFER_SIZE) {
264 g_fileCache.Write(dataBuffer_.data(), dataBuffer_.size());
265 dataBuffer_.erase(dataBuffer_.begin(), dataBuffer_.end());
266 }
267 }
268
269 if (resultWriter_->isProtobufSerialize) {
270 FlushData(dataProto.get());
271 dataProto.reset();
272 } else {
273 FlushDataOptimize(hilogInfo.get());
274 hilogInfo.reset();
275 }
276 PROFILER_LOG_INFO(LOG_CORE, "HilogPlugin::Run done!");
277 }
278
ParseLogLineInfo(const char * data,size_t len,T & hilogLineInfo)279 template <typename T> void HilogPlugin::ParseLogLineInfo(const char* data, size_t len, T& hilogLineInfo)
280 {
281 if (data == nullptr || len < TIME_NS_WIDTH) {
282 PROFILER_LOG_ERROR(LOG_CORE, "HilogPlugin:%s param invalid", __func__);
283 return;
284 }
285
286 for (size_t i = 0; i < len && protoConfig_.need_record(); i++) {
287 dataBuffer_.push_back(data[i]);
288 }
289
290 SetHilogLineDetails(data, hilogLineInfo);
291 return;
292 }
293
ParseLogLineData(const char * data,size_t len,T hilogInfoProto)294 template <typename T> void HilogPlugin::ParseLogLineData(const char* data, size_t len, T hilogInfoProto)
295 {
296 CHECK_NOTNULL(data, NO_RETVAL, "data is nullptr");
297 if (*data >= '0' && *data <= '9') {
298 auto* info = hilogInfoProto->add_info();
299 ParseLogLineInfo(data, len, *info);
300 info->set_id(g_id);
301 g_id++;
302 }
303 }
304
SetHilogLineDetails(const char * data,T & hilogLineInfo)305 template <typename T> bool HilogPlugin::SetHilogLineDetails(const char* data, T& hilogLineInfo)
306 {
307 char* end = nullptr;
308 struct timespec ts = {0};
309 char* pTmp = const_cast<char*>(data);
310
311 TimeStringToNS(data, &ts);
312 auto* detail = hilogLineInfo.mutable_detail();
313 detail->set_tv_sec(ts.tv_sec);
314 detail->set_tv_nsec(ts.tv_nsec);
315 pTmp = pTmp + TIME_SEC_WIDTH;
316 CHECK_TRUE(FindFirstSpace(&pTmp), false, "HilogPlugin:FindFirstSpace failed!");
317 uint32_t value = static_cast<uint32_t>(strtoul(pTmp, &end, DEC_BASE));
318 CHECK_TRUE(value > 0, false, "HilogPlugin:strtoull pid failed!");
319 detail->set_pid(value);
320 pTmp = end;
321 value = static_cast<uint32_t>(strtoul(pTmp, &end, DEC_BASE));
322 CHECK_TRUE(value > 0, false, "HilogPlugin:strtoull tid failed!");
323 detail->set_tid(value);
324 pTmp = end;
325 CHECK_TRUE(RemoveSpaces(&pTmp), false, "HilogPlugin:RemoveSpaces failed!");
326 detail->set_level(*pTmp);
327 pTmp++;
328 CHECK_TRUE(RemoveSpaces(&pTmp), false, "HilogPlugin:RemoveSpaces failed!");
329
330 if (*pTmp >= '0' && *pTmp <= '9') {
331 while (*pTmp != '/') { // 找 '/'
332 if (*pTmp == '\0' || *pTmp == '\n') {
333 return false;
334 }
335 pTmp++;
336 }
337 pTmp++;
338 end = pTmp;
339 } else if ((*pTmp >= 'a' && *pTmp <= 'z') || (*pTmp >= 'A' && *pTmp <= 'Z')) {
340 end = pTmp;
341 }
342 int index = 1;
343 if (end == nullptr) {
344 return false;
345 }
346 while (*pTmp != ':') { // 结束符 ':'
347 if (*pTmp == '\0' || *pTmp == '\n') {
348 return false;
349 }
350 pTmp++;
351 index++;
352 }
353 detail->set_tag(std::string(end, end + index - 1));
354 pTmp++;
355 CHECK_TRUE(RemoveSpaces(&pTmp), false, "HilogPlugin: RemoveSpaces failed!");
356 size_t dataLen = strlen(pTmp) > 1 ? strlen(pTmp) -1 : 0;
357 if (google::protobuf::internal::IsStructurallyValidUTF8(pTmp, dataLen)) {
358 hilogLineInfo.set_context(pTmp, dataLen); // - \n
359 } else {
360 PROFILER_LOG_ERROR(LOG_CORE, "HilogPlugin: log context include invalid UTF-8 data");
361 hilogLineInfo.set_context("");
362 }
363
364 return true;
365 }
366
FindFirstNum(char ** p)367 bool HilogPlugin::FindFirstNum(char** p)
368 {
369 CHECK_NOTNULL(*p, false, "HilogPlugin:%s", __func__);
370 while (**p > '9' || **p < '0') {
371 if (**p == '\0' || **p == '\n') {
372 return false;
373 }
374 (*p)++;
375 }
376 return true;
377 }
378
RemoveSpaces(char ** p)379 bool HilogPlugin::RemoveSpaces(char** p)
380 {
381 CHECK_NOTNULL(*p, false, "HilogPlugin:%s", __func__);
382 if (**p == '\0' || **p == '\n') {
383 return false;
384 }
385 while (**p == ' ') {
386 (*p)++;
387 if (**p == '\0' || **p == '\n') {
388 return false;
389 }
390 }
391 return true;
392 }
393
FindFirstSpace(char ** p)394 bool HilogPlugin::FindFirstSpace(char** p)
395 {
396 CHECK_NOTNULL(*p, false, "HilogPlugin:%s", __func__);
397 while (**p != ' ') {
398 if (**p == '\0' || **p == '\n') {
399 return false;
400 }
401 (*p)++;
402 }
403 return true;
404 }
405
StringToL(const char * word,long & value)406 bool HilogPlugin::StringToL(const char* word, long& value)
407 {
408 char* end = nullptr;
409 errno = 0;
410 value = strtol(word, &end, DEC_BASE);
411 if ((errno == ERANGE && (value == LONG_MAX)) || (errno != 0 && value == 0)) {
412 return false;
413 } else if (end == word && (*word >= '0' && *word <= '9')) {
414 return false;
415 }
416
417 return true;
418 }
419
TimeStringToNS(const char * data,struct timespec * tsTime)420 bool HilogPlugin::TimeStringToNS(const char* data, struct timespec *tsTime)
421 {
422 struct tm tmTime = {0};
423 struct tm result;
424 time_t timetTime;
425 char* end = nullptr;
426 char* pTmp = nullptr;
427 time_t nSeconds = time(nullptr);
428 uint32_t nsec = 0;
429 long fixHour = 0;
430
431 if (localtime_r(&nSeconds, &result) == nullptr) {
432 const int bufSize = 128;
433 char buf[bufSize] = { 0 };
434 strerror_r(errno, buf, bufSize);
435 PROFILER_LOG_ERROR(LOG_CORE, "HilogPlugin: get localtime failed!, errno(%d:%s)", errno, buf);
436 return false;
437 }
438 tmTime.tm_year = result.tm_year;
439 strptime(data, "%m-%d %H:%M:%S", &tmTime);
440 pTmp = const_cast<char*>(data) + TIME_HOUR_WIDTH;
441 CHECK_TRUE(StringToL(pTmp, fixHour), false, "%s:strtol fixHour failed", __func__);
442 if (static_cast<int>(fixHour) != tmTime.tm_hour) { // hours since midnight - [0, 23]
443 PROFILER_LOG_INFO(LOG_CORE, "HilogPlugin: hour(%d) <==> fix hour(%ld)!", tmTime.tm_hour, fixHour);
444 tmTime.tm_hour = fixHour;
445 }
446 pTmp = const_cast<char*>(data) + TIME_SEC_WIDTH;
447 FindFirstNum(&pTmp);
448 nsec = static_cast<uint32_t>(strtoul(pTmp, &end, DEC_BASE));
449 CHECK_TRUE(nsec > 0, false, "%s:strtoull nsec failed", __func__);
450
451 timetTime = mktime(&tmTime);
452 tsTime->tv_sec = timetTime;
453 tsTime->tv_nsec = nsec;
454
455 char buff[TIME_BUFF_LEN] = {0};
456 if (snprintf_s(buff, sizeof(buff), sizeof(buff) - 1, "%ld.%09u\n", timetTime, nsec) < 0) {
457 PROFILER_LOG_ERROR(LOG_CORE, "%s:snprintf_s error", __func__);
458 }
459 size_t buffSize = strlen(buff);
460 for (size_t i = 0; i < buffSize && protoConfig_.need_record(); i++) {
461 dataBuffer_.push_back(buff[i]);
462 }
463
464 return true;
465 }
466
GetDateTime(char * psDateTime,uint32_t size)467 int HilogPlugin::GetDateTime(char* psDateTime, uint32_t size)
468 {
469 CHECK_NOTNULL(psDateTime, -1, "HilogPlugin:%s param invalid", __func__);
470 CHECK_TRUE(size > 1, -1, "HilogPlugin:%s param invalid!", __func__);
471
472 time_t nSeconds;
473 struct tm* pTM;
474
475 nSeconds = time(nullptr);
476 pTM = localtime(&nSeconds);
477 if (pTM == nullptr) {
478 const int bufSize = 128;
479 char buf[bufSize] = { 0 };
480 strerror_r(errno, buf, bufSize);
481 PROFILER_LOG_ERROR(LOG_CORE, "HilogPlugin: get localtime failed!, errno(%d:%s)", errno, buf);
482 return -1;
483 }
484
485 if (snprintf_s(psDateTime, size, size - 1, "%04d%02d%02d%02d%02d%02d", pTM->tm_year + BASE_YEAR, pTM->tm_mon + 1,
486 pTM->tm_mday, pTM->tm_hour, pTM->tm_min, pTM->tm_sec) < 0) {
487 PROFILER_LOG_ERROR(LOG_CORE, "%s:snprintf_s error", __func__);
488 }
489
490 return 0;
491 }
492
FlushData(const T hilogLineProto)493 template <typename T> void HilogPlugin::FlushData(const T hilogLineProto)
494 {
495 protoBuffer_.resize(hilogLineProto->ByteSizeLong());
496 hilogLineProto->SerializeToArray(protoBuffer_.data(), protoBuffer_.size());
497 resultWriter_->write(resultWriter_, protoBuffer_.data(), protoBuffer_.size());
498 resultWriter_->flush(resultWriter_);
499 }
500
FlushDataOptimize(const T hilogLineProto)501 template <typename T> void HilogPlugin::FlushDataOptimize(const T hilogLineProto)
502 {
503 int messageLen = hilogLineProto->Finish();
504 resultWriter_->finishReport(resultWriter_, messageLen);
505 resultWriter_->flush(resultWriter_);
506 }