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