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