diff --git a/webrtc/base/BUILD.gn b/webrtc/base/BUILD.gn index a82748eccd..7236ee8b7b 100644 --- a/webrtc/base/BUILD.gn +++ b/webrtc/base/BUILD.gn @@ -688,6 +688,8 @@ if (rtc_include_tests) { sources = [ # Also use this as a convenient dumping ground for misc files that are # included by multiple targets below. + "cpu_time.cc", + "cpu_time.h", "fakeclock.cc", "fakeclock.h", "fakenetwork.h", @@ -733,6 +735,7 @@ if (rtc_include_tests) { rtc_source_set("rtc_base_nonparallel_tests") { testonly = true sources = [ + "cpu_time_unittest.cc", "filerotatingstream_unittest.cc", "nullsocketserver_unittest.cc", "physicalsocketserver_unittest.cc", diff --git a/webrtc/base/cpu_time.cc b/webrtc/base/cpu_time.cc new file mode 100644 index 0000000000..5fce36654e --- /dev/null +++ b/webrtc/base/cpu_time.cc @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2017 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 "webrtc/base/cpu_time.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/timeutils.h" + +#if defined(WEBRTC_LINUX) +#include +#elif defined(WEBRTC_MAC) +#include +#include +#include +#include +#include +#include +#include +#elif defined(WEBRTC_WIN) +#include +#endif + +#if defined(WEBRTC_WIN) +namespace { +// FILETIME resolution is 100 nanosecs. +const int64_t kNanosecsPerFiletime = 100; +} // namespace +#endif + +namespace rtc { + +int64_t GetProcessCpuTimeNanos() { +#if defined(WEBRTC_LINUX) + struct timespec ts; + if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0) { + return ts.tv_sec * kNumNanosecsPerSec + ts.tv_nsec; + } else { + LOG_ERR(LS_ERROR) << "clock_gettime() failed."; + } +#elif defined(WEBRTC_MAC) + struct rusage rusage; + if (getrusage(RUSAGE_SELF, &rusage) == 0) { + return rusage.ru_utime.tv_sec * kNumNanosecsPerSec + + rusage.ru_utime.tv_usec * kNumNanosecsPerMicrosec; + } else { + LOG_ERR(LS_ERROR) << "getrusage() failed."; + } +#elif defined(WEBRTC_WIN) + FILETIME createTime; + FILETIME exitTime; + FILETIME kernelTime; + FILETIME userTime; + if (GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, + &userTime) != 0) { + return ((static_cast(userTime.dwHighDateTime) << 32) + + userTime.dwLowDateTime) * + kNanosecsPerFiletime; + } else { + LOG_ERR(LS_ERROR) << "GetProcessTimes() failed."; + } +#else + // Not implemented yet. + static_assert( + false, "GetProcessCpuTimeNanos() platform support not yet implemented."); +#endif + return -1; +} + +int64_t GetThreadCpuTimeNanos() { +#if defined(WEBRTC_LINUX) + struct timespec ts; + if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) { + return ts.tv_sec * kNumNanosecsPerSec + ts.tv_nsec; + } else { + LOG_ERR(LS_ERROR) << "clock_gettime() failed."; + } +#elif defined(WEBRTC_MAC) + thread_basic_info_data_t info; + mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; + if (thread_info(mach_thread_self(), THREAD_BASIC_INFO, (thread_info_t)&info, + &count) == KERN_SUCCESS) { + return info.user_time.seconds * kNumNanosecsPerSec + + info.user_time.microseconds * kNumNanosecsPerMicrosec; + } else { + LOG_ERR(LS_ERROR) << "thread_info() failed."; + } +#elif defined(WEBRTC_WIN) + FILETIME createTime; + FILETIME exitTime; + FILETIME kernelTime; + FILETIME userTime; + if (GetThreadTimes(GetCurrentThread(), &createTime, &exitTime, &kernelTime, + &userTime) != 0) { + return ((static_cast(userTime.dwHighDateTime) << 32) + + userTime.dwLowDateTime) * + kNanosecsPerFiletime; + } else { + LOG_ERR(LS_ERROR) << "GetThreadTimes() failed."; + } +#else + // Not implemented yet. + static_assert( + false, "GetProcessCpuTimeNanos() platform support not yet implemented."); +#endif + return -1; +} + +} // namespace rtc diff --git a/webrtc/base/cpu_time.h b/webrtc/base/cpu_time.h new file mode 100644 index 0000000000..87e941801b --- /dev/null +++ b/webrtc/base/cpu_time.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017 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_BASE_CPU_TIME_H_ +#define WEBRTC_BASE_CPU_TIME_H_ + +#include + +namespace rtc { + +// Returns total CPU time of a current process in nanoseconds. +// Time base is unknown, therefore use only to calculate deltas. +int64_t GetProcessCpuTimeNanos(); + +// Returns total CPU time of a current thread in nanoseconds. +// Time base is unknown, therefore use only to calculate deltas. +int64_t GetThreadCpuTimeNanos(); + +} // namespace rtc + +#endif // WEBRTC_BASE_CPU_TIME_H_ diff --git a/webrtc/base/cpu_time_unittest.cc b/webrtc/base/cpu_time_unittest.cc new file mode 100644 index 0000000000..cb025ea44c --- /dev/null +++ b/webrtc/base/cpu_time_unittest.cc @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2017 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 +#include +#include "webrtc/base/cpu_time.h" +#include "webrtc/base/platform_thread.h" +#include "webrtc/base/timeutils.h" +#include "webrtc/test/gtest.h" +#include "webrtc/system_wrappers/include/cpu_info.h" + +namespace { +const int kAllowedErrorMillisecs = 30; +const int kProcessingTimeMillisecs = 300; + +// Consumes approximately kProcessingTimeMillisecs of CPU time. +bool WorkingFunction(void* counter_pointer) { + int64_t* counter = reinterpret_cast(counter_pointer); + *counter = 0; + int64_t stop_time = rtc::SystemTimeNanos() + + kProcessingTimeMillisecs * rtc::kNumNanosecsPerMillisec; + while (rtc::SystemTimeNanos() < stop_time) { + (*counter)++; + } + return false; +} +} // namespace + +namespace rtc { + +TEST(GetProcessCpuTimeTest, SingleThread) { + int64_t start_time_nanos = GetProcessCpuTimeNanos(); + int64_t counter; + WorkingFunction(reinterpret_cast(&counter)); + EXPECT_GT(counter, 0); + int64_t duration_nanos = GetProcessCpuTimeNanos() - start_time_nanos; + // Should be about kProcessingTimeMillisecs. + EXPECT_NEAR(duration_nanos, + kProcessingTimeMillisecs * kNumNanosecsPerMillisec, + kAllowedErrorMillisecs * kNumNanosecsPerMillisec); +} + +TEST(GetProcessCpuTimeTest, TwoThreads) { + int64_t start_time_nanos = GetProcessCpuTimeNanos(); + int64_t counter1; + int64_t counter2; + PlatformThread thread1(WorkingFunction, reinterpret_cast(&counter1), + "Thread1"); + PlatformThread thread2(WorkingFunction, reinterpret_cast(&counter2), + "Thread2"); + thread1.Start(); + thread2.Start(); + thread1.Stop(); + thread2.Stop(); + + EXPECT_GE(counter1, 0); + EXPECT_GE(counter2, 0); + int64_t duration_nanos = GetProcessCpuTimeNanos() - start_time_nanos; + const uint32_t kWorkingThreads = 2; + uint32_t used_cores = + std::min(webrtc::CpuInfo::DetectNumberOfCores(), kWorkingThreads); + // Two working threads for kProcessingTimeMillisecs consume double CPU time + // if there are at least 2 cores. + EXPECT_NEAR(duration_nanos, + used_cores * kProcessingTimeMillisecs * kNumNanosecsPerMillisec, + used_cores * kAllowedErrorMillisecs * kNumNanosecsPerMillisec); +} + +TEST(GetThreadCpuTimeTest, SingleThread) { + int64_t start_times_nanos = GetThreadCpuTimeNanos(); + int64_t counter; + WorkingFunction(reinterpret_cast(&counter)); + EXPECT_GT(counter, 0); + int64_t duration_nanos = GetThreadCpuTimeNanos() - start_times_nanos; + EXPECT_NEAR(duration_nanos, + kProcessingTimeMillisecs * kNumNanosecsPerMillisec, + kAllowedErrorMillisecs * kNumNanosecsPerMillisec); +} + +TEST(GetThreadCpuTimeTest, TwoThreads) { + int64_t start_time_nanos = GetThreadCpuTimeNanos(); + int64_t counter1; + int64_t counter2; + PlatformThread thread1(WorkingFunction, reinterpret_cast(&counter1), + "Thread1"); + PlatformThread thread2(WorkingFunction, reinterpret_cast(&counter2), + "Thread2"); + thread1.Start(); + thread2.Start(); + thread1.Stop(); + thread2.Stop(); + + EXPECT_GE(counter1, 0); + EXPECT_GE(counter2, 0); + int64_t duration_nanos = GetThreadCpuTimeNanos() - start_time_nanos; + // This thread didn't do any work. + EXPECT_NEAR(duration_nanos, 0, + kAllowedErrorMillisecs * kNumNanosecsPerMillisec); +} + +} // namespace rtc