1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     dbg_Logger.cpp
4 
5   Copyright (C)2009 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: 27790 $
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     ネットワーク系の動作確認に適したログ出力形式に切り替え
32     - 絶対時間を表示
33     - WARN, ERROR, FATAL を目立つように
34     - モジュール名のみを先頭に表示
35     - 自由記述部分の位置がそろうように
36     - 長い関数名の真ん中を省略
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チェック
112 	// TODO: destのNULLチェック
113     char8* pSrcTop = const_cast<char8*>(src);
114     char8* pSrcSigHead = ::std::strrchr(pSrcTop, '(');
115     // 末尾を決定
116     // ENABLE_SIGNATURE ならば、srcの末尾
117     // そうでないなら、関数の ( の位置
118     char8* pSrcLast = (s_ShowFlag & ENABLE_SIGNATURE) ?
119                 pSrcTop + ::std::strlen(src) :
120                 pSrcSigHead;
121 
122     // 先頭を決定
123     // ENABLE_NAMESPACE ならば、srcの先頭、そうでなければ、
124     // 後ろから3つ目の:の位置 + 1
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     // 設定レベル範囲外ならすぐ抜ける
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         // 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             // メソッド名・関数名を取得
259             size_t funcNameLen = MakeFuncName(funcName, &buffer[len], sizeof(buffer) - len);
260             if (funcNameLen > FUNCTION_LENGTH)
261             {
262                 // 最大長を超えていたら途中を .. で省略
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         // ログメッセージを展開
279         // スレッド間でばらばらに混ざって表示されるのを防ぐため、最大文字列長を犠牲にして一気に出力する
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         // userMessageの末尾に\nが入っている場合、削除する
286         for (; len > 0 && buffer[len - 1] == '\n'; --len)
287         {
288             buffer[len - 1] = '\0';
289         }
290     }
291 
292     {
293         // 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     // 溢れていても改行は必ずセット
316     len = std::min<size_t>(len, sizeof(buffer) - 2);
317     buffer[len++] = '\n';
318     buffer[len] = '\0'; // 長さを指定しているが念のため
319 
320     // 一度に出力
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