1 /*
2 * Copyright (c) 2016, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements the logging related functions.
32 */
33
34 #include "logging.hpp"
35
36 #include "common/code_utils.hpp"
37 #include "common/instance.hpp"
38 #include "common/string.hpp"
39
40 /*
41 * Verify debug uart dependency.
42 *
43 * It is reasonable to only enable the debug uart and not enable logs to the DEBUG uart.
44 */
45 #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && (!OPENTHREAD_CONFIG_ENABLE_DEBUG_UART)
46 #error OPENTHREAD_CONFIG_ENABLE_DEBUG_UART_LOG requires OPENTHREAD_CONFIG_ENABLE_DEBUG_UART
47 #endif
48
49 #ifdef __cplusplus
50 extern "C" {
51 #endif
52
53 #if !OPENTHREAD_CONFIG_LOG_DEFINE_AS_MACRO_ONLY
54
Log(otLogLevel aLogLevel,otLogRegion aLogRegion,const char * aFormat,va_list aArgs)55 static void Log(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs)
56 {
57 ot::String<OPENTHREAD_CONFIG_LOG_MAX_SIZE> logString;
58
59 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
60 VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
61 #endif
62
63 #if OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL
64 logString.Append("%s", otLogLevelToPrefixString(aLogLevel));
65 #endif
66
67 #if OPENTHREAD_CONFIG_LOG_PREPEND_REGION
68 {
69 static const char *const kRegionPrefixStrings[] = {
70 _OT_REGION_SUFFIX, _OT_REGION_API_PREFIX, _OT_REGION_MLE_PREFIX, _OT_REGION_ARP_PREFIX,
71 _OT_REGION_NET_DATA_PREFIX, _OT_REGION_ICMP_PREFIX, _OT_REGION_IP6_PREFIX, _OT_REGION_TCP_PREFIX,
72 _OT_REGION_MAC_PREFIX, _OT_REGION_MEM_PREFIX, _OT_REGION_NCP_PREFIX, _OT_REGION_MESH_COP_PREFIX,
73 _OT_REGION_NET_DIAG_PREFIX, _OT_REGION_PLATFORM_PREFIX, _OT_REGION_COAP_PREFIX, _OT_REGION_CLI_PREFIX,
74 _OT_REGION_CORE_PREFIX, _OT_REGION_UTIL_PREFIX, _OT_REGION_BBR_PREFIX, _OT_REGION_MLR_PREFIX,
75 _OT_REGION_DUA_PREFIX, _OT_REGION_BR_PREFIX, _OT_REGION_SRP_PREFIX, _OT_REGION_DNS_PREFIX,
76 };
77
78 if (aLogRegion < OT_ARRAY_LENGTH(kRegionPrefixStrings))
79 {
80 logString.Append("%s", kRegionPrefixStrings[aLogRegion]);
81 }
82 else
83 {
84 logString.Append("%s", _OT_REGION_SUFFIX);
85 }
86 }
87 #else
88 logString.Append("%s", _OT_REGION_SUFFIX);
89 #endif
90
91 logString.AppendVarArgs(aFormat, aArgs);
92 otPlatLog(aLogLevel, aLogRegion, "%s" OPENTHREAD_CONFIG_LOG_SUFFIX, logString.AsCString());
93
94 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
95 exit:
96 return;
97 #endif
98 }
99
100 #if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_CRIT
_otLogCrit(otLogRegion aRegion,const char * aFormat,...)101 void _otLogCrit(otLogRegion aRegion, const char *aFormat, ...)
102 {
103 va_list args;
104
105 va_start(args, aFormat);
106 Log(OT_LOG_LEVEL_CRIT, aRegion, aFormat, args);
107 va_end(args);
108 }
109 #endif
110
111 #if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN
_otLogWarn(otLogRegion aRegion,const char * aFormat,...)112 void _otLogWarn(otLogRegion aRegion, const char *aFormat, ...)
113 {
114 va_list args;
115
116 va_start(args, aFormat);
117 Log(OT_LOG_LEVEL_WARN, aRegion, aFormat, args);
118 va_end(args);
119 }
120 #endif
121
122 #if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE
_otLogNote(otLogRegion aRegion,const char * aFormat,...)123 void _otLogNote(otLogRegion aRegion, const char *aFormat, ...)
124 {
125 va_list args;
126
127 va_start(args, aFormat);
128 Log(OT_LOG_LEVEL_NOTE, aRegion, aFormat, args);
129 va_end(args);
130 }
131 #endif
132
133 #if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO
_otLogInfo(otLogRegion aRegion,const char * aFormat,...)134 void _otLogInfo(otLogRegion aRegion, const char *aFormat, ...)
135 {
136 va_list args;
137
138 va_start(args, aFormat);
139 Log(OT_LOG_LEVEL_INFO, aRegion, aFormat, args);
140 va_end(args);
141 }
142 #endif
143
144 #if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG
_otLogDebg(otLogRegion aRegion,const char * aFormat,...)145 void _otLogDebg(otLogRegion aRegion, const char *aFormat, ...)
146 {
147 va_list args;
148
149 va_start(args, aFormat);
150 Log(OT_LOG_LEVEL_DEBG, aRegion, aFormat, args);
151 va_end(args);
152 }
153 #endif
154
155 #if OPENTHREAD_CONFIG_LOG_MAC
otLogMac(otLogLevel aLogLevel,const char * aFormat,...)156 void otLogMac(otLogLevel aLogLevel, const char *aFormat, ...)
157 {
158 va_list args;
159
160 VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
161
162 va_start(args, aFormat);
163 Log(aLogLevel, OT_LOG_REGION_MAC, aFormat, args);
164 va_end(args);
165
166 exit:
167 return;
168 }
169 #endif
170
171 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
otLogCertMeshCoP(const char * aFormat,...)172 void otLogCertMeshCoP(const char *aFormat, ...)
173 {
174 va_list args;
175
176 va_start(args, aFormat);
177 Log(OT_LOG_LEVEL_NONE, OT_LOG_REGION_MESH_COP, aFormat, args);
178 va_end(args);
179 }
180 #endif
181
182 #if OPENTHREAD_CONFIG_OTNS_ENABLE
otLogOtns(const char * aFormat,...)183 void otLogOtns(const char *aFormat, ...)
184 {
185 va_list args;
186
187 va_start(args, aFormat);
188 Log(OT_LOG_LEVEL_NONE, OT_LOG_REGION_CORE, aFormat, args);
189 va_end(args);
190 }
191 #endif
192
193 #endif // #if !OPENTHREAD_CONFIG_LOG_DEFINE_AS_MACRO_ONLY
194
195 #if OPENTHREAD_CONFIG_LOG_PKT_DUMP
196
197 #if OPENTHREAD_CONFIG_LOG_DEFINE_AS_MACRO_ONLY
198 #define otLogDump(aLogLevel, aLogRegion, aFormat, ...) \
199 _otDynamicLog(aLogLevel, aLogRegion, aFormat OPENTHREAD_CONFIG_LOG_SUFFIX, __VA_ARGS__)
200 #else
otLogDump(otLogLevel aLogLevel,otLogRegion aRegion,const char * aFormat,...)201 static void otLogDump(otLogLevel aLogLevel, otLogRegion aRegion, const char *aFormat, ...)
202 {
203 va_list args;
204
205 VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
206
207 va_start(args, aFormat);
208 Log(aLogLevel, aRegion, aFormat, args);
209 va_end(args);
210
211 exit:
212 return;
213 }
214 #endif
215
216 static constexpr uint8_t kStringLineLength = 80;
217 static constexpr uint8_t kDumpBytesPerLine = 16;
218
DumpLine(otLogLevel aLogLevel,otLogRegion aLogRegion,const uint8_t * aBytes,const size_t aLength)219 static void DumpLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const uint8_t *aBytes, const size_t aLength)
220 {
221 ot::String<kStringLineLength> string;
222
223 string.Append("|");
224
225 for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
226 {
227 if (i < aLength)
228 {
229 string.Append(" %02X", aBytes[i]);
230 }
231 else
232 {
233 string.Append(" ..");
234 }
235
236 if (!((i + 1) % 8))
237 {
238 string.Append(" |");
239 }
240 }
241
242 string.Append(" ");
243
244 for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
245 {
246 char c = '.';
247
248 if (i < aLength)
249 {
250 char byteAsChar = static_cast<char>(0x7f & aBytes[i]);
251
252 if (isprint(byteAsChar))
253 {
254 c = byteAsChar;
255 }
256 }
257
258 string.Append("%c", c);
259 }
260
261 otLogDump(aLogLevel, aLogRegion, "%s", string.AsCString());
262 }
263
otDump(otLogLevel aLogLevel,otLogRegion aLogRegion,const char * aId,const void * aBuf,const size_t aLength)264 void otDump(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aId, const void *aBuf, const size_t aLength)
265 {
266 constexpr uint8_t kWidth = 72;
267
268 size_t idLen = strlen(aId);
269 ot::String<kStringLineLength> string;
270
271 for (size_t i = 0; i < (kWidth - idLen) / 2 - 5; i++)
272 {
273 string.Append("=");
274 }
275
276 string.Append("[%s len=%03u]", aId, static_cast<unsigned>(aLength));
277
278 for (size_t i = 0; i < (kWidth - idLen) / 2 - 4; i++)
279 {
280 string.Append("=");
281 }
282
283 otLogDump(aLogLevel, aLogRegion, "%s", string.AsCString());
284
285 for (size_t i = 0; i < aLength; i += kDumpBytesPerLine)
286 {
287 DumpLine(aLogLevel, aLogRegion, static_cast<const uint8_t *>(aBuf) + i,
288 OT_MIN((aLength - i), static_cast<size_t>(kDumpBytesPerLine)));
289 }
290
291 string.Clear();
292
293 for (size_t i = 0; i < kWidth; i++)
294 {
295 string.Append("-");
296 }
297
298 otLogDump(aLogLevel, aLogRegion, "%s", string.AsCString());
299 }
300 #else // OPENTHREAD_CONFIG_LOG_PKT_DUMP
otDump(otLogLevel,otLogRegion,const char *,const void *,const size_t)301 void otDump(otLogLevel, otLogRegion, const char *, const void *, const size_t)
302 {
303 }
304 #endif // OPENTHREAD_CONFIG_LOG_PKT_DUMP
305
306 #if OPENTHREAD_CONFIG_LOG_DEFINE_AS_MACRO_ONLY || OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL
307
otLogLevelToPrefixString(otLogLevel aLogLevel)308 const char *otLogLevelToPrefixString(otLogLevel aLogLevel)
309 {
310 static const char *const kLevelStrings[] = {
311 _OT_LEVEL_NONE_PREFIX, _OT_LEVEL_CRIT_PREFIX, _OT_LEVEL_WARN_PREFIX,
312 _OT_LEVEL_NOTE_PREFIX, _OT_LEVEL_INFO_PREFIX, _OT_LEVEL_DEBG_PREFIX,
313 };
314
315 return ((aLogLevel >= 0) && (aLogLevel < static_cast<int>(OT_ARRAY_LENGTH(kLevelStrings))))
316 ? kLevelStrings[aLogLevel]
317 : "";
318 }
319 #endif
320
321 #if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_NONE
322 /* this provides a stub, in case something uses the function */
otPlatLog(otLogLevel aLogLevel,otLogRegion aLogRegion,const char * aFormat,...)323 void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
324 {
325 OT_UNUSED_VARIABLE(aLogLevel);
326 OT_UNUSED_VARIABLE(aLogRegion);
327 OT_UNUSED_VARIABLE(aFormat);
328 }
329 #endif
330
otPlatLogLine(otLogLevel aLogLevel,otLogRegion aLogRegion,const char * aLogLine)331 OT_TOOL_WEAK void otPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine)
332 {
333 otPlatLog(aLogLevel, aLogRegion, "%s", aLogLine);
334 }
335
336 #ifdef __cplusplus
337 }
338 #endif
339