git-svn-id: http://webrtc.googlecode.com/svn/trunk@6 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/system_wrappers/source/cpu_windows.cc b/system_wrappers/source/cpu_windows.cc
new file mode 100644
index 0000000..60923ac
--- /dev/null
+++ b/system_wrappers/source/cpu_windows.cc
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "cpu_windows.h"
+
+#define _WIN32_DCOM
+
+#include <assert.h>
+#include <iostream>
+#include <Wbemidl.h>
+
+#pragma comment(lib, "wbemuuid.lib")
+
+#include "condition_variable_wrapper.h"
+#include "critical_section_wrapper.h"
+#include "event_wrapper.h"
+#include "thread_wrapper.h"
+
+namespace webrtc {
+WebRtc_Word32 CpuWindows::CpuUsage()
+{
+ if (!initialized_)
+ {
+ return -1;
+ }
+ // Last element is the average
+ return cpu_usage_[number_of_objects_ - 1];
+}
+
+WebRtc_Word32 CpuWindows::CpuUsageMultiCore(WebRtc_UWord32& num_cores,
+ WebRtc_UWord32*& cpu_usage)
+{
+ if (!initialized_)
+ {
+ return -1;
+ }
+ num_cores = number_of_objects_ - 1;
+ cpu_usage = cpu_usage_;
+ return cpu_usage_[number_of_objects_-1];
+}
+
+CpuWindows::CpuWindows()
+ : cpu_polling_thread(NULL),
+ initialize_(true),
+ initialized_(false),
+ terminate_(false),
+ cpu_usage_(NULL),
+ wbem_enum_access_(NULL),
+ number_of_objects_(0),
+ cpu_usage_handle_(0),
+ previous_processor_timestamp_(NULL),
+ timestamp_sys_100_ns_handle_(0),
+ previous_100ns_timestamp_(NULL),
+ wbem_service_(NULL),
+ wbem_refresher_(NULL),
+ wbem_enum_(NULL)
+{
+ // All resources are allocated in PollingCpu().
+ if (AllocateComplexDataTypes())
+ {
+ const bool success = StartPollingCpu();
+ assert(success);
+ }
+ else
+ {
+ assert(false);
+ }
+}
+
+CpuWindows::~CpuWindows()
+{
+ // All resources are reclaimed in StopPollingCpu().
+ const bool success = StopPollingCpu();
+ assert(success);
+ DeAllocateComplexDataTypes();
+}
+
+bool CpuWindows::AllocateComplexDataTypes()
+{
+ cpu_polling_thread = ThreadWrapper::CreateThread(
+ CpuWindows::Process,
+ reinterpret_cast<void*>(this),
+ kNormalPriority,
+ "CpuWindows");
+ init_crit_ = CriticalSectionWrapper::CreateCriticalSection();
+ init_cond_ = ConditionVariableWrapper::CreateConditionVariable();
+ terminate_crit_ = CriticalSectionWrapper::CreateCriticalSection();
+ terminate_cond_ = ConditionVariableWrapper::CreateConditionVariable();
+ sleep_event = EventWrapper::Create();
+ return (cpu_polling_thread != NULL) && (init_crit_ != NULL) &&
+ (init_cond_ != NULL) && (terminate_crit_ != NULL) &&
+ (terminate_cond_ != NULL) && (sleep_event != NULL);
+}
+
+void CpuWindows::DeAllocateComplexDataTypes()
+{
+ if (sleep_event != NULL)
+ {
+ delete sleep_event;
+ sleep_event = NULL;
+ }
+ if (terminate_cond_ != NULL)
+ {
+ delete terminate_cond_;
+ terminate_cond_ = NULL;
+ }
+ if (terminate_crit_ != NULL)
+ {
+ delete terminate_crit_;
+ terminate_crit_ = NULL;
+ }
+ if (init_cond_ != NULL)
+ {
+ delete init_cond_;
+ init_cond_ = NULL;
+ }
+ if (init_crit_ != NULL)
+ {
+ delete init_crit_;
+ init_crit_ = NULL;
+ }
+ if (cpu_polling_thread != NULL)
+ {
+ delete cpu_polling_thread;
+ cpu_polling_thread = NULL;
+ }
+}
+
+bool CpuWindows::StartPollingCpu()
+{
+ unsigned int dummy_id = 0;
+ if (!cpu_polling_thread->Start(dummy_id))
+ {
+ return false;
+ }
+ {
+ CriticalSectionScoped cs(*init_crit_);
+ while(initialize_)
+ {
+ init_cond_->SleepCS(*init_crit_);
+ }
+ }
+ if (!initialized_)
+ {
+ cpu_polling_thread->Stop();
+ return false;
+ }
+ return initialized_;
+}
+
+bool CpuWindows::StopPollingCpu()
+{
+ if (!initialized_)
+ {
+ return false;
+ }
+ CriticalSectionScoped cs(*terminate_crit_);
+ terminate_ = true;
+ sleep_event->Set();
+ while (!terminated_)
+ {
+ terminate_cond_->SleepCS(*terminate_crit_);
+ }
+ cpu_polling_thread->Stop();
+ delete cpu_polling_thread;
+ cpu_polling_thread = NULL;
+ return true;
+}
+
+bool CpuWindows::Process(void* thread_object)
+{
+ return reinterpret_cast<CpuWindows*>(thread_object)->ProcessImpl();
+}
+
+bool CpuWindows::ProcessImpl()
+{
+ {
+ CriticalSectionScoped cs(*terminate_crit_);
+ if (terminate_)
+ {
+ const bool success = Terminate();
+ assert(success);
+ terminated_ = true;
+ terminate_cond_->WakeAll();
+ return false;
+ }
+ }
+ // Initialize on first iteration
+ if (initialize_)
+ {
+ CriticalSectionScoped cs(*init_crit_);
+ initialize_ = false;
+ const bool success = Initialize();
+ init_cond_->WakeAll();
+ if (!success || !initialized_)
+ {
+ initialized_ = false;
+ terminate_ = true;
+ return false;
+ }
+ }
+ // Approximately one seconds sleep for each CPU measurement. Precision is
+ // not important. 1 second refresh rate is also used by Performance Monitor
+ // (perfmon).
+ if(kEventTimeout != sleep_event->Wait(1000))
+ {
+ // Terminating. No need to update CPU usage.
+ assert(terminate_);
+ return true;
+ }
+
+ // UpdateCpuUsage() returns false if a single (or more) CPU read(s) failed.
+ // Not a major problem if it happens but make sure it doesnt trigger in
+ // debug.
+ const bool success = UpdateCpuUsage();
+ assert(success);
+ return true;
+}
+
+bool CpuWindows::CreateWmiConnection()
+{
+ IWbemLocator* service_locator = NULL;
+ HRESULT hr = CoCreateInstance(CLSID_WbemLocator, NULL,
+ CLSCTX_INPROC_SERVER, IID_IWbemLocator,
+ reinterpret_cast<void**> (&service_locator));
+ if (FAILED(hr))
+ {
+ return false;
+ }
+ // To get the WMI service specify the WMI namespace.
+ BSTR wmi_namespace = SysAllocString(L"\\\\.\\root\\cimv2");
+ if (wmi_namespace == NULL)
+ {
+ // This type of failure signifies running out of memory.
+ service_locator->Release();
+ return false;
+ }
+ hr = service_locator->ConnectServer(wmi_namespace, NULL, NULL, NULL, 0L,
+ NULL, NULL, &wbem_service_);
+ SysFreeString(wmi_namespace);
+ service_locator->Release();
+ return !FAILED(hr);
+}
+
+// Sets up WMI refresher and enum
+bool CpuWindows::CreatePerfOsRefresher()
+{
+ // Create refresher.
+ HRESULT hr = CoCreateInstance(CLSID_WbemRefresher, NULL,
+ CLSCTX_INPROC_SERVER, IID_IWbemRefresher,
+ reinterpret_cast<void**> (&wbem_refresher_));
+ if (FAILED(hr))
+ {
+ return false;
+ }
+ // Create PerfOS_Processor enum.
+ IWbemConfigureRefresher* wbem_refresher_config = NULL;
+ hr = wbem_refresher_->QueryInterface(
+ IID_IWbemConfigureRefresher,
+ reinterpret_cast<void**> (&wbem_refresher_config));
+ if (FAILED(hr))
+ {
+ return false;
+ }
+ // Don't care about the particular id for the enum.
+ long enum_id = 0;
+ hr = wbem_refresher_config->AddEnum(wbem_service_,
+ L"Win32_PerfRawData_PerfOS_Processor",
+ 0, NULL, &wbem_enum_, &enum_id);
+ wbem_refresher_config->Release();
+ wbem_refresher_config = NULL;
+ return !FAILED(hr);
+}
+
+// Have to pull the first round of data to be able set the handles.
+bool CpuWindows::CreatePerfOsCpuHandles()
+{
+ // Update the refresher so that there is data available in wbem_enum_.
+ wbem_refresher_->Refresh(0L);
+
+ // The number of enumerators is the number of processor + 1 (the total).
+ // This is unknown at this point.
+ DWORD number_returned = 0;
+ HRESULT hr = wbem_enum_->GetObjects(0L, number_of_objects_,
+ wbem_enum_access_, &number_returned);
+ // number_returned indicates the number of enumerators that are needed.
+ if (hr == WBEM_E_BUFFER_TOO_SMALL &&
+ number_returned > number_of_objects_)
+ {
+ // Allocate the number IWbemObjectAccess asked for by the
+ // GetObjects(..) function.
+ wbem_enum_access_ = new IWbemObjectAccess*[number_returned];
+ cpu_usage_ = new WebRtc_UWord32[number_returned];
+ previous_processor_timestamp_ = new unsigned __int64[number_returned];
+ previous_100ns_timestamp_ = new unsigned __int64[number_returned];
+ if ((wbem_enum_access_ == NULL) || (cpu_usage_ == NULL) ||
+ (previous_processor_timestamp_ == NULL) ||
+ (previous_100ns_timestamp_ == NULL))
+ {
+ // Out of memory.
+ return false;
+ }
+
+ SecureZeroMemory(wbem_enum_access_, number_returned *
+ sizeof(IWbemObjectAccess*));
+ memset(cpu_usage_, 0, sizeof(int) * number_returned);
+ memset(previous_processor_timestamp_, 0, sizeof(unsigned __int64) *
+ number_returned);
+ memset(previous_100ns_timestamp_, 0, sizeof(unsigned __int64) *
+ number_returned);
+
+ number_of_objects_ = number_returned;
+ // Read should be successfull now that memory has been allocated.
+ hr = wbem_enum_->GetObjects(0L, number_of_objects_, wbem_enum_access_,
+ &number_returned);
+ if (FAILED(hr))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // 0 enumerators should not be enough. Something has gone wrong here.
+ return false;
+ }
+
+ // Get the enumerator handles that are needed for calculating CPU usage.
+ CIMTYPE cpu_usage_type;
+ hr = wbem_enum_access_[0]->GetPropertyHandle(L"PercentProcessorTime",
+ &cpu_usage_type,
+ &cpu_usage_handle_);
+ if (FAILED(hr))
+ {
+ return false;
+ }
+ CIMTYPE timestamp_sys_100_ns_type;
+ hr = wbem_enum_access_[0]->GetPropertyHandle(L"TimeStamp_Sys100NS",
+ ×tamp_sys_100_ns_type,
+ ×tamp_sys_100_ns_handle_);
+ return !FAILED(hr);
+}
+
+bool CpuWindows::Initialize()
+{
+ if (terminate_)
+ {
+ return false;
+ }
+ // Initialize COM library.
+ HRESULT hr = CoInitializeEx(NULL,COINIT_MULTITHREADED);
+ if (FAILED(hr))
+ {
+ return false;
+ }
+ hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_NONE,
+ RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0);
+ if (FAILED(hr))
+ {
+ return false;
+ }
+
+ if (!CreateWmiConnection())
+ {
+ return false;
+ }
+ if (!CreatePerfOsRefresher())
+ {
+ return false;
+ }
+ if (!CreatePerfOsCpuHandles())
+ {
+ return false;
+ }
+ initialized_ = true;
+ return true;
+}
+
+bool CpuWindows::Terminate()
+{
+ // Reverse order of Initialize().
+ // Some compilers complain about deleting NULL though it's well defined
+ if (previous_100ns_timestamp_ != NULL)
+ {
+ delete[] previous_100ns_timestamp_;
+ previous_100ns_timestamp_ = NULL;
+ }
+ if (previous_processor_timestamp_ != NULL)
+ {
+ delete[] previous_processor_timestamp_;
+ previous_processor_timestamp_ = NULL;
+ }
+ if (cpu_usage_ != NULL)
+ {
+ delete[] cpu_usage_;
+ cpu_usage_ = NULL;
+ }
+ if (wbem_enum_access_ != NULL)
+ {
+ for (DWORD i = 0; i < number_of_objects_; i++)
+ {
+ if(wbem_enum_access_[i] != NULL)
+ {
+ wbem_enum_access_[i]->Release();
+ }
+ }
+ delete[] wbem_enum_access_;
+ wbem_enum_access_ = NULL;
+ }
+ if (wbem_enum_ != NULL)
+ {
+ wbem_enum_->Release();
+ wbem_enum_ = NULL;
+ }
+ if (wbem_refresher_ != NULL)
+ {
+ wbem_refresher_->Release();
+ wbem_refresher_ = NULL;
+ }
+ if (wbem_service_ != NULL)
+ {
+ wbem_service_->Release();
+ wbem_service_ = NULL;
+ }
+ // CoUninitialized should be called once for every CoInitializeEx.
+ // Regardless if it failed or not.
+ CoUninitialize();
+ return false;
+}
+
+bool CpuWindows::UpdateCpuUsage()
+{
+ wbem_refresher_->Refresh(0L);
+ DWORD number_returned = 0;
+ HRESULT hr = wbem_enum_->GetObjects(0L, number_of_objects_,
+ wbem_enum_access_,&number_returned);
+ if (FAILED(hr))
+ {
+ // wbem_enum_access_ has already been allocated. Unless the number of
+ // CPUs change runtime this should not happen.
+ return false;
+ }
+ unsigned __int64 cpu_usage = 0;
+ unsigned __int64 timestamp_100ns = 0;
+ bool returnValue = true;
+ for (DWORD i = 0; i < number_returned; i++)
+ {
+ hr = wbem_enum_access_[i]->ReadQWORD(cpu_usage_handle_,&cpu_usage);
+ if (FAILED(hr))
+ {
+ returnValue = false;
+ }
+ hr = wbem_enum_access_[i]->ReadQWORD(timestamp_sys_100_ns_handle_,
+ ×tamp_100ns);
+ if (FAILED(hr))
+ {
+ returnValue = false;
+ }
+ wbem_enum_access_[i]->Release();
+ wbem_enum_access_[i] = NULL;
+
+ const bool wrapparound =
+ (previous_processor_timestamp_[i] > cpu_usage) ||
+ (previous_100ns_timestamp_[i] > timestamp_100ns);
+ const bool first_time = (previous_processor_timestamp_[i] == 0) ||
+ (previous_100ns_timestamp_[i] == 0);
+ if (wrapparound || first_time)
+ {
+ previous_processor_timestamp_[i] = cpu_usage;
+ previous_100ns_timestamp_[i] = timestamp_100ns;
+ continue;
+ }
+ const unsigned __int64 processor_timestamp_delta =
+ cpu_usage - previous_processor_timestamp_[i];
+ const unsigned __int64 timestamp_100ns_delta =
+ timestamp_100ns - previous_100ns_timestamp_[i];
+
+ if (processor_timestamp_delta >= timestamp_100ns_delta)
+ {
+ cpu_usage_[i] = 0;
+ } else {
+ // Quotient must be float since the division is guaranteed to yield
+ // a value between 0 and 1 which is 0 in integer division.
+ const float delta_quotient =
+ static_cast<float>(processor_timestamp_delta) /
+ static_cast<float>(timestamp_100ns_delta);
+ cpu_usage_[i] = 100 - static_cast<WebRtc_UWord32>(delta_quotient *
+ 100);
+ }
+ previous_processor_timestamp_[i] = cpu_usage;
+ previous_100ns_timestamp_[i] = timestamp_100ns;
+ }
+ return returnValue;
+}
+} // namespace webrtc