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