• 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 
16 #include "properties.h"
17 
18 #include <cstdarg>
19 #include <cstdio>
20 #include <fstream>
21 #include <iostream>
22 #include <sys/syscall.h>
23 #include <unistd.h>
24 #include <ctime>
25 #include <cerrno>
26 #include <securec.h>
27 
28 #include "log_time_stamp.h"
29 #include "hilog_trace.h"
30 #include "hilog_inner.h"
31 #include "hilog/log.h"
32 #include "hilog_common.h"
33 #include "hilog_input_socket_client.h"
34 #include "vsnprintf_s_p.h"
35 
36 using namespace std;
37 static RegisterFunc g_registerFunc = nullptr;
38 static atomic_int g_hiLogGetIdCallCount = 0;
39 static const char P_LIMIT_TAG[] = "LOGLIMITP";
40 #ifdef DEBUG
41 static const int MAX_PATH_LEN = 1024;
42 #endif
43 static const int DEFAULT_QUOTA = 13050;
44 static const int LOG_FLOWCTRL_QUOTA_STR_LEN = 6;
HiLogRegisterGetIdFun(RegisterFunc registerFunc)45 int HiLogRegisterGetIdFun(RegisterFunc registerFunc)
46 {
47     if (g_registerFunc != nullptr) {
48         return -1;
49     }
50     g_registerFunc = registerFunc;
51     return 0;
52 }
53 
HiLogUnregisterGetIdFun(RegisterFunc registerFunc)54 void HiLogUnregisterGetIdFun(RegisterFunc registerFunc)
55 {
56     if (g_registerFunc != registerFunc) {
57         return;
58     }
59 
60     g_registerFunc = nullptr;
61     while (atomic_load(&g_hiLogGetIdCallCount) != 0) {
62         /* do nothing, just wait current callback return */
63     }
64 
65     return;
66 }
67 
GetFinalLevel(unsigned int domain,const std::string & tag)68 static uint16_t GetFinalLevel(unsigned int domain, const std::string& tag)
69 {
70     uint16_t domainLevel = GetDomainLevel(domain);
71     uint16_t tagLevel = GetTagLevel(tag);
72     uint16_t globalLevel = GetGlobalLevel();
73     uint16_t maxLevel = LOG_LEVEL_MIN;
74     maxLevel = (maxLevel < domainLevel) ? domainLevel : maxLevel;
75     maxLevel = (maxLevel < tagLevel) ? tagLevel : maxLevel;
76     maxLevel = (maxLevel < globalLevel) ? globalLevel : maxLevel;
77     return maxLevel;
78 }
79 
ParseProcessQuota()80 static uint32_t ParseProcessQuota()
81 {
82     uint32_t proQuota = DEFAULT_QUOTA;
83     std::string proName = GetProgName();
84     static constexpr char flowCtrlQuotaFile[] = "/system/etc/hilog_flowcontrol_quota.conf";
85     std::ifstream ifs(flowCtrlQuotaFile, std::ifstream::in);
86     if (!ifs.is_open()) {
87         return proQuota;
88     }
89     std::string line;
90     while (!ifs.eof()) {
91         getline(ifs, line);
92         if (line.empty() || line.at(0) == '#') {
93             continue;
94         }
95         std::string processName;
96         std::string processQuotaValue;
97         std::size_t processNameEnd = line.find_first_of(" ");
98         if (processNameEnd == std::string::npos) {
99             continue;
100         }
101         processName = line.substr(0, processNameEnd);
102         if (++processNameEnd >= line.size()) {
103             continue;
104         }
105         if (proName == processName) {
106             int ret = 0;
107             processQuotaValue = line.substr(processNameEnd, LOG_FLOWCTRL_QUOTA_STR_LEN);
108             char quotaValue[LOG_FLOWCTRL_QUOTA_STR_LEN];
109             ret = strcpy_s(quotaValue, LOG_FLOWCTRL_QUOTA_STR_LEN, processQuotaValue.c_str());
110             if (ret != 0) {
111                 break;
112             }
113             ret = sscanf_s(quotaValue, "%d", &proQuota);
114             if (ret <= 0) {
115                 cout << "invalid quota config" << endl;
116             }
117             break;
118         }
119     }
120     ifs.close();
121     return proQuota;
122 }
123 
HiLogFlowCtrlProcess(int len,uint16_t logType,bool debug)124 static int HiLogFlowCtrlProcess(int len, uint16_t logType, bool debug)
125 {
126     if (logType == LOG_APP || !IsProcessSwitchOn() || debug) {
127         return 0;
128     }
129     static uint32_t processQuota = DEFAULT_QUOTA;
130     static atomic_int gSumLen = 0;
131     static atomic_int gDropped = 0;
132     LogTimeStamp startTime(0, 0);
133     static atomic<LogTimeStamp> gStartTime(startTime);
134     static std::atomic_flag isFirstFlag = ATOMIC_FLAG_INIT;
135     if (!isFirstFlag.test_and_set()) {
136         processQuota = ParseProcessQuota();
137     }
138     LogTimeStamp tsStart = atomic_load(&gStartTime);
139     LogTimeStamp tsNow(CLOCK_MONOTONIC);
140     /* in statistic period(1 second) */
141     if ((tsNow -= tsStart) <= LogTimeStamp(1)) {
142         uint32_t sumLen = (uint32_t)atomic_load(&gSumLen);
143         if (sumLen > processQuota) { /* over quota, -1 means don't print */
144             atomic_fetch_add_explicit(&gDropped, 1, memory_order_relaxed);
145             return -1;
146         } else { /* under quota, 0 means do print */
147             atomic_fetch_add_explicit(&gSumLen, len, memory_order_relaxed);
148             return 0;
149         }
150     } else  { /* new statistic period, return how many lines were dropped */
151         int dropped = atomic_exchange_explicit(&gDropped, 0, memory_order_relaxed);
152         atomic_store(&gStartTime, tsNow);
153         atomic_store(&gSumLen, len);
154         return dropped;
155     }
156     return 0;
157 }
158 
159 #ifdef DEBUG
GetExecutablePath(char * processdir,char * processname,size_t len)160 static size_t GetExecutablePath(char *processdir, char *processname, size_t len)
161 {
162     char* path_end = nullptr;
163     if (readlink("/proc/self/exe", processdir, len) <= 0)
164         return -1;
165     path_end = strrchr(processdir, '/');
166     if (path_end == NULL)
167         return -1;
168     ++path_end;
169     if (strncpy_s(processname, MAX_PATH_LEN, path_end, MAX_PATH_LEN - 1)) {
170         return 0;
171     }
172     *path_end = '\0';
173     return (size_t)(path_end - processdir);
174 }
175 #endif
176 
HiLogPrintArgs(const LogType type,const LogLevel level,const unsigned int domain,const char * tag,const char * fmt,va_list ap)177 int HiLogPrintArgs(const LogType type, const LogLevel level, const unsigned int domain, const char *tag,
178     const char *fmt, va_list ap)
179 {
180 #ifdef DEBUG
181     char dir[MAX_PATH_LEN] = {0};
182     char name[MAX_PATH_LEN] = {0};
183     (void)GetExecutablePath(dir, name, MAX_PATH_LEN);
184     if (strcmp(name, "hilog_test") != 0) {
185         return -1;
186     }
187 #endif
188 
189     int ret;
190     char buf[MAX_LOG_LEN] = {0};
191     char *logBuf = buf;
192     int traceBufLen = 0;
193     HilogMsg header = {0};
194     bool debug = false;
195     bool priv = HILOG_DEFAULT_PRIVACY;
196 
197     if (tag == nullptr) {
198         return -1;
199     }
200 
201     if (!HiLogIsLoggable(domain, tag, level)) {
202         return -1;
203     }
204 
205     /* print traceid */
206     if (g_registerFunc != NULL) {
207         uint64_t chainId = 0;
208         uint32_t flag = 0;
209         uint64_t spanId = 0;
210         uint64_t parentSpanId = 0;
211         int ret = -1;  /* default value -1: invalid trace id */
212         atomic_fetch_add_explicit(&g_hiLogGetIdCallCount, 1, memory_order_relaxed);
213         RegisterFunc func = g_registerFunc;
214         if (g_registerFunc != NULL) {
215             ret = func(&chainId, &flag, &spanId, &parentSpanId);
216         }
217         atomic_fetch_sub_explicit(&g_hiLogGetIdCallCount, 1, memory_order_relaxed);
218         if (ret == 0) {  /* 0: trace id with span id */
219             traceBufLen = snprintf_s(logBuf, MAX_LOG_LEN, MAX_LOG_LEN - 1, "[%llx, %llx, %llx] ",
220                 (unsigned long long)chainId, (unsigned long long)spanId, (unsigned long long)parentSpanId);
221         } else if (ret != -1) {  /* trace id without span id, -1: invalid trace id */
222             traceBufLen = snprintf_s(logBuf, MAX_LOG_LEN, MAX_LOG_LEN - 1, "[%llx] ",
223                 (unsigned long long)chainId);
224         }
225         if (traceBufLen > 0) {
226             logBuf += traceBufLen;
227         } else {
228             traceBufLen = 0;
229         }
230     }
231 
232     /* format log string */
233     debug = IsDebugOn();
234     priv = (!debug) && IsPrivateSwitchOn();
235 
236 #ifdef __clang__
237 /* code specific to clang compiler */
238 #pragma clang diagnostic push
239 #pragma clang diagnostic ignored "-Wformat-nonliteral"
240 #elif __GNUC__
241 /* code for GNU C compiler */
242 #pragma GCC diagnostic push
243 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
244 #endif
245     ret = vsnprintfp_s(logBuf, MAX_LOG_LEN - traceBufLen, MAX_LOG_LEN - traceBufLen - 1, priv, fmt, ap);
246 #ifdef __clang__
247 #pragma clang diagnostic pop
248 #elif __GNUC__
249 #pragma GCC diagnostic pop
250 #endif
251 
252     /* fill header info */
253     auto tagLen = strnlen(tag, MAX_TAG_LEN - 1);
254     auto logLen = strnlen(buf, MAX_LOG_LEN - 1);
255     header.type = type;
256     header.level = level;
257 #ifndef __RECV_MSG_WITH_UCRED_
258     header.pid = getpid();
259 #endif
260     header.tid = static_cast<uint32_t>(syscall(SYS_gettid));
261     header.domain = domain;
262 
263     /* flow control */
264     ret = HiLogFlowCtrlProcess(tagLen + logLen, type, debug);
265     if (ret < 0) {
266         return ret;
267     } else if (ret > 0) {
268         char dropLogBuf[MAX_LOG_LEN] = {0};
269         (void)snprintf_s(dropLogBuf, MAX_LOG_LEN, MAX_LOG_LEN - 1, "%d line(s) dopped!", ret);
270         HilogWriteLogMessage(&header, P_LIMIT_TAG, strlen(P_LIMIT_TAG) + 1, dropLogBuf,
271                              strnlen(dropLogBuf, MAX_LOG_LEN - 1) + 1);
272     }
273 
274     return HilogWriteLogMessage(&header, tag, tagLen + 1, buf, logLen + 1);
275 }
276 
HiLogPrint(LogType type,LogLevel level,unsigned int domain,const char * tag,const char * fmt,...)277 int HiLogPrint(LogType type, LogLevel level, unsigned int domain, const char *tag, const char *fmt, ...)
278 {
279     int ret;
280     va_list ap;
281     va_start(ap, fmt);
282     ret = HiLogPrintArgs(type, level, domain, tag, fmt, ap);
283     va_end(ap);
284     return ret;
285 }
286 
HiLogIsLoggable(unsigned int domain,const char * tag,LogLevel level)287 bool HiLogIsLoggable(unsigned int domain, const char *tag, LogLevel level)
288 {
289     if ((level <= LOG_LEVEL_MIN) || (level >= LOG_LEVEL_MAX) || tag == nullptr) {
290         return false;
291     }
292     if (level < GetFinalLevel(domain, tag)) {
293         return false;
294     }
295     return true;
296 }
297