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 constexpr int HILOG_PROP_VALUE_MAX = 92;
69 static constexpr int DEFAULT_QUOTA = 51200;
70 static int LockByProp(PropType propType);
71 static void UnlockByProp(PropType propType);
72
73 static pthread_mutex_t g_privateLock = PTHREAD_MUTEX_INITIALIZER;
74 static pthread_mutex_t g_onceDebugLock = PTHREAD_MUTEX_INITIALIZER;
75 static pthread_mutex_t g_persistDebugLock = PTHREAD_MUTEX_INITIALIZER;
76 static pthread_mutex_t g_globalLevelLock = PTHREAD_MUTEX_INITIALIZER;
77 static pthread_mutex_t g_domainLevelLock = PTHREAD_MUTEX_INITIALIZER;
78 static pthread_mutex_t g_tagLevelLock = PTHREAD_MUTEX_INITIALIZER;
79 static pthread_mutex_t g_domainFlowLock = PTHREAD_MUTEX_INITIALIZER;
80 static pthread_mutex_t g_processFlowLock = PTHREAD_MUTEX_INITIALIZER;
81 static constexpr const char* HAP_DEBUGGABLE = "HAP_DEBUGGABLE";
82
83 using PropRes = struct {
84 string name;
85 pthread_mutex_t* lock;
86 };
87 static 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
GetPropertyName(PropType propType)107 static string GetPropertyName(PropType propType)
108 {
109 return g_propResources[static_cast<int>(propType)].name;
110 }
111
LockByProp(PropType propType)112 static 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
UnlockByProp(PropType propType)120 static 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
PropertyGet(const string & key,char * value,int len)129 static 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
PropertySet(const string & key,const string & value)145 static 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
160 class PropertyTypeLocker {
161 public:
PropertyTypeLocker(PropType propType)162 explicit PropertyTypeLocker(PropType propType) : m_propType(propType), m_isLocked(false)
163 {
164 m_isLocked = !LockByProp(m_propType);
165 }
166
~PropertyTypeLocker()167 ~PropertyTypeLocker()
168 {
169 if (m_isLocked) {
170 UnlockByProp(m_propType);
171 }
172 }
173
isLocked() const174 bool isLocked() const
175 {
176 return m_isLocked;
177 }
178
179 private:
180 PropType m_propType;
181 bool m_isLocked;
182 };
183
184 using RawPropertyData = std::array<char, HILOG_PROP_VALUE_MAX>;
185
186 template<typename T>
187 class CacheData {
188 public:
189 using DataConverter = std::function<T(const RawPropertyData&, const T& defaultVal)>;
190
CacheData(DataConverter converter,const T & defaultValue,PropType propType,const std::string & suffix="")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
getValue()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
224 private:
getRawValue(char * value,unsigned int len)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
getDirectValue()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
updateValue()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
266 using SwitchCache = CacheData<bool>;
267 using LogLevelCache = CacheData<uint16_t>;
268
TextToBool(const RawPropertyData & data,bool defaultVal)269 static 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
TextToLogLevel(const RawPropertyData & data,uint16_t defaultVal)279 static 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
IsPrivateSwitchOn()288 bool 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
IsOnceDebugOn()297 bool 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
IsPersistDebugOn()306 bool 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
IsDebugOn()315 bool IsDebugOn()
316 {
317 return IsOnceDebugOn() || IsPersistDebugOn();
318 }
319
IsDebuggableHap()320 bool 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
GetGlobalLevel()329 uint16_t GetGlobalLevel()
330 {
331 static auto *logLevelCache = new LogLevelCache(TextToLogLevel, LOG_LEVEL_MIN, PropType::PROP_GLOBAL_LOG_LEVEL);
332 return logLevelCache->getValue();
333 }
334
GetDomainLevel(uint32_t domain)335 uint16_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
GetTagLevel(const string & tag)364 uint16_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
IsProcessSwitchOn()393 bool 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
IsDomainSwitchOn()402 bool 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
IsKmsgSwitchOn()411 bool 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
GetBufferSizePropName(uint16_t type,bool persist)421 static 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
GetBufferSize(uint16_t type,bool persist)435 size_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
IsStatsEnable()451 bool 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
IsTagStatsEnable()461 bool 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
GetProcessQuota(const string & proc)471 int 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
GetDomainQuota(uint32_t domain)483 int 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
SetBoolValue(PropType type,bool val)495 static int SetBoolValue(PropType type, bool val)
496 {
497 string key = GetPropertyName(type);
498 return key == "" ? RET_FAIL : (PropertySet(key, val ? "true" : "false"));
499 }
500
SetLevel(PropType type,const string & suffix,uint16_t lvl)501 static 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
SetPrivateSwitchOn(bool on)507 int SetPrivateSwitchOn(bool on)
508 {
509 return SetBoolValue(PropType::PROP_PRIVATE, on);
510 }
511
SetOnceDebugOn(bool on)512 int SetOnceDebugOn(bool on)
513 {
514 return SetBoolValue(PropType::PROP_ONCE_DEBUG, on);
515 }
516
SetPersistDebugOn(bool on)517 int SetPersistDebugOn(bool on)
518 {
519 return SetBoolValue(PropType::PROP_PERSIST_DEBUG, on);
520 }
521
SetGlobalLevel(uint16_t lvl)522 int SetGlobalLevel(uint16_t lvl)
523 {
524 return SetLevel(PropType::PROP_GLOBAL_LOG_LEVEL, "", lvl);
525 }
526
SetTagLevel(const std::string & tag,uint16_t lvl)527 int SetTagLevel(const std::string& tag, uint16_t lvl)
528 {
529 return SetLevel(PropType::PROP_TAG_LOG_LEVEL, tag, lvl);
530 }
531
SetDomainLevel(uint32_t domain,uint16_t lvl)532 int SetDomainLevel(uint32_t domain, uint16_t lvl)
533 {
534 return SetLevel(PropType::PROP_DOMAIN_LOG_LEVEL, Uint2HexStr(domain), lvl);
535 }
536
SetProcessSwitchOn(bool on)537 int SetProcessSwitchOn(bool on)
538 {
539 return SetBoolValue(PropType::PROP_PROCESS_FLOWCTRL, on);
540 }
541
SetDomainSwitchOn(bool on)542 int SetDomainSwitchOn(bool on)
543 {
544 return SetBoolValue(PropType::PROP_DOMAIN_FLOWCTRL, on);
545 }
546
SetKmsgSwitchOn(bool on)547 int SetKmsgSwitchOn(bool on)
548 {
549 return SetBoolValue(PropType::PROP_KMSG, on);
550 }
551
SetBufferSize(uint16_t type,bool persist,size_t size)552 int 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