| // This is a utility program that prints out realtime priorities for processes |
| // on a system. It is useful both because the standard tools don't format that |
| // information very well and the roboRIO's busybox ones don't seem to do it at |
| // all. |
| // |
| // The output format is the following comma-separated columns: |
| // exe,name,cpumask,policy,nice,priority,tid,pid,ppid,sid,cpu |
| |
| #include <sched.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <stdint.h> |
| #include <sys/time.h> |
| #include <sys/resource.h> |
| #include <unistd.h> |
| |
| #include <string> |
| |
| #include "aos/logging/logging.h" |
| #include "aos/logging/implementations.h" |
| #include "aos/time/time.h" |
| |
| namespace { |
| |
| const char *policy_string(uint32_t policy) { |
| switch (policy) { |
| case SCHED_OTHER: |
| return "OTHER"; |
| case SCHED_BATCH: |
| return "BATCH"; |
| case SCHED_IDLE: |
| return "IDLE"; |
| case SCHED_FIFO: |
| return "FIFO"; |
| case SCHED_RR: |
| return "RR"; |
| #ifdef SCHED_DEADLINE |
| case SCHED_DEADLINE: |
| return "DEADLINE"; |
| #endif |
| default: |
| return "???"; |
| } |
| } |
| |
| ::std::string strip_string_prefix(size_t length, ::std::string str) { |
| str = str.substr(length); |
| while (str[0] == ' ' || str[0] == '\t') { |
| str = str.substr(1); |
| } |
| return str.substr(0, str.size() - 1); |
| } |
| |
| int find_pid_max() { |
| int r; |
| FILE *pid_max_file = fopen("/proc/sys/kernel/pid_max", "r"); |
| if (pid_max_file == nullptr) { |
| AOS_PLOG(FATAL, "fopen(\"/proc/sys/kernel/pid_max\")"); |
| } |
| AOS_CHECK_EQ(1, fscanf(pid_max_file, "%d", &r)); |
| AOS_PCHECK(fclose(pid_max_file)); |
| return r; |
| } |
| |
| cpu_set_t find_all_cpus() { |
| long nproc = sysconf(_SC_NPROCESSORS_CONF); |
| if (nproc == -1) { |
| AOS_PLOG(FATAL, "sysconf(_SC_NPROCESSORS_CONF)"); |
| } |
| cpu_set_t r; |
| CPU_ZERO(&r); |
| for (long i = 0; i < nproc; ++i) { |
| CPU_SET(i, &r); |
| } |
| return r; |
| } |
| |
| cpu_set_t find_cpu_mask(int process, bool *not_there) { |
| cpu_set_t r; |
| const int result = sched_getaffinity(process, sizeof(r), &r); |
| if (result == -1 && errno == ESRCH) { |
| *not_there = true; |
| return cpu_set_t(); |
| } |
| if (result != 0) { |
| AOS_PLOG(FATAL, "sched_getaffinity(%d, %zu, %p)", process, sizeof(r), &r); |
| } |
| return r; |
| } |
| |
| sched_param find_sched_param(int process, bool *not_there) { |
| sched_param r; |
| const int result = sched_getparam(process, &r); |
| if (result == -1 && errno == ESRCH) { |
| *not_there = true; |
| return sched_param(); |
| } |
| if (result != 0) { |
| AOS_PLOG(FATAL, "sched_getparam(%d)", process); |
| } |
| return r; |
| } |
| |
| int find_scheduler(int process, bool *not_there) { |
| int scheduler = sched_getscheduler(process); |
| if (scheduler == -1 && errno == ESRCH) { |
| *not_there = true; |
| return 0; |
| } |
| if (scheduler == -1) { |
| AOS_PLOG(FATAL, "sched_getscheduler(%d)", process); |
| } |
| return scheduler; |
| } |
| |
| ::std::string find_exe(int process, bool *not_there) { |
| ::std::string exe_filename = "/proc/" + ::std::to_string(process) + "/exe"; |
| char exe_buffer[1024]; |
| ssize_t exe_size = |
| readlink(exe_filename.c_str(), exe_buffer, sizeof(exe_buffer)); |
| if (exe_size == -1 && errno == ENOENT) { |
| return "ENOENT"; |
| } else { |
| if (exe_size == -1 && errno == ESRCH) { |
| *not_there = true; |
| return ""; |
| } |
| if (exe_size == -1) { |
| AOS_PLOG(FATAL, "readlink(%s, %p, %zu)", exe_filename.c_str(), exe_buffer, |
| sizeof(exe_buffer)); |
| } |
| return ::std::string(exe_buffer, exe_size); |
| } |
| } |
| |
| int find_nice_value(int process, bool *not_there) { |
| errno = 0; |
| int nice_value = getpriority(PRIO_PROCESS, process); |
| if (errno == ESRCH) { |
| *not_there = true; |
| return 0; |
| } |
| if (errno != 0) { |
| AOS_PLOG(FATAL, "getpriority(PRIO_PROCESS, %d)", process); |
| } |
| return nice_value; |
| } |
| |
| void read_stat(int process, int *ppid, int *sid, bool *not_there) { |
| ::std::string stat_filename = "/proc/" + ::std::to_string(process) + "/stat"; |
| FILE *stat = fopen(stat_filename.c_str(), "r"); |
| if (stat == nullptr && errno == ENOENT) { |
| *not_there = true; |
| return; |
| } |
| if (stat == nullptr) { |
| AOS_PLOG(FATAL, "fopen(%s, \"r\")", stat_filename.c_str()); |
| } |
| |
| char buffer[2048]; |
| if (fgets(buffer, sizeof(buffer), stat) == nullptr) { |
| if (ferror(stat)) { |
| if (errno == ESRCH) { |
| *not_there = true; |
| return; |
| } |
| AOS_PLOG(FATAL, "fgets(%p, %zu, %p)", buffer, sizeof(buffer), stat); |
| } |
| } |
| |
| int pid = 0; |
| |
| int field = 0; |
| size_t field_start = 0; |
| int parens = 0; |
| for (size_t i = 0; i < sizeof(buffer); ++i) { |
| if (buffer[i] == '\0') break; |
| if (buffer[i] == '(') ++parens; |
| if (parens > 0) { |
| if (buffer[i] == ')') --parens; |
| } else if (buffer[i] == ' ') { |
| ::std::string field_string(buffer, field_start, i - field_start); |
| switch (field) { |
| case 0: |
| pid = ::std::stoi(field_string); |
| break; |
| case 3: |
| *ppid = ::std::stoi(field_string); |
| break; |
| case 4: |
| *sid = ::std::stoi(field_string); |
| break; |
| default: |
| break; |
| } |
| ++field; |
| field_start = i + 1; |
| } |
| } |
| AOS_PCHECK(fclose(stat)); |
| |
| if (field < 4) { |
| AOS_LOG(FATAL, "couldn't get fields from /proc/%d/stat\n", process); |
| } |
| AOS_CHECK_EQ(pid, process); |
| } |
| |
| void read_status(int process, int ppid, int *pgrp, ::std::string *name, |
| bool *not_there) { |
| ::std::string status_filename = |
| "/proc/" + ::std::to_string(process) + "/status"; |
| FILE *status = fopen(status_filename.c_str(), "r"); |
| if (status == nullptr && errno == ENOENT) { |
| *not_there = true; |
| return; |
| } |
| if (status == nullptr) { |
| AOS_PLOG(FATAL, "fopen(%s, \"r\")", status_filename.c_str()); |
| } |
| |
| int pid = 0, status_ppid = 0; |
| while (true) { |
| char buffer[1024]; |
| if (fgets(buffer, sizeof(buffer), status) == nullptr) { |
| if (ferror(status)) { |
| AOS_PLOG(FATAL, "fgets(%p, %zu, %p)", buffer, sizeof(buffer), status); |
| } else { |
| break; |
| } |
| } |
| ::std::string line(buffer); |
| if (line.substr(0, 5) == "Name:") { |
| *name = strip_string_prefix(5, line); |
| } else if (line.substr(0, 4) == "Pid:") { |
| pid = ::std::stoi(strip_string_prefix(4, line)); |
| } else if (line.substr(0, 5) == "PPid:") { |
| status_ppid = ::std::stoi(strip_string_prefix(5, line)); |
| } else if (line.substr(0, 5) == "Tgid:") { |
| *pgrp = ::std::stoi(strip_string_prefix(5, line)); |
| } |
| } |
| AOS_PCHECK(fclose(status)); |
| AOS_CHECK_EQ(pid, process); |
| AOS_CHECK_EQ(status_ppid, ppid); |
| } |
| |
| } // namespace |
| |
| int main() { |
| ::aos::logging::Init(); |
| ::aos::logging::SetImplementation( |
| new ::aos::logging::StreamLogImplementation(stdout)); |
| |
| const int pid_max = find_pid_max(); |
| const cpu_set_t all_cpus = find_all_cpus(); |
| |
| for (int i = 0; i < pid_max; ++i) { |
| bool not_there = false; |
| |
| const cpu_set_t cpu_mask = find_cpu_mask(i, ¬_there); |
| const sched_param param = find_sched_param(i, ¬_there); |
| const int scheduler = find_scheduler(i, ¬_there); |
| const ::std::string exe = find_exe(i, ¬_there); |
| const int nice_value = find_nice_value(i, ¬_there); |
| |
| int ppid = 0, sid = 0; |
| read_stat(i, &ppid, &sid, ¬_there); |
| |
| int pgrp = 0; |
| ::std::string name; |
| read_status(i, ppid, &pgrp, &name, ¬_there); |
| |
| if (not_there) continue; |
| |
| const char *cpu_mask_string = |
| CPU_EQUAL(&cpu_mask, &all_cpus) ? "all" : "???"; |
| |
| printf("%s,%s,%s,%s,%d,%d,%d,%d,%d,%d\n", exe.c_str(), name.c_str(), |
| cpu_mask_string, policy_string(scheduler), nice_value, |
| param.sched_priority, i, pgrp, ppid, sid); |
| } |
| } |