Stroika Library 3.0d23x
 
Loading...
Searching...
No Matches
WMICollector.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#if qStroika_Foundation_Common_Platform_Windows
7#include <Pdh.h>
8#include <PdhMsg.h>
9#include <Windows.h>
10#else
11#error "WINDOWS REQUIRED FOR THIS MODULE"
12#endif
13
15#include "Stroika/Foundation/Characters/FloatConversion.h"
17#include "Stroika/Foundation/Containers/Sequence.h"
23
24#include "WMICollector.h"
25
26using namespace Stroika::Foundation;
30using namespace Stroika::Foundation::Execution;
31using namespace Stroika::Foundation::Memory;
32
33using namespace Stroika::Frameworks;
34using namespace Stroika::Frameworks::SystemPerformance;
35using namespace Stroika::Frameworks::SystemPerformance::Support;
36
38
39#if defined(_MSC_VER)
40// Use #pragma comment lib instead of explicit entry in the lib entry of the project file
41#pragma comment(lib, "Pdh.lib")
42#endif
43
44// Comment this in to turn on aggressive noisy DbgTrace in this module
45//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
46
47/*
48 ********************************************************************************
49 ********** SystemPerformance::Support::WMICollector::PerInstanceData_ **********
50 ********************************************************************************
51 */
52WMICollector::PerInstanceData_::PerInstanceData_ (const String& objectName, const String& instance, const Iterable<String>& counterNames)
53 : fObjectName_{objectName}
54 , fInstance_{instance}
55{
56#if USE_NOISY_TRACE_IN_THIS_MODULE_
57 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::PerInstanceData_::PerInstanceData_"};
58#endif
59 PDH_STATUS x = ::PdhOpenQuery (NULL, NULL, &fQuery_);
60 if (x != 0) {
61 Throw (Exception{"PdhOpenQuery: {}"_f(x)});
62 }
63 counterNames.Apply ([this] (String i) { AddCounter (i); });
64}
65
66WMICollector::PerInstanceData_::~PerInstanceData_ ()
67{
68#if USE_NOISY_TRACE_IN_THIS_MODULE_
69 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::PerInstanceData_::~PerInstanceData_"};
70#endif
71 AssertNotNull (fQuery_);
72 //@todo add cehck result (this SB assert??)Verify
73 ::PdhCloseQuery (fQuery_);
74}
75
76void WMICollector::PerInstanceData_::AddCounter (const String& counterName)
77{
78 Require (not fCounters_.ContainsKey (counterName));
79 PDH_HCOUNTER newCounter = nullptr;
80 PDH_STATUS x = ::PdhAddCounter (fQuery_, "\\{}({})\\{}"_f(fObjectName_, fInstance_, counterName).As<wstring> ().c_str (), NULL, &newCounter);
81 if (x != 0) {
82 [[maybe_unused]] bool isPDH_CSTATUS_NO_OBJECT = (x == PDH_CSTATUS_NO_OBJECT);
83 [[maybe_unused]] bool isPDH_CSTATUS_NO_COUNTER = (x == PDH_CSTATUS_NO_COUNTER);
84 Throw (Exception{"PdhAddCounter: {}"_f(x)});
85 }
86 fCounters_.Add (counterName, newCounter);
87}
88
89double WMICollector::PerInstanceData_::GetCurrentValue (const String& counterName)
90{
91 PDH_FMT_COUNTERVALUE counterVal;
92 PDH_HCOUNTER counter = *fCounters_.Lookup (counterName);
93 PDH_STATUS x = ::PdhGetFormattedCounterValue (counter, PDH_FMT_DOUBLE, nullptr, &counterVal);
94 if (x != 0) {
95 [[maybe_unused]] bool isPDH_PDH_INVALID_DATA = (x == PDH_INVALID_DATA);
96 Throw (Exception{"PdhGetFormattedCounterValue: {}"_f(x)});
97 }
98 return counterVal.doubleValue;
99}
100
101optional<double> WMICollector::PerInstanceData_::PeekCurrentValue (const String& counterName)
102{
103 PDH_FMT_COUNTERVALUE counterVal{};
104 PDH_HCOUNTER counter = *fCounters_.Lookup (counterName);
105 PDH_STATUS x = ::PdhGetFormattedCounterValue (counter, PDH_FMT_DOUBLE, nullptr, &counterVal);
106 if (x != 0) {
107 return nullopt;
108 }
109 return counterVal.doubleValue;
110}
111
112Mapping<String, double> WMICollector::PerInstanceData_::GetCurrentValues (const String& counterName)
113{
114 PDH_HCOUNTER counter{*fCounters_.Lookup (counterName)};
115 DWORD dwBufferSize{}; // Size of the pItems buffer
116 DWORD dwItemCount{}; // Number of items in the pItems buffer
118 // Get the required size of the pItems buffer.
119 PDH_STATUS status = ::PdhGetFormattedCounterArray (counter, PDH_FMT_DOUBLE, &dwBufferSize, &dwItemCount, nullptr);
120 if (PDH_MORE_DATA == status) {
121 items.GrowToSize ((dwBufferSize + sizeof (PDH_FMT_COUNTERVALUE_ITEM) - 1) / sizeof (PDH_FMT_COUNTERVALUE_ITEM));
122 }
123 status = ::PdhGetFormattedCounterArray (counter, PDH_FMT_DOUBLE, &dwBufferSize, &dwItemCount, items.begin ());
124
125 if (status == PDH_CSTATUS_INVALID_DATA) {
126 /*
127 * From: https://msdn.microsoft.com/en-us/library/windows/desktop/aa371894%28v=vs.85%29.aspx
128 * PDH_CSTATUS_INVALID_DATA The counter was successfully found, but the data returned is not valid.
129 * This error can occur if the counter value is less than the previous value. (Because counter values always
130 * increment, the counter value rolls over to zero when it reaches its maximum value.)
131 * Another possible cause is a system timer that is not correct.
132 */
134 }
135 if (status != 0) {
136 //PDH_CSTATUS_INVALID_DATA
137 [[maybe_unused]] bool isPDH_PDH_INVALID_DATA = (status == PDH_INVALID_DATA);
138 Throw (Exception{"PdhGetFormattedCounterArray: {}"_f(status)});
139 }
140
142 for (DWORD i = 0; i < dwItemCount; ++i) {
143 result.Add (items[i].szName, items[i].FmtValue.doubleValue);
144 }
145 return result;
146}
147
148/*
149 ********************************************************************************
150 ********************* SystemPerformance::Support::WMICollector *****************
151 ********************************************************************************
152 */
153String WMICollector::kWildcardInstance = "*"sv;
154
155WMICollector::WMICollector (const String& objectName, const Iterable<String>& instances, const Iterable<String>& counterName)
156 : fObjectName_{objectName}
157{
158#if USE_NOISY_TRACE_IN_THIS_MODULE_
159 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::WMICollector"};
160#endif
161 instances.Apply ([this] (String i) { AddInstance_ (i); });
162 counterName.Apply ([this] (String i) { AddCounter_ (i); });
163}
164
165WMICollector::WMICollector (const WMICollector& from)
166 : WMICollector{from.fObjectName_, from.fInstanceData_.Keys (), from.fCounterNames_}
167{
168#if USE_NOISY_TRACE_IN_THIS_MODULE_
169 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::WMICollector"};
170#endif
171 /// @todo auto&& critSec = lock_guard{fCritSection_}; before copy elts!!!
172
173 // Note the above copy CTOR does a second collect, because we don't know how to clone collected data?
174}
175
176WMICollector& WMICollector::operator= (const WMICollector& rhs)
177{
178#if USE_NOISY_TRACE_IN_THIS_MODULE_
179 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::operator="};
180#endif
181 if (this != &rhs) {
182 AssertExternallySynchronizedMutex::ReadContext critSec1{rhs};
183 AssertExternallySynchronizedMutex::WriteContext critSec2{*this};
184 fInstanceData_.clear ();
185 fObjectName_ = rhs.fObjectName_;
186 rhs.fInstanceData_.Keys ().Apply ([this] (String i) { AddInstance_ (i); });
187 rhs.fCounterNames_.Apply ([this] (String i) { AddCounter_ (i); });
188 }
189 return *this;
190}
191
192void WMICollector::Collect ()
193{
194#if USE_NOISY_TRACE_IN_THIS_MODULE_
195 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::Collect"};
196#endif
197 AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
198 fInstanceData_.Apply ([this] (KeyValuePair<String, std::shared_ptr<PerInstanceData_>> i) {
199 PDH_STATUS x = ::PdhCollectQueryData (i.fValue->fQuery_);
200 if (x != 0) {
201 bool isPDH_PDH_NO_DATA = (x == PDH_NO_DATA);
202 if (not isPDH_PDH_NO_DATA) {
203 // happens when we try to read data about compact disk??? anyhow - best to just not throw here I think?
204 // --LGP 2015-05-06 - at least not til I understand better
205 Throw (Exception{"PdhCollectQueryData: {}"_f(x)});
206 }
207 }
208 });
209 fTimeOfLastCollection_ = Time::GetTickCount ();
210}
211
213{
214 /*
215 * Note: we only want the instance-ids here, but this appears to fail if you only request instance ids and not counters at the same time.
216 * Perhaps try again more carefully once I understand PDH better.
217 */
218 DWORD dwCounterListSize = 0;
219 DWORD dwInstanceListSize = 0;
220
221 PDH_STATUS pdhStatus = ::PdhEnumObjectItems (nullptr, nullptr, fObjectName_.As<wstring> ().c_str (), nullptr, &dwCounterListSize,
222 nullptr, &dwInstanceListSize, PERF_DETAIL_WIZARD, 0);
223 Assert (pdhStatus == PDH_MORE_DATA);
224
225 StackBuffer<Characters::SDKChar> counterBuf{dwCounterListSize + 2};
226 StackBuffer<Characters::SDKChar> instanceBuf{dwInstanceListSize + 2};
227
228 pdhStatus = ::PdhEnumObjectItems (nullptr, nullptr, fObjectName_.As<wstring> ().c_str (), counterBuf.begin (), &dwCounterListSize,
229 instanceBuf.begin (), &dwInstanceListSize, PERF_DETAIL_WIZARD, 0);
230 if (pdhStatus != 0) {
231 Throw (Exception{"PdhEnumObjectItems: {}"_f(pdhStatus)});
232 }
233
234 Set<String> result;
235 for (const TCHAR* p = instanceBuf.begin (); *p != '\0'; p += Characters::CString::Length (p) + 1) {
236 result.Add (String::FromSDKString (p));
237 }
238 return result;
239}
240
242{
243 /*
244 * Note: we only want the instance-ids here, but this appears to fail if you only request instance ids and not counters at the same time.
245 * Perhaps try again more carefully once I understand PDH better.
246 */
247 DWORD dwCounterListSize = 0;
248 DWORD dwInstanceListSize = 0;
249
250 PDH_STATUS pdhStatus = ::PdhEnumObjectItems (NULL, NULL, fObjectName_.As<wstring> ().c_str (), nullptr, &dwCounterListSize, nullptr,
251 &dwInstanceListSize, PERF_DETAIL_WIZARD, 0);
252 Assert (pdhStatus == PDH_MORE_DATA);
253
254 StackBuffer<Characters::SDKChar> counterBuf{dwCounterListSize + 2};
255 StackBuffer<Characters::SDKChar> instanceBuf{dwInstanceListSize + 2};
256
257 pdhStatus = ::PdhEnumObjectItems (NULL, NULL, fObjectName_.As<wstring> ().c_str (), counterBuf.begin (), &dwCounterListSize,
258 instanceBuf.begin (), &dwInstanceListSize, PERF_DETAIL_WIZARD, 0);
259 if (pdhStatus != 0) {
260 Throw (Exception{"PdhEnumObjectItems: {}"_f(pdhStatus)});
261 }
262
263 Set<String> result;
264 for (const TCHAR* p = counterBuf.begin (); *p != '\0'; p += Characters::CString::Length (p) + 1) {
265 result.Add (String::FromSDKString (p));
266 }
267 return result;
268}
269
270void WMICollector::AddCounters (const String& counterName)
271{
272#if USE_NOISY_TRACE_IN_THIS_MODULE_
273 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::AddCounters"};
274#endif
275 AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
276 AddCounter_ (counterName);
277}
278
279void WMICollector::AddCounters (const Iterable<String>& counterNames)
280{
281#if USE_NOISY_TRACE_IN_THIS_MODULE_
282 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::AddCounters"};
283#endif
284 AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
285 counterNames.Apply ([this] (String i) { AddCounter_ (i); });
286}
287
288void WMICollector::AddInstances (const String& instance)
289{
290#if USE_NOISY_TRACE_IN_THIS_MODULE_
291 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::AddInstances"};
292#endif
293 AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
294 AddInstance_ (instance);
295}
296
297void WMICollector::AddInstances (const Iterable<String>& instances)
298{
299#if USE_NOISY_TRACE_IN_THIS_MODULE_
300 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::AddInstances"};
301#endif
302 AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
303 instances.Apply ([this] (String i) { AddInstance_ (i); });
304}
305
307{
308#if USE_NOISY_TRACE_IN_THIS_MODULE_
309 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::AddInstancesIf"};
310#endif
311 AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
312 if (not fInstanceData_.ContainsKey (instance)) {
313 AddInstance_ (instance);
314 return true;
315 }
316 return false;
317}
318
320{
321#if USE_NOISY_TRACE_IN_THIS_MODULE_
322 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::AddInstancesIf"};
323#endif
324 AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
325 bool anyAdded = false;
326 instances.Apply ([this, &anyAdded] (String i) {
327 if (not fInstanceData_.ContainsKey (i)) {
328 AddInstance_ (i);
329 anyAdded = true;
330 }
331 });
332 return anyAdded;
333}
334
335double WMICollector::GetCurrentValue (const String& instance, const String& counterName)
336{
337#if USE_NOISY_TRACE_IN_THIS_MODULE_
338 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::GetCurrentValue"};
339#endif
340 AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
341 Require (fInstanceData_.ContainsKey (instance));
342 return fInstanceData_.Lookup (instance)->get ()->GetCurrentValue (counterName);
343}
344
345optional<double> WMICollector::PeekCurrentValue (const String& instance, const String& counterName)
346{
347#if USE_NOISY_TRACE_IN_THIS_MODULE_
348 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::PeekCurrentValue"};
349#endif
350 AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
351 Require (fInstanceData_.ContainsKey (instance));
352 return fInstanceData_.Lookup (instance)->get ()->PeekCurrentValue (counterName);
353}
354
356{
357#if USE_NOISY_TRACE_IN_THIS_MODULE_
358 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::GetCurrentValues"};
359#endif
360 AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
361 Require (fInstanceData_.ContainsKey (WMICollector::kWildcardInstance));
362 return fInstanceData_.Lookup (WMICollector::kWildcardInstance)->get ()->GetCurrentValues (counterName);
363}
364
365void WMICollector::AddCounter_ (const String& counterName)
366{
367#if USE_NOISY_TRACE_IN_THIS_MODULE_
368 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::AddCounter_"};
369#endif
370 //RENEABLKE WHEN WE HAVE RECURSIVE DEBUG LOCK - AssertExternallySynchronizedMutex::WriteContext declareContext { *this };
371 Require (not fCounterNames_.Contains (counterName));
372 fInstanceData_.Apply (
373 [this, counterName] (KeyValuePair<String, std::shared_ptr<PerInstanceData_>> i) { i.fValue->AddCounter (counterName); });
374 fCounterNames_.Add (counterName);
375}
376
377void WMICollector::AddInstance_ (const String& instance)
378{
379#if USE_NOISY_TRACE_IN_THIS_MODULE_
380 Debug::TraceContextBumper ctx{"Stroika::Frameworks::SystemPerformance::Support::WMICollector::AddInstance_"};
381#endif
382 //RENEABLKE WHEN WE HAVE RECURSIVE DEBUG LOCK - AssertExternallySynchronizedMutex::WriteContext declareContext { *this };
383 Require (not fInstanceData_.ContainsKey (instance));
384 fInstanceData_.Add (instance, MakeSharedPtr<PerInstanceData_> (fObjectName_, instance, fCounterNames_));
385}
#define AssertNotNull(p)
Definition Assertions.h:333
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
static String FromSDKString(const SDKChar *from)
Definition String.inl:447
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
Definition Mapping.inl:188
nonvirtual optional< mapped_type > Lookup(ArgByValueType< key_type > key) const
Definition Mapping.inl:142
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
nonvirtual void Add(ArgByValueType< value_type > item)
Definition Set.inl:138
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::excep...
Definition Exceptions.h:157
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
nonvirtual void Apply(const function< void(ArgByValueType< T > item)> &doToElement, Execution::SequencePolicy seq=Execution::SequencePolicy::eDEFAULT) const
Run the argument function (or lambda) on each element of the container.
nonvirtual void AddCounters(const String &counterName)
nonvirtual optional< double > PeekCurrentValue(const String &instance, const String &counterName)
nonvirtual Mapping< String, double > GetCurrentValues(const String &counterName)
nonvirtual bool AddInstancesIf(const String &instance)
nonvirtual double GetCurrentValue(const String &instance, const String &counterName)
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43