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 32namespace { 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 46namespace OHOS { 47namespace HiviewDFX { 48using namespace std; 49using namespace std::chrono; 50 51// Buffer Size&Char Map 52static 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 57string 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 80uint64_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 94static 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 146string ErrorCode2Str(int16_t errorCode) 147{ 148 return g_ErrorMsgs.GetValue((uint16_t)errorCode) + " [CODE: " + to_string(errorCode) + "]"; 149} 150 151// Log Types&Strings Map 152static 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 157string LogType2Str(uint16_t logType) 158{ 159 return g_LogTypes.GetValue(logType); 160} 161 162uint16_t Str2LogType(const string& str) 163{ 164 return g_LogTypes.GetKey(str); 165} 166 167string 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 189uint16_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 211vector<uint16_t> GetAllLogTypes() 212{ 213 return g_LogTypes.GetAllKeys(); 214} 215 216// Log Levels&Strings Map 217static 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"} 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 230string LogLevel2Str(uint16_t logLevel) 231{ 232 return g_LogLevels.GetValue(logLevel); 233} 234 235uint16_t Str2LogLevel(const string& str) 236{ 237 return g_LogLevels.GetKey(str); 238} 239 240// Log Levels&Short Strings Map 241static 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"} 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 248string LogLevel2ShortStr(uint16_t logLevel) 249{ 250 return g_ShortLogLevels.GetValue(logLevel); 251} 252 253uint16_t ShortStr2LogLevel(const string& str) 254{ 255 return g_ShortLogLevels.GetKey(str); 256} 257 258uint16_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 267string 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 289uint16_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 311void 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 335uint32_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 345uint16_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 357enum class Radix { 358 RADIX_DEC, 359 RADIX_HEX, 360}; 361template<typename T> 362static 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 373template<typename T> 374static 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 388string Uint2DecStr(uint32_t i) 389{ 390 return Num2Str(i, Radix::RADIX_DEC); 391} 392 393uint32_t DecStr2Uint(const string& str) 394{ 395 uint32_t i = 0; 396 Str2Num(str, i, Radix::RADIX_DEC); 397 return i; 398} 399 400string Uint2HexStr(uint32_t i) 401{ 402 return Num2Str(i, Radix::RADIX_HEX); 403} 404 405uint32_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__) 413string GetProgName() 414{ 415#ifdef HILOG_USE_MUSL 416 return program_invocation_short_name; 417#else 418 return getprogname(); 419#endif 420} 421#endif 422 423string 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 449uint32_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 480uint64_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 494void 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 506int 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 524std::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