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