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