Revert 3736 "Removed CPU APIs from VoEHardware. Code is now only..."
> Removed CPU APIs from VoEHardware. Code is now only used by test applications.
>
> BUG=8404677
>
> Review URL: https://webrtc-codereview.appspot.com/1238004
TBR=henrike@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/1267004
git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@3737 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/system_wrappers/interface/cpu_wrapper.h b/system_wrappers/interface/cpu_wrapper.h
new file mode 100644
index 0000000..0911213
--- /dev/null
+++ b/system_wrappers/interface/cpu_wrapper.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_CPU_WRAPPER_H_
+#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_CPU_WRAPPER_H_
+
+#include "typedefs.h"
+
+namespace webrtc {
+
+class CpuWrapper {
+ public:
+ static CpuWrapper* CreateCpu();
+ virtual ~CpuWrapper() {}
+
+ // Returns the average CPU usage for all processors. The CPU usage can be
+ // between and including 0 to 100 (%)
+ virtual WebRtc_Word32 CpuUsage() = 0;
+ virtual WebRtc_Word32 CpuUsage(WebRtc_Word8* process_name,
+ WebRtc_UWord32 length) = 0;
+ virtual WebRtc_Word32 CpuUsage(WebRtc_UWord32 process_id) = 0;
+
+ // The CPU usage per core is returned in cpu_usage. The CPU can be between
+ // and including 0 to 100 (%)
+ // Note that the pointer passed as cpu_usage is redirected to a local member
+ // of the CPU wrapper.
+ // num_cores is the number of cores in the cpu_usage array.
+ // The return value is -1 for failure or 0-100, indicating the average
+ // CPU usage across all cores.
+ // Note: on some OSs this class is initialized lazy. This means that it
+ // might not yet be possible to retrieve any CPU metrics. When this happens
+ // the return value will be zero (indicating that there is not a failure),
+ // num_cores will be 0 and cpu_usage will be set to NULL (indicating that
+ // no metrics are available yet). Once the initialization is completed,
+ // which can take in the order of seconds, CPU metrics can be retrieved.
+ virtual WebRtc_Word32 CpuUsageMultiCore(WebRtc_UWord32& num_cores,
+ WebRtc_UWord32*& cpu_usage) = 0;
+
+ virtual void Reset() = 0;
+ virtual void Stop() = 0;
+
+ protected:
+ CpuWrapper() {}
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_CPU_WRAPPER_H_
diff --git a/system_wrappers/source/Android.mk b/system_wrappers/source/Android.mk
index ac64c3a..f59d880 100644
--- a/system_wrappers/source/Android.mk
+++ b/system_wrappers/source/Android.mk
@@ -24,6 +24,7 @@
aligned_malloc.cc \
atomic32_posix.cc \
condition_variable.cc \
+ cpu_no_op.cc \
cpu_features.cc \
cpu_info.cc \
critical_section.cc \
diff --git a/system_wrappers/source/cpu.cc b/system_wrappers/source/cpu.cc
new file mode 100644
index 0000000..d81f015
--- /dev/null
+++ b/system_wrappers/source/cpu.cc
@@ -0,0 +1,36 @@
+/*
+ * 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 "system_wrappers/interface/cpu_wrapper.h"
+
+#if defined(_WIN32)
+#include "cpu_win.h"
+#elif defined(WEBRTC_MAC)
+#include "cpu_mac.h"
+#elif defined(WEBRTC_ANDROID)
+// Not implemented yet, might be possible to use Linux implementation
+#else // defined(WEBRTC_LINUX)
+#include "cpu_linux.h"
+#endif
+
+namespace webrtc {
+CpuWrapper* CpuWrapper::CreateCpu() {
+#if defined(_WIN32)
+ return new CpuWindows();
+#elif defined(WEBRTC_MAC)
+ return new CpuWrapperMac();
+#elif defined(WEBRTC_ANDROID)
+ return 0;
+#else
+ return new CpuLinux();
+#endif
+}
+
+} // namespace webrtc
diff --git a/system_wrappers/source/cpu_info.cc b/system_wrappers/source/cpu_info.cc
index 514f41f..9bed35c 100644
--- a/system_wrappers/source/cpu_info.cc
+++ b/system_wrappers/source/cpu_info.cc
@@ -8,7 +8,7 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include "webrtc/system_wrappers/interface/cpu_info.h"
+#include "system_wrappers/interface/cpu_info.h"
#if defined(_WIN32)
#include <Windows.h>
@@ -22,7 +22,7 @@
#include <sys/sysinfo.h>
#endif
-#include "system_wrappers/interface/trace.h"
+#include "trace.h"
namespace webrtc {
diff --git a/system_wrappers/source/cpu_linux.cc b/system_wrappers/source/cpu_linux.cc
new file mode 100644
index 0000000..74783b9
--- /dev/null
+++ b/system_wrappers/source/cpu_linux.cc
@@ -0,0 +1,184 @@
+/*
+ * 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 "system_wrappers/source/cpu_linux.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+namespace webrtc {
+
+CpuLinux::CpuLinux()
+ : old_busy_time_(0),
+ old_idle_time_(0),
+ old_busy_time_multi_(NULL),
+ old_idle_time_multi_(NULL),
+ idle_array_(NULL),
+ busy_array_(NULL),
+ result_array_(NULL),
+ num_cores_(0) {
+ const int result = GetNumCores();
+ if (result != -1) {
+ num_cores_ = result;
+ old_busy_time_multi_ = new long long[num_cores_];
+ memset(old_busy_time_multi_, 0, sizeof(long long) * num_cores_);
+ old_idle_time_multi_ = new long long[num_cores_];
+ memset(old_idle_time_multi_, 0, sizeof(long long) * num_cores_);
+ idle_array_ = new long long[num_cores_];
+ memset(idle_array_, 0, sizeof(long long) * num_cores_);
+ busy_array_ = new long long[num_cores_];
+ memset(busy_array_, 0, sizeof(long long) * num_cores_);
+ result_array_ = new WebRtc_UWord32[num_cores_];
+
+ GetData(old_busy_time_, old_idle_time_, busy_array_, idle_array_);
+ }
+}
+
+CpuLinux::~CpuLinux() {
+ delete [] old_busy_time_multi_;
+ delete [] old_idle_time_multi_;
+ delete [] idle_array_;
+ delete [] busy_array_;
+ delete [] result_array_;
+}
+
+WebRtc_Word32 CpuLinux::CpuUsage() {
+ WebRtc_UWord32 dummy = 0;
+ WebRtc_UWord32* dummy_array = NULL;
+ return CpuUsageMultiCore(dummy, dummy_array);
+}
+
+WebRtc_Word32 CpuLinux::CpuUsageMultiCore(WebRtc_UWord32& num_cores,
+ WebRtc_UWord32*& core_array) {
+ core_array = result_array_;
+ num_cores = num_cores_;
+ long long busy = 0;
+ long long idle = 0;
+ if (GetData(busy, idle, busy_array_, idle_array_) != 0)
+ return -1;
+
+ long long delta_busy = busy - old_busy_time_;
+ long long delta_idle = idle - old_idle_time_;
+ old_busy_time_ = busy;
+ old_idle_time_ = idle;
+
+ int ret_val = -1;
+ if (delta_busy + delta_idle == 0) {
+ ret_val = 0;
+ } else {
+ ret_val = (int)(100 * (delta_busy) / (delta_busy + delta_idle));
+ }
+
+ if (core_array == NULL) {
+ return ret_val;
+ }
+
+ for (WebRtc_UWord32 i = 0; i < num_cores_; ++i) {
+ delta_busy = busy_array_[i] - old_busy_time_multi_[i];
+ delta_idle = idle_array_[i] - old_idle_time_multi_[i];
+ old_busy_time_multi_[i] = busy_array_[i];
+ old_idle_time_multi_[i] = idle_array_[i];
+ if (delta_busy + delta_idle == 0) {
+ core_array[i] = 0;
+ } else {
+ core_array[i] = (int)(100 * (delta_busy) / (delta_busy + delta_idle));
+ }
+ }
+ return ret_val;
+}
+
+int CpuLinux::GetData(long long& busy, long long& idle, long long*& busy_array,
+ long long*& idle_array) {
+ FILE* fp = fopen("/proc/stat", "r");
+ if (!fp) {
+ return -1;
+ }
+
+ char line[100];
+ if (fgets(line, 100, fp) == NULL) {
+ fclose(fp);
+ return -1;
+ }
+ char first_word[100];
+ if (sscanf(line, "%s ", first_word) != 1) {
+ fclose(fp);
+ return -1;
+ }
+ if (strncmp(first_word, "cpu", 3) != 0) {
+ fclose(fp);
+ return -1;
+ }
+ char s_user[100];
+ char s_nice[100];
+ char s_system[100];
+ char s_idle[100];
+ if (sscanf(line, "%s %s %s %s %s ",
+ first_word, s_user, s_nice, s_system, s_idle) != 5) {
+ fclose(fp);
+ return -1;
+ }
+ long long luser = atoll(s_user);
+ long long lnice = atoll(s_nice);
+ long long lsystem = atoll(s_system);
+ long long lidle = atoll(s_idle);
+
+ busy = luser + lnice + lsystem;
+ idle = lidle;
+ for (WebRtc_UWord32 i = 0; i < num_cores_; ++i) {
+ if (fgets(line, 100, fp) == NULL) {
+ fclose(fp);
+ return -1;
+ }
+ if (sscanf(line, "%s %s %s %s %s ", first_word, s_user, s_nice, s_system,
+ s_idle) != 5) {
+ fclose(fp);
+ return -1;
+ }
+ luser = atoll(s_user);
+ lnice = atoll(s_nice);
+ lsystem = atoll(s_system);
+ lidle = atoll(s_idle);
+ busy_array[i] = luser + lnice + lsystem;
+ idle_array[i] = lidle;
+ }
+ fclose(fp);
+ return 0;
+}
+
+int CpuLinux::GetNumCores() {
+ FILE* fp = fopen("/proc/stat", "r");
+ if (!fp) {
+ return -1;
+ }
+ // Skip first line
+ char line[100];
+ if (!fgets(line, 100, fp)) {
+ fclose(fp);
+ return -1;
+ }
+ int num_cores = -1;
+ char first_word[100];
+ do {
+ num_cores++;
+ if (fgets(line, 100, fp)) {
+ if (sscanf(line, "%s ", first_word) != 1) {
+ first_word[0] = '\0';
+ }
+ } else {
+ break;
+ }
+ } while (strncmp(first_word, "cpu", 3) == 0);
+ fclose(fp);
+ return num_cores;
+}
+
+} // namespace webrtc
diff --git a/system_wrappers/source/cpu_linux.h b/system_wrappers/source/cpu_linux.h
new file mode 100644
index 0000000..804b53e
--- /dev/null
+++ b/system_wrappers/source/cpu_linux.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_CPU_LINUX_H_
+#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_CPU_LINUX_H_
+
+#include "system_wrappers/interface/cpu_wrapper.h"
+
+namespace webrtc {
+
+class CpuLinux : public CpuWrapper {
+ public:
+ CpuLinux();
+ virtual ~CpuLinux();
+
+ virtual WebRtc_Word32 CpuUsage();
+ virtual WebRtc_Word32 CpuUsage(WebRtc_Word8* process_name,
+ WebRtc_UWord32 length) {
+ return 0;
+ }
+ virtual WebRtc_Word32 CpuUsage(WebRtc_UWord32 process_id) {
+ return 0;
+ }
+
+ virtual WebRtc_Word32 CpuUsageMultiCore(WebRtc_UWord32& num_cores,
+ WebRtc_UWord32*& array);
+
+ virtual void Reset() {
+ return;
+ }
+ virtual void Stop() {
+ return;
+ }
+ private:
+ int GetData(long long& busy, long long& idle, long long*& busy_array,
+ long long*& idle_array);
+ int GetNumCores();
+
+ long long old_busy_time_;
+ long long old_idle_time_;
+
+ long long* old_busy_time_multi_;
+ long long* old_idle_time_multi_;
+
+ long long* idle_array_;
+ long long* busy_array_;
+ WebRtc_UWord32* result_array_;
+ WebRtc_UWord32 num_cores_;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_CPU_LINUX_H_
diff --git a/system_wrappers/source/cpu_mac.cc b/system_wrappers/source/cpu_mac.cc
new file mode 100644
index 0000000..0342802
--- /dev/null
+++ b/system_wrappers/source/cpu_mac.cc
@@ -0,0 +1,130 @@
+/*
+ * 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 "system_wrappers/source/cpu_mac.h"
+
+#include <iostream>
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+
+#include "tick_util.h"
+
+namespace webrtc {
+
+CpuWrapperMac::CpuWrapperMac()
+ : cpu_count_(0),
+ cpu_usage_(NULL),
+ total_cpu_usage_(0),
+ last_tick_count_(NULL),
+ last_time_(0) {
+ natural_t cpu_count;
+ processor_info_array_t info_array;
+ mach_msg_type_number_t info_count;
+
+ kern_return_t error = host_processor_info(mach_host_self(),
+ PROCESSOR_CPU_LOAD_INFO,
+ &cpu_count,
+ &info_array,
+ &info_count);
+ if (error) {
+ return;
+ }
+
+ cpu_count_ = cpu_count;
+ cpu_usage_ = new WebRtc_UWord32[cpu_count];
+ last_tick_count_ = new WebRtc_Word64[cpu_count];
+ last_time_ = TickTime::MillisecondTimestamp();
+
+ processor_cpu_load_info_data_t* cpu_load_info =
+ (processor_cpu_load_info_data_t*) info_array;
+ for (unsigned int cpu = 0; cpu < cpu_count; ++cpu) {
+ WebRtc_Word64 ticks = 0;
+ for (int state = 0; state < 2; ++state) {
+ ticks += cpu_load_info[cpu].cpu_ticks[state];
+ }
+ last_tick_count_[cpu] = ticks;
+ cpu_usage_[cpu] = 0;
+ }
+ vm_deallocate(mach_task_self(), (vm_address_t)info_array, info_count);
+}
+
+CpuWrapperMac::~CpuWrapperMac() {
+ delete[] cpu_usage_;
+ delete[] last_tick_count_;
+}
+
+WebRtc_Word32 CpuWrapperMac::CpuUsage() {
+ WebRtc_UWord32 num_cores;
+ WebRtc_UWord32* array = NULL;
+ return CpuUsageMultiCore(num_cores, array);
+}
+
+WebRtc_Word32
+CpuWrapperMac::CpuUsageMultiCore(WebRtc_UWord32& num_cores,
+ WebRtc_UWord32*& array) {
+ // sanity check
+ if (cpu_usage_ == NULL) {
+ return -1;
+ }
+
+ WebRtc_Word64 now = TickTime::MillisecondTimestamp();
+ WebRtc_Word64 time_diff_ms = now - last_time_;
+ if (time_diff_ms >= 500) {
+ if (Update(time_diff_ms) != 0) {
+ return -1;
+ }
+ last_time_ = now;
+ }
+
+ num_cores = cpu_count_;
+ array = cpu_usage_;
+ return total_cpu_usage_ / cpu_count_;
+}
+
+WebRtc_Word32 CpuWrapperMac::Update(WebRtc_Word64 time_diff_ms) {
+ natural_t cpu_count;
+ processor_info_array_t info_array;
+ mach_msg_type_number_t info_count;
+
+ kern_return_t error = host_processor_info(mach_host_self(),
+ PROCESSOR_CPU_LOAD_INFO,
+ &cpu_count,
+ &info_array,
+ &info_count);
+ if (error) {
+ return -1;
+ }
+
+ processor_cpu_load_info_data_t* cpu_load_info =
+ (processor_cpu_load_info_data_t*) info_array;
+
+ total_cpu_usage_ = 0;
+ for (unsigned int cpu = 0; cpu < cpu_count; ++cpu) {
+ WebRtc_Word64 ticks = 0;
+ for (int state = 0; state < 2; ++state) {
+ ticks += cpu_load_info[cpu].cpu_ticks[state];
+ }
+ if (time_diff_ms <= 0) {
+ cpu_usage_[cpu] = 0;
+ } else {
+ cpu_usage_[cpu] = (WebRtc_UWord32)((1000 *
+ (ticks - last_tick_count_[cpu])) /
+ time_diff_ms);
+ }
+ last_tick_count_[cpu] = ticks;
+ total_cpu_usage_ += cpu_usage_[cpu];
+ }
+
+ vm_deallocate(mach_task_self(), (vm_address_t)info_array, info_count);
+
+ return 0;
+}
+
+} // namespace webrtc
diff --git a/system_wrappers/source/cpu_mac.h b/system_wrappers/source/cpu_mac.h
new file mode 100644
index 0000000..7e58003
--- /dev/null
+++ b/system_wrappers/source/cpu_mac.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_CPU_MAC_H_
+#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_CPU_MAC_H_
+
+#include "system_wrappers/interface/cpu_wrapper.h"
+
+namespace webrtc {
+
+class CpuWrapperMac : public CpuWrapper {
+ public:
+ CpuWrapperMac();
+ virtual ~CpuWrapperMac();
+
+ virtual WebRtc_Word32 CpuUsage();
+ virtual WebRtc_Word32 CpuUsage(WebRtc_Word8* process_name,
+ WebRtc_UWord32 length) {
+ return -1;
+ }
+ virtual WebRtc_Word32 CpuUsage(WebRtc_UWord32 process_id) {
+ return -1;
+ }
+
+ // Note: this class will block the call and sleep if called too fast
+ // This function blocks the calling thread if the thread is calling it more
+ // often than every 500 ms.
+ virtual WebRtc_Word32 CpuUsageMultiCore(WebRtc_UWord32& num_cores,
+ WebRtc_UWord32*& array);
+
+ virtual void Reset() {}
+ virtual void Stop() {}
+
+ private:
+ WebRtc_Word32 Update(WebRtc_Word64 time_diffMS);
+
+ WebRtc_UWord32 cpu_count_;
+ WebRtc_UWord32* cpu_usage_;
+ WebRtc_Word32 total_cpu_usage_;
+ WebRtc_Word64* last_tick_count_;
+ WebRtc_Word64 last_time_;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_CPU_MAC_H_
diff --git a/system_wrappers/source/cpu_measurement_harness.cc b/system_wrappers/source/cpu_measurement_harness.cc
new file mode 100644
index 0000000..237e776
--- /dev/null
+++ b/system_wrappers/source/cpu_measurement_harness.cc
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2012 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 "system_wrappers/interface/cpu_wrapper.h"
+#include "system_wrappers/interface/event_wrapper.h"
+#include "system_wrappers/interface/scoped_ptr.h"
+#include "system_wrappers/source/cpu_measurement_harness.h"
+
+const int kCpuCheckPeriodMs = 100;
+
+namespace webrtc {
+
+CpuMeasurementHarness* CpuMeasurementHarness::Create(
+ CpuTarget* target,
+ int work_period_ms,
+ int work_iterations_per_period,
+ int duration_ms) {
+ if (target == NULL) {
+ return NULL;
+ }
+ if (work_period_ms > duration_ms) {
+ return NULL;
+ }
+ if (work_period_ms < 0) {
+ return NULL;
+ }
+ if (duration_ms < 0) {
+ return NULL;
+ }
+ if (work_iterations_per_period < 1) {
+ return NULL;
+ }
+ return new CpuMeasurementHarness(target, work_period_ms,
+ work_iterations_per_period, duration_ms);
+}
+
+CpuMeasurementHarness::CpuMeasurementHarness(CpuTarget* target,
+ int work_period_ms,
+ int work_iterations_per_period,
+ int duration_ms)
+ : cpu_target_(target),
+ work_period_ms_(work_period_ms),
+ work_iterations_per_period_(work_iterations_per_period),
+ duration_ms_(duration_ms),
+ cpu_sum_(0),
+ cpu_iterations_(0),
+ cpu_(CpuWrapper::CreateCpu()),
+ event_(EventWrapper::Create()) {
+}
+
+CpuMeasurementHarness::~CpuMeasurementHarness() {
+}
+
+bool CpuMeasurementHarness::Run() {
+ if (!WaitForCpuInit()) {
+ return false;
+ }
+ // No need for precision. Run for approximately the asked for duration.
+ // TODO(hellner): very low prio if at all, the actual duration of the test
+ // will be longer if calling DoWork() is not negligable and/or called many
+ // times. It may make sense to compensate for drift here. This will,
+ // however, only add complexity with minimal gains. Perhaps renaming the
+ // duration_ms_ to something more fuzzy is a better idea. However, the name
+ // would be very convoluted if it is to be self documenting.
+ int elapsed_time_ms = 0;
+ int last_measured_time = 0;
+ while (elapsed_time_ms < duration_ms_) {
+ if (((elapsed_time_ms - last_measured_time) / kCpuCheckPeriodMs) >= 1) {
+ last_measured_time = elapsed_time_ms;
+ Measure();
+ }
+ if (!DoWork()) {
+ return false;
+ }
+ event_->Wait(work_period_ms_);
+ elapsed_time_ms += work_period_ms_;
+ }
+ return true;
+}
+
+int CpuMeasurementHarness::AverageCpu() {
+ if (cpu_iterations_ == 0) {
+ return 0;
+ }
+ assert(cpu_sum_ >= 0);
+ assert(cpu_iterations_ >= 0);
+ return cpu_sum_ / cpu_iterations_;
+}
+
+bool CpuMeasurementHarness::WaitForCpuInit() {
+ bool cpu_usage_available = false;
+ int num_iterations = 0;
+ // Initializing the CPU measurements may take a couple of seconds on Windows.
+ // Since the initialization is lazy we need to wait until it is completed.
+ // Should not take more than 10000 ms.
+ while (!cpu_usage_available && (++num_iterations < 10000)) {
+ event_->Wait(1);
+ cpu_usage_available = cpu_->CpuUsage() != -1;
+ }
+ return cpu_usage_available;
+}
+
+void CpuMeasurementHarness::Measure() {
+ WebRtc_UWord32 num_cores = 0;
+ WebRtc_UWord32* cores = NULL;
+ // Return the average CPU for now.
+ cpu_sum_ = cpu_->CpuUsageMultiCore(num_cores, cores);
+ ++cpu_iterations_;
+}
+
+bool CpuMeasurementHarness::DoWork() {
+ for (int i = 0; i < work_iterations_per_period_; ++i) {
+ if (!cpu_target_->DoWork()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace webrtc
diff --git a/system_wrappers/source/cpu_measurement_harness.h b/system_wrappers/source/cpu_measurement_harness.h
new file mode 100644
index 0000000..3b87f27
--- /dev/null
+++ b/system_wrappers/source/cpu_measurement_harness.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012 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.
+ */
+
+#ifndef SRC_SYSTEM_WRAPPERS_SOURCE_CPU_MEASUREMENT_HARNESS_H_
+#define SRC_SYSTEM_WRAPPERS_SOURCE_CPU_MEASUREMENT_HARNESS_H_
+
+#include "system_wrappers/interface/scoped_ptr.h"
+
+namespace webrtc {
+
+class CpuWrapper;
+class EventWrapper;
+class ThreadWrapper;
+
+// This abstract class provides an interface that should be passed to
+// CpuMeasurementHarness. CpuMeasurementHarness will call it with the
+// frequency requested and measure the CPU usage for all calls.
+class CpuTarget {
+ public:
+ // Callback function for which the CPU usage should be calculated.
+ virtual bool DoWork() = 0;
+
+ protected:
+ CpuTarget() {}
+ virtual ~CpuTarget() {}
+};
+
+class CpuMeasurementHarness {
+ public:
+ static CpuMeasurementHarness* Create(CpuTarget* target,
+ int work_period_ms,
+ int work_iterations_per_period,
+ int duration_ms);
+ ~CpuMeasurementHarness();
+ bool Run();
+ int AverageCpu();
+
+ protected:
+ CpuMeasurementHarness(CpuTarget* target, int work_period_ms,
+ int work_iterations_per_period, int duration_ms);
+
+ private:
+ bool WaitForCpuInit();
+ void Measure();
+ bool DoWork();
+
+ CpuTarget* cpu_target_;
+ const int work_period_ms_;
+ const int work_iterations_per_period_;
+ const int duration_ms_;
+ int cpu_sum_;
+ int cpu_iterations_;
+ scoped_ptr<CpuWrapper> cpu_;
+ scoped_ptr<EventWrapper> event_;
+};
+
+} // namespace webrtc
+
+#endif // SRC_SYSTEM_WRAPPERS_SOURCE_CPU_MEASUREMENT_HARNESS_H_
diff --git a/system_wrappers/source/cpu_no_op.cc b/system_wrappers/source/cpu_no_op.cc
new file mode 100644
index 0000000..f666961
--- /dev/null
+++ b/system_wrappers/source/cpu_no_op.cc
@@ -0,0 +1,21 @@
+/*
+ * 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 "system_wrappers/interface/cpu_wrapper.h"
+
+#include <stddef.h>
+
+namespace webrtc {
+
+CpuWrapper* CpuWrapper::CreateCpu() {
+ return NULL;
+}
+
+} // namespace webrtc
diff --git a/system_wrappers/source/cpu_win.cc b/system_wrappers/source/cpu_win.cc
new file mode 100644
index 0000000..7231d4d
--- /dev/null
+++ b/system_wrappers/source/cpu_win.cc
@@ -0,0 +1,526 @@
+/*
+ * Copyright (c) 2012 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_win.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 (!has_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 (has_terminated_) {
+ num_cores = 0;
+ cpu_usage = NULL;
+ return -1;
+ }
+ if (!has_initialized_)
+ {
+ num_cores = 0;
+ cpu_usage = NULL;
+ 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),
+ has_initialized_(false),
+ terminate_(false),
+ has_terminated_(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_service_proxy_(NULL),
+ wbem_refresher_(NULL),
+ wbem_enum_(NULL)
+{
+ // All resources are allocated in PollingCpu().
+ if (AllocateComplexDataTypes())
+ {
+ StartPollingCpu();
+ }
+ else
+ {
+ assert(false);
+ }
+}
+
+CpuWindows::~CpuWindows()
+{
+ // All resources are reclaimed in StopPollingCpu().
+ StopPollingCpu();
+ 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;
+ }
+}
+
+void CpuWindows::StartPollingCpu()
+{
+ unsigned int dummy_id = 0;
+ if (!cpu_polling_thread->Start(dummy_id))
+ {
+ initialize_ = false;
+ has_terminated_ = true;
+ assert(false);
+ }
+}
+
+bool CpuWindows::StopPollingCpu()
+{
+ {
+ // If StopPollingCpu is called immediately after StartPollingCpu() it is
+ // possible that cpu_polling_thread is in the process of initializing.
+ // Let initialization finish to avoid getting into a bad state.
+ CriticalSectionScoped cs(init_crit_);
+ while(initialize_)
+ {
+ init_cond_->SleepCS(*init_crit_);
+ }
+ }
+
+ CriticalSectionScoped cs(terminate_crit_);
+ terminate_ = true;
+ sleep_event->Set();
+ while (!has_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_)
+ {
+ Terminate();
+ 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 || !has_initialized_)
+ {
+ has_initialized_ = false;
+ terminate_ = true;
+ return true;
+ }
+ }
+ // 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.
+ UpdateCpuUsage();
+ 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;
+ }
+
+ // Create a proxy to the IWbemServices so that a local authentication
+ // can be set up (this is needed to be able to successfully call
+ // IWbemConfigureRefresher::AddEnum). Setting authentication with
+ // CoInitializeSecurity is process-wide (which is too intrusive).
+ hr = CoCopyProxy(static_cast<IUnknown*> (wbem_service_),
+ reinterpret_cast<IUnknown**> (&wbem_service_proxy_));
+ if(FAILED(hr))
+ {
+ return false;
+ }
+ // Set local authentication.
+ // RPC_C_AUTHN_WINNT means using NTLM instead of Kerberos which is default.
+ hr = CoSetProxyBlanket(static_cast<IUnknown*> (wbem_service_proxy_),
+ RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
+ RPC_C_AUTHN_LEVEL_DEFAULT,
+ RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
+ 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_proxy_,
+ 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;
+ }
+
+ if (!CreateWmiConnection())
+ {
+ return false;
+ }
+ if (!CreatePerfOsRefresher())
+ {
+ return false;
+ }
+ if (!CreatePerfOsCpuHandles())
+ {
+ return false;
+ }
+ has_initialized_ = true;
+ return true;
+}
+
+bool CpuWindows::Terminate()
+{
+ if (has_terminated_)
+ {
+ return false;
+ }
+ // 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_proxy_ != NULL)
+ {
+ wbem_service_proxy_->Release();
+ wbem_service_proxy_ = 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();
+ has_terminated_ = true;
+ return true;
+}
+
+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
diff --git a/system_wrappers/source/cpu_win.h b/system_wrappers/source/cpu_win.h
new file mode 100644
index 0000000..d15073c
--- /dev/null
+++ b/system_wrappers/source/cpu_win.h
@@ -0,0 +1,103 @@
+// This file contains a Windows implementation of CpuWrapper.
+// Note: Windows XP, Windows Server 2003 are the minimum requirements.
+// The requirements are due to the implementation being based on
+// WMI.
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_CPU_WINDOWS_NO_CPOL_H_
+#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_CPU_WINDOWS_NO_CPOL_H_
+
+#include "cpu_wrapper.h"
+
+#include <Wbemidl.h>
+
+namespace webrtc {
+class ConditionVariableWrapper;
+class CriticalSectionWrapper;
+class EventWrapper;
+class ThreadWrapper;
+
+class CpuWindows : public CpuWrapper
+{
+public:
+ virtual WebRtc_Word32 CpuUsage();
+ virtual WebRtc_Word32 CpuUsage(WebRtc_Word8* /*pProcessName*/,
+ WebRtc_UWord32 /*length*/) {return -1;}
+ virtual WebRtc_Word32 CpuUsage(WebRtc_UWord32 /*dwProcessID*/) {return -1;}
+
+ virtual WebRtc_Word32 CpuUsageMultiCore(WebRtc_UWord32& num_cores,
+ WebRtc_UWord32*& cpu_usage);
+
+ virtual void Reset() {}
+ virtual void Stop() {}
+
+ CpuWindows();
+ virtual ~CpuWindows();
+private:
+ bool AllocateComplexDataTypes();
+ void DeAllocateComplexDataTypes();
+
+ void StartPollingCpu();
+ bool StopPollingCpu();
+
+ static bool Process(void* thread_object);
+ bool ProcessImpl();
+
+ bool CreateWmiConnection();
+ bool CreatePerfOsRefresher();
+ bool CreatePerfOsCpuHandles();
+ bool Initialize();
+ bool Terminate();
+
+ bool UpdateCpuUsage();
+
+ ThreadWrapper* cpu_polling_thread;
+
+ bool initialize_;
+ bool has_initialized_;
+ CriticalSectionWrapper* init_crit_;
+ ConditionVariableWrapper* init_cond_;
+
+ bool terminate_;
+ bool has_terminated_;
+ CriticalSectionWrapper* terminate_crit_;
+ ConditionVariableWrapper* terminate_cond_;
+
+ // For sleep with wake-up functionality.
+ EventWrapper* sleep_event;
+
+ // Will be an array. Just care about CPU 0 for now.
+ WebRtc_UWord32* cpu_usage_;
+
+ // One IWbemObjectAccess for each processor and one for the total.
+ // 0-n-1 is the individual processors.
+ // n is the total.
+ IWbemObjectAccess** wbem_enum_access_;
+ DWORD number_of_objects_;
+
+ // Cpu timestamp
+ long cpu_usage_handle_;
+ unsigned __int64* previous_processor_timestamp_;
+
+ // Timestamp
+ long timestamp_sys_100_ns_handle_;
+ unsigned __int64* previous_100ns_timestamp_;
+
+ IWbemServices* wbem_service_;
+ IWbemServices* wbem_service_proxy_;
+
+ IWbemRefresher* wbem_refresher_;
+
+ IWbemHiPerfEnum* wbem_enum_;
+
+};
+} // namespace webrtc
+#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_CPU_WINDOWS_NO_CPOL_H_
diff --git a/system_wrappers/source/cpu_wrapper_unittest.cc b/system_wrappers/source/cpu_wrapper_unittest.cc
new file mode 100644
index 0000000..c849689
--- /dev/null
+++ b/system_wrappers/source/cpu_wrapper_unittest.cc
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2012 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 "gtest/gtest.h"
+#include "system_wrappers/interface/cpu_info.h"
+#include "system_wrappers/interface/cpu_wrapper.h"
+#include "system_wrappers/interface/event_wrapper.h"
+#include "system_wrappers/interface/scoped_ptr.h"
+#include "system_wrappers/interface/trace.h"
+#include "testsupport/fileutils.h"
+
+using webrtc::CpuInfo;
+using webrtc::CpuWrapper;
+using webrtc::EventWrapper;
+using webrtc::scoped_ptr;
+using webrtc::Trace;
+
+// This test is flaky on Windows/Release.
+// http://code.google.com/p/webrtc/issues/detail?id=290
+#ifdef _WIN32
+#define MAYBE_Usage DISABLED_Usage
+#else
+#define MAYBE_Usage Usage
+#endif
+TEST(CpuWrapperTest, MAYBE_Usage) {
+ Trace::CreateTrace();
+ std::string trace_file = webrtc::test::OutputPath() +
+ "cpu_wrapper_unittest.txt";
+ Trace::SetTraceFile(trace_file.c_str());
+ Trace::SetLevelFilter(webrtc::kTraceAll);
+ printf("Number of cores detected:%u\n", CpuInfo::DetectNumberOfCores());
+ scoped_ptr<CpuWrapper> cpu(CpuWrapper::CreateCpu());
+ ASSERT_TRUE(cpu.get() != NULL);
+ scoped_ptr<EventWrapper> sleep_event(EventWrapper::Create());
+ ASSERT_TRUE(sleep_event.get() != NULL);
+
+ int num_iterations = 0;
+ WebRtc_UWord32 num_cores = 0;
+ WebRtc_UWord32* cores = NULL;
+ bool cpu_usage_available = cpu->CpuUsageMultiCore(num_cores, cores) != -1;
+ // Initializing the CPU measurements may take a couple of seconds on Windows.
+ // Since the initialization is lazy we need to wait until it is completed.
+ // Should not take more than 10000 ms.
+ while (!cpu_usage_available && (++num_iterations < 10000)) {
+ if (cores != NULL) {
+ ASSERT_GT(num_cores, 0u);
+ break;
+ }
+ sleep_event->Wait(1);
+ cpu_usage_available = cpu->CpuUsageMultiCore(num_cores, cores) != -1;
+ }
+ ASSERT_TRUE(cpu_usage_available);
+
+ const WebRtc_Word32 average = cpu->CpuUsageMultiCore(num_cores, cores);
+ ASSERT_TRUE(cores != NULL);
+ EXPECT_GT(num_cores, 0u);
+ EXPECT_GE(average, 0);
+ EXPECT_LE(average, 100);
+
+ printf("\nNumber of cores:%d\n", num_cores);
+ printf("Average cpu:%d\n", average);
+ for (WebRtc_UWord32 i = 0; i < num_cores; i++) {
+ printf("Core:%u CPU:%u \n", i, cores[i]);
+ EXPECT_GE(cores[i], 0u);
+ EXPECT_LE(cores[i], 100u);
+ }
+
+ Trace::ReturnTrace();
+};
diff --git a/system_wrappers/source/system_wrappers.gyp b/system_wrappers/source/system_wrappers.gyp
index 8bc947b..36aa7d6 100644
--- a/system_wrappers/source/system_wrappers.gyp
+++ b/system_wrappers/source/system_wrappers.gyp
@@ -28,6 +28,7 @@
'../interface/compile_assert.h',
'../interface/condition_variable_wrapper.h',
'../interface/cpu_info.h',
+ '../interface/cpu_wrapper.h',
'../interface/cpu_features_wrapper.h',
'../interface/critical_section_wrapper.h',
'../interface/data_log.h',
@@ -64,7 +65,15 @@
'condition_variable_event_win.h',
'condition_variable_native_win.cc',
'condition_variable_native_win.h',
+ 'cpu.cc',
+ 'cpu_no_op.cc',
'cpu_info.cc',
+ 'cpu_linux.cc',
+ 'cpu_linux.h',
+ 'cpu_mac.cc',
+ 'cpu_mac.h',
+ 'cpu_win.cc',
+ 'cpu_win.h',
'cpu_features.cc',
'critical_section.cc',
'critical_section_posix.cc',
@@ -153,6 +162,18 @@
'libraries': [ '-lwinmm.lib', ],
},
}],
+ ['build_with_chromium==1', {
+ 'sources!': [
+ 'cpu.cc',
+ 'cpu_linux.h',
+ 'cpu_mac.h',
+ 'cpu_win.h',
+ ],
+ }, {
+ 'sources!': [
+ 'cpu_no_op.cc',
+ ],
+ }],
], # conditions
'target_conditions': [
# We need to do this in a target_conditions block to override the
@@ -162,6 +183,7 @@
# by file name rules).
'sources/': [
['include', '^atomic32_mac\\.'],
+ ['include', '^cpu_mac\\.'],
],
'sources!': [
'atomic32_posix.cc',
diff --git a/system_wrappers/source/system_wrappers_tests.gyp b/system_wrappers/source/system_wrappers_tests.gyp
index fd1db25..66939e9 100644
--- a/system_wrappers/source/system_wrappers_tests.gyp
+++ b/system_wrappers/source/system_wrappers_tests.gyp
@@ -20,6 +20,9 @@
'sources': [
'aligned_malloc_unittest.cc',
'condition_variable_unittest.cc',
+ 'cpu_wrapper_unittest.cc',
+ 'cpu_measurement_harness.h',
+ 'cpu_measurement_harness.cc',
'critical_section_unittest.cc',
'event_tracer_unittest.cc',
'list_unittest.cc',
@@ -33,6 +36,7 @@
'stringize_macros_unittest.cc',
'thread_unittest.cc',
'thread_posix_unittest.cc',
+ 'trace_unittest.cc',
'unittest_utilities_unittest.cc',
],
'conditions': [
diff --git a/system_wrappers/source/trace_unittest.cc b/system_wrappers/source/trace_unittest.cc
new file mode 100644
index 0000000..62422ad
--- /dev/null
+++ b/system_wrappers/source/trace_unittest.cc
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2012 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 "gtest/gtest.h"
+#include "webrtc/system_wrappers/interface/trace.h"
+#include "webrtc/system_wrappers/source/cpu_measurement_harness.h"
+#include "webrtc/test/testsupport/fileutils.h"
+
+using webrtc::CpuMeasurementHarness;
+using webrtc::Trace;
+using webrtc::kTraceWarning;
+using webrtc::kTraceUtility;
+
+class Logger : public webrtc::CpuTarget {
+ public:
+ Logger() {
+ Trace::CreateTrace();
+ std::string trace_file = webrtc::test::OutputPath() + "trace_unittest.txt";
+ Trace::SetTraceFile(trace_file.c_str());
+ Trace::SetLevelFilter(webrtc::kTraceAll);
+ }
+ virtual ~Logger() {
+ Trace::ReturnTrace();
+ }
+
+ virtual bool DoWork() {
+ // Use input paremeters to WEBRTC_TRACE that are not likely to be removed
+ // in future code. E.g. warnings will likely be kept and this file is in
+ // utility so it should use kTraceUtility.
+ WEBRTC_TRACE(kTraceWarning, kTraceUtility, 0, "Log line");
+ return true;
+ }
+};
+
+// This test is disabled because it measures CPU usage. This is flaky because
+// the CPU usage for a machine may spike due to OS or other application.
+TEST(TraceTest, DISABLED_CpuUsage) {
+ Logger logger;
+ const int periodicity_ms = 1;
+ const int iterations_per_period = 10;
+ const int duration_ms = 1000;
+ CpuMeasurementHarness* cpu_harness =
+ CpuMeasurementHarness::Create(&logger, periodicity_ms,
+ iterations_per_period, duration_ms);
+ cpu_harness->Run();
+ const int average_cpu = cpu_harness->AverageCpu();
+ EXPECT_GE(5, average_cpu);
+}
diff --git a/voice_engine/include/voe_hardware.h b/voice_engine/include/voe_hardware.h
index 1537aab..e973c11 100644
--- a/voice_engine/include/voe_hardware.h
+++ b/voice_engine/include/voe_hardware.h
@@ -93,6 +93,13 @@
// of total CPU availability. [Windows only]
virtual int GetCPULoad(int& loadPercent) = 0;
+ // Gets the computer's current CPU consumption in terms of the percent
+ // of the total CPU availability. This method may fail a few times on
+ // Windows because it needs a certain warm-up time before reporting the
+ // result. You should check the return value and either try again or
+ // give up when it fails.
+ virtual int GetSystemCPULoad(int& loadPercent) = 0;
+
// Not supported
virtual int ResetAudioDevice() = 0;
diff --git a/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc b/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc
index 6136543..edb7f56 100644
--- a/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc
+++ b/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc
@@ -51,6 +51,17 @@
// Tests that only apply to desktop:
#if !defined(WEBRTC_IOS) & !defined(WEBRTC_ANDROID)
+TEST_F(HardwareBeforeStreamingTest, GetSystemCpuLoadSucceeds) {
+#ifdef _WIN32
+ // This method needs some warm-up time on Windows. We sleep a good amount
+ // of time instead of retrying to make the test simpler.
+ Sleep(2000);
+#endif
+
+ int load_percent;
+ EXPECT_EQ(0, voe_hardware_->GetSystemCPULoad(load_percent));
+}
+
TEST_F(HardwareBeforeStreamingTest, GetPlayoutDeviceStatusReturnsTrue) {
bool play_available = false;
EXPECT_EQ(0, voe_hardware_->GetPlayoutDeviceStatus(play_available));
diff --git a/voice_engine/test/auto_test/standard/hardware_test.cc b/voice_engine/test/auto_test/standard/hardware_test.cc
index d026f51..7310e52 100644
--- a/voice_engine/test/auto_test/standard/hardware_test.cc
+++ b/voice_engine/test/auto_test/standard/hardware_test.cc
@@ -69,6 +69,20 @@
}
#endif
+#if !defined(WEBRTC_MAC) && !defined(WEBRTC_ANDROID)
+TEST_F(HardwareTest, GetSystemCpuLoadWorksExceptOnMacAndAndroid) {
+#ifdef _WIN32
+ // This method needs some warm-up time on Windows. We sleep a good amount
+ // of time instead of retrying to make the test simpler.
+ Sleep(2000);
+#endif
+ int load = -1;
+ EXPECT_EQ(0, voe_hardware_->GetSystemCPULoad(load));
+ EXPECT_GE(load, 0);
+ TEST_LOG("System CPU load = %d%%\n", load);
+}
+#endif
+
TEST_F(HardwareTest, BuiltInWasapiAECWorksForAudioWindowsCoreAudioLayer) {
#ifdef WEBRTC_IOS
// Ensure the sound device is reset on iPhone.
diff --git a/voice_engine/voe_hardware_impl.cc b/voice_engine/voe_hardware_impl.cc
index a374bc9..db7e18f 100644
--- a/voice_engine/voe_hardware_impl.cc
+++ b/voice_engine/voe_hardware_impl.cc
@@ -12,6 +12,7 @@
#include <cassert>
+#include "cpu_wrapper.h"
#include "critical_section_wrapper.h"
#include "trace.h"
#include "voe_errors.h"
@@ -37,16 +38,29 @@
#ifdef WEBRTC_VOICE_ENGINE_HARDWARE_API
-VoEHardwareImpl::VoEHardwareImpl(voe::SharedData* shared) : _shared(shared)
+VoEHardwareImpl::VoEHardwareImpl(voe::SharedData* shared) :
+ _cpu(NULL), _shared(shared)
{
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
"VoEHardwareImpl() - ctor");
+
+ _cpu = CpuWrapper::CreateCpu();
+ if (_cpu)
+ {
+ _cpu->CpuUsage(); // init cpu usage
+ }
}
VoEHardwareImpl::~VoEHardwareImpl()
{
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
"~VoEHardwareImpl() - dtor");
+
+ if (_cpu)
+ {
+ delete _cpu;
+ _cpu = NULL;
+ }
}
int VoEHardwareImpl::SetAudioDeviceLayer(AudioLayers audioLayer)
@@ -739,6 +753,45 @@
return 0;
}
+int VoEHardwareImpl::GetSystemCPULoad(int& loadPercent)
+{
+ WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
+ "GetSystemCPULoad(loadPercent=?)");
+ ANDROID_NOT_SUPPORTED(_shared->statistics());
+ IPHONE_NOT_SUPPORTED(_shared->statistics());
+
+ if (!_shared->statistics().Initialized())
+ {
+ _shared->SetLastError(VE_NOT_INITED, kTraceError);
+ return -1;
+ }
+
+ // Check if implemented for this platform
+ if (!_cpu)
+ {
+ _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
+ " no support for getting system CPU load");
+ return -1;
+ }
+
+ // Get CPU load
+ WebRtc_Word32 load = _cpu->CpuUsage();
+ if (load < 0)
+ {
+ _shared->SetLastError(VE_CPU_INFO_ERROR, kTraceError,
+ " error getting system CPU load");
+ return -1;
+ }
+
+ loadPercent = static_cast<int> (load);
+
+ WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
+ VoEId(_shared->instance_id(), -1),
+ " Output: loadPercent = %d", loadPercent);
+
+ return 0;
+}
+
int VoEHardwareImpl::EnableBuiltInAEC(bool enable)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
diff --git a/voice_engine/voe_hardware_impl.h b/voice_engine/voe_hardware_impl.h
index 1b2f343..9477567 100644
--- a/voice_engine/voe_hardware_impl.h
+++ b/voice_engine/voe_hardware_impl.h
@@ -17,6 +17,7 @@
namespace webrtc
{
+class CpuWrapper;
class VoEHardwareImpl: public VoEHardware
{
@@ -49,6 +50,8 @@
virtual int GetCPULoad(int& loadPercent);
+ virtual int GetSystemCPULoad(int& loadPercent);
+
virtual int ResetAudioDevice();
virtual int AudioDeviceControl(unsigned int par1,
@@ -72,6 +75,7 @@
virtual ~VoEHardwareImpl();
private:
+ CpuWrapper* _cpu;
voe::SharedData* _shared;
};