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