Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Trace.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include <cmath>
7#include <cstdarg>
8#include <cstdio>
9#include <fstream>
10#include <map>
11#include <mutex>
12
15#include "Stroika/Foundation/Characters/LineEndings.h"
20#include "Stroika/Foundation/Execution/Common.h"
21#include "Stroika/Foundation/Execution/Module.h"
22#include "Stroika/Foundation/Execution/Process.h"
24#include "Stroika/Foundation/Memory/Common.h"
28
29#if qStroika_Foundation_Debug_TraceToFile
32#endif
33
34using namespace Stroika::Foundation;
35
36using namespace Characters;
37using namespace Debug;
38using namespace Execution;
39
41
42using Debug::Private_::Emitter;
43
44/*
45 * TODO:
46 *
47 * @todo The buffering code here maybe now correct, but isn't simple/clear, so rewrite/improve...
48 * -- LGP 2011-10-03
49 */
50
51CompileTimeFlagChecker_SOURCE (Stroika::Foundation::Debug, qTraceToFile, qStroika_Foundation_Debug_TraceToFile);
52CompileTimeFlagChecker_SOURCE (Stroika::Foundation::Debug, qDefaultTracingOn, qStroika_Foundation_Debug_DefaultTracingOn);
53
54namespace {
55 // This is MOSTLY to remove NEWLINES from the MIDDLE of a message - replace with kBadChar.
56 const char kBadChar_ = ' ';
57 void SquishBadCharacters_ (string* s) noexcept
58 {
60 size_t end = s->length ();
61 // ignore last 2 in case crlf
62 if (end > 2) {
63 end -= 2;
64 }
65 for (size_t i = 0; i < end; ++i) {
66 if ((*s)[i] == '\n' or (*s)[i] == '\r') {
67 (*s)[i] = kBadChar_;
68 }
69 }
70 }
71 void SquishBadCharacters_ (wstring* s) noexcept
72 {
74 size_t end = s->length ();
75 // ignore last 2 in case crlf
76 if (end > 2) {
77 end -= 2;
78 }
79 for (size_t i = 0; i < end; ++i) {
80 if ((*s)[i] == '\n' or (*s)[i] == '\r') {
81 (*s)[i] = kBadChar_;
82 }
83 }
84 }
85}
86
87namespace {
88#if qStroika_Foundation_Debug_DefaultTracingOn
89 thread_local unsigned int tTraceContextDepth_{0}; // no need for atomic access because thread_local
90#endif
91
92 // Declared HERE instead of the template so they get shared across TYPE values for CHARTYPE
93 Thread::IDType sMainThread_ = Execution::Thread::GetCurrentThreadID ();
94}
95
96/*
97 ********************************************************************************
98 ******************************* Debug::Private_::Emitter ***********************
99 ********************************************************************************
100 */
101
102template <typename CHARTYPE>
103inline void Debug::Private_::Emitter::EmitUnadornedText (const CHARTYPE* p)
104{
105 DoEmit_ (p);
106}
107
108/*
109 ********************************************************************************
110 ************************* Debug::Private_::ModuleInit_ *************************
111 ********************************************************************************
112 */
113namespace {
114 struct PrivateModuleData_ {
115 recursive_mutex fModuleMutex; // see GetEmitCritSection_
116 Emitter fEmitter;
117
118#if qStroika_Foundation_Debug_TraceToFile
119 ofstream fTraceFile;
120#endif
121
122#if qStroika_Foundation_Debug_TraceToFile
123 PrivateModuleData_ ()
124 {
125 fTraceFile.open (Debug::Private_::Emitter::GetTraceFileName ().native ().c_str (), ios::out | ios::binary);
126 }
127#endif
128 };
129 PrivateModuleData_* sModuleData_{nullptr};
130}
131
132Debug::Private_::ModuleInit_::ModuleInit_ () noexcept
133{
134 Assert (sModuleData_ == nullptr);
135 sModuleData_ = new PrivateModuleData_ ();
136}
137Debug::Private_::ModuleInit_::~ModuleInit_ ()
138{
139 Assert (sModuleData_ != nullptr);
140 delete sModuleData_;
141#if qStroika_Foundation_Debug_AssertionsChecked
142 sModuleData_ = nullptr;
143#endif
144}
145
146/*
147 ********************************************************************************
148 ************************** Private_::Emitter ***********************************
149 ********************************************************************************
150 */
151auto Debug::Private_::Emitter::Get () noexcept -> Emitter&
152{
153 auto emitFirstTime = [] () {
154 // Cannot call DbgTrace or TraceContextBumper in this code (else hang cuz calls back to Emitter::Get ())
155 // which is why this function takes Emitter as argument!
156 sModuleData_->fEmitter.EmitTraceMessage ("***Starting TraceLog***"_f);
157 sModuleData_->fEmitter.EmitTraceMessage ("Starting at {}"_f, Time::DateTime::Now ().Format ());
158#if qStroika_Foundation_Debug_TraceToFile
159 sModuleData_->fEmitter.EmitTraceMessage ("TraceFileName: {}"_f, Emitter::GetTraceFileName ());
160#endif
161 sModuleData_->fEmitter.EmitTraceMessage ("EXEPath={}"_f, Execution::GetEXEPath ());
162 sModuleData_->fEmitter.EmitTraceMessage ("<debug-state>"_f);
163 sModuleData_->fEmitter.EmitTraceMessage (" Debug::kBuiltWithAddressSanitizer = {}"_f, Debug::kBuiltWithAddressSanitizer);
164 sModuleData_->fEmitter.EmitTraceMessage (" Debug::kBuiltWithThreadSanitizer = {}"_f, Debug::kBuiltWithThreadSanitizer);
165 sModuleData_->fEmitter.EmitTraceMessage (" Debug::kBuiltWithUndefinedBehaviorSanitizer = {}(?)"_f,
166 Debug::kBuiltWithUndefinedBehaviorSanitizer); // warning maybe falsely reported as false on gcc
167 sModuleData_->fEmitter.EmitTraceMessage (" Debug::IsRunningUnderValgrind () = {}"_f, Debug::IsRunningUnderValgrind ());
168 sModuleData_->fEmitter.EmitTraceMessage ("</debug-state>"_f);
169 };
170 static once_flag sOnceFlag_;
171 call_once (sOnceFlag_, [=] () { emitFirstTime (); });
172 return sModuleData_->fEmitter;
173}
174
175#if qStroika_Foundation_Debug_TraceToFile
176filesystem::path Debug::Private_::Emitter::GetTraceFileName ()
177{
178 auto mkTraceFileName_ = [] () -> filesystem::path {
179 // Use TempDir instead of EXEDir because on vista, installation permissions prevent us from (easily) writing in EXEDir.
180 // (could fix of course, but I'm not sure desirable - reasonable defaults)
181 //
182 // Don't want to use TempFileLibrarian cuz we don't want these deleted on app exit
183 SDKString mfname;
184 {
185 try {
186 mfname = Execution::GetEXEPath ().native ();
187 }
188 catch (...) {
189 mfname = SDKSTR ("{unknown}");
190 }
191 {
192 size_t i = mfname.rfind (filesystem::path::preferred_separator);
193 if (i != SDKString::npos) {
194 mfname = mfname.substr (i + 1);
195 }
196 i = mfname.rfind ('.');
197 if (i != SDKString::npos) {
198 mfname.erase (i);
199 }
200 }
201 for (auto i = mfname.begin (); i != mfname.end (); ++i) {
202 if (*i == ' ') {
203 *i = '-';
204 }
205 }
206 }
207 SDKString nowstr = Time::DateTime::Now ().Format (Time::DateTime::kISO8601Format).AsSDKString (); // use eISO8601 instead of eCurrentLocale cuz locale CTOR not safe to construct before main
208 for (auto i = nowstr.begin (); i != nowstr.end (); ++i) {
209 if (*i == ':') {
210 *i = '-';
211 }
212 if (*i == '/' or *i == ' ') {
213 *i = '_';
214 }
215 }
217 CString::Format (SDKSTR ("TraceLog_%s_PID#%d-%s.txt"), mfname.c_str (), (int)Execution::GetCurrentProcessID (), nowstr.c_str ());
218 };
219 static filesystem::path sTraceFileName_ = mkTraceFileName_ ();
220 return sTraceFileName_;
221}
222#endif
223
224#if qStroika_Foundation_Debug_TraceToFile
225namespace {
226 void Emit2File_ (const char* text) noexcept
227 {
228 RequireNotNull (text);
229 AssertNotNull (sModuleData_);
230 try {
231 if (sModuleData_->fTraceFile.is_open ()) {
232 sModuleData_->fTraceFile << text;
233 sModuleData_->fTraceFile.flush ();
234 }
235 }
236 catch (...) {
238 }
239 }
240 void Emit2File_ (const wchar_t* text) noexcept
241 {
242 RequireNotNull (text);
243 try {
244 Emit2File_ (String{text}.AsNarrowSDKString (eIgnoreErrors).c_str ());
245 }
246 catch (...) {
248 }
249 }
250}
251#endif
252
253/*
254@DESCRIPTION: <p>This function takes a 'format' argument and then any number of additional arguments - exactly
255 like std::printf (). It calls std::vsprintf () internally. This can be called directly - regardless of the
256 @'qStroika_Foundation_Debug_DefaultTracingOn' flag - but is typically just called indirectly by calling
257 @'DbgTrace'.</p>
258*/
259void Debug::Private_::Emitter::EmitTraceMessage (const char* format, ...) noexcept
260{
261 if (TraceContextSuppressor::GetSuppressTraceInThisThread ()) {
262 return;
263 }
265 try {
266 va_list argsList;
267 va_start (argsList, format);
268 string tmp = Characters::CString::FormatV (format, argsList);
269 va_end (argsList);
270 SquishBadCharacters_ (&tmp);
271 AssureHasLineTermination (&tmp);
272 DoEmitMessage_ (0, Containers::Start (tmp), Containers::End (tmp));
273 }
274 catch (...) {
275 WeakAssert (false); // Should NEVER happen anymore becuase of new vsnprintf() stuff
276 // Most likely indicates invalid format string for varargs parameters
277 DoEmit_ (L"EmitTraceMessage FAILED internally (buffer overflow?)");
278 }
279}
280
281void Debug::Private_::Emitter::EmitTraceMessage (const wchar_t* format, ...) noexcept
282{
283 if (TraceContextSuppressor::GetSuppressTraceInThisThread ()) {
284 return;
285 }
287 try {
288 va_list argsList;
289 va_start (argsList, format);
290 wstring tmp = Characters::CString::FormatV (format, argsList);
291 va_end (argsList);
292 SquishBadCharacters_ (&tmp);
293 AssureHasLineTermination (&tmp);
294 DoEmitMessage_ (0, Containers::Start (tmp), Containers::End (tmp));
295 }
296 catch (...) {
297 WeakAssert (false); // Should NEVER happen anymore becuase of new vsnprintf() stuff
298 // Most likely indicates invalid format string for varargs parameters
299 DoEmit_ (L"EmitTraceMessage FAILED internally (buffer overflow?)");
300 }
301}
302
303#if 0
304auto Debug::Private_::Emitter::EmitTraceMessage_ (size_t bufferLastNChars, const wchar_t* format, ...) noexcept -> TraceLastBufferedWriteTokenType
305{
306 if (TraceContextSuppressor::GetSuppressTraceInThisThread ()) {
307 return 0;
308 }
310 try {
311 va_list argsList;
312 va_start (argsList, format);
313 wstring tmp = Characters::CString::FormatV (format, argsList);
314 va_end (argsList);
315 SquishBadCharacters_ (&tmp);
316 AssureHasLineTermination (&tmp);
317 return DoEmitMessage_ (bufferLastNChars, Containers::Start (tmp), Containers::End (tmp));
318 }
319 catch (...) {
320 WeakAssert (false); // Should NEVER happen anymore becuase of new vsnprintf() stuff
321 // Most likely indicates invalid format string for varargs parameters
322 DoEmit_ (L"EmitTraceMessage FAILED internally (buffer overflow?)");
323 return 0;
324 }
325}
326#endif
327
328auto Debug::Private_::Emitter::EmitTraceMessage_ (size_t bufferLastNChars, wstring_view format, Common::StdCompat::wformat_args&& args) noexcept
329 -> TraceLastBufferedWriteTokenType
330{
331 if (TraceContextSuppressor::GetSuppressTraceInThisThread ()) {
332 return 0;
333 }
335 try {
336 wstring tmp = Common::StdCompat::vformat (qStroika_Foundation_Characters_FMT_PREFIX_::wstring_view{format}, args);
337 SquishBadCharacters_ (&tmp);
338 AssureHasLineTermination (&tmp);
339 return DoEmitMessage_ (bufferLastNChars, Containers::Start (tmp), Containers::End (tmp));
340 }
341 catch (...) {
342 WeakAssert (false); // Should NEVER happen anymore becuase of new vsnprintf() stuff
343 // Most likely indicates invalid format string for varargs parameters
344 DoEmit_ (L"EmitTraceMessage FAILED internally (buffer overflow?)");
345 return 0;
346 }
347}
348
349void Debug::Private_::Emitter::EmitTraceMessage_ (wstring_view format, Common::StdCompat::wformat_args&& args) noexcept
350{
351 try {
352 EmitTraceMessage_ (vformat (qStroika_Foundation_Characters_FMT_PREFIX_::wstring_view{format}, args));
353 }
354 catch (...) {
355 }
356}
357void Debug::Private_::Emitter::EmitTraceMessage_ (string_view format, Common::StdCompat::format_args&& args) noexcept
358{
359 try {
360 Debug::Private_::Emitter::EmitTraceMessage_ (
361 Characters::String::FromNarrowSDKString (vformat (qStroika_Foundation_Characters_FMT_PREFIX_::string_view{format}, args)).As<wstring> ());
362 }
363 catch (...) {
364 }
365}
366
367void Debug::Private_::Emitter::EmitTraceMessage_ (const wstring& raw) noexcept
368{
369 if (TraceContextSuppressor::GetSuppressTraceInThisThread ()) {
370 return;
371 }
373 try {
374 wstring tmp = raw;
375 SquishBadCharacters_ (&tmp);
376 AssureHasLineTermination (&tmp);
377 DoEmitMessage_ (0, Containers::Start (tmp), Containers::End (tmp));
378 }
379 catch (...) {
380 WeakAssert (false); // Should NEVER happen anymore becuase of new vsnprintf() stuff
381 // Most likely indicates invalid format string for varargs parameters
382 DoEmit_ (L"EmitTraceMessage FAILED internally (buffer overflow?)");
383 }
384}
385
386namespace {
387 // .first is true iff added, and false if already present
388 // .second is the threadid to display
389 pair<bool, string> mkThreadLabelForThreadID_ (const Thread::IDType& threadID)
390 {
392 static atomic<int> sMinWidth_ = 4; // for MAIN
393 bool wasNew = false;
394 unsigned int threadIndex2Show = Thread::IndexRegistrar::sThe.GetIndex (threadID, &wasNew);
395 if (wasNew) {
396 if (threadIndex2Show >= 10000) {
397 sMinWidth_.store (5); // could enhance if we anticipate more threads
398 }
399 }
400 if (threadID == sMainThread_) {
401 static const string kMAIN_{"MAIN"sv};
402 return make_pair (wasNew, kMAIN_);
403 }
404 else {
405 char buf[1024];
406 (void)::snprintf (buf, Memory::NEltsOf (buf), "%.*d", sMinWidth_.load (), threadIndex2Show);
407 return make_pair (wasNew, buf);
408 }
409 }
410 else {
411 // If this is deemed useful, then re-instate the mapping of threadID == sMainThread_ to "MAIN" with appropriate -- around it
412 return make_pair (false, Thread::FormatThreadID_A (threadID));
413 }
414 }
415}
416template <typename CHARTYPE>
417auto Debug::Private_::Emitter::DoEmitMessage_ (size_t bufferLastNChars, const CHARTYPE* s, const CHARTYPE* e) -> TraceLastBufferedWriteTokenType
418{
419 [[maybe_unused]] lock_guard critSec{sModuleData_->fModuleMutex};
420 FlushBufferedCharacters_ ();
421
422 auto curRelativeTime = Time::DisplayedRealtimeClock::now (); // same as Time::clock_cast<Time::DisplayedRealtimeClock> (Time::GetTickCount ())
423
424 {
425 char buf[1024];
426 Thread::IDType threadID = Execution::Thread::GetCurrentThreadID ();
427 pair<bool, string> threadIDInfo = mkThreadLabelForThreadID_ (threadID);
428 Verify (::snprintf (buf, Memory::NEltsOf (buf), "[%s][%08.3f]\t", threadIDInfo.second.c_str (),
429 static_cast<double> (curRelativeTime.time_since_epoch ().count ())) > 0);
430 if (threadIDInfo.first) {
431 char buf2[1024]; // intentionally un-initialized
432 Verify (snprintf (buf2, Memory::NEltsOf (buf2), "(NEW THREAD, index=%s Real Thread ID=%s)\t", threadIDInfo.second.c_str (),
433 Thread::FormatThreadID_A (threadID).c_str ()) > 0);
434#if __STDC_WANT_SECURE_LIB__
435 (void)::strcat_s (buf, buf2);
436#else
437 (void)::strcat (buf, buf2);
438#endif
439#if qStroika_Foundation_Common_Platform_POSIX
440 Verify (::snprintf (buf2, Memory::NEltsOf (buf2), "(pthread_self=0x%lx)\t", (unsigned long)pthread_self ()) > 0);
441#if __STDC_WANT_SECURE_LIB__
442 (void)::strcat_s (buf, buf2);
443#else
444 (void)::strcat (buf, buf2);
445#endif
446#endif
447 }
448 Assert (::strlen (buf) < Memory::NEltsOf (buf) / 2); // really just needs to be <, but since this buffer unchecked, break if we get CLOSE
449 DoEmit_ (buf);
450 }
451#if qStroika_Foundation_Debug_DefaultTracingOn
452 unsigned int contextDepth = TraceContextBumper::GetCount ();
453 for (unsigned int i = 0; i < contextDepth; ++i) {
454 DoEmit_ (L"\t");
455 }
456#endif
457 if (bufferLastNChars == 0) {
458 DoEmit_ (s, e);
459 ++fLastNCharBuf_Token_; // even if not buffering, increment, so other buffers known to be invalid
460 }
461 else {
462 Assert ((e - s) > static_cast<ptrdiff_t> (bufferLastNChars));
463 BufferNChars_ (bufferLastNChars, e - bufferLastNChars);
464 DoEmit_ (s, e - bufferLastNChars);
465 fLastNCharBuf_WriteTickcount_ = curRelativeTime;
466 ++fLastNCharBuf_Token_; // even if not buffering, increment, so other buffers known to be invalid
467 }
468 return fLastNCharBuf_Token_;
469}
470
471void Debug::Private_::Emitter::BufferNChars_ (size_t bufferLastNChars, const char* p)
472{
473 Assert (bufferLastNChars < Memory::NEltsOf (fLastNCharBuf_CHAR_));
474 fLastNCharBufCharCount_ = bufferLastNChars;
475 (void)::memcpy (fLastNCharBuf_CHAR_, p, bufferLastNChars); // no need to nul-terminate because fLastNCharBufCharCount_ stores length
476 fLastNCharBuf_WCHARFlag_ = false;
477}
478
479void Debug::Private_::Emitter::BufferNChars_ (size_t bufferLastNChars, const wchar_t* p)
480{
481 Assert (bufferLastNChars < Memory::NEltsOf (fLastNCharBuf_WCHAR_));
482 fLastNCharBufCharCount_ = bufferLastNChars;
483 (void)::memcpy (fLastNCharBuf_WCHAR_, p, bufferLastNChars * sizeof (wchar_t)); // no need to nul-terminate because fLastNCharBufCharCount_ stores length
484 fLastNCharBuf_WCHARFlag_ = true;
485}
486
487void Debug::Private_::Emitter::FlushBufferedCharacters_ ()
488{
489 if (fLastNCharBufCharCount_ != 0) {
490 if (fLastNCharBuf_WCHARFlag_) {
491 DoEmit_ (fLastNCharBuf_WCHAR_, fLastNCharBuf_WCHAR_ + fLastNCharBufCharCount_);
492 }
493 else {
494 DoEmit_ (fLastNCharBuf_CHAR_, fLastNCharBuf_CHAR_ + fLastNCharBufCharCount_);
495 }
496 fLastNCharBufCharCount_ = 0;
497 }
498}
499
500bool Debug::Private_::Emitter::UnputBufferedCharactersForMatchingToken (TraceLastBufferedWriteTokenType token)
501{
502 RequireNotNull (sModuleData_);
503 [[maybe_unused]] lock_guard critSec{sModuleData_->fModuleMutex};
504 // If the fLastNCharBuf_Token_ matches (no new tokens written since the saved one) and the time
505 // hasn't been too long (we currently write 1/100th second timestamp resolution).
506 // then blank unput (ignore) buffered characters, and return true so caller knows to write
507 // funky replacement for those characters.
508 if (fLastNCharBuf_Token_ == token and (Time::DisplayedRealtimeClock::now () - fLastNCharBuf_WriteTickcount_ < 20ms)) {
509 fLastNCharBufCharCount_ = 0;
510 return true;
511 }
512 return false; // assume old behavior for now
513}
514
515void Debug::Private_::Emitter::DoEmit_ (const char* p) noexcept
516{
517#if qStroika_Foundation_Common_Platform_Windows
518 constexpr size_t kMaxLen_ = 1023; // no docs on limit, but various hints the limit is somewhere between 1k and 4k. Empirically - just chops off after a point...
519 if (::strlen (p) < kMaxLen_) {
520 ::OutputDebugStringA (p);
521 }
522 else {
523 char buf[1024]; // @todo if/when we always support constexpr can use that here!
524 (void)::memcpy (buf, p, sizeof (buf));
525 buf[Memory::NEltsOf (buf) - 1] = 0;
526 ::OutputDebugStringA (buf);
527 ::OutputDebugStringA ("...");
528 ::OutputDebugStringA (kEOL<char>);
529 }
530#endif
531#if qStroika_Foundation_Debug_TraceToFile
532 Emit2File_ (p);
533#endif
534}
535
536void Debug::Private_::Emitter::DoEmit_ (const wchar_t* p) noexcept
537{
538#if qStroika_Foundation_Common_Platform_Windows
539 constexpr size_t kMaxLen_ = 1023; // no docs on limit, but various hints the limit is somewhere between 1k and 4k. Empirically - just chops off after a point...
540 if (::wcslen (p) < kMaxLen_) {
541 ::OutputDebugStringW (p);
542 }
543 else {
544 wchar_t buf[1024]; // @todo if/when we always support constexpr can use that here!
545 (void)::memcpy (buf, p, sizeof (buf));
546 buf[Memory::NEltsOf (buf) - 1] = 0;
547 ::OutputDebugStringW (buf);
548 ::OutputDebugStringW (L"...");
549 ::OutputDebugStringW (kEOL<wchar_t>);
550 }
551#endif
552#if qStroika_Foundation_Debug_TraceToFile
553 Emit2File_ (p);
554#endif
555}
556
557void Debug::Private_::Emitter::DoEmit_ (const char* p, const char* e) noexcept
558{
559 try {
560 size_t len = e - p;
561 StackBuffer<char> buf{Memory::eUninitialized, len + 1};
562 (void)::memcpy (buf.begin (), p, len);
563 buf.begin ()[len] = '\0';
564 DoEmit_ (buf.begin ());
565 }
566 catch (...) {
568 }
569}
570
571/*
572 [Succeeded] (1 seconds) [42] Foundation::Execution::Other (scp ../Builds/raspberrypi-g++-12-release-sanitize_address/Tests/Test42...; ssh lewis@192.168.244.20 /tmp/Test42)
573=================================================================
574==24967==ERROR: AddressSanitizer: stack-use-after-scope on address 0xbefb9b80 at pc 0x0048c10f bp 0xbefb9688 sp 0xbefb9694
575WRITE of size 100 at 0xbefb9b80 thread T0
576 #0 0x48c10c in __interceptor_memcpy (/tmp/Test43+0x6c10c)
577 #1 0x6189ee in memcpy /usr/arm-linux-gnueabihf/include/bits/string_fortified.h:29
578 #2 0x6189ee in Stroika::Foundation::Debug::Private_::Emitter::DoEmit_(wchar_t const*, wchar_t const*) /home/lewis/Sandbox/Stroika-Build-Dir-Ubuntu2204-Cross-Compile2RaspberryPi/Library/Sources/Stroika/Foundation/Debug/Trace.cpp:553
579 #3 0x6236c8 in int Stroika::Foundation::Debug::Private_::Emitter::DoEmitMessage_<wchar_t>(unsigned int, wchar_t const*, wchar_t const*) /home/lewis/Sandbox/Stroika-Build-Dir-Ubuntu2204-Cross-Compile2RaspberryPi/Library/Sources/Stroika/Foundation/Debug/Trace.cpp:432
580 #4 0x61928e in Stroika::Foundation::Debug::Private_::Emitter::EmitTraceMessage(wchar_t const*, ...) /home/lewis/Sandbox/Stroika-Build-Dir-Ubuntu2204-Cross-Compile2RaspberryPi/Library/Sources/Stroika/Foundation/Debug/Trace.cpp:299
581 #5 0x6198ba in Stroika::Foundation::Debug::TraceContextBumper::~TraceContextBumper() /home/lewis/Sandbox/Stroika-Build-Dir-Ubuntu2204-Cross-Compile2RaspberryPi/Library/Sources/Stroika/Foundation/Debug/Trace.cpp:645
582 #6 0x6198ba in Stroika::Foundation::Debug::TraceContextBumper::~TraceContextBumper() /home/lewis/Sandbox/Stroika-Build-Dir-Ubuntu2204-Cross-Compile2RaspberryPi/Library/Sources/Stroika/Foundation/Debug/Trace.cpp:634
583 #7 0x5423cc in DoTests_ /home/lewis/Sandbox/Stroika-Build-Dir-Ubuntu2204-Cross-Compile2RaspberryPi/Tests/43/Test.cpp:376
584 #8 0x54cb52 in DoRegressionTests_ /home/lewis/Sandbox/Stroika-Build-Dir-Ubuntu2204-Cross-Compile2RaspberryPi/Tests/43/Test.cpp:568
585 #9 0x56ac3c in Stroika::TestHarness::PrintPassOrFail(void (*)()) ../TestHarness/TestHarness.cpp:89
586 #10 0xb6d1a3bc in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
587 #11 0xb6d1a4c4 in __libc_start_main_impl csu/libc-start.c:360
588
589Address 0xbefb9b80 is located in stack of thread T0 at offset 128 in frame
590 #0 0x6188e8 in Stroika::Foundation::Debug::Private_::Emitter::DoEmit_(wchar_t const*, wchar_t const*) /home/lewis/Sandbox/Stroika-Build-Dir-Ubuntu2204-Cross-Compile2RaspberryPi/Library/Sources/Stroika/Foundation/Debug/Trace.cpp:549
591
592 This frame has 1 object(s):
593 [48, 4152) 'buf' (line 552) <== Memory access at offset 128 is inside this variable
594HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
595*/
596#if qCompilerAndStdLib_arm_asan_FaultStackUseAfterScope_Buggy
597Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_ADDRESS
598#endif
599 void
600 Debug::Private_::Emitter::DoEmit_ (const wchar_t* p, const wchar_t* e) noexcept
601{
602 try {
603 size_t len = e - p;
604 StackBuffer<wchar_t> buf{Memory::eUninitialized, len + 1};
605 (void)::memcpy (buf.begin (), p, len * sizeof (wchar_t));
606 buf.begin ()[len] = '\0';
607 DoEmit_ (buf.begin ());
608 }
609 catch (...) {
611 }
612}
613
614/*
615 ********************************************************************************
616 ************************** Debug::GetDbgTraceThreadName ************************
617 ********************************************************************************
618 */
619wstring Debug::GetDbgTraceThreadName (thread::id threadID)
620{
621 return String::FromNarrowSDKString (GetDbgTraceThreadName_A (threadID)).As<wstring> ();
622}
623string Debug::GetDbgTraceThreadName_A (thread::id threadID)
624{
625 return mkThreadLabelForThreadID_ (threadID).second;
626}
627
628/*
629 ********************************************************************************
630 ****************************** TraceContextBumper ******************************
631 ********************************************************************************
632 */
633#if qStroika_Foundation_Debug_DefaultTracingOn
634TraceContextBumper::TraceContextBumper (CHAR_ARRAY_T mainName, CHAR_ARRAY_T extraTextAtTop) noexcept
635 : fDoEndMarker{true}
636{
637 Require (char_traits<wchar_t>::length (mainName.data ()) <= kMaxContextNameLen_); // assert NUL-terminated
638 if (extraTextAtTop.empty () or extraTextAtTop[0] == '\0') {
639 auto mainNameData = mainName.data ();
640 fLastWriteToken_ = Private_::Emitter::Get ().EmitTraceMessage_ (3 + ::wcslen (kEOL<wchar_t>), L"<{}> {{"sv,
641 Common::StdCompat::make_wformat_args (mainNameData));
642 }
643 else {
644 auto mainNameData = mainName.data ();
645 auto extraTextAtTopData = extraTextAtTop.data ();
646 fLastWriteToken_ = Emitter::Get ().EmitTraceMessage_ (3 + ::wcslen (kEOL<wchar_t>), L"<{} ({})> {{"sv,
647 Common::StdCompat::make_wformat_args (mainNameData, extraTextAtTopData));
648 }
649 size_t len = char_traits<wchar_t>::length (mainName.data ());
650 char_traits<wchar_t>::copy (fSavedContextName_.data (), mainName.data (), len);
651 if (len >= kMaxContextNameLen_ - 1) {
652 char_traits<wchar_t>::copy (&fSavedContextName_.data ()[len - 3], L"...", 3);
653 }
654 fSavedContextName_[len] = '\0';
655 *(std::end (fSavedContextName_) - 1) = '\0';
656 IncCount_ ();
657}
658
659TraceContextBumper::TraceContextBumper (const wchar_t* contextName, const wchar_t* extraFmt, ...) noexcept
660 : fDoEndMarker{true}
661{
662 // ********************** DEPRECATED API *********************
663 try {
664 va_list argsList;
665 va_start (argsList, extraFmt);
666 wstring tmpFmtV = Characters::CString::FormatV (extraFmt, argsList);
667 fLastWriteToken_ = Emitter::Get ().EmitTraceMessage_ (3 + ::wcslen (kEOL<wchar_t>), L"<{} ({})> {{"sv,
668 Common::StdCompat::make_wformat_args (contextName, tmpFmtV));
669 va_end (argsList);
670 size_t len = min (kMaxContextNameLen_ - 1, char_traits<wchar_t>::length (contextName));
671 char_traits<wchar_t>::copy (fSavedContextName_.data (), contextName, len);
672 if (len >= kMaxContextNameLen_ - 1) {
673 char_traits<wchar_t>::copy (&fSavedContextName_.data ()[len - 3], L"...", 3);
674 }
675 *(std::end (fSavedContextName_) - 1) = '\0';
676 fSavedContextName_[len] = '\0';
677 IncCount_ ();
678 }
679 catch (...) {
680 }
681}
682
683unsigned int TraceContextBumper::GetCount ()
684{
685 return tTraceContextDepth_;
686}
687
688void TraceContextBumper::IncCount_ () noexcept
689{
690 ++tTraceContextDepth_;
691}
692
693void TraceContextBumper::DecrCount_ () noexcept
694{
695 --tTraceContextDepth_;
696}
697
698TraceContextBumper::~TraceContextBumper () noexcept
699{
700 DecrCount_ ();
701 if (fDoEndMarker) {
702 RequireNotNull (sModuleData_);
703 try {
704 [[maybe_unused]] lock_guard critSec{sModuleData_->fModuleMutex};
705 if (Emitter::Get ().UnputBufferedCharactersForMatchingToken (fLastWriteToken_)) {
706 Emitter::Get ().EmitUnadornedText ("/>");
707 Emitter::Get ().EmitUnadornedText (kEOL<char>);
708 }
709 else {
710 Emitter::Get ().EmitTraceMessage ("}} </{}>"_f, fSavedContextName_.data ());
711 }
712 }
713 catch (...) {
714 // not much of a chance of successfully reporting a problem here, but DTOR must be noexcept
715 }
716 }
717}
718#endif
#define AssertNotNull(p)
Definition Assertions.h:333
#define RequireNotNull(p)
Definition Assertions.h:347
#define WeakAssert(c)
A WeakAssert() is for things that aren't guaranteed to be true, but are overwhelmingly likely to be t...
Definition Assertions.h:438
#define AssertNotReached()
Definition Assertions.h:355
#define Verify(c)
Definition Assertions.h:419
#define CompileTimeFlagChecker_SOURCE(NS_PREFIX, NAME, VALUE)
#define qStroika_Foundation_Debug_ShowThreadIndex
if true, emit a much shorter thread ID, making - I suspect (testing) for terser and clearer tracelogs...
Definition Trace.h:83
#define qStroika_Foundation_Debug_TraceToFile
Definition Trace.h:45
Include this file VERY EARLY ON - before including stuff like <cstdio> - to allow use of Valgrind (so...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual string AsNarrowSDKString() const
Definition String.inl:830
static String FromNarrowSDKString(const char *from)
Definition String.inl:470
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
basic_string< SDKChar > SDKString
Definition SDKString.h:38
CONTAINER::value_type * End(CONTAINER &c)
For a contiguous container (such as a vector or basic_string) - find the pointer to the end of the co...
CONTAINER::value_type * Start(CONTAINER &c)
For a contiguous container (such as a vector or basic_string) - find the pointer to the start of the ...
filesystem::path GetEXEPath()
Definition Module.cpp:53