1 /*---------------------------------------------------------------------------*
2 Project: Horizon
3 File: dbg_Logger.cpp
4 Copyright (C)2009 Nintendo Co., Ltd. All rights reserved.
5 These coded instructions, statements, and computer programs contain
6 proprietary information of Nintendo of America Inc. and/or Nintendo
7 Company Ltd., and are protected by Federal copyright law. They may
8 not be disclosed to third parties or copied or duplicated in any form,
9 in whole or in part, without the prior written consent of Nintendo.
10 $Rev: 27790 $
11 *---------------------------------------------------------------------------
12
13
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