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