1+ #include " DiagnosticDriver.h"
2+ #include " ../Exception.h"
3+ #include " ../win/WinAPI.h"
4+ #include < iostream>
5+ #include < span>
6+ #include < algorithm>
7+
8+ using namespace std ::chrono_literals;
9+ namespace rn = std::ranges;
10+
11+ namespace pmon ::util::log
12+ {
13+ DiagnosticDriver::DiagnosticDriver (const PM_DIAGNOSTIC_CONFIGURATION* pConfig)
14+ {
15+ if (pConfig) {
16+ config_ = DiagnosticConfig{ *pConfig };
17+ }
18+ }
19+ DiagnosticDriver::~DiagnosticDriver ()
20+ {
21+ dying_ = true ;
22+ manualUnblockEvent_.Set ();
23+ }
24+ void DiagnosticDriver::Submit (const Entry& e)
25+ {
26+ // ignore non-diagnostic entries and low-priority levels
27+ if (!e.diagnosticLayer_ || int (e.level_ ) > int (config_.filterLevel )) {
28+ return ;
29+ }
30+ // filtering by subsystem
31+ if (!config_.subsystems .empty () && !rn::contains (config_.subsystems ,
32+ (PM_DIAGNOSTIC_SUBSYSTEM)e.subsystem_ )) {
33+ return ;
34+ }
35+ // prepare string payloads
36+ auto msg = str::ToNarrow (e.note_ );
37+ std::string timestamp;
38+ if (config_.enableTimestamp ) {
39+ const auto zt = std::chrono::zoned_time{ std::chrono::current_zone (), e.timestamp_ };
40+ timestamp = std::format (" {}" , zt);
41+ }
42+ std::string trace;
43+ if (config_.enableTrace ) {
44+ if (e.pTrace_ && e.pTrace_ ->Resolved ()) {
45+ trace = str::ToNarrow (e.pTrace_ ->ToString ());
46+ }
47+ }
48+ std::string location;
49+ if (config_.enableLocation ) {
50+ location = std::format (" {}({})" , str::ToNarrow (e.GetSourceFileName ()), e.sourceLine_ );
51+ }
52+ // create diagnostic message based on logging entry
53+ auto pMessage = std::make_unique<DiagnosticMessage>();
54+ // set values
55+ pMessage->level = (PM_DIAGNOSTIC_LEVEL)e.level_ ;
56+ pMessage->system = (PM_DIAGNOSTIC_SUBSYSTEM)e.subsystem_ ;
57+ pMessage->pid = e.pid_ ;
58+ pMessage->tid = e.tid_ ;
59+ // handle buffers
60+ pMessage->messageBuffer_ = std::move (msg);
61+ pMessage->locationBuffer_ = std::move (location);
62+ pMessage->timestampBuffer_ = std::move (timestamp);
63+ pMessage->traceBuffer_ = std::move (trace);
64+ pMessage->SyncBuffers_ ();
65+ // process message
66+ ProcessCommon_ (std::move (pMessage));
67+ }
68+ void DiagnosticDriver::Flush () {}
69+ uint32_t DiagnosticDriver::GetQueuedMessageCount ()
70+ {
71+ return (uint32_t )messageQueue_.size_approx ();
72+ }
73+ uint32_t DiagnosticDriver::GetMaxQueuedMessages ()
74+ {
75+ return maxQueuedMessages_;
76+ }
77+ void DiagnosticDriver::SetMaxQueuedMessages (uint32_t max)
78+ {
79+ maxQueuedMessages_ = max;
80+ }
81+ uint32_t DiagnosticDriver::GetDiscardedMessageCount ()
82+ {
83+ return discardedCount_;
84+ }
85+ std::unique_ptr<DiagnosticMessage> DiagnosticDriver::DequeueMessage ()
86+ {
87+ messageWaitEvent_.Reset ();
88+ std::unique_ptr<DiagnosticMessage> pMessage;
89+ messageQueue_.try_dequeue (pMessage);
90+ return pMessage;
91+ }
92+ void DiagnosticDriver::EnqueueMessage (const PM_DIAGNOSTIC_MESSAGE* pMessage)
93+ {
94+ if (!pMessage) {
95+ throw Except<Exception>(" Diagnostic message injection with null message pointer" );
96+ }
97+ ProcessCommon_ (std::make_unique<DiagnosticMessage>(*pMessage));
98+ }
99+ PM_DIAGNOSTIC_WAKE_REASON DiagnosticDriver::WaitForMessage (uint32_t timeoutMs)
100+ {
101+ if (GetQueuedMessageCount () > 0 ) {
102+ return PM_DIAGNOSTIC_WAKE_REASON_MESSAGE_AVAILABLE;
103+ }
104+ const auto wakeSignal = timeoutMs ?
105+ win::WaitAnyEventFor (timeoutMs * 1ms, messageWaitEvent_, manualUnblockEvent_) :
106+ win::WaitAnyEvent (messageWaitEvent_, manualUnblockEvent_);
107+ if (!wakeSignal) {
108+ return PM_DIAGNOSTIC_WAKE_REASON_TIMEOUT;
109+ }
110+ if (*wakeSignal == 0 ) {
111+ return PM_DIAGNOSTIC_WAKE_REASON_MESSAGE_AVAILABLE;
112+ }
113+ if (*wakeSignal == 1 ) {
114+ return dying_ ?
115+ PM_DIAGNOSTIC_WAKE_REASON_SHUTDOWN :
116+ PM_DIAGNOSTIC_WAKE_REASON_MANUAL_UNBLOCK;
117+ }
118+ throw Except<Exception>(" Bad event index waiting on message+unblock events" );
119+ }
120+ void DiagnosticDriver::UnblockWaitingThread ()
121+ {
122+ manualUnblockEvent_.Set ();
123+ }
124+ void DiagnosticDriver::Enqueue_ (std::unique_ptr<DiagnosticMessage> pMsg)
125+ {
126+ // ensure that # of queued does not exceed max
127+ while (GetQueuedMessageCount () >= GetMaxQueuedMessages ()) {
128+ std::unique_ptr<DiagnosticMessage> p;
129+ if (messageQueue_.try_dequeue (p)) {
130+ discardedCount_++;
131+ }
132+ }
133+ // enqueue the message
134+ messageQueue_.enqueue (std::move (pMsg));
135+ // signal message availability
136+ messageWaitEvent_.Set ();
137+ }
138+ void DiagnosticDriver::ProcessCommon_ (std::unique_ptr<DiagnosticMessage> pMsg)
139+ {
140+ using namespace std ::string_literals;
141+ // handle direct output
142+ std::string formattedMsg;
143+ auto format = [&] { if (formattedMsg.empty ()) {
144+ auto level = str::ToNarrow (GetLevelName ((Level)pMsg->level ));
145+ auto sys = str::ToNarrow (GetSubsystemName ((Subsystem)pMsg->system ));
146+ if (pMsg->pTimestamp ) {
147+ formattedMsg = std::format (" [PMON:{} {}] {{{}}} {}\n " , sys, level, pMsg->pTimestamp , pMsg->pText );
148+ }
149+ else {
150+ formattedMsg = std::format (" [PMON:{} {}] {}\n " , sys, level, pMsg->pText );
151+ }
152+ } };
153+ // output formatted string directly to active media
154+ if (config_.outputFlags & PM_DIAGNOSTIC_OUTPUT_FLAGS_DEBUGGER) {
155+ format ();
156+ OutputDebugStringA (formattedMsg.c_str ());
157+ }
158+ if (config_.outputFlags & PM_DIAGNOSTIC_OUTPUT_FLAGS_STDERR) {
159+ format ();
160+ std::cerr << formattedMsg;
161+ }
162+ if (config_.outputFlags & PM_DIAGNOSTIC_OUTPUT_FLAGS_STDOUT) {
163+ format ();
164+ std::cout << formattedMsg;
165+ }
166+ // handle queue
167+ if (config_.outputFlags & PM_DIAGNOSTIC_OUTPUT_FLAGS_QUEUE) {
168+ Enqueue_ (std::move (pMsg));
169+ }
170+ }
171+ DiagnosticConfig::DiagnosticConfig (const PM_DIAGNOSTIC_CONFIGURATION& src)
172+ :
173+ PM_DIAGNOSTIC_CONFIGURATION{ src }
174+ {
175+ if (src.pSubsystems ) {
176+ subsystems.append_range (std::span (src.pSubsystems , src.nSubsystems ));
177+ nSubsystems = (uint32_t )subsystems.size ();
178+ }
179+ }
180+ DiagnosticConfig::DiagnosticConfig ()
181+ :
182+ PM_DIAGNOSTIC_CONFIGURATION{
183+ .filterLevel { PM_DIAGNOSTIC_LEVEL_WARNING },
184+ .outputFlags { PM_DIAGNOSTIC_OUTPUT_FLAGS_DEBUGGER },
185+ .pSubsystems { nullptr },
186+ .nSubsystems { 0 },
187+ .enableTimestamp { false },
188+ .enableTrace { PM_DIAGNOSTIC_LEVEL_NONE },
189+ .enableLocation { false }
190+ }
191+ {}
192+ DiagnosticMessage::DiagnosticMessage (const PM_DIAGNOSTIC_MESSAGE& msg)
193+ :
194+ PM_DIAGNOSTIC_MESSAGE{ msg }
195+ {
196+ if (!msg.pText ) {
197+ throw Except<Exception>(" Diagnostic message text pointer not set" );
198+ }
199+ messageBuffer_ = msg.pText ;
200+ if (msg.pTimestamp ) {
201+ timestampBuffer_ = msg.pTimestamp ;
202+ }
203+ if (msg.pTrace ) {
204+ traceBuffer_ = msg.pTrace ;
205+ }
206+ if (msg.pLocation ) {
207+ locationBuffer_ = msg.pLocation ;
208+ }
209+ SyncBuffers_ ();
210+ }
211+ DiagnosticMessage::DiagnosticMessage () : PM_DIAGNOSTIC_MESSAGE{}
212+ {}
213+ void DiagnosticMessage::SyncBuffers_ ()
214+ {
215+ if (!messageBuffer_.empty ()) {
216+ pText = messageBuffer_.c_str ();
217+ }
218+ if (!timestampBuffer_.empty ()) {
219+ pTimestamp = timestampBuffer_.c_str ();
220+ }
221+ if (!traceBuffer_.empty ()) {
222+ pTrace = traceBuffer_.c_str ();
223+ }
224+ if (!locationBuffer_.empty ()) {
225+ pLocation = locationBuffer_.c_str ();
226+ }
227+ }
228+ }
0 commit comments