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