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