YSTest: Debug.cpp 源文件

YSTest PreAlpha_b900_20201012 The YSLib Test Project

Debug.cpp
浏览该文件的文档.
1 /*
2 © 2011-2020 FrankHB.
3
4 This file is part of the YSLib project, and may only be used,
5 modified, and distributed under the terms of the YSLib project
6 license, LICENSE.TXT. By continuing to use, modify, or distribute
7 this file you indicate that you have read the license and
8 understand and accept it fully.
9 */
10
28 #include "YCLib/YModules.h"
29 #include YFM_YCLib_Debug // for wstring, string_view, std::puts, std::fflush,
30 // stderr;
31 #if YCL_Win32
32 # include YFM_Win32_YCLib_Consoles // for platform_ex::WConsole,
33 // STD_OUTPUT_HANDLE, STD_ERROR_HANDLE, platform_ex::Win32Exception;
34 # include YFM_Win32_YCLib_NLS // for platform_ex::UTF8ToWCS,
35 // platform_ex::WCSToUTF8;
36 # include YFM_YCLib_NativeAPI // for ::OutputDebugStringA, ::MessageBoxA;
37 # include <csignal> // for std::raise, SIGABRT;
38 #elif YCL_Android
39 # include <android/log.h>
40 #endif
41 #include YFM_YCLib_Host // for platfrom_ex::EncodeArg;
42 #if YF_Multithread == 1
43 # include <ystdex/concurrency.h>
44 #endif
45 #if !YCL_DS
46 # include <iostream> // for std::cerr;
47 #endif
48 #include <cstdarg> // for std::va_list, va_start, va_end;
49
50 namespace platform
51 {
52
53 namespace
54 {
55
57 inline PDefH(const char*, chk_null, const char* s)
58 ImplRet(s && *s != char()? s : "<unknown>")
59
60 #if YF_Multithread == 1
61 std::string
63 FetchCurrentThreadID() ynothrow
64 {
65 TryRet(ystdex::get_this_thread_id())
66 // XXX: Nothing more can be done.
67 CatchIgnore(...)
68 return {};
69 }
70 #endif
71
72 #if YCL_Win32
73 YB_NONNULL(3) size_t
75 WConsoleOutput(wstring& wstr, unsigned long h, const char* str)
76 {
77 using namespace platform_ex;
78
79 wstr = UTF8ToWCS(str) + L'\n';
80 return WConsole(h).WriteString(wstr);
81 }
82 #elif YCL_Android
83 template<typename... _tParams>
85 inline int
86 PrintAndroidLog(Descriptions::RecordLevel lv, const char* tag, const char *fmt,
87 _tParams&&... args)
88 {
89 return ::__android_log_print(platform_ex::MapAndroidLogLevel(lv),
90 Nonnull(tag), Nonnull(fmt), yforward(args)...);
91 }
92 #endif
93
95 using namespace Concurrency;
96
97 } // unnamed namespace;
98
99 bool
100 Echo(string_view sv) ynoexcept(YF_Platform == YF_Platform_DS)
101 {
102 #if YCL_DS
103 return std::puts(Nonnull(sv.data())) >= 0 && std::fflush(stdout) == 0;
104 #elif YCL_Win32
105 wstring wstr;
106 size_t n(0);
107
108 TryExpr(n = WConsoleOutput(wstr, STD_OUTPUT_HANDLE, sv.data()))
109 CatchIgnore(platform_ex::Win32Exception&)
110 if(n < wstr.length())
111 std::cout << platform_ex::EncodeArg(platform_ex::WCSToUTF8(&wstr[n]));
112 return bool(std::cout.flush());
113 #elif YF_Hosted
114 return bool(std::cout << platform_ex::EncodeArg(sv) << std::endl);
115 #else
116 return bool(std::cout << sv << std::endl);
117 #endif
118 }
119
120
121 Logger::Logger(const Logger& logger)
122 : FilterLevel(logger.FilterLevel), filter(logger.filter),
123 sender(logger.sender)
124 {}
125 Logger::Logger(Logger&& logger) ynothrow
126 : Logger()
127 {
128 swap(logger, *this);
129 }
130
131 void
132 Logger::SetFilter(Filter f)
133 {
134 if(f)
135 filter = std::move(f);
136 }
137 void
138 Logger::SetSender(Sender s)
139 {
140 if(s)
141 sender = std::move(s);
142 }
143
144 bool
145 Logger::DefaultFilter(Level lv, Logger& logger) ynothrow
146 {
147 return lv <= logger.FilterLevel;
148 }
149
150 void
151 Logger::DefaultSendLog(Level lv, Logger&, const char* str) ynothrowv
152 {
153 #if YCL_DS
154 yunused(lv), yunused(str);
155 #else
156 SendLog(std::cerr, lv, str);
157 #endif
158 }
159
160 void
161 Logger::DefaultSendLogToFile(Level lv, Logger&, const char* str)
162 ynothrowv
163 {
164 SendLogToFile(stderr, lv, str);
165 }
166
167 void
168 Logger::DoLog(Level level, const char* str)
169 {
170 if(str)
171 {
172 lock_guard<recursive_mutex> lck(record_mutex);
173
174 DoLogRaw(level, str);
175 }
176 }
177
178 void
179 Logger::DoLogRaw(Level level, const char* str)
180 {
181 sender(level, *this, Nonnull(str));
182 }
183
184 void
185 Logger::DoLogException(Level lv, const std::exception& e) ynothrow
186 {
187 const auto do_log_excetpion_raw([this](const char* msg)
188 YB_ATTR_LAMBDA_QUAL(ynothrow, YB_NONNULL(2)){
189 try
190 {
191 DoLogRaw(Descriptions::Emergent,
192 "Another exception thrown when handling exception.");
193 DoLogRaw(Descriptions::Emergent, msg);
194 }
195 CatchExpr(...,
196 ystdex::ytrace(stderr, Descriptions::Emergent, Descriptions::Notice,
197 __FILE__, __LINE__, "Logging error: unhandled exception."))
198 });
199 const auto& msg(e.what());
200 lock_guard<recursive_mutex> lck(record_mutex);
201
202 try
203 {
204 // XXX: Provide no throw guarantee and put it out of the critical
205 // section?
206 // XXX: Log demangled type name.
207 DoLogRaw(lv, sfmt("<%s>: %s", typeid(e).name(), msg));
208 }
209 CatchExpr(std::exception& ex, do_log_excetpion_raw(ex.what()))
210 CatchExpr(..., do_log_excetpion_raw("<unknown>"))
211 }
212
213 Logger::Sender
214 Logger::FetchDefaultSender(string_view tag)
215 {
216 yunused(tag);
217 YAssertNonnull(tag.data());
218 #if YCL_Win32
219 return [](Level lv, Logger& logger, const char* str){
220 // TODO: Avoid throwing of %WriteString here for better performance?
221 // XXX: Assume underlying output would always be completely updated.
222 try
223 {
224 wstring wstr;
225
226 WConsoleOutput(wstr, STD_ERROR_HANDLE, str);
227 }
228 CatchExpr(platform_ex::Win32Exception&, DefaultSendLog(lv, logger, str))
229 };
230 #elif YCL_Android
231 return platform_ex::AndroidLogSender(tag);
232 #else
233 return DefaultSendLog;
234 #endif
235 }
236
237 void
238 Logger::LogFailure() ynothrow
239 {
240 SendLogToFile(stderr, Descriptions::Emergent, "Log failure.");
241 }
242
243 void
244 Logger::SendLog(std::ostream& os, Level lv, const char* str)
245 ynothrowv
246 {
247 try
248 {
249 #if YF_Multithread == 1
250 const auto& t_id(FetchCurrentThreadID());
251
252 if(!t_id.empty())
253 // XXX: Error is ignored.
254 os << ystdex::sfmt("[%s:%#X]: %s\n", t_id.c_str(), unsigned(lv),
255 Nonnull(str));
256 else
257 #endif
258 // XXX: Error is ignored.
259 os << ystdex::sfmt("[%#X]: %s\n", unsigned(lv), Nonnull(str));
260 // XXX: Error is ignored.
261 os.flush();
262 }
263 CatchIgnore(...)
264 }
265
266 void
267 Logger::SendLogToFile(std::FILE* stream, Level lv, const char* str)
268 ynothrowv
269 {
270 YAssertNonnull(stream);
271 #if YF_Multithread == 1
272 const auto& t_id(FetchCurrentThreadID());
273
274 if(!t_id.empty())
275 // XXX: Error from 'std::fprintf' is ignored.
276 std::fprintf(stream, "[%s:%#X]: %s\n", t_id.c_str(), unsigned(lv),
277 Nonnull(str));
278 else
279 #endif
280 // XXX: Error from 'std::fprintf' is ignored.
281 std::fprintf(stream, "[%#X]: %s\n", unsigned(lv), Nonnull(str));
282 // XXX: Error is ignored.
283 std::fflush(stream);
284 }
285
286 void
287 swap(Logger& x, Logger& y) ynothrow
288 {
289 std::swap(x.FilterLevel, y.FilterLevel);
290 x.filter.swap(y.filter);
291 x.sender.swap(y.sender);
292 }
293
294
295 Logger&
296 FetchCommonLogger()
297 {
298 static Logger logger;
299
300 return logger;
301 }
302
303
304 string
305 LogWithSource(const char* file, int line, const char* fmt, ...)
306 {
307 try
308 {
309 std::va_list args;
310
311 va_start(args, fmt);
312
313 string str(vsfmt(fmt, args));
314
315 va_end(args);
316 return sfmt<string>("\"%s\":%i:\n", chk_null(file), line)
317 + std::move(str);
318 }
319 CatchExpr(...,
320 ystdex::ytrace(stderr, Descriptions::Emergent, Descriptions::Notice,
321 chk_null(file), line, "LogWithSource error: unhandled exception."))
322 return {};
323 }
324
325 } // namespace platform;
326
327 using namespace platform;
328
329 namespace platform_ex
330 {
331
332 #if YF_Multithread == 1
333 void
334 LogAssert(const char* expr_str, const char* file, int line,
335 const char* msg) ynothrow
336 {
337 # if YCL_Android
338 ::__android_log_assert(expr_str, "YFramework",
339 "Assertion failed @ \"%s\":%i:\n %s .\nMessage: \n%s\n", file, line,
340 expr_str, msg);
341 # else
342 # if YCL_Win32
343 try
344 {
345 char prog[MAX_PATH]{"<unknown>"};
346
347 ::GetModuleFileNameA({}, prog, MAX_PATH);
348
349 const auto& errstr(sfmt("Assertion failed @ program %s: "
350 "\"%s\":%i:\n %s .\nMessage: \n%s\n", prog, chk_null(file),
351 line, chk_null(expr_str), chk_null(msg)));
352
353 ::OutputDebugStringA(errstr.c_str());
354 // XXX: Not safe in windows procedure, but not used in YFramework.
355 // TODO: Use custom windows creation?
356 switch(::MessageBoxA({}, errstr.c_str(),
357 "YCLib Runtime Assertion", MB_ABORTRETRYIGNORE | MB_ICONHAND
358 | MB_SETFOREGROUND | MB_TASKMODAL))
359 {
360 case IDIGNORE:
361 return;
362 case IDABORT:
363 std::raise(SIGABRT);
364 default:
365 break;
366 }
367 std::terminate();
368 }
369 catch(...)
370 {
371 YF_TraceRaw(Descriptions::Emergent, "Unknown exception found.");
372 ystdex::yassert(expr_str, file, line, msg);
373 }
374 # endif
375 TryExpr(FetchCommonLogger().AccessRecord([=]{
376 ystdex::yassert(expr_str, file, line, msg);
377 }))
378 catch(...)
379 {
380 // XXX: Error from 'std::fprintf' is ignored.
381 std::fprintf(stderr, "Fetch logger failed.");
382 // XXX: Error is ignored.
383 std::fflush(stderr);
384 }
385 ystdex::yassert(expr_str, file, line, msg);
386 # endif
387 }
388 #endif
389
390
391 #if YCL_Win32
392 void
393 SendDebugString(Logger::Level lv, Logger&, const char* str) ynothrowv
394 {
395 try
396 {
397 const auto& t_id(FetchCurrentThreadID());
398
399 // TODO: Use %::WaitForDebugEventEx if possible. See https://msdn.microsoft.com/en-us/library/windows/desktop/mt171594(v=vs.85).aspx.
400 ::OutputDebugStringA((!t_id.empty() ? ystdex::sfmt("[%s:%#X]: %s",
401 t_id.c_str(), unsigned(lv), Nonnull(str)) : ystdex::sfmt(
402 "[%#X]: %s", unsigned(lv), Nonnull(str))).c_str());
403 }
404 CatchIgnore(...)
405 }
406 #elif YCL_Android
407 int
408 MapAndroidLogLevel(Descriptions::RecordLevel lv)
409 {
410 return lv < Descriptions::Critical ? ANDROID_LOG_FATAL
411 : ANDROID_LOG_FATAL - (std::uint8_t(lv) - 0x40) / 0x20;
412 }
413
414
415 AndroidLogSender::AndroidLogSender(string_view sv)
416 : tag((yunused(Nonnull(sv.data())), sv))
417 {}
418 ImplDeDtor(AndroidLogSender)
419
420 void
421 AndroidLogSender::operator()(Level lv, Logger& logger, const char* str) const
422 {
423 Logger::DefaultSendLog(lv, logger, str);
424 if(lv < Descriptions::Critical)
425 ::__android_log_assert("", "YFramework", "[%#X]: %s\n", unsigned(lv),
426 str);
427 else
428 PrintAndroidLog(lv, tag.c_str(), "[%#X]: %s\n", unsigned(lv), str);
429 }
430
431 #endif
432
433 } // namespace platform_ex;
434
platform::Echo
YF_API bool Echo(string_view) ynoexcept(YF_Platform
使用标准输出流打印字符串并刷新。
YF_TraceRaw
#define YF_TraceRaw(_lv,...)
YFramework 直接日志追踪。
Definition: Debug.h:378
CHRLib::e
const char const char * e
Definition: CharacterProcessing.h:112
x
const EncapsulationBase & x
Definition: NPLA1Forms.cpp:1536
platform::Nonnull
YB_PURE _type && Nonnull(_type &&p) ynothrowv
断言并返回非空参数。
Definition: Debug.h:518
ystdex::cpp2017::swap
void swap(optional< _type > &x, optional< _type > &y)
Definition: optional.h:888
CatchIgnore
#define CatchIgnore(_ex)
指定异常处理器忽略指定异常。
Definition: YBaseMacro.h:133
ystdex::cpp2017::basic_string
Definition: string.hpp:77
platform_ex
平台扩展命名空间。
Definition: Android.h:46
YB_ATTR_LAMBDA_QUAL
#define YB_ATTR_LAMBDA_QUAL(_q,...)
允许在 lambda 表达式的参数列表后和限定符共用的属性。
Definition: ydef.h:619
ystdex::get_this_thread_id
std::string get_this_thread_id()
取当前线程标识的字符串表示。
Definition: concurrency.h:78
platform::Descriptions::Emergent
@ Emergent
Definition: YCommon.h:179
platform::Logger::SetSender
PDefHOp(Logger &,=, const Logger &logger) ImplRet(ystdex void SetSender(Sender)
设置过滤器。
Definition: Debug.h:183
platform::Descriptions::RecordLevel
RecordLevel
记录等级。
Definition: YCommon.h:178
ystdex::cpp2017::string_view
basic_string_view< char > string_view
Definition: string_view.hpp:437
ystdex::ytrace
void ytrace(std::FILE *, std::uint8_t, std::uint8_t, const char *, int, const char *,...)
YCLib 调试跟踪函数。
platform::size_t
YB_ATTR_nodiscard YF_API size_t
Definition: FileIO.h:1345
ImplDeDtor
#define ImplDeDtor(_t)
Definition: YBaseMacro.h:183
platform::if
if(!p) p
YF_Platform
#define YF_Platform
目标平台。
Definition: Platform.h:231
platform_ex::Windows::WCSToUTF8
const char *str string_view sv WCSToUTF8
Definition: NLS.h:79
CHRLib::f
f(d, s)
platform::terminate
YB_NORETURN YF_API void terminate() ynothrow
终止函数。
Definition: YCommon.cpp:48
CHRLib::args
YB_ATTR_nodiscard _tParams && args
Definition: CharacterProcessing.h:649
YSLib::lv
const char RecordLevel lv
Definition: YException.cpp:61
ystdex::vsfmt
_tString vsfmt(const typename string_traits< _tString >::value_type *fmt, std::va_list args)
以 C 标准输出格式的输出字符串类型的对象。
Definition: string.hpp:1998
platform::FetchCommonLogger
YF_API Logger & FetchCommonLogger()
取公共日志记录器。
concurrency.h
并发操作。
platform::Descriptions::Critical
@ Critical
Definition: YCommon.h:181
platform_ex::Windows::UTF8ToWCS
UTF8ToWCS
Definition: NLS.h:72
platform::ImplRet
YB_ATTR_nodiscard const char *s ImplRet(Nonnull(s)) YB_ATTR_nodiscard inline PDefH(const string &
YF_Platform_DS
#define YF_Platform_DS
目标平台标识。
Definition: Platform.h:130
yforward
#define yforward(_expr)
根据参数类型使用 std::forward 转发对应参数。
Definition: ydef.h:1508
platform::ynothrow
const _tString &path ynothrow
Definition: YCLib/FileSystem.h:453
ystdex::nptr::swap
void swap(nptr< _type > &x, nptr< _type > &y)
Definition: pointer.hpp:252
ystdex::cpp2017::data
auto data(_tRange &c) -> decltype(c.data())
Definition: range.hpp:581
filter
Filter filter
Definition: NPLA1.cpp:360
platform::Descriptions::Notice
@ Notice
Definition: YCommon.h:184
platform_ex::Windows::WConsole
Windows 控制台对象。
Definition: Consoles.h:70
std
Definition: optional.h:982
ystdex::sfmt
_tString sfmt(const typename string_traits< _tString >::value_type *fmt,...)
以 C 标准输出格式的输出 basic_string 实例的对象。
Definition: string.hpp:2028
platform::Logger::Logger
Logger(Logger &&) ynothrow
转移构造：转移过滤等级、转移过滤器和发送器，使用新创建的互斥量。
YAssertNonnull
#define YAssertNonnull(_expr)
Definition: cassert.h:165
platform::Logger::DefaultFilter
static YB_PURE bool DefaultFilter(Level, Logger &) ynothrow
默认过滤：仅允许等级不大于阈值的日志被记录。
platform::KeyCodes::DS::L
@ L
Definition: Keys.h:228
YSLib::UI::y
SPos SPos y
Definition: YWidget.cpp:44
platform::YB_NONNULL
YB_NONNULL(2) YB_PURE inline PDefH(std
组合消息和函数签名字符串。
Definition: Debug.h:560
CatchExpr
#define CatchExpr(_ex,...)
指定表达式作为异常处理器。
Definition: YBaseMacro.h:129
platform::Logger::filter
Filter filter
Definition: Debug.h:130
platform
默认平台命名空间。
Definition: Container.h:56
CHRLib::s
_tDst const _tSrc * s
Definition: CharacterProcessing.h:143
platform_ex::Windows::WConsole::WriteString
size_t WriteString(string_view)
输出字符串。
NPL::A1::name
const char * name
Definition: NPLA1.h:1876
ystdex::yassert
void yassert(const char *, const char *, int, const char *)
YBase 默认断言函数。
Definition: cassert.cpp:36
YSLib::_tParams
YB_ATTR_nodiscard YB_PURE typename _tParams
Definition: ValueNode.h:1294
YModules.h
YCLib 库模块配置文件。
NPL::c_str
YF_API const char src c_str())) YF_API void InstallDirectory(const string &
安装目录：复制目录树。
Definition: Dependency.h:140
platform::PDefH
YB_ATTR_nodiscard PDefH(string, MakePathString, u16string_view sv) ImplRet(MakePathString(sv.data())) using FileNodeID
文件节点标识类型。
platform::Logger
日志记录器。
Definition: Debug.h:97
platform::ynothrowv
YB_ATTR_nodiscard YF_API int amode ynothrowv
Definition: FileIO.h:550
platform::LogWithSource
YF_API string LogWithSource(const char *, int, const char *,...)
YSLib::Text::str
YB_ATTR_nodiscard YB_PURE const char const char * str
Definition: TextFile.h:83
platform::Logger::sender
Sender sender
Definition: Debug.h:132
platform::Logger::LogFailure
static void LogFailure() ynothrow
记录失败输出。
const
const Decapsulate const Decapsulate &y ynothrow ContextNode &ctx const
Definition: NPLA1Forms.cpp:1629
platform::Logger::record_mutex
Concurrency::recursive_mutex record_mutex
日志记录互斥量。
Definition: Debug.h:140
platform::bool
YB_ATTR_nodiscard std::FILE *fp bool
Definition: FileIO.h:1271
ystdex::cpp2017::string
basic_string< char > string
Definition: string.hpp:772
ynothrow
#define ynothrow
YSLib 无异常抛出保证：若支持 noexcept 关键字， 指定特定的 noexcept 异常规范。
Definition: ydef.h:1199
TryRet
#define TryRet(...)
指定表达式在 try 块中求值并返回。
Definition: YBaseMacro.h:124
TryExpr
#define TryExpr(...)
指定表达式在 try 块中求值。
Definition: YBaseMacro.h:120
yunused
#define yunused(...)
标记未使用的表达式。
Definition: ydef.h:1458
ynoexcept
#define ynoexcept(...)
YSLib 无异常抛出保证：指定特定的异常规范。
Definition: ydef.h:1151
platform::Logger::DoLog
void DoLog(Level, const char *)
转发等级和日志至发送器。
YSLib::level
YF_API size_t level
Definition: YException.h:156
ystdex::sfmt< string >
template string sfmt< string >(const char *,...)
显式实例化：以 C 标准输出格式的输出 string 对象。