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