diff --git a/webrtc/system_wrappers/interface/cpu_wrapper.h b/webrtc/system_wrappers/interface/cpu_wrapper.h new file mode 100644 index 0000000000..09112134ae --- /dev/null +++ b/webrtc/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/webrtc/system_wrappers/source/Android.mk b/webrtc/system_wrappers/source/Android.mk index ac64c3a055..f59d88063e 100644 --- a/webrtc/system_wrappers/source/Android.mk +++ b/webrtc/system_wrappers/source/Android.mk @@ -24,6 +24,7 @@ LOCAL_SRC_FILES := \ 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/webrtc/system_wrappers/source/cpu.cc b/webrtc/system_wrappers/source/cpu.cc new file mode 100644 index 0000000000..d81f015317 --- /dev/null +++ b/webrtc/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/webrtc/system_wrappers/source/cpu_info.cc b/webrtc/system_wrappers/source/cpu_info.cc index 514f41f025..9bed35cac8 100644 --- a/webrtc/system_wrappers/source/cpu_info.cc +++ b/webrtc/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 @@ -22,7 +22,7 @@ #include #endif -#include "system_wrappers/interface/trace.h" +#include "trace.h" namespace webrtc { diff --git a/webrtc/system_wrappers/source/cpu_linux.cc b/webrtc/system_wrappers/source/cpu_linux.cc new file mode 100644 index 0000000000..74783b9b42 --- /dev/null +++ b/webrtc/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 +#include +#include +#include + +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/webrtc/system_wrappers/source/cpu_linux.h b/webrtc/system_wrappers/source/cpu_linux.h new file mode 100644 index 0000000000..804b53e27c --- /dev/null +++ b/webrtc/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/webrtc/system_wrappers/source/cpu_mac.cc b/webrtc/system_wrappers/source/cpu_mac.cc new file mode 100644 index 0000000000..0342802207 --- /dev/null +++ b/webrtc/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 +#include +#include + +#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/webrtc/system_wrappers/source/cpu_mac.h b/webrtc/system_wrappers/source/cpu_mac.h new file mode 100644 index 0000000000..7e580030e6 --- /dev/null +++ b/webrtc/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/webrtc/system_wrappers/source/cpu_measurement_harness.cc b/webrtc/system_wrappers/source/cpu_measurement_harness.cc new file mode 100644 index 0000000000..237e776506 --- /dev/null +++ b/webrtc/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/webrtc/system_wrappers/source/cpu_measurement_harness.h b/webrtc/system_wrappers/source/cpu_measurement_harness.h new file mode 100644 index 0000000000..3b87f2775b --- /dev/null +++ b/webrtc/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 cpu_; + scoped_ptr event_; +}; + +} // namespace webrtc + +#endif // SRC_SYSTEM_WRAPPERS_SOURCE_CPU_MEASUREMENT_HARNESS_H_ diff --git a/webrtc/system_wrappers/source/cpu_no_op.cc b/webrtc/system_wrappers/source/cpu_no_op.cc new file mode 100644 index 0000000000..f666961396 --- /dev/null +++ b/webrtc/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 + +namespace webrtc { + +CpuWrapper* CpuWrapper::CreateCpu() { + return NULL; +} + +} // namespace webrtc diff --git a/webrtc/system_wrappers/source/cpu_win.cc b/webrtc/system_wrappers/source/cpu_win.cc new file mode 100644 index 0000000000..7231d4d538 --- /dev/null +++ b/webrtc/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 +#include +#include + +#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(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(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 (&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 (&wbem_refresher_)); + if (FAILED(hr)) + { + return false; + } + // Create PerfOS_Processor enum. + IWbemConfigureRefresher* wbem_refresher_config = NULL; + hr = wbem_refresher_->QueryInterface( + IID_IWbemConfigureRefresher, + reinterpret_cast (&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 (wbem_service_), + reinterpret_cast (&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 (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(processor_timestamp_delta) / + static_cast(timestamp_100ns_delta); + cpu_usage_[i] = 100 - static_cast(delta_quotient * + 100); + } + previous_processor_timestamp_[i] = cpu_usage; + previous_100ns_timestamp_[i] = timestamp_100ns; + } + return returnValue; +} +} // namespace webrtc diff --git a/webrtc/system_wrappers/source/cpu_win.h b/webrtc/system_wrappers/source/cpu_win.h new file mode 100644 index 0000000000..d15073c0ba --- /dev/null +++ b/webrtc/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 + +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/webrtc/system_wrappers/source/cpu_wrapper_unittest.cc b/webrtc/system_wrappers/source/cpu_wrapper_unittest.cc new file mode 100644 index 0000000000..c849689166 --- /dev/null +++ b/webrtc/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 cpu(CpuWrapper::CreateCpu()); + ASSERT_TRUE(cpu.get() != NULL); + scoped_ptr 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/webrtc/system_wrappers/source/system_wrappers.gyp b/webrtc/system_wrappers/source/system_wrappers.gyp index 8bc947b40c..36aa7d6ea9 100644 --- a/webrtc/system_wrappers/source/system_wrappers.gyp +++ b/webrtc/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/webrtc/system_wrappers/source/system_wrappers_tests.gyp b/webrtc/system_wrappers/source/system_wrappers_tests.gyp index fd1db25401..66939e99c4 100644 --- a/webrtc/system_wrappers/source/system_wrappers_tests.gyp +++ b/webrtc/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/webrtc/system_wrappers/source/trace_unittest.cc b/webrtc/system_wrappers/source/trace_unittest.cc new file mode 100644 index 0000000000..62422ad58a --- /dev/null +++ b/webrtc/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/webrtc/voice_engine/include/voe_hardware.h b/webrtc/voice_engine/include/voe_hardware.h index 1537aab697..e973c110ab 100644 --- a/webrtc/voice_engine/include/voe_hardware.h +++ b/webrtc/voice_engine/include/voe_hardware.h @@ -93,6 +93,13 @@ public: // 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/webrtc/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc b/webrtc/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc index 6136543b76..edb7f56288 100644 --- a/webrtc/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc +++ b/webrtc/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc @@ -51,6 +51,17 @@ TEST_F(HardwareBeforeStreamingTest, ResetsAudioDeviceOnIphone) { // 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/webrtc/voice_engine/test/auto_test/standard/hardware_test.cc b/webrtc/voice_engine/test/auto_test/standard/hardware_test.cc index d026f516ca..7310e52d2e 100644 --- a/webrtc/voice_engine/test/auto_test/standard/hardware_test.cc +++ b/webrtc/voice_engine/test/auto_test/standard/hardware_test.cc @@ -69,6 +69,20 @@ TEST_F(HardwareTest, GetCpuLoadReturnsErrorOnNonWindowsPlatform) { } #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/webrtc/voice_engine/voe_hardware_impl.cc b/webrtc/voice_engine/voe_hardware_impl.cc index a374bc9a6e..db7e18fa82 100644 --- a/webrtc/voice_engine/voe_hardware_impl.cc +++ b/webrtc/voice_engine/voe_hardware_impl.cc @@ -12,6 +12,7 @@ #include +#include "cpu_wrapper.h" #include "critical_section_wrapper.h" #include "trace.h" #include "voe_errors.h" @@ -37,16 +38,29 @@ VoEHardware* VoEHardware::GetInterface(VoiceEngine* voiceEngine) #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 @@ int VoEHardwareImpl::GetCPULoad(int& loadPercent) 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 (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/webrtc/voice_engine/voe_hardware_impl.h b/webrtc/voice_engine/voe_hardware_impl.h index 1b2f34347a..94775672c7 100644 --- a/webrtc/voice_engine/voe_hardware_impl.h +++ b/webrtc/voice_engine/voe_hardware_impl.h @@ -17,6 +17,7 @@ namespace webrtc { +class CpuWrapper; class VoEHardwareImpl: public VoEHardware { @@ -49,6 +50,8 @@ public: virtual int GetCPULoad(int& loadPercent); + virtual int GetSystemCPULoad(int& loadPercent); + virtual int ResetAudioDevice(); virtual int AudioDeviceControl(unsigned int par1, @@ -72,6 +75,7 @@ protected: virtual ~VoEHardwareImpl(); private: + CpuWrapper* _cpu; voe::SharedData* _shared; };