• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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