blob: 60923acb1219a9afc272ee23fb5117a7e02a97a0 [file] [log] [blame]
niklase@google.comf0779a22011-05-30 11:39:38 +00001/*
2 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "cpu_windows.h"
12
13#define _WIN32_DCOM
14
15#include <assert.h>
16#include <iostream>
17#include <Wbemidl.h>
18
19#pragma comment(lib, "wbemuuid.lib")
20
21#include "condition_variable_wrapper.h"
22#include "critical_section_wrapper.h"
23#include "event_wrapper.h"
24#include "thread_wrapper.h"
25
26namespace webrtc {
27WebRtc_Word32 CpuWindows::CpuUsage()
28{
29 if (!initialized_)
30 {
31 return -1;
32 }
33 // Last element is the average
34 return cpu_usage_[number_of_objects_ - 1];
35}
36
37WebRtc_Word32 CpuWindows::CpuUsageMultiCore(WebRtc_UWord32& num_cores,
38 WebRtc_UWord32*& cpu_usage)
39{
40 if (!initialized_)
41 {
42 return -1;
43 }
44 num_cores = number_of_objects_ - 1;
45 cpu_usage = cpu_usage_;
46 return cpu_usage_[number_of_objects_-1];
47}
48
49CpuWindows::CpuWindows()
50 : cpu_polling_thread(NULL),
51 initialize_(true),
52 initialized_(false),
53 terminate_(false),
54 cpu_usage_(NULL),
55 wbem_enum_access_(NULL),
56 number_of_objects_(0),
57 cpu_usage_handle_(0),
58 previous_processor_timestamp_(NULL),
59 timestamp_sys_100_ns_handle_(0),
60 previous_100ns_timestamp_(NULL),
61 wbem_service_(NULL),
62 wbem_refresher_(NULL),
63 wbem_enum_(NULL)
64{
65 // All resources are allocated in PollingCpu().
66 if (AllocateComplexDataTypes())
67 {
68 const bool success = StartPollingCpu();
69 assert(success);
70 }
71 else
72 {
73 assert(false);
74 }
75}
76
77CpuWindows::~CpuWindows()
78{
79 // All resources are reclaimed in StopPollingCpu().
80 const bool success = StopPollingCpu();
81 assert(success);
82 DeAllocateComplexDataTypes();
83}
84
85bool CpuWindows::AllocateComplexDataTypes()
86{
87 cpu_polling_thread = ThreadWrapper::CreateThread(
88 CpuWindows::Process,
89 reinterpret_cast<void*>(this),
90 kNormalPriority,
91 "CpuWindows");
92 init_crit_ = CriticalSectionWrapper::CreateCriticalSection();
93 init_cond_ = ConditionVariableWrapper::CreateConditionVariable();
94 terminate_crit_ = CriticalSectionWrapper::CreateCriticalSection();
95 terminate_cond_ = ConditionVariableWrapper::CreateConditionVariable();
96 sleep_event = EventWrapper::Create();
97 return (cpu_polling_thread != NULL) && (init_crit_ != NULL) &&
98 (init_cond_ != NULL) && (terminate_crit_ != NULL) &&
99 (terminate_cond_ != NULL) && (sleep_event != NULL);
100}
101
102void CpuWindows::DeAllocateComplexDataTypes()
103{
104 if (sleep_event != NULL)
105 {
106 delete sleep_event;
107 sleep_event = NULL;
108 }
109 if (terminate_cond_ != NULL)
110 {
111 delete terminate_cond_;
112 terminate_cond_ = NULL;
113 }
114 if (terminate_crit_ != NULL)
115 {
116 delete terminate_crit_;
117 terminate_crit_ = NULL;
118 }
119 if (init_cond_ != NULL)
120 {
121 delete init_cond_;
122 init_cond_ = NULL;
123 }
124 if (init_crit_ != NULL)
125 {
126 delete init_crit_;
127 init_crit_ = NULL;
128 }
129 if (cpu_polling_thread != NULL)
130 {
131 delete cpu_polling_thread;
132 cpu_polling_thread = NULL;
133 }
134}
135
136bool CpuWindows::StartPollingCpu()
137{
138 unsigned int dummy_id = 0;
139 if (!cpu_polling_thread->Start(dummy_id))
140 {
141 return false;
142 }
143 {
144 CriticalSectionScoped cs(*init_crit_);
145 while(initialize_)
146 {
147 init_cond_->SleepCS(*init_crit_);
148 }
149 }
150 if (!initialized_)
151 {
152 cpu_polling_thread->Stop();
153 return false;
154 }
155 return initialized_;
156}
157
158bool CpuWindows::StopPollingCpu()
159{
160 if (!initialized_)
161 {
162 return false;
163 }
164 CriticalSectionScoped cs(*terminate_crit_);
165 terminate_ = true;
166 sleep_event->Set();
167 while (!terminated_)
168 {
169 terminate_cond_->SleepCS(*terminate_crit_);
170 }
171 cpu_polling_thread->Stop();
172 delete cpu_polling_thread;
173 cpu_polling_thread = NULL;
174 return true;
175}
176
177bool CpuWindows::Process(void* thread_object)
178{
179 return reinterpret_cast<CpuWindows*>(thread_object)->ProcessImpl();
180}
181
182bool CpuWindows::ProcessImpl()
183{
184 {
185 CriticalSectionScoped cs(*terminate_crit_);
186 if (terminate_)
187 {
188 const bool success = Terminate();
189 assert(success);
190 terminated_ = true;
191 terminate_cond_->WakeAll();
192 return false;
193 }
194 }
195 // Initialize on first iteration
196 if (initialize_)
197 {
198 CriticalSectionScoped cs(*init_crit_);
199 initialize_ = false;
200 const bool success = Initialize();
201 init_cond_->WakeAll();
202 if (!success || !initialized_)
203 {
204 initialized_ = false;
205 terminate_ = true;
206 return false;
207 }
208 }
209 // Approximately one seconds sleep for each CPU measurement. Precision is
210 // not important. 1 second refresh rate is also used by Performance Monitor
211 // (perfmon).
212 if(kEventTimeout != sleep_event->Wait(1000))
213 {
214 // Terminating. No need to update CPU usage.
215 assert(terminate_);
216 return true;
217 }
218
219 // UpdateCpuUsage() returns false if a single (or more) CPU read(s) failed.
220 // Not a major problem if it happens but make sure it doesnt trigger in
221 // debug.
222 const bool success = UpdateCpuUsage();
223 assert(success);
224 return true;
225}
226
227bool CpuWindows::CreateWmiConnection()
228{
229 IWbemLocator* service_locator = NULL;
230 HRESULT hr = CoCreateInstance(CLSID_WbemLocator, NULL,
231 CLSCTX_INPROC_SERVER, IID_IWbemLocator,
232 reinterpret_cast<void**> (&service_locator));
233 if (FAILED(hr))
234 {
235 return false;
236 }
237 // To get the WMI service specify the WMI namespace.
238 BSTR wmi_namespace = SysAllocString(L"\\\\.\\root\\cimv2");
239 if (wmi_namespace == NULL)
240 {
241 // This type of failure signifies running out of memory.
242 service_locator->Release();
243 return false;
244 }
245 hr = service_locator->ConnectServer(wmi_namespace, NULL, NULL, NULL, 0L,
246 NULL, NULL, &wbem_service_);
247 SysFreeString(wmi_namespace);
248 service_locator->Release();
249 return !FAILED(hr);
250}
251
252// Sets up WMI refresher and enum
253bool CpuWindows::CreatePerfOsRefresher()
254{
255 // Create refresher.
256 HRESULT hr = CoCreateInstance(CLSID_WbemRefresher, NULL,
257 CLSCTX_INPROC_SERVER, IID_IWbemRefresher,
258 reinterpret_cast<void**> (&wbem_refresher_));
259 if (FAILED(hr))
260 {
261 return false;
262 }
263 // Create PerfOS_Processor enum.
264 IWbemConfigureRefresher* wbem_refresher_config = NULL;
265 hr = wbem_refresher_->QueryInterface(
266 IID_IWbemConfigureRefresher,
267 reinterpret_cast<void**> (&wbem_refresher_config));
268 if (FAILED(hr))
269 {
270 return false;
271 }
272 // Don't care about the particular id for the enum.
273 long enum_id = 0;
274 hr = wbem_refresher_config->AddEnum(wbem_service_,
275 L"Win32_PerfRawData_PerfOS_Processor",
276 0, NULL, &wbem_enum_, &enum_id);
277 wbem_refresher_config->Release();
278 wbem_refresher_config = NULL;
279 return !FAILED(hr);
280}
281
282// Have to pull the first round of data to be able set the handles.
283bool CpuWindows::CreatePerfOsCpuHandles()
284{
285 // Update the refresher so that there is data available in wbem_enum_.
286 wbem_refresher_->Refresh(0L);
287
288 // The number of enumerators is the number of processor + 1 (the total).
289 // This is unknown at this point.
290 DWORD number_returned = 0;
291 HRESULT hr = wbem_enum_->GetObjects(0L, number_of_objects_,
292 wbem_enum_access_, &number_returned);
293 // number_returned indicates the number of enumerators that are needed.
294 if (hr == WBEM_E_BUFFER_TOO_SMALL &&
295 number_returned > number_of_objects_)
296 {
297 // Allocate the number IWbemObjectAccess asked for by the
298 // GetObjects(..) function.
299 wbem_enum_access_ = new IWbemObjectAccess*[number_returned];
300 cpu_usage_ = new WebRtc_UWord32[number_returned];
301 previous_processor_timestamp_ = new unsigned __int64[number_returned];
302 previous_100ns_timestamp_ = new unsigned __int64[number_returned];
303 if ((wbem_enum_access_ == NULL) || (cpu_usage_ == NULL) ||
304 (previous_processor_timestamp_ == NULL) ||
305 (previous_100ns_timestamp_ == NULL))
306 {
307 // Out of memory.
308 return false;
309 }
310
311 SecureZeroMemory(wbem_enum_access_, number_returned *
312 sizeof(IWbemObjectAccess*));
313 memset(cpu_usage_, 0, sizeof(int) * number_returned);
314 memset(previous_processor_timestamp_, 0, sizeof(unsigned __int64) *
315 number_returned);
316 memset(previous_100ns_timestamp_, 0, sizeof(unsigned __int64) *
317 number_returned);
318
319 number_of_objects_ = number_returned;
320 // Read should be successfull now that memory has been allocated.
321 hr = wbem_enum_->GetObjects(0L, number_of_objects_, wbem_enum_access_,
322 &number_returned);
323 if (FAILED(hr))
324 {
325 return false;
326 }
327 }
328 else
329 {
330 // 0 enumerators should not be enough. Something has gone wrong here.
331 return false;
332 }
333
334 // Get the enumerator handles that are needed for calculating CPU usage.
335 CIMTYPE cpu_usage_type;
336 hr = wbem_enum_access_[0]->GetPropertyHandle(L"PercentProcessorTime",
337 &cpu_usage_type,
338 &cpu_usage_handle_);
339 if (FAILED(hr))
340 {
341 return false;
342 }
343 CIMTYPE timestamp_sys_100_ns_type;
344 hr = wbem_enum_access_[0]->GetPropertyHandle(L"TimeStamp_Sys100NS",
345 &timestamp_sys_100_ns_type,
346 &timestamp_sys_100_ns_handle_);
347 return !FAILED(hr);
348}
349
350bool CpuWindows::Initialize()
351{
352 if (terminate_)
353 {
354 return false;
355 }
356 // Initialize COM library.
357 HRESULT hr = CoInitializeEx(NULL,COINIT_MULTITHREADED);
358 if (FAILED(hr))
359 {
360 return false;
361 }
362 hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_NONE,
363 RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0);
364 if (FAILED(hr))
365 {
366 return false;
367 }
368
369 if (!CreateWmiConnection())
370 {
371 return false;
372 }
373 if (!CreatePerfOsRefresher())
374 {
375 return false;
376 }
377 if (!CreatePerfOsCpuHandles())
378 {
379 return false;
380 }
381 initialized_ = true;
382 return true;
383}
384
385bool CpuWindows::Terminate()
386{
387 // Reverse order of Initialize().
388 // Some compilers complain about deleting NULL though it's well defined
389 if (previous_100ns_timestamp_ != NULL)
390 {
391 delete[] previous_100ns_timestamp_;
392 previous_100ns_timestamp_ = NULL;
393 }
394 if (previous_processor_timestamp_ != NULL)
395 {
396 delete[] previous_processor_timestamp_;
397 previous_processor_timestamp_ = NULL;
398 }
399 if (cpu_usage_ != NULL)
400 {
401 delete[] cpu_usage_;
402 cpu_usage_ = NULL;
403 }
404 if (wbem_enum_access_ != NULL)
405 {
406 for (DWORD i = 0; i < number_of_objects_; i++)
407 {
408 if(wbem_enum_access_[i] != NULL)
409 {
410 wbem_enum_access_[i]->Release();
411 }
412 }
413 delete[] wbem_enum_access_;
414 wbem_enum_access_ = NULL;
415 }
416 if (wbem_enum_ != NULL)
417 {
418 wbem_enum_->Release();
419 wbem_enum_ = NULL;
420 }
421 if (wbem_refresher_ != NULL)
422 {
423 wbem_refresher_->Release();
424 wbem_refresher_ = NULL;
425 }
426 if (wbem_service_ != NULL)
427 {
428 wbem_service_->Release();
429 wbem_service_ = NULL;
430 }
431 // CoUninitialized should be called once for every CoInitializeEx.
432 // Regardless if it failed or not.
433 CoUninitialize();
434 return false;
435}
436
437bool CpuWindows::UpdateCpuUsage()
438{
439 wbem_refresher_->Refresh(0L);
440 DWORD number_returned = 0;
441 HRESULT hr = wbem_enum_->GetObjects(0L, number_of_objects_,
442 wbem_enum_access_,&number_returned);
443 if (FAILED(hr))
444 {
445 // wbem_enum_access_ has already been allocated. Unless the number of
446 // CPUs change runtime this should not happen.
447 return false;
448 }
449 unsigned __int64 cpu_usage = 0;
450 unsigned __int64 timestamp_100ns = 0;
451 bool returnValue = true;
452 for (DWORD i = 0; i < number_returned; i++)
453 {
454 hr = wbem_enum_access_[i]->ReadQWORD(cpu_usage_handle_,&cpu_usage);
455 if (FAILED(hr))
456 {
457 returnValue = false;
458 }
459 hr = wbem_enum_access_[i]->ReadQWORD(timestamp_sys_100_ns_handle_,
460 &timestamp_100ns);
461 if (FAILED(hr))
462 {
463 returnValue = false;
464 }
465 wbem_enum_access_[i]->Release();
466 wbem_enum_access_[i] = NULL;
467
468 const bool wrapparound =
469 (previous_processor_timestamp_[i] > cpu_usage) ||
470 (previous_100ns_timestamp_[i] > timestamp_100ns);
471 const bool first_time = (previous_processor_timestamp_[i] == 0) ||
472 (previous_100ns_timestamp_[i] == 0);
473 if (wrapparound || first_time)
474 {
475 previous_processor_timestamp_[i] = cpu_usage;
476 previous_100ns_timestamp_[i] = timestamp_100ns;
477 continue;
478 }
479 const unsigned __int64 processor_timestamp_delta =
480 cpu_usage - previous_processor_timestamp_[i];
481 const unsigned __int64 timestamp_100ns_delta =
482 timestamp_100ns - previous_100ns_timestamp_[i];
483
484 if (processor_timestamp_delta >= timestamp_100ns_delta)
485 {
486 cpu_usage_[i] = 0;
487 } else {
488 // Quotient must be float since the division is guaranteed to yield
489 // a value between 0 and 1 which is 0 in integer division.
490 const float delta_quotient =
491 static_cast<float>(processor_timestamp_delta) /
492 static_cast<float>(timestamp_100ns_delta);
493 cpu_usage_[i] = 100 - static_cast<WebRtc_UWord32>(delta_quotient *
494 100);
495 }
496 previous_processor_timestamp_[i] = cpu_usage;
497 previous_100ns_timestamp_[i] = timestamp_100ns;
498 }
499 return returnValue;
500}
501} // namespace webrtc