| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * 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 <fcntl.h> |
| #include <stdint.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <time.h> |
| #include <unistd.h> |
| |
| #include <cinttypes> |
| #include <string> |
| |
| #include "perfetto/base/logging.h" |
| #include "perfetto/base/time.h" |
| #include "perfetto/ext/base/file_utils.h" |
| #include "perfetto/ext/base/getopt.h" |
| #include "perfetto/ext/base/scoped_file.h" |
| |
| // Periodically prints an un-normalized cpu usage ratio (full use of a single |
| // core = 1.0) of a target process. Based on /proc/pid/stat utime (userspace) & |
| // stime (kernel space). |
| |
| namespace perfetto { |
| namespace { |
| |
| uint64_t TimespecToMs(struct timespec ts) { |
| PERFETTO_CHECK(ts.tv_sec >= 0 && ts.tv_nsec >= 0); |
| return static_cast<uint64_t>(ts.tv_sec) * 1000 + |
| static_cast<uint64_t>(ts.tv_nsec) / 1000 / 1000; |
| } |
| |
| uint64_t ReadWallTimeMs(clockid_t clk) { |
| struct timespec ts = {}; |
| PERFETTO_CHECK(clock_gettime(clk, &ts) == 0); |
| return TimespecToMs(ts); |
| } |
| |
| void ReadUtimeStime(const base::ScopedFile& stat_fd, |
| unsigned long* utime_out, |
| unsigned long* stime_out) { |
| char buf[1024] = {}; |
| lseek(stat_fd.get(), 0, SEEK_SET); |
| PERFETTO_CHECK(read(stat_fd.get(), buf, sizeof(buf)) > 0); |
| buf[sizeof(buf) - 1] = '\0'; |
| |
| PERFETTO_CHECK( |
| sscanf(buf, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu %lu", |
| utime_out, stime_out) == 2); |
| } |
| |
| int CpuUtilizationMain(int argc, char** argv) { |
| unsigned sleep_duration_us = 10 * 1000 * 1000; // 10s |
| int sleep_intervals = 6; |
| int target_pid = -1; |
| |
| static option long_options[] = { |
| {"pid", required_argument, nullptr, 'p'}, |
| {"sleep-duration-us", required_argument, nullptr, 't'}, |
| {"sleep-intervals", required_argument, nullptr, 'n'}, |
| {nullptr, 0, nullptr, 0}}; |
| int c; |
| while ((c = getopt_long(argc, argv, "", long_options, nullptr)) != -1) { |
| switch (c) { |
| case 'p': |
| target_pid = atoi(optarg); |
| break; |
| case 't': |
| sleep_duration_us = static_cast<unsigned>(atoi(optarg)); |
| break; |
| case 'n': |
| sleep_intervals = atoi(optarg); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| if (target_pid < 1) { |
| PERFETTO_ELOG( |
| "Usage: %s --pid=target_pid [--sleep-duration-us=N] " |
| "[--sleep-intervals=N]", |
| argv[0]); |
| return 1; |
| } |
| |
| // Resolution of utime/stime from procfs, at least 10 ms. |
| long ticks = sysconf(_SC_CLK_TCK); |
| PERFETTO_CHECK(ticks >= 100); |
| unsigned long ticks_per_s = static_cast<unsigned long>(ticks); |
| |
| // Resolution of wallclock time, at least 1 ms. Should be O(ns) in practice. |
| auto clk = CLOCK_MONOTONIC_RAW; |
| struct timespec ts = {}; |
| PERFETTO_CHECK(clock_getres(clk, &ts) == 0); |
| PERFETTO_CHECK(ts.tv_sec == 0 && ts.tv_nsec <= 1 * 1000 * 1000); |
| |
| PERFETTO_LOG("--- setup: ---"); |
| PERFETTO_LOG("target pid: %d", target_pid); |
| PERFETTO_LOG("intervals: %d x %uus", sleep_intervals, sleep_duration_us); |
| PERFETTO_LOG("utime/stime ticks per sec: %ld", ticks_per_s); |
| PERFETTO_LOG("wall clock resolution (ns): %ld", ts.tv_nsec); |
| PERFETTO_LOG("--- timings: ---"); |
| |
| base::ScopedFile fd = base::OpenFile( |
| std::string("/proc/") + std::to_string(target_pid) + std::string("/stat"), |
| O_RDONLY); |
| PERFETTO_CHECK(fd); |
| |
| // Read the base times. |
| unsigned long first_utime = 0; |
| unsigned long first_stime = 0; |
| ReadUtimeStime(fd, &first_utime, &first_stime); |
| uint64_t first_walltime_ms = ReadWallTimeMs(clk); |
| |
| uint64_t last_walltime_ms = first_walltime_ms; |
| unsigned long last_utime = first_utime; |
| unsigned long last_stime = first_stime; |
| |
| // Report the utilization for each fixed duration chunk. |
| for (int i = 0; i < sleep_intervals; i++) { |
| base::SleepMicroseconds(sleep_duration_us); |
| |
| unsigned long utime = 0; |
| unsigned long stime = 0; |
| ReadUtimeStime(fd, &utime, &stime); |
| uint64_t walltime_ms = ReadWallTimeMs(clk); |
| |
| uint64_t wall_diff_ms = walltime_ms - last_walltime_ms; |
| PERFETTO_LOG("wall_ms : [%" PRIu64 "] - [%" PRIu64 "] = [%" PRIu64 "]", |
| walltime_ms, last_walltime_ms, wall_diff_ms); |
| |
| unsigned long utime_diff = utime - last_utime; |
| unsigned long stime_diff = stime - last_stime; |
| PERFETTO_LOG("utime_ticks: [%lu] - [%lu] = [%lu]", utime, last_utime, |
| utime_diff); |
| PERFETTO_LOG("stime_ticks: [%lu] - [%lu] = [%lu]", stime, last_stime, |
| stime_diff); |
| |
| // Calculate the utilization, resolution of inputs will be no worse than |
| // 10ms due to the above assert. At the default 10s wall time, we therefore |
| // get a resolution of at least 0.1%. |
| double utime_diff_ms = static_cast<double>(utime_diff * 1000 / ticks_per_s); |
| double stime_diff_ms = static_cast<double>(stime_diff * 1000 / ticks_per_s); |
| |
| double utime_ratio = utime_diff_ms / static_cast<double>(wall_diff_ms); |
| double stime_ratio = stime_diff_ms / static_cast<double>(wall_diff_ms); |
| |
| PERFETTO_LOG("utime ratio : %f", utime_ratio); |
| PERFETTO_LOG("stime ratio : %f", stime_ratio); |
| PERFETTO_LOG("combined ratio: %f\n", utime_ratio + stime_ratio); |
| |
| last_walltime_ms = walltime_ms; |
| last_utime = utime; |
| last_stime = stime; |
| } |
| |
| PERFETTO_LOG("--- timings over the whole period: ---"); |
| unsigned long utime_diff = last_utime - first_utime; |
| unsigned long stime_diff = last_stime - first_stime; |
| uint64_t wall_diff_ms = last_walltime_ms - first_walltime_ms; |
| double utime_diff_ms = static_cast<double>(utime_diff * 1000 / ticks_per_s); |
| double stime_diff_ms = static_cast<double>(stime_diff * 1000 / ticks_per_s); |
| double utime_ratio = utime_diff_ms / static_cast<double>(wall_diff_ms); |
| double stime_ratio = stime_diff_ms / static_cast<double>(wall_diff_ms); |
| PERFETTO_LOG("utime ratio : %f", utime_ratio); |
| PERFETTO_LOG("stime ratio : %f", stime_ratio); |
| PERFETTO_LOG("combined ratio: %f\n", utime_ratio + stime_ratio); |
| |
| return 0; |
| } |
| |
| } // namespace |
| } // namespace perfetto |
| |
| int main(int argc, char** argv) { |
| return perfetto::CpuUtilizationMain(argc, argv); |
| } |