1 /*
2  *  Copyright (c) 2017-2022, 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 "log.hpp"
35 
36 #include <ctype.h>
37 
38 #include <openthread/platform/logging.h>
39 
40 #include "common/code_utils.hpp"
41 #include "common/instance.hpp"
42 #include "common/num_utils.hpp"
43 #include "common/string.hpp"
44 
45 /*
46  * Verify debug UART dependency.
47  *
48  * It is reasonable to only enable the debug UART and not enable logs to the DEBUG UART.
49  */
50 #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && (!OPENTHREAD_CONFIG_ENABLE_DEBUG_UART)
51 #error "OPENTHREAD_CONFIG_ENABLE_DEBUG_UART_LOG requires OPENTHREAD_CONFIG_ENABLE_DEBUG_UART"
52 #endif
53 
54 #if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME && !OPENTHREAD_CONFIG_UPTIME_ENABLE
55 #error "OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME requires OPENTHREAD_CONFIG_UPTIME_ENABLE"
56 #endif
57 
58 #if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME && OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
59 #error "OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME is not supported under OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE"
60 #endif
61 
62 namespace ot {
63 
64 #if OT_SHOULD_LOG
65 
LogAtLevel(const char * aModuleName,const char * aFormat,...)66 template <LogLevel kLogLevel> void Logger::LogAtLevel(const char *aModuleName, const char *aFormat, ...)
67 {
68     va_list args;
69 
70     va_start(args, aFormat);
71     LogVarArgs(aModuleName, kLogLevel, aFormat, args);
72     va_end(args);
73 }
74 
75 // Explicit instantiations
76 template void Logger::LogAtLevel<kLogLevelNone>(const char *aModuleName, const char *aFormat, ...);
77 template void Logger::LogAtLevel<kLogLevelCrit>(const char *aModuleName, const char *aFormat, ...);
78 template void Logger::LogAtLevel<kLogLevelWarn>(const char *aModuleName, const char *aFormat, ...);
79 template void Logger::LogAtLevel<kLogLevelNote>(const char *aModuleName, const char *aFormat, ...);
80 template void Logger::LogAtLevel<kLogLevelInfo>(const char *aModuleName, const char *aFormat, ...);
81 template void Logger::LogAtLevel<kLogLevelDebg>(const char *aModuleName, const char *aFormat, ...);
82 
LogInModule(const char * aModuleName,LogLevel aLogLevel,const char * aFormat,...)83 void Logger::LogInModule(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, ...)
84 {
85     va_list args;
86 
87     va_start(args, aFormat);
88     LogVarArgs(aModuleName, aLogLevel, aFormat, args);
89     va_end(args);
90 }
91 
LogVarArgs(const char * aModuleName,LogLevel aLogLevel,const char * aFormat,va_list aArgs)92 void Logger::LogVarArgs(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, va_list aArgs)
93 {
94     static const char kModuleNamePadding[] = "--------------";
95 
96     ot::String<OPENTHREAD_CONFIG_LOG_MAX_SIZE> logString;
97 
98     static_assert(sizeof(kModuleNamePadding) == kMaxLogModuleNameLength + 1, "Padding string is not correct");
99 
100 #if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME
101     ot::Uptime::UptimeToString(ot::Instance::Get().Get<ot::Uptime>().GetUptime(), logString, /* aInlcudeMsec */ true);
102     logString.Append(" ");
103 #endif
104 
105 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
106     VerifyOrExit(Instance::GetLogLevel() >= aLogLevel);
107 #endif
108 
109 #if OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL
110     {
111         static const char kLevelChars[] = {
112             '-', /* kLogLevelNone */
113             'C', /* kLogLevelCrit */
114             'W', /* kLogLevelWarn */
115             'N', /* kLogLevelNote */
116             'I', /* kLogLevelInfo */
117             'D', /* kLogLevelDebg */
118         };
119 
120         logString.Append("[%c] ", kLevelChars[aLogLevel]);
121     }
122 #endif
123 
124     logString.Append("%.*s%s: ", kMaxLogModuleNameLength, aModuleName,
125                      &kModuleNamePadding[StringLength(aModuleName, kMaxLogModuleNameLength)]);
126 
127     logString.AppendVarArgs(aFormat, aArgs);
128 
129     logString.Append("%s", OPENTHREAD_CONFIG_LOG_SUFFIX);
130     otPlatLog(aLogLevel, OT_LOG_REGION_CORE, "%s", logString.AsCString());
131 
132     ExitNow();
133 
134 exit:
135     return;
136 }
137 
138 #if OPENTHREAD_CONFIG_LOG_PKT_DUMP
139 
140 template <LogLevel kLogLevel>
DumpAtLevel(const char * aModuleName,const char * aText,const void * aData,uint16_t aDataLength)141 void Logger::DumpAtLevel(const char *aModuleName, const char *aText, const void *aData, uint16_t aDataLength)
142 {
143     DumpInModule(aModuleName, kLogLevel, aText, aData, aDataLength);
144 }
145 
146 // Explicit instantiations
147 template void Logger::DumpAtLevel<kLogLevelNone>(const char *aModuleName,
148                                                  const char *aText,
149                                                  const void *aData,
150                                                  uint16_t    aDataLength);
151 template void Logger::DumpAtLevel<kLogLevelCrit>(const char *aModuleName,
152                                                  const char *aText,
153                                                  const void *aData,
154                                                  uint16_t    aDataLength);
155 template void Logger::DumpAtLevel<kLogLevelWarn>(const char *aModuleName,
156                                                  const char *aText,
157                                                  const void *aData,
158                                                  uint16_t    aDataLength);
159 template void Logger::DumpAtLevel<kLogLevelNote>(const char *aModuleName,
160                                                  const char *aText,
161                                                  const void *aData,
162                                                  uint16_t    aDataLength);
163 template void Logger::DumpAtLevel<kLogLevelInfo>(const char *aModuleName,
164                                                  const char *aText,
165                                                  const void *aData,
166                                                  uint16_t    aDataLength);
167 template void Logger::DumpAtLevel<kLogLevelDebg>(const char *aModuleName,
168                                                  const char *aText,
169                                                  const void *aData,
170                                                  uint16_t    aDataLength);
171 
DumpLine(const char * aModuleName,LogLevel aLogLevel,const uint8_t * aData,const uint16_t aDataLength)172 void Logger::DumpLine(const char *aModuleName, LogLevel aLogLevel, const uint8_t *aData, const uint16_t aDataLength)
173 {
174     ot::String<kStringLineLength> string;
175 
176     string.Append("|");
177 
178     for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
179     {
180         if (i < aDataLength)
181         {
182             string.Append(" %02X", aData[i]);
183         }
184         else
185         {
186             string.Append(" ..");
187         }
188 
189         if (!((i + 1) % 8))
190         {
191             string.Append(" |");
192         }
193     }
194 
195     string.Append(" ");
196 
197     for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
198     {
199         char c = '.';
200 
201         if (i < aDataLength)
202         {
203             char byteAsChar = static_cast<char>(0x7f & aData[i]);
204 
205             if (isprint(byteAsChar))
206             {
207                 c = byteAsChar;
208             }
209         }
210 
211         string.Append("%c", c);
212     }
213 
214     LogInModule(aModuleName, aLogLevel, "%s", string.AsCString());
215 }
216 
DumpInModule(const char * aModuleName,LogLevel aLogLevel,const char * aText,const void * aData,uint16_t aDataLength)217 void Logger::DumpInModule(const char *aModuleName,
218                           LogLevel    aLogLevel,
219                           const char *aText,
220                           const void *aData,
221                           uint16_t    aDataLength)
222 {
223     constexpr uint16_t kWidth         = 72;
224     constexpr uint16_t kTextSuffixLen = sizeof("[ len=000]") - 1;
225 
226     uint16_t                      txtLen = StringLength(aText, kWidth - kTextSuffixLen) + kTextSuffixLen;
227     ot::String<kStringLineLength> string;
228 
229     VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
230 
231     for (uint16_t i = 0; i < static_cast<uint16_t>((kWidth - txtLen) / 2); i++)
232     {
233         string.Append("=");
234     }
235 
236     string.Append("[%s len=%03u]", aText, aDataLength);
237 
238     for (uint16_t i = 0; i < static_cast<uint16_t>(kWidth - txtLen - (kWidth - txtLen) / 2); i++)
239     {
240         string.Append("=");
241     }
242 
243     LogInModule(aModuleName, aLogLevel, "%s", string.AsCString());
244 
245     for (uint16_t i = 0; i < aDataLength; i += kDumpBytesPerLine)
246     {
247         DumpLine(aModuleName, aLogLevel, static_cast<const uint8_t *>(aData) + i,
248                  Min(static_cast<uint8_t>(aDataLength - i), kDumpBytesPerLine));
249     }
250 
251     string.Clear();
252 
253     for (uint16_t i = 0; i < kWidth; i++)
254     {
255         string.Append("-");
256     }
257 
258     LogInModule(aModuleName, aLogLevel, "%s", string.AsCString());
259 
260 exit:
261     return;
262 }
263 #endif // OPENTHREAD_CONFIG_LOG_PKT_DUMP
264 
265 #endif // OT_SHOULD_LOG
266 
267 } // namespace ot
268