1/* 2 * Copyright (c) 2021 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 <array> 16#include <atomic> 17#include <cassert> 18#include <climits> 19#include <cstdio> 20#include <cstring> 21#include <ctime> 22#include <fstream> 23#include <functional> 24#include <iostream> 25#include <pthread.h> 26#include <shared_mutex> 27#include <string> 28#include <sys/uio.h> 29#include <unistd.h> 30#include <unordered_map> 31 32#include <parameter.h> 33#include <sysparam_errno.h> 34#include <hilog/log.h> 35#include <hilog_common.h> 36#include <log_utils.h> 37 38#include "properties.h" 39 40namespace OHOS { 41namespace HiviewDFX { 42using namespace std; 43 44enum class PropType { 45 // Below properties are used in HiLog API, which will be invoked frequently, so they should be cached 46 PROP_PRIVATE = 0, 47 PROP_ONCE_DEBUG, 48 PROP_PERSIST_DEBUG, 49 PROP_GLOBAL_LOG_LEVEL, 50 PROP_DOMAIN_LOG_LEVEL, 51 PROP_TAG_LOG_LEVEL, 52 PROP_DOMAIN_FLOWCTRL, 53 PROP_PROCESS_FLOWCTRL, 54 55 // Below properties needn't be cached, used in low frequency 56 PROP_KMSG, 57 PROP_BUFFER_SIZE, 58 PROP_PROC_QUOTA, 59 PROP_STATS_ENABLE, 60 PROP_STATS_TAG_ENABLE, 61 PROP_DOMAIN_QUOTA, 62 63 PROP_MAX, 64}; 65using ReadLock = shared_lock<shared_timed_mutex>; 66using InsertLock = unique_lock<shared_timed_mutex>; 67 68static constexpr int HILOG_PROP_VALUE_MAX = 92; 69static constexpr int DEFAULT_QUOTA = 51200; 70static int LockByProp(PropType propType); 71static void UnlockByProp(PropType propType); 72 73static pthread_mutex_t g_privateLock = PTHREAD_MUTEX_INITIALIZER; 74static pthread_mutex_t g_onceDebugLock = PTHREAD_MUTEX_INITIALIZER; 75static pthread_mutex_t g_persistDebugLock = PTHREAD_MUTEX_INITIALIZER; 76static pthread_mutex_t g_globalLevelLock = PTHREAD_MUTEX_INITIALIZER; 77static pthread_mutex_t g_domainLevelLock = PTHREAD_MUTEX_INITIALIZER; 78static pthread_mutex_t g_tagLevelLock = PTHREAD_MUTEX_INITIALIZER; 79static pthread_mutex_t g_domainFlowLock = PTHREAD_MUTEX_INITIALIZER; 80static pthread_mutex_t g_processFlowLock = PTHREAD_MUTEX_INITIALIZER; 81static constexpr const char* HAP_DEBUGGABLE = "HAP_DEBUGGABLE"; 82 83using PropRes = struct { 84 string name; 85 pthread_mutex_t* lock; 86}; 87static PropRes g_propResources[static_cast<int>(PropType::PROP_MAX)] = { 88 // Cached: 89 {"hilog.private.on", &g_privateLock}, // PROP_PRIVATE 90 {"hilog.debug.on", &g_onceDebugLock}, // PROP_ONCE_DEBUG 91 {"persist.sys.hilog.debug.on", &g_persistDebugLock}, // PROP_PERSIST_DEBUG 92 {"hilog.loggable.global", &g_globalLevelLock}, // PROP_GLOBAL_LOG_LEVEL 93 {"hilog.loggable.domain.", &g_domainLevelLock}, // PROP_DOMAIN_LOG_LEVEL 94 {"hilog.loggable.tag.", &g_tagLevelLock}, // PROP_TAG_LOG_LEVEL 95 {"hilog.flowctrl.domain.on", &g_domainFlowLock}, // PROP_DOMAIN_FLOWCTRL 96 {"hilog.flowctrl.proc.on", &g_processFlowLock}, // PROP_PROCESS_FLOWCTRL 97 98 // Non cached: 99 {"persist.sys.hilog.kmsg.on", nullptr}, // PROP_KMSG, 100 {"hilog.buffersize.", nullptr}, // PROP_BUFFER_SIZE, 101 {"hilog.quota.proc.", nullptr}, // PROP_PROC_QUOTA 102 {"persist.sys.hilog.stats", nullptr}, // PROP_STATS_ENABLE, 103 {"persist.sys.hilog.stats.tag", nullptr}, // PROP_STATS_TAG_ENABLE, 104 {"hilog.quota.domain.", nullptr}, // DOMAIN_QUOTA 105}; 106 107static string GetPropertyName(PropType propType) 108{ 109 return g_propResources[static_cast<int>(propType)].name; 110} 111 112static int LockByProp(PropType propType) 113{ 114 if (g_propResources[static_cast<int>(propType)].lock == nullptr) { 115 return -1; 116 } 117 return pthread_mutex_trylock(g_propResources[static_cast<int>(propType)].lock); 118} 119 120static void UnlockByProp(PropType propType) 121{ 122 if (g_propResources[static_cast<int>(propType)].lock == nullptr) { 123 return; 124 } 125 pthread_mutex_unlock(g_propResources[static_cast<int>(propType)].lock); 126 return; 127} 128 129static int PropertyGet(const string &key, char *value, int len) 130{ 131 int handle = static_cast<int>(FindParameter(key.c_str())); 132 if (handle == -1) { 133 return RET_FAIL; 134 } 135 136 auto res = GetParameterValue(handle, value, len); 137 if (res < 0) { 138 std::cerr << "PropertyGet() -> GetParameterValue -> Can't get value for key: " << key; 139 std::cerr << " handle: " << handle << " Result: " << res << "\n"; 140 return RET_FAIL; 141 } 142 return RET_SUCCESS; 143} 144 145static int PropertySet(const string &key, const string &value) 146{ 147 auto result = SetParameter(key.c_str(), value.c_str()); 148 if (result < 0) { 149 if (result == EC_INVALID) { 150 std::cerr << "PropertySet(): Invalid arguments.\n"; 151 } else { 152 std::cerr << "PropertySet(): key: " << key.c_str() << ", value: " << value << 153 ", error: " << result << "\n"; 154 } 155 return RET_FAIL; 156 } 157 return RET_SUCCESS; 158} 159 160class PropertyTypeLocker { 161public: 162 explicit PropertyTypeLocker(PropType propType) : m_propType(propType), m_isLocked(false) 163 { 164 m_isLocked = !LockByProp(m_propType); 165 } 166 167 ~PropertyTypeLocker() 168 { 169 if (m_isLocked) { 170 UnlockByProp(m_propType); 171 } 172 } 173 174 bool isLocked() const 175 { 176 return m_isLocked; 177 } 178 179private: 180 PropType m_propType; 181 bool m_isLocked; 182}; 183 184using RawPropertyData = std::array<char, HILOG_PROP_VALUE_MAX>; 185 186template<typename T> 187class CacheData { 188public: 189 using DataConverter = std::function<T(const RawPropertyData&, const T& defaultVal)>; 190 191 CacheData(DataConverter converter, const T& defaultValue, PropType propType, const std::string& suffix = "") 192 : m_value(defaultValue), m_defaultValue(defaultValue), m_propType(propType), m_converter(converter) 193 { 194 m_key = GetPropertyName(m_propType) + suffix; 195 } 196 197 T getValue() 198 { 199 long long sysCommitId = GetSystemCommitId(); 200 if (sysCommitId == m_sysCommit) { 201 return m_value; 202 } 203 m_sysCommit = sysCommitId; 204 if (m_handle == -1) { 205 int handle = static_cast<int>(FindParameter(m_key.c_str())); 206 if (handle == -1) { 207 return m_defaultValue; 208 } 209 m_handle = handle; 210 } 211 int currentCommit = static_cast<int>(GetParameterCommitId(m_handle)); 212 PropertyTypeLocker locker(m_propType); 213 if (locker.isLocked()) { 214 if (currentCommit != m_commit) { 215 updateValue(); 216 m_commit = currentCommit; 217 } 218 return m_value; 219 } else { 220 return getDirectValue(); 221 } 222 } 223 224private: 225 bool getRawValue(char *value, unsigned int len) 226 { 227 auto res = GetParameterValue(m_handle, value, len); 228 if (res < 0) { 229 std::cerr << "CacheData -> GetParameterValue -> Can't get value for key: " << m_key; 230 std::cerr << " handle: " << m_handle << " Result: " << res << "\n"; 231 return false; 232 } 233 return true; 234 } 235 236 T getDirectValue() 237 { 238 RawPropertyData tempData; 239 if (!getRawValue(tempData.data(), tempData.size())) { 240 return m_defaultValue; 241 } 242 m_value = m_converter(tempData, m_defaultValue); 243 return m_value; 244 } 245 246 void updateValue() 247 { 248 RawPropertyData rawData = {0}; 249 if (!getRawValue(rawData.data(), rawData.size())) { 250 m_value = m_defaultValue; 251 return; 252 } 253 m_value = m_converter(rawData, m_defaultValue); 254 } 255 256 int m_handle = -1; 257 int m_commit = -1; 258 long long m_sysCommit = -1; 259 T m_value; 260 const T m_defaultValue; 261 const PropType m_propType; 262 std::string m_key; 263 DataConverter m_converter; 264}; 265 266using SwitchCache = CacheData<bool>; 267using LogLevelCache = CacheData<uint16_t>; 268 269static bool TextToBool(const RawPropertyData& data, bool defaultVal) 270{ 271 if (!strcmp(data.data(), "true")) { 272 return true; 273 } else if (!strcmp(data.data(), "false")) { 274 return false; 275 } 276 return defaultVal; 277} 278 279static uint16_t TextToLogLevel(const RawPropertyData& data, uint16_t defaultVal) 280{ 281 uint16_t level = PrettyStr2LogLevel(data.data()); 282 if (level == LOG_LEVEL_MIN) { 283 level = defaultVal; 284 } 285 return level; 286} 287 288bool IsPrivateSwitchOn() 289{ 290 static auto *switchCache = new SwitchCache(TextToBool, true, PropType::PROP_PRIVATE); 291 if (switchCache == nullptr) { 292 return false; 293 } 294 return switchCache->getValue(); 295} 296 297bool IsOnceDebugOn() 298{ 299 static auto *switchCache = new SwitchCache(TextToBool, false, PropType::PROP_ONCE_DEBUG); 300 if (switchCache == nullptr) { 301 return false; 302 } 303 return switchCache->getValue(); 304} 305 306bool IsPersistDebugOn() 307{ 308 static auto *switchCache = new SwitchCache(TextToBool, false, PropType::PROP_PERSIST_DEBUG); 309 if (switchCache == nullptr) { 310 return false; 311 } 312 return switchCache->getValue(); 313} 314 315bool IsDebugOn() 316{ 317 return IsOnceDebugOn() || IsPersistDebugOn(); 318} 319 320bool IsDebuggableHap() 321{ 322 const char *path = getenv(HAP_DEBUGGABLE); 323 if ((path == nullptr) || (strcmp(path, "true") != 0)) { 324 return false; 325 } 326 return true; 327} 328 329uint16_t GetGlobalLevel() 330{ 331 static auto *logLevelCache = new LogLevelCache(TextToLogLevel, LOG_LEVEL_MIN, PropType::PROP_GLOBAL_LOG_LEVEL); 332 return logLevelCache->getValue(); 333} 334 335uint16_t GetDomainLevel(uint32_t domain) 336{ 337 static auto *domainMap = new std::unordered_map<uint32_t, LogLevelCache*>(); 338 static shared_timed_mutex* mtx = new shared_timed_mutex; 339 std::decay<decltype(*domainMap)>::type::iterator it; 340 { 341 ReadLock lock(*mtx); 342 it = domainMap->find(domain); 343 if (it != domainMap->end()) { 344 LogLevelCache* levelCache = it->second; 345 return levelCache->getValue(); 346 } 347 } 348 InsertLock lock(*mtx); 349 it = domainMap->find(domain); // secured for two thread went across above condition 350 if (it == domainMap->end()) { 351 LogLevelCache* levelCache = new LogLevelCache(TextToLogLevel, LOG_LEVEL_MIN, 352 PropType::PROP_DOMAIN_LOG_LEVEL, Uint2HexStr(domain)); 353 auto result = domainMap->insert({ domain, levelCache }); 354 if (!result.second) { 355 delete levelCache; 356 return LOG_LEVEL_MIN; 357 } 358 it = result.first; 359 } 360 LogLevelCache* levelCache = it->second; 361 return levelCache->getValue(); 362} 363 364uint16_t GetTagLevel(const string& tag) 365{ 366 static auto *tagMap = new std::unordered_map<std::string, LogLevelCache*>(); 367 static shared_timed_mutex* mtx = new shared_timed_mutex; 368 std::decay<decltype(*tagMap)>::type::iterator it; 369 { 370 ReadLock lock(*mtx); 371 it = tagMap->find(tag); 372 if (it != tagMap->end()) { 373 LogLevelCache* levelCache = it->second; 374 return levelCache->getValue(); 375 } 376 } 377 InsertLock lock(*mtx); 378 it = tagMap->find(tag); // secured for two thread went across above condition 379 if (it == tagMap->end()) { 380 LogLevelCache* levelCache = new LogLevelCache(TextToLogLevel, LOG_LEVEL_MIN, 381 PropType::PROP_TAG_LOG_LEVEL, tag); 382 auto result = tagMap->insert({ tag, levelCache }); 383 if (!result.second) { 384 delete levelCache; 385 return LOG_LEVEL_MIN; 386 } 387 it = result.first; 388 } 389 LogLevelCache* levelCache = it->second; 390 return levelCache->getValue(); 391} 392 393bool IsProcessSwitchOn() 394{ 395 static auto *switchCache = new SwitchCache(TextToBool, false, PropType::PROP_PROCESS_FLOWCTRL); 396 if (switchCache == nullptr) { 397 return false; 398 } 399 return switchCache->getValue(); 400} 401 402bool IsDomainSwitchOn() 403{ 404 static auto *switchCache = new SwitchCache(TextToBool, false, PropType::PROP_DOMAIN_FLOWCTRL); 405 if (switchCache == nullptr) { 406 return false; 407 } 408 return switchCache->getValue(); 409} 410 411bool IsKmsgSwitchOn() 412{ 413 RawPropertyData rawData; 414 int ret = PropertyGet(GetPropertyName(PropType::PROP_KMSG), rawData.data(), HILOG_PROP_VALUE_MAX); 415 if (ret == RET_FAIL) { 416 return false; 417 } 418 return TextToBool(rawData, false); 419} 420 421static string GetBufferSizePropName(uint16_t type, bool persist) 422{ 423 string name = persist ? "persist.sys." : ""; 424 string suffix; 425 426 if (type >= LOG_TYPE_MAX) { 427 suffix = "global"; 428 } else { 429 suffix = LogType2Str(type); 430 } 431 432 return name + GetPropertyName(PropType::PROP_BUFFER_SIZE) + suffix; 433} 434 435size_t GetBufferSize(uint16_t type, bool persist) 436{ 437 char value[HILOG_PROP_VALUE_MAX] = {0}; 438 439 if (type > LOG_TYPE_MAX || type < LOG_TYPE_MIN) { 440 return 0; 441 } 442 443 int ret = PropertyGet(GetBufferSizePropName(type, persist), value, HILOG_PROP_VALUE_MAX); 444 if (ret == RET_FAIL || value[0] == 0) { 445 return 0; 446 } 447 448 return std::stoi(value); 449} 450 451bool IsStatsEnable() 452{ 453 RawPropertyData rawData; 454 int ret = PropertyGet(GetPropertyName(PropType::PROP_STATS_ENABLE), rawData.data(), HILOG_PROP_VALUE_MAX); 455 if (ret == RET_FAIL) { 456 return false; 457 } 458 return TextToBool(rawData, false); 459} 460 461bool IsTagStatsEnable() 462{ 463 RawPropertyData rawData; 464 int ret = PropertyGet(GetPropertyName(PropType::PROP_STATS_TAG_ENABLE), rawData.data(), HILOG_PROP_VALUE_MAX); 465 if (ret == RET_FAIL) { 466 return false; 467 } 468 return TextToBool(rawData, false); 469} 470 471int GetProcessQuota(const string& proc) 472{ 473 char value[HILOG_PROP_VALUE_MAX] = {0}; 474 string prop = GetPropertyName(PropType::PROP_PROC_QUOTA) + proc; 475 476 int ret = PropertyGet(prop, value, HILOG_PROP_VALUE_MAX); 477 if (ret == RET_FAIL || value[0] == 0) { 478 return DEFAULT_QUOTA; 479 } 480 return std::stoi(value); 481} 482 483int GetDomainQuota(uint32_t domain) 484{ 485 char value[HILOG_PROP_VALUE_MAX] = {0}; 486 string prop = GetPropertyName(PropType::PROP_DOMAIN_QUOTA) + Uint2HexStr(domain); 487 488 int ret = PropertyGet(prop, value, HILOG_PROP_VALUE_MAX); 489 if (ret == RET_FAIL || value[0] == 0) { 490 return DEFAULT_QUOTA; 491 } 492 return std::stoi(value); 493} 494 495static int SetBoolValue(PropType type, bool val) 496{ 497 string key = GetPropertyName(type); 498 return key == "" ? RET_FAIL : (PropertySet(key, val ? "true" : "false")); 499} 500 501static int SetLevel(PropType type, const string& suffix, uint16_t lvl) 502{ 503 string key = GetPropertyName(type) + suffix; 504 return key == "" ? RET_FAIL : (PropertySet(key, LogLevel2ShortStr(lvl))); 505} 506 507int SetPrivateSwitchOn(bool on) 508{ 509 return SetBoolValue(PropType::PROP_PRIVATE, on); 510} 511 512int SetOnceDebugOn(bool on) 513{ 514 return SetBoolValue(PropType::PROP_ONCE_DEBUG, on); 515} 516 517int SetPersistDebugOn(bool on) 518{ 519 return SetBoolValue(PropType::PROP_PERSIST_DEBUG, on); 520} 521 522int SetGlobalLevel(uint16_t lvl) 523{ 524 return SetLevel(PropType::PROP_GLOBAL_LOG_LEVEL, "", lvl); 525} 526 527int SetTagLevel(const std::string& tag, uint16_t lvl) 528{ 529 return SetLevel(PropType::PROP_TAG_LOG_LEVEL, tag, lvl); 530} 531 532int SetDomainLevel(uint32_t domain, uint16_t lvl) 533{ 534 return SetLevel(PropType::PROP_DOMAIN_LOG_LEVEL, Uint2HexStr(domain), lvl); 535} 536 537int SetProcessSwitchOn(bool on) 538{ 539 return SetBoolValue(PropType::PROP_PROCESS_FLOWCTRL, on); 540} 541 542int SetDomainSwitchOn(bool on) 543{ 544 return SetBoolValue(PropType::PROP_DOMAIN_FLOWCTRL, on); 545} 546 547int SetKmsgSwitchOn(bool on) 548{ 549 return SetBoolValue(PropType::PROP_KMSG, on); 550} 551 552int SetBufferSize(uint16_t type, bool persist, size_t size) 553{ 554 if (type > LOG_TYPE_MAX || type < LOG_TYPE_MIN) { 555 return RET_FAIL; 556 } 557 return PropertySet(GetBufferSizePropName(type, persist), to_string(size)); 558} 559} // namespace HiviewDFX 560} // namespace OHOS