1 /*
2  * Copyright (c) 2022 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 <codecvt>
16 #include <cstdint>
17 #include <cstdlib>
18 #include <chrono>
19 #include <functional>
20 #include <regex>
21 #include <sstream>
22 #include <thread>
23 #include <fstream>
24 #include <fcntl.h>
25 #include <securec.h>
26 #include <hilog/log.h>
27 #include <unistd.h>
28 #include "hilog_common.h"
29 #include "hilog_cmd.h"
30 #include "log_utils.h"
31 
32 namespace {
33     constexpr uint32_t ONE_KB = (1UL << 10);
34     constexpr uint32_t ONE_MB = (1UL << 20);
35     constexpr uint32_t ONE_GB = (1UL << 30);
36     constexpr uint64_t ONE_TB = (1ULL << 40);
37     constexpr uint32_t DOMAIN_MIN = DOMAIN_APP_MIN;
38     constexpr uint32_t DOMAIN_MAX = DOMAIN_OS_MAX;
39     constexpr int CMDLINE_PATH_LEN = 32;
40     constexpr int CMDLINE_LEN = 128;
41     constexpr int STATUS_PATH_LEN = 32;
42     constexpr int STATUS_LEN = 1024;
43     const std::string SH_NAMES[] = { "sh", "/bin/sh", "/system/bin/sh", "/xbin/sh", "/system/xbin/sh"};
44 }
45 
46 namespace OHOS {
47 namespace HiviewDFX {
48 using namespace std;
49 using namespace std::chrono;
50 
51 // Buffer Size&Char Map
52 static const KVMap<char, uint64_t> g_SizeMap({
53     {'B', 1}, {'K', ONE_KB}, {'M', ONE_MB},
54     {'G', ONE_GB}, {'T', ONE_TB}
55 }, ' ', 0);
56 
Size2Str(uint64_t size)57 string Size2Str(uint64_t size)
58 {
59     string str;
60     uint64_t unit = 1;
61     switch (size) {
62         case 0 ... ONE_KB - 1: unit = 1; break;
63         case ONE_KB ... ONE_MB - 1: unit = ONE_KB; break;
64         case ONE_MB ... ONE_GB - 1: unit = ONE_MB; break;
65         case ONE_GB ... ONE_TB - 1: unit = ONE_GB; break;
66         default: unit = ONE_TB; break;
67     }
68     float i = (static_cast<float>(size)) / unit;
69     constexpr int len = 16;
70     char buf[len] = { 0 };
71     int ret = snprintf_s(buf, len,  len - 1, "%.1f", i);
72     if (ret <= 0) {
73         str = to_string(size);
74     } else {
75         str = buf;
76     }
77     return str + g_SizeMap.GetKey(unit);
78 }
79 
Str2Size(const string & str)80 uint64_t Str2Size(const string& str)
81 {
82     std::regex reg("[0-9]+[BKMGT]?");
83     if (!std::regex_match(str, reg)) {
84         return 0;
85     }
86     uint64_t index = str.size() - 1;
87     uint64_t unit = g_SizeMap.GetValue(str[index]);
88 
89     uint64_t value = stoull(str.substr(0, unit !=0 ? index : index + 1));
90     return value * (unit != 0 ? unit : 1);
91 }
92 
93 // Error Codes&Strings Map
94 static const KVMap<int16_t, string> g_ErrorMsgs({
95     {RET_SUCCESS, "Success"},
96     {RET_FAIL, "Unknown failure reason"},
97     {ERR_LOG_LEVEL_INVALID, "Invalid log level, the valid log levels include D/I/W/E/F"
98     " or DEBUG/INFO/WARN/ERROR/FATAL"},
99     {ERR_LOG_TYPE_INVALID, "Invalid log type, the valid log types include app/core/init/kmsg/only_prerelease"},
100     {ERR_INVALID_RQST_CMD, "Invalid request cmd, please check sourcecode"},
101     {ERR_QUERY_TYPE_INVALID, "Can't query kmsg type logs combined with other types logs."},
102     {ERR_INVALID_DOMAIN_STR, "Invalid domain string"},
103     {ERR_LOG_PERSIST_FILE_SIZE_INVALID, "Invalid log persist file size, file size should be in range ["
104     + Size2Str(MIN_LOG_FILE_SIZE) + ", " + Size2Str(MAX_LOG_FILE_SIZE) + "]"},
105     {ERR_LOG_PERSIST_FILE_NAME_INVALID, "Invalid log persist file name, file name should not contain [\\/:*?\"<>|]"},
106     {ERR_LOG_PERSIST_COMPRESS_BUFFER_EXP, "Invalid Log persist compression buffer"},
107     {ERR_LOG_PERSIST_FILE_PATH_INVALID, "Invalid persister file path or persister directory does not exist"},
108     {ERR_LOG_PERSIST_COMPRESS_INIT_FAIL, "Log persist compression initialization failed"},
109     {ERR_LOG_PERSIST_FILE_OPEN_FAIL, "Log persist open file failed"},
110     {ERR_LOG_PERSIST_JOBID_FAIL, "Log persist jobid not exist"},
111     {ERR_LOG_PERSIST_TASK_EXISTED, "Log persist task is existed"},
112     {ERR_DOMAIN_INVALID, ("Invalid domain, domain should be in range (" + Uint2HexStr(DOMAIN_MIN)
113     + ", " +Uint2HexStr(DOMAIN_MAX) +"]")},
114     {ERR_MSG_LEN_INVALID, "Invalid message length"},
115     {ERR_LOG_PERSIST_JOBID_INVALID, "Invalid jobid, jobid should be in range [" + to_string(JOB_ID_MIN)
116     + ", " + to_string(JOB_ID_MAX) + ")"},
117     {ERR_BUFF_SIZE_INVALID, ("Invalid buffer size, buffer size should be in range [" + Size2Str(MIN_BUFFER_SIZE)
118     + ", " + Size2Str(MAX_BUFFER_SIZE) + "]")},
119     {ERR_COMMAND_INVALID, "Mutlti commands can't be used in combination"},
120     {ERR_LOG_FILE_NUM_INVALID, "Invalid number of files"},
121     {ERR_NOT_NUMBER_STR, "Not a numeric string"},
122     {ERR_TOO_MANY_ARGUMENTS, "Too many arguments"},
123     {ERR_DUPLICATE_OPTION, "Too many duplicate options"},
124     {ERR_INVALID_ARGUMENT, "Invalid argument"},
125     {ERR_TOO_MANY_DOMAINS, "Max domain count is " + to_string(MAX_DOMAINS)},
126     {ERR_INVALID_SIZE_STR, "Invalid size string"},
127     {ERR_TOO_MANY_PIDS, "Max pid count is " + to_string(MAX_PIDS)},
128     {ERR_TOO_MANY_TAGS, "Max tag count is " + to_string(MAX_TAGS)},
129     {ERR_TAG_STR_TOO_LONG, ("Tag string too long, max length is " + to_string(MAX_TAG_LEN - 1))},
130     {ERR_REGEX_STR_TOO_LONG, ("Regular expression too long, max length is " + to_string(MAX_REGEX_STR_LEN - 1))},
131     {ERR_FILE_NAME_TOO_LONG, ("File name too long, max length is " + to_string(MAX_FILE_NAME_LEN))},
132     {ERR_SOCKET_CLIENT_INIT_FAIL, "Socket client init failed"},
133     {ERR_SOCKET_WRITE_MSG_HEADER_FAIL, "Socket rite message header failed"},
134     {ERR_SOCKET_WRITE_CMD_FAIL, "Socket write command failed"},
135     {ERR_SOCKET_RECEIVE_RSP, "Unable to receive message from socket"},
136     {ERR_PERSIST_TASK_EMPTY, "No running persist task, please check"},
137     {ERR_JOBID_NOT_EXSIST, "Persist task of this job id doesn't exist, please check"},
138     {ERR_TOO_MANY_JOBS, ("Too many jobs are running, max job count is:" + to_string(MAX_JOBS))},
139     {ERR_STATS_NOT_ENABLE, "Statistic feature is not enable, "
140      "please set param persist.sys.hilog.stats true to enable it, "
141      "further more, you can set persist.sys.hilog.stats.tag true to enable counting log by tags"},
142     {ERR_NO_RUNNING_TASK, "No running persistent task"},
143     {ERR_NO_PID_PERMISSION, "Permission denied, only shell and root can filter logs by pid"},
144 }, RET_FAIL, "Unknown error code");
145 
ErrorCode2Str(int16_t errorCode)146 string ErrorCode2Str(int16_t errorCode)
147 {
148     return g_ErrorMsgs.GetValue((uint16_t)errorCode) + " [CODE: " + to_string(errorCode) + "]";
149 }
150 
151 // Log Types&Strings Map
152 static const StringMap g_LogTypes({
153         {LOG_INIT, "init"}, {LOG_CORE, "core"}, {LOG_APP, "app"}, {LOG_KMSG, "kmsg"},
154         {LOG_ONLY_PRERELEASE, "only_prerelease"}
155 }, LOG_TYPE_MAX, "invalid");
156 
LogType2Str(uint16_t logType)157 string LogType2Str(uint16_t logType)
158 {
159     return g_LogTypes.GetValue(logType);
160 }
161 
Str2LogType(const string & str)162 uint16_t Str2LogType(const string& str)
163 {
164     return g_LogTypes.GetKey(str);
165 }
166 
ComboLogType2Str(uint16_t shiftType)167 string ComboLogType2Str(uint16_t shiftType)
168 {
169     vector<uint16_t> types = g_LogTypes.GetAllKeys();
170     string str = "";
171     uint16_t typeAll = 0;
172 
173     for (uint16_t t : types) {
174         typeAll |= (1 << t);
175     }
176     shiftType &= typeAll;
177     for (uint16_t t: types) {
178         if ((1 << t) & shiftType) {
179             shiftType &= (~(1 << t));
180             str += (LogType2Str(t) + (shiftType != 0 ? "," : ""));
181         }
182         if (shiftType == 0) {
183             break;
184         }
185     }
186     return str;
187 }
188 
Str2ComboLogType(const string & str)189 uint16_t Str2ComboLogType(const string& str)
190 {
191     uint16_t logTypes = 0;
192     if (str == "") {
193         logTypes = (1 << LOG_CORE) | (1 << LOG_APP) | (1 << LOG_ONLY_PRERELEASE);
194         return logTypes;
195     }
196     vector<string> vec;
197     Split(str, vec);
198     for (auto& it : vec) {
199         if (it == "") {
200             continue;
201         }
202         uint16_t t = Str2LogType(it);
203         if (t == LOG_TYPE_MAX) {
204             return 0;
205         }
206         logTypes |= (1 << t);
207     }
208     return logTypes;
209 }
210 
GetAllLogTypes()211 vector<uint16_t> GetAllLogTypes()
212 {
213     return g_LogTypes.GetAllKeys();
214 }
215 
216 // Log Levels&Strings Map
217 static const StringMap g_LogLevels({
218     {LOG_DEBUG, "DEBUG"}, {LOG_INFO, "INFO"}, {LOG_WARN, "WARN"},
219     {LOG_ERROR, "ERROR"}, {LOG_FATAL, "FATAL"}, {LOG_LEVEL_MAX, "X"}
__anon9d62ac760202(const string& l1, const string& l2) 220 }, LOG_LEVEL_MIN, "INVALID", [](const string& l1, const string& l2) {
221     if (l1.length() == l2.length()) {
222         return std::equal(l1.begin(), l1.end(), l2.begin(), [](char a, char b) {
223             return std::tolower(a) == std::tolower(b);
224         });
225     } else {
226         return false;
227     }
228 });
229 
LogLevel2Str(uint16_t logLevel)230 string LogLevel2Str(uint16_t logLevel)
231 {
232     return g_LogLevels.GetValue(logLevel);
233 }
234 
Str2LogLevel(const string & str)235 uint16_t Str2LogLevel(const string& str)
236 {
237     return g_LogLevels.GetKey(str);
238 }
239 
240 // Log Levels&Short Strings Map
241 static const StringMap g_ShortLogLevels({
242     {LOG_DEBUG, "D"}, {LOG_INFO, "I"}, {LOG_WARN, "W"},
243     {LOG_ERROR, "E"}, {LOG_FATAL, "F"}, {LOG_LEVEL_MAX, "X"}
__anon9d62ac760402(const string& l1, const string& l2) 244 }, LOG_LEVEL_MIN, "V", [](const string& l1, const string& l2) {
245     return (l1.length() == 1 && std::tolower(l1[0]) == std::tolower(l2[0]));
246 });
247 
LogLevel2ShortStr(uint16_t logLevel)248 string LogLevel2ShortStr(uint16_t logLevel)
249 {
250     return g_ShortLogLevels.GetValue(logLevel);
251 }
252 
ShortStr2LogLevel(const string & str)253 uint16_t ShortStr2LogLevel(const string& str)
254 {
255     return g_ShortLogLevels.GetKey(str);
256 }
257 
PrettyStr2LogLevel(const string & str)258 uint16_t PrettyStr2LogLevel(const string& str)
259 {
260     uint16_t level = ShortStr2LogLevel(str);
261     if (level == static_cast<uint16_t>(LOG_LEVEL_MIN)) {
262         return Str2LogLevel(str);
263     }
264     return level;
265 }
266 
ComboLogLevel2Str(uint16_t shiftLevel)267 string ComboLogLevel2Str(uint16_t shiftLevel)
268 {
269     vector<uint16_t> levels = g_ShortLogLevels.GetAllKeys();
270     string str = "";
271     uint16_t levelAll = 0;
272 
273     for (uint16_t l : levels) {
274         levelAll |= (1 << l);
275     }
276     shiftLevel &= levelAll;
277     for (uint16_t l: levels) {
278         if ((1 << l) & shiftLevel) {
279             shiftLevel &= (~(1 << l));
280             str += (LogLevel2Str(l) + (shiftLevel != 0 ? "," : ""));
281         }
282         if (shiftLevel == 0) {
283             break;
284         }
285     }
286     return str;
287 }
288 
Str2ComboLogLevel(const string & str)289 uint16_t Str2ComboLogLevel(const string& str)
290 {
291     uint16_t logLevels = 0;
292     if (str == "") {
293         logLevels = 0xFFFF;
294         return logLevels;
295     }
296     vector<string> vec;
297     Split(str, vec);
298     for (auto& it : vec) {
299         if (it == "") {
300             continue;
301         }
302         uint16_t t = PrettyStr2LogLevel(it);
303         if (t == LOG_LEVEL_MIN || t >= LOG_LEVEL_MAX) {
304             return 0;
305         }
306         logLevels |= (1 << t);
307     }
308     return logLevels;
309 }
310 
Split(const std::string & src,std::vector<std::string> & dest,const std::string & separator)311 void Split(const std::string& src, std::vector<std::string>& dest, const std::string& separator)
312 {
313     std::string str = src;
314     std::string substring;
315     std::string::size_type start = 0;
316     std::string::size_type index;
317     dest.clear();
318     index = str.find_first_of(separator, start);
319     if (index == std::string::npos) {
320         dest.emplace_back(str);
321         return;
322     }
323     do {
324         substring = str.substr(start, index - start);
325         dest.emplace_back(substring);
326         start = index + separator.size();
327         index = str.find(separator, start);
328     } while (index != std::string::npos);
329     substring = str.substr(start);
330     if (substring != "") {
331         dest.emplace_back(substring);
332     }
333 }
334 
GetBitsCount(uint64_t n)335 uint32_t GetBitsCount(uint64_t n)
336 {
337     uint32_t count = 0;
338     while (n != 0) {
339         ++count;
340         n = n & (n-1);
341     }
342     return count;
343 }
344 
GetBitPos(uint64_t n)345 uint16_t GetBitPos(uint64_t n)
346 {
347     if (!(n && (!(n & (n-1))))) { // only accpet the number which is power of 2
348         return 0;
349     }
350 
351     uint16_t i = 0;
352     while (n >> (i++)) {}
353     i--;
354     return i-1;
355 }
356 
357 enum class Radix {
358     RADIX_DEC,
359     RADIX_HEX,
360 };
361 template<typename T>
Num2Str(T num,Radix radix)362 static string Num2Str(T num, Radix radix)
363 {
364     stringstream ss;
365     auto r = std::dec;
366     if (radix == Radix::RADIX_HEX) {
367         r = std::hex;
368     }
369     ss << r << num;
370     return ss.str();
371 }
372 
373 template<typename T>
Str2Num(const string & str,T & num,Radix radix)374 static void Str2Num(const string& str, T& num, Radix radix)
375 {
376     T  i = 0;
377     std::stringstream ss;
378     auto r = std::dec;
379     if (radix == Radix::RADIX_HEX) {
380         r = std::hex;
381     }
382     ss << r << str;
383     ss >> i;
384     num = i;
385     return;
386 }
387 
Uint2DecStr(uint32_t i)388 string Uint2DecStr(uint32_t i)
389 {
390     return Num2Str(i, Radix::RADIX_DEC);
391 }
392 
DecStr2Uint(const string & str)393 uint32_t DecStr2Uint(const string& str)
394 {
395     uint32_t i = 0;
396     Str2Num(str, i, Radix::RADIX_DEC);
397     return i;
398 }
399 
Uint2HexStr(uint32_t i)400 string Uint2HexStr(uint32_t i)
401 {
402     return Num2Str(i, Radix::RADIX_HEX);
403 }
404 
HexStr2Uint(const string & str)405 uint32_t HexStr2Uint(const string& str)
406 {
407     uint32_t i = 0;
408     Str2Num(str, i, Radix::RADIX_HEX);
409     return i;
410 }
411 
412 #if !defined(__WINDOWS__) and !defined(__LINUX__)
GetProgName()413 string GetProgName()
414 {
415 #ifdef HILOG_USE_MUSL
416     return program_invocation_short_name;
417 #else
418     return getprogname();
419 #endif
420 }
421 #endif
422 
GetNameByPid(uint32_t pid)423 string GetNameByPid(uint32_t pid)
424 {
425     char path[CMDLINE_PATH_LEN] = { 0 };
426     if (snprintf_s(path, CMDLINE_PATH_LEN, CMDLINE_PATH_LEN - 1, "/proc/%d/cmdline", pid) <= 0) {
427         return "";
428     }
429     char cmdline[CMDLINE_LEN] = { 0 };
430     int i = 0;
431     FILE *fp = fopen(path, "r");
432     if (fp == nullptr) {
433         return "";
434     }
435     while (i < (CMDLINE_LEN - 1)) {
436         char c = static_cast<char>(fgetc(fp));
437         // 0. don't need args of cmdline
438         // 1. ignore unvisible character
439         if (!isgraph(c)) {
440             break;
441         }
442         cmdline[i] = c;
443         i++;
444     }
445     (void)fclose(fp);
446     return cmdline;
447 }
448 
GetPPidByPid(uint32_t pid)449 uint32_t GetPPidByPid(uint32_t pid)
450 {
451     uint32_t ppid = 0;
452     char path[STATUS_PATH_LEN] = { 0 };
453     if (snprintf_s(path, STATUS_PATH_LEN, STATUS_PATH_LEN - 1, "/proc/%u/status", pid) <= 0) {
454         return ppid;
455     }
456     FILE *fp = fopen(path, "r");
457     if (fp == nullptr) {
458         return ppid;
459     }
460     char buf[STATUS_LEN] = { 0 };
461     size_t ret = fread(buf, sizeof(char), STATUS_LEN - 1, fp);
462     (void)fclose(fp);
463     if (ret <= 0) {
464         return ppid;
465     } else {
466         buf[ret++] = '\0';
467     }
468     char *ppidLoc = strstr(buf, "PPid:");
469     if ((ppidLoc == nullptr) || (sscanf_s(ppidLoc, "PPid:%d", &ppid) == -1)) {
470         return ppid;
471     }
472     std::string ppidName = GetNameByPid(ppid);
473     // ppid fork the sh to execute hilog, sh is not wanted ppid
474     if (std::find(std::begin(SH_NAMES), std::end(SH_NAMES), ppidName) != std::end(SH_NAMES)) {
475         return GetPPidByPid(ppid);
476     }
477     return ppid;
478 }
479 
GenerateHash(const char * p,size_t size)480 uint64_t GenerateHash(const char *p, size_t size)
481 {
482     static const uint64_t PRIME = 0x100000001B3ULL;
483     static const uint64_t BASIS = 0xCBF29CE484222325ULL;
484     uint64_t ret {BASIS};
485     unsigned long i = 0;
486     while (i < size) {
487         ret ^= *(p + i);
488         ret *= PRIME;
489         i++;
490     }
491     return ret;
492 }
493 
PrintErrorno(int err)494 void PrintErrorno(int err)
495 {
496     constexpr int bufSize = 256;
497     char buf[bufSize] = { 0 };
498 #ifndef __WINDOWS__
499     (void)strerror_r(err, buf, bufSize);
500 #else
501     (void)strerror_s(buf, bufSize, err);
502 #endif
503     std::cerr << "Errno: " << err << ", " << buf << std::endl;
504 }
505 
WaitingToDo(int max,const string & path,function<int (const string & path)> func)506 int WaitingToDo(int max, const string& path, function<int(const string &path)> func)
507 {
508     chrono::steady_clock::time_point start = chrono::steady_clock::now();
509     chrono::milliseconds wait(max);
510     while (true) {
511         if (func(path) != RET_FAIL) {
512             cout << "waiting for " << path << " successfully!" << endl;
513             return RET_SUCCESS;
514         }
515 
516         std::this_thread::sleep_for(10ms);
517         if ((chrono::steady_clock::now() - start) > wait) {
518             cerr << "waiting for " << path << " failed!" << endl;
519             return RET_FAIL;
520         }
521     }
522 }
523 
StringToWstring(const std::string & input)524 std::wstring StringToWstring(const std::string& input)
525 {
526     std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
527     return converter.from_bytes(input);
528 }
529 } // namespace HiviewDFX
530 } // namespace OHOS
531