-
Notifications
You must be signed in to change notification settings - Fork 121
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add measuring the CPU usage for testing the performance. b/302715613
- Loading branch information
Showing
5 changed files
with
328 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
// Copyright 2024 The Cobalt Authors. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#include "cobalt/media/player/web_media_player_perf.h" | ||
|
||
#include "base/bind.h" | ||
#include "base/files/file_util.h" | ||
#include "base/logging.h" | ||
#include "base/strings/string_number_conversions.h" | ||
#include "base/strings/string_split.h" | ||
#include "base/strings/string_util.h" | ||
#include "base/threading/thread_restrictions.h" | ||
#include "starboard/common/system_property.h" | ||
#include "starboard/system.h" | ||
|
||
namespace cobalt { | ||
namespace media { | ||
|
||
const char kProcDir[] = "/proc"; | ||
|
||
const char kStatFile[] = "stat"; | ||
|
||
void WebMediaPlayerPerf::Start() { | ||
LOG(ERROR) << "Brown WebMediaPlayerPerf::Start"; | ||
std::vector<SbSystemPropertyId> sb_system_property_ids{ | ||
kSbSystemPropertyBrandName, kSbSystemPropertyModelName, | ||
kSbSystemPropertyPlatformName}; | ||
for (auto property_id : sb_system_property_ids) { | ||
char property[512]; | ||
if (!SbSystemGetProperty(property_id, property, | ||
SB_ARRAY_SIZE_INT(property))) { | ||
DLOG(FATAL) << "Failed to get kSbSystemPropertyPlatformName."; | ||
} | ||
LOG(ERROR) << "Brown property " << property; | ||
} | ||
|
||
task_runner_->PostTask(FROM_HERE, | ||
base::Bind(&WebMediaPlayerPerf::StartTask, this)); | ||
} | ||
|
||
void WebMediaPlayerPerf::StartTask() { | ||
DCHECK(task_runner_->BelongsToCurrentThread()); | ||
LOG(ERROR) << "Brown WebMediaPlayerPerf::StartTask " << this << "/" | ||
<< process_pid_; | ||
|
||
timer_.Start(FROM_HERE, base::Seconds(1), this, | ||
&WebMediaPlayerPerf::GetCpuUsage); | ||
} | ||
|
||
void WebMediaPlayerPerf::Stop() { | ||
LOG(ERROR) << "Brown WebMediaPlayerPerf::Stop"; | ||
|
||
task_runner_->PostTask(FROM_HERE, | ||
base::Bind(&WebMediaPlayerPerf::StopTask, this)); | ||
} | ||
|
||
void WebMediaPlayerPerf::StopTask() { | ||
DCHECK(task_runner_->BelongsToCurrentThread()); | ||
LOG(ERROR) << "Brown WebMediaPlayerPerf::StopTask"; | ||
timer_.Stop(); | ||
} | ||
|
||
void WebMediaPlayerPerf::GetCpuUsage() { | ||
DCHECK(task_runner_->BelongsToCurrentThread()); | ||
LOG(ERROR) << "Brown GetCpuUsage pid: " << process_pid_ | ||
<< ", usage: " << GetPlatformIndependentCPUUsage() << "%"; | ||
} | ||
|
||
base::FilePath WebMediaPlayerPerf::GetProcPidDir(pid_t pid) { | ||
return base::FilePath(kProcDir).Append(base::IntToString(pid)); | ||
} | ||
|
||
bool WebMediaPlayerPerf::ReadProcFile(const base::FilePath& file, | ||
std::string* buffer) { | ||
buffer->clear(); | ||
// Synchronously reading files in /proc is safe. | ||
base::ThreadRestrictions::ScopedAllowIO allow_io; | ||
|
||
if (!base::ReadFileToString(file, buffer)) { | ||
DLOG(WARNING) << "Failed to read " << file.MaybeAsASCII(); | ||
return false; | ||
} | ||
return !buffer->empty(); | ||
} | ||
|
||
bool WebMediaPlayerPerf::ReadProcStats(pid_t pid, std::string* buffer) { | ||
base::FilePath stat_file = GetProcPidDir(pid).Append(kStatFile); | ||
return ReadProcFile(stat_file, buffer); | ||
} | ||
|
||
bool WebMediaPlayerPerf::ParseProcStats(const std::string& stats_data, | ||
std::vector<std::string>* proc_stats) { | ||
// |stats_data| may be empty if the process disappeared somehow. | ||
// e.g. http://crbug.com/145811 | ||
if (stats_data.empty()) return false; | ||
|
||
// The stat file is formatted as: | ||
// pid (process name) data1 data2 .... dataN | ||
// Look for the closing paren by scanning backwards, to avoid being fooled by | ||
// processes with ')' in the name. | ||
size_t open_parens_idx = stats_data.find(" ("); | ||
size_t close_parens_idx = stats_data.rfind(") "); | ||
if (open_parens_idx == std::string::npos || | ||
close_parens_idx == std::string::npos || | ||
open_parens_idx > close_parens_idx) { | ||
DLOG(WARNING) << "Failed to find matched parens in '" << stats_data << "'"; | ||
NOTREACHED(); | ||
return false; | ||
} | ||
open_parens_idx++; | ||
|
||
proc_stats->clear(); | ||
// PID. | ||
proc_stats->push_back(stats_data.substr(0, open_parens_idx)); | ||
// Process name without parentheses. | ||
proc_stats->push_back(stats_data.substr( | ||
open_parens_idx + 1, close_parens_idx - (open_parens_idx + 1))); | ||
|
||
// Split the rest. | ||
std::vector<std::string> other_stats = | ||
SplitString(stats_data.substr(close_parens_idx + 2), " ", | ||
base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | ||
for (size_t i = 0; i < other_stats.size(); ++i) | ||
proc_stats->push_back(other_stats[i]); | ||
return true; | ||
} | ||
|
||
int64_t WebMediaPlayerPerf::GetProcStatsFieldAsInt64( | ||
const std::vector<std::string>& proc_stats, ProcStatsFields field_num) { | ||
DCHECK_GE(field_num, VM_PPID); | ||
CHECK_LT(static_cast<size_t>(field_num), proc_stats.size()); | ||
|
||
int64_t value; | ||
return base::StringToInt64(proc_stats[field_num], &value) ? value : 0; | ||
} | ||
|
||
// Get the total CPU of a single process. Return value is number of jiffies | ||
// on success or -1 on error. | ||
int64_t WebMediaPlayerPerf::GetProcessCPU(pid_t pid) { | ||
std::string buffer; | ||
std::vector<std::string> proc_stats; | ||
if (!ReadProcStats(pid, &buffer) || !ParseProcStats(buffer, &proc_stats)) { | ||
return -1; | ||
} | ||
|
||
int64_t total_cpu = GetProcStatsFieldAsInt64(proc_stats, VM_UTIME) + | ||
GetProcStatsFieldAsInt64(proc_stats, VM_STIME); | ||
return total_cpu; | ||
} | ||
|
||
base::TimeDelta WebMediaPlayerPerf::ClockTicksToTimeDelta(int64_t clock_ticks) { | ||
// This queries the /proc-specific scaling factor which is | ||
// conceptually the system hertz. To dump this value on another | ||
// system, try | ||
// od -t dL /proc/self/auxv | ||
// and look for the number after 17 in the output; mine is | ||
// 0000040 17 100 3 134512692 | ||
// which means the answer is 100. | ||
// It may be the case that this value is always 100. | ||
static const int64_t kHertz = sysconf(_SC_CLK_TCK); | ||
|
||
return base::Microseconds(base::Time::kMicrosecondsPerSecond * clock_ticks / | ||
kHertz); | ||
} | ||
|
||
base::TimeDelta WebMediaPlayerPerf::GetCumulativeCPUUsage() { | ||
return ClockTicksToTimeDelta(GetProcessCPU(process_pid_)); | ||
} | ||
|
||
double WebMediaPlayerPerf::GetPlatformIndependentCPUUsage() { | ||
base::TimeDelta cumulative_cpu = GetCumulativeCPUUsage(); | ||
base::TimeTicks time = base::TimeTicks::Now(); | ||
|
||
if (last_cumulative_cpu_.is_zero()) { | ||
// First call, just set the last values. | ||
last_cumulative_cpu_ = cumulative_cpu; | ||
last_cpu_time_ = time; | ||
return 0; | ||
} | ||
|
||
base::TimeDelta system_time_delta = cumulative_cpu - last_cumulative_cpu_; | ||
base::TimeDelta time_delta = time - last_cpu_time_; | ||
DCHECK(!time_delta.is_zero()); | ||
if (time_delta.is_zero()) return 0; | ||
|
||
last_cumulative_cpu_ = cumulative_cpu; | ||
last_cpu_time_ = time; | ||
|
||
return 100.0 * system_time_delta.InMicrosecondsF() / | ||
time_delta.InMicrosecondsF(); | ||
} | ||
|
||
WebMediaPlayerPerf::WebMediaPlayerPerf( | ||
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) | ||
: task_runner_(task_runner), process_pid_(getpid()) { | ||
LOG(ERROR) << "Brown WebMediaPlayerPerf() " << this << "/" << process_pid_; | ||
} | ||
|
||
WebMediaPlayerPerf::~WebMediaPlayerPerf() { | ||
LOG(ERROR) << "Brown ~WebMediaPlayerPerf() " << this << "/" << process_pid_; | ||
} | ||
|
||
} // namespace media | ||
} // namespace cobalt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// Copyright 2024 The Cobalt Authors. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#ifndef COBALT_MEDIA_PLAYER_WEB_MEDIA_PLAYER_PERF_H_ | ||
#define COBALT_MEDIA_PLAYER_WEB_MEDIA_PLAYER_PERF_H_ | ||
|
||
#include <unistd.h> | ||
|
||
#include <memory> | ||
#include <string> | ||
#include <vector> | ||
|
||
#include "base/files/file_path.h" | ||
#include "base/memory/ref_counted.h" | ||
#include "base/task_runner.h" | ||
#include "base/threading/thread.h" | ||
#include "base/time/time.h" | ||
#include "base/timer/timer.h" | ||
#include "starboard/types.h" | ||
|
||
namespace cobalt { | ||
namespace media { | ||
|
||
// Fields from /proc/<pid>/stat, 0-based. See man 5 proc. | ||
// If the ordering ever changes, carefully review functions that use these | ||
// values. | ||
enum ProcStatsFields { | ||
VM_COMM = 1, // Filename of executable, without parentheses. | ||
VM_STATE = 2, // Letter indicating the state of the process. | ||
VM_PPID = 3, // PID of the parent. | ||
VM_PGRP = 4, // Process group id. | ||
VM_MINFLT = 9, // Minor page fault count excluding children. | ||
VM_MAJFLT = 11, // Major page fault count excluding children. | ||
VM_UTIME = 13, // Time scheduled in user mode in clock ticks. | ||
VM_STIME = 14, // Time scheduled in kernel mode in clock ticks. | ||
VM_NUMTHREADS = 19, // Number of threads. | ||
VM_STARTTIME = 21, // The time the process started in clock ticks. | ||
VM_VSIZE = 22, // Virtual memory size in bytes. | ||
VM_RSS = 23, // Resident Set Size in pages. | ||
}; | ||
|
||
class WebMediaPlayerPerf | ||
: public base::RefCountedThreadSafe<WebMediaPlayerPerf> { | ||
public: | ||
// Constructs a measure pipeline that will execute on |task_runner|. | ||
WebMediaPlayerPerf( | ||
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); | ||
~WebMediaPlayerPerf(); | ||
|
||
void Start(); | ||
void StartTask(); | ||
void Stop(); | ||
void StopTask(); | ||
|
||
private: | ||
// Refer base/process/process_metrics.h for GetPlatformIndependentCPUUsage() | ||
void GetCpuUsage(); | ||
int64_t GetProcessCPU(pid_t pid); | ||
base::FilePath GetProcPidDir(pid_t pid); | ||
bool ReadProcFile(const base::FilePath& file, std::string* buffer); | ||
bool ReadProcStats(pid_t pid, std::string* buffer); | ||
bool ParseProcStats(const std::string& stats_data, | ||
std::vector<std::string>* proc_stats); | ||
int64_t GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats, | ||
ProcStatsFields field_num); | ||
base::TimeDelta ClockTicksToTimeDelta(int64_t clock_ticks); | ||
base::TimeDelta GetCumulativeCPUUsage(); | ||
double GetPlatformIndependentCPUUsage(); | ||
|
||
// Message loop used to execute pipeline tasks. It is thread-safe. | ||
scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | ||
base::RepeatingTimer timer_; | ||
|
||
pid_t process_pid_; | ||
char sys_pid_stat_[32] = {}; | ||
|
||
// Used to store the previous times and CPU usage counts so we can | ||
// compute the CPU usage between calls. | ||
base::TimeTicks last_cpu_time_; | ||
base::TimeDelta last_cumulative_cpu_; | ||
}; | ||
|
||
} // namespace media | ||
} // namespace cobalt | ||
|
||
#endif // COBALT_MEDIA_PLAYER_WEB_MEDIA_PLAYER_PERF_H_ |