1 /*---------------------------------------------------------------------------*
2 Project: Horizon
3 File: dbg_Logger.cpp
4
5 Copyright (C)2009-2012 Nintendo Co., Ltd. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Rev: 46347 $
14 *---------------------------------------------------------------------------*/
15
16 #include <nn/types.h>
17 #include <nn/dbg/dbg_Logger.h>
18 #include <nn/nstd.h>
19 #include <nn/os.h>
20 #include <cstring>
21 #include <cstdio>
22 #include <algorithm>
23
24 /****************************************************************************/
25 namespace nn {
26 namespace dbg {
27 namespace detail {
28 /****************************************************************************/
29
30 /*
31 Switches to a log output format appropriate for network system operation confirmation
32 - Displays absolute time
33 - Makes WARN, ERROR, and FATAL stand out
34 - Displays only module name at the start
35 - Aligns the position of the free description portion
36 - Omit the middle of long function names
37 */
38 //#define ANOTHER_FORMAT
39
40 namespace {
41 #ifndef NN_SWITCH_DISABLE_DEBUG_PRINT
SafeTVSNPrintf(char * dst,size_t bufferLength,const char * fmt,va_list vlist)42 size_t SafeTVSNPrintf(char* dst, size_t bufferLength, const char* fmt, va_list vlist)
43 {
44 size_t length = std::min<size_t>(nn::nstd::TVSNPrintf(dst, bufferLength, fmt, vlist), bufferLength);
45 return length;
46 }
47
SafeTSNPrintf(char * dst,size_t bufferLength,const char * fmt,...)48 size_t SafeTSNPrintf(char* dst, size_t bufferLength, const char* fmt, ...)
49 {
50 va_list vlist;
51 va_start(vlist, fmt);
52 size_t length = std::min<size_t>(nn::nstd::TVSNPrintf(dst, bufferLength, fmt, vlist), bufferLength);
53 va_end(vlist);
54 return length;
55 }
56
57 #ifdef ANOTHER_FORMAT
AddSpace(char * dst,size_t len)58 size_t AddSpace(char* dst, size_t len)
59 {
60 for(size_t i = 0; i < len; ++i)
61 {
62 dst[i] = ' ';
63 }
64 return len;
65 }
66
AddSpaceUsingTab(char * dst,s32 width)67 size_t AddSpaceUsingTab(char* dst, s32 width)
68 {
69 const s32 TAB_WIDTH = 8;
70 size_t len = 0;
71 for(s32 i = 0; i < width % TAB_WIDTH; ++i, ++len)
72 {
73 dst[len] = ' ';
74 }
75 for(s32 i = 0; i < width / TAB_WIDTH; ++i, ++len)
76 {
77 dst[len] = '\t';
78 }
79 return len;
80 }
81 #endif
82
GetBaseName(const char * path)83 char* GetBaseName(const char* path)
84 {
85 char* pFileNameTop = (char8*)std::strrchr(path, '\\');
86 if (!pFileNameTop)
87 {
88 return const_cast<char*>(path);
89 }
90 return pFileNameTop + 1;
91 }
92 #endif
93 }
94
95 char8* Logger::s_LevelStrings[] = {
96 "DEBUG",
97 "INFO",
98 "WARN",
99 "ERROR",
100 "FATAL",
101 "FORCE"
102 };
103
104 u32 Logger::s_UpperLevel = Logger::LEVEL_FATAL;
105 u32 Logger::s_LowerLevel = NN_LOG_LEVEL;
106 bool Logger::s_Initialized = false;
107 u8 Logger::s_ShowFlag = 0;
108
MakeFuncName(const char8 * src,char8 * dest,size_t length)109 size_t Logger::MakeFuncName(const char8* src, char8* dest, size_t length)
110 {
111 // TODO: src NULL check
112 // TODO: dest NULL check
113 char8* pSrcTop = const_cast<char8*>(src);
114 char8* pSrcSigHead = ::std::strrchr(pSrcTop, '(');
115 // Determine end
116 // If ENABLE_SIGNATURE, end of src
117 // if not, the ( position of the function
118 char8* pSrcLast = (s_ShowFlag & ENABLE_SIGNATURE) ?
119 pSrcTop + ::std::strlen(src) :
120 pSrcSigHead;
121
122 // Determine start
123 // If ENABLE_NAMESPACE, use the start of src. If not, use the position of the third ":" from the end + 1.
124 //
125 if(!(s_ShowFlag & ENABLE_NAMESPACE))
126 {
127 #ifndef ANOTHER_FORMAT
128 pSrcTop = pSrcSigHead;
129 for(int count = 0;pSrcTop != src && count < 3; pSrcTop--)
130 {
131 count = (*pSrcTop == ':') ? count + 1 : count;
132 }
133 pSrcTop += 2;
134 #else
135 char* p;
136 for(p = pSrcSigHead; p > pSrcTop; --p)
137 {
138 if (std::strncmp(p, "::", 2) == 0)
139 {
140 break;
141 }
142 }
143 if (p != pSrcTop)
144 {
145 pSrcTop = p + 2;
146 }
147 else
148 {
149 for(p = pSrcSigHead; p > pSrcTop; --p)
150 {
151 if (*p == ' ')
152 {
153 break;
154 }
155 }
156 if (p != pSrcTop)
157 {
158 pSrcTop = p + 1;
159 }
160 }
161 #endif
162 }
163 size_t copyLength = ::std::min<size_t>(pSrcLast - pSrcTop, length - 1);
164 ::std::memcpy(dest, pSrcTop, copyLength);
165 dest[copyLength] = '\0';
166 return copyLength;
167 }
168
PrintLog(const u32 level,const char8 * funcName,const char8 * fileName,const int line,const char8 * fmt,...)169 void Logger::PrintLog(const u32 level, const char8* funcName, const char8* fileName, const int line,
170 const char8* fmt, ...)
171 {
172 #ifndef NN_SWITCH_DISABLE_DEBUG_PRINT
173 // static int count = 0;
174 // Exit if outside of the setting level range
175 if(!s_Initialized)
176 {
177 s_LowerLevel = NN_LOG_LEVEL;
178 }
179
180 if(level != LEVEL_FORCE && (level < s_LowerLevel || s_UpperLevel < level))
181 {
182 return;
183 }
184
185 char8 buffer[BUF_SIZE];
186 size_t len = 0;
187
188 #ifndef ANOTHER_FORMAT
189 {
190 // Process funcName
191 len += SafeTSNPrintf(&buffer[len], sizeof(buffer) - len, "[%s] ", s_LevelStrings[level]);
192
193 len += MakeFuncName(funcName, &buffer[len], BUF_SIZE - len - 3);
194
195 ::std::strncat(&buffer[len], " : ", sizeof(buffer) - len - 3);
196 len += 3;
197 }
198 #else
199 const size_t MODULE_LENGTH = 3;
200 const size_t FUNCTION_LENGTH = 22;
201 const size_t FUNCTION_TAIL_LENGTH = 9;
202 const s32 PREFIX1_POSITION = 11 + MODULE_LENGTH;
203 const s32 PREFIX2_POSITION = PREFIX1_POSITION + 8;
204 const s32 PREFIX3_POSITION = PREFIX2_POSITION + FUNCTION_LENGTH + 2/* ": " */;
205 const s32 SUFFIX_POSITION = 104;
206 {
207 // 012345678901234567890123456
208 // "12345.67 [soc]<WARNING> Startup..ocket: HOGEHOGE (socket_Main.cpp:175)"
209 // ~~~ MODULE_LENGTH
210 // ~~~~~~~~~~~~~~~ FUNCTION_LENGTH
211 // ~~~~~ FUNCTION_TAIL_LENGTH
212 // ^ PREFIX1_POSITION
213 // ^ PREFIX2_POSITION
214 // ^ PREFIX3_POSITION
215 // SUFFIX_POSITION ^
216 {
217 #ifndef NN_SYSTEM_KERNEL
218 s64 msec = nn::os::Tick::GetSystemCurrent().ToTimeSpan().GetMilliSeconds();
219 s32 sec = static_cast<s32>(msec/1000);
220 if (sec < 10000)
221 {
222 len += SafeTSNPrintf(&buffer[len], sizeof(buffer) - len, "%4ld.%02ld ", sec, static_cast<s32>(msec % 1000 / 10));
223 }
224 else
225 {
226 len += SafeTSNPrintf(&buffer[len], sizeof(buffer) - len, "%7ld ", sec);
227 }
228 #else
229 len += AddSpace(&buffer[len], 8);
230 #endif
231 }
232
233 {
234 char* pFileNameTop = GetBaseName(fileName);
235 char* pModulePosition = std::strchr(pFileNameTop, '_');
236
237 if (pModulePosition)
238 {
239 size_t moduleLength = std::min<size_t>(MODULE_LENGTH, reinterpret_cast<uptr>(pModulePosition) - reinterpret_cast<uptr>(pFileNameTop));
240 len += SafeTSNPrintf(&buffer[len], sizeof(buffer) - len, "[%0.*s]", moduleLength, pFileNameTop);
241 }
242 if (len < PREFIX1_POSITION)
243 {
244 len += AddSpace(&buffer[len], PREFIX1_POSITION - len);
245 }
246 if (level >= LEVEL_WARN && level <= Logger::LEVEL_FATAL)
247 {
248 len += SafeTSNPrintf(&buffer[len], sizeof(buffer) - len, "<%s>", s_LevelStrings[level]);
249 }
250
251 if (len < PREFIX2_POSITION)
252 {
253 len += AddSpace(&buffer[len], PREFIX2_POSITION - len);
254 }
255 }
256
257 {
258 // Gets method name and function name
259 size_t funcNameLen = MakeFuncName(funcName, &buffer[len], sizeof(buffer) - len);
260 if (funcNameLen > FUNCTION_LENGTH)
261 {
262 // Indicate maximum length exceeded by ".." in the middle
263 std::strcpy(&buffer[len + FUNCTION_LENGTH - FUNCTION_TAIL_LENGTH - 2], "..");
264 std::memmove(&buffer[len + FUNCTION_LENGTH - FUNCTION_TAIL_LENGTH], &buffer[len + funcNameLen - FUNCTION_TAIL_LENGTH], FUNCTION_TAIL_LENGTH);
265 funcNameLen = FUNCTION_LENGTH;
266 }
267 len += funcNameLen;
268 len += SafeTSNPrintf(&buffer[len], sizeof(buffer) - len, ": ");
269 if (len < PREFIX3_POSITION)
270 {
271 len += AddSpace(&buffer[len], PREFIX3_POSITION - len);
272 }
273
274 }
275 }
276 #endif
277 {
278 // Expand long message
279 // To prevent displaying a jumble between threads, output all at once sacrificing the maximum string length
280 va_list vlist;
281 va_start(vlist, fmt);
282 len += SafeTVSNPrintf(&buffer[len], sizeof(buffer) - len, fmt, vlist);
283 va_end(vlist);
284
285 // Delete when \n is at the end of the userMessage
286 for (; len > 0 && buffer[len - 1] == '\n'; --len)
287 {
288 buffer[len - 1] = '\0';
289 }
290 }
291
292 {
293 // Process fileName
294 char8* pFileNameTop;
295 if(!(s_ShowFlag & ENABLE_LONGPATH))
296 {
297 pFileNameTop = GetBaseName(fileName);
298 }
299 else
300 {
301 pFileNameTop = (char8*)fileName;
302 }
303
304 #ifdef ANOTHER_FORMAT
305 {
306 if (len < SUFFIX_POSITION)
307 {
308 len += AddSpaceUsingTab(&buffer[len], SUFFIX_POSITION - len);
309 }
310 }
311 #endif
312 len += SafeTSNPrintf(&buffer[len], sizeof(buffer) - len, " (%s:%d)", pFileNameTop, line);
313 }
314
315 // Always set a newline even for an overflow
316 len = std::min<size_t>(len, sizeof(buffer) - 2);
317 buffer[len++] = '\n';
318 buffer[len] = '\0'; // Just in case even though the length is specified
319
320 // Output once
321 nn::dbg::detail::PutString(buffer, len);
322 #else
323 NN_UNUSED_VAR(level);
324 NN_UNUSED_VAR(funcName);
325 NN_UNUSED_VAR(fileName);
326 NN_UNUSED_VAR(line);
327 NN_UNUSED_VAR(fmt);
328 #endif
329 }
330
331 /****************************************************************************/
332
333
334 /****************************************************************************/
335 }
336 } // namespace dbg
337 } // namespace nn
338