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