blob: 5d9a0b49646f42b0c9d8f8beb75499e8b4eda2a1 [file] [log] [blame]
// 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, &not_there);
const sched_param param = find_sched_param(i, &not_there);
const int scheduler = find_scheduler(i, &not_there);
const ::std::string exe = find_exe(i, &not_there);
const int nice_value = find_nice_value(i, &not_there);
int ppid = 0, sid = 0;
read_stat(i, &ppid, &sid, &not_there);
int pgrp = 0;
::std::string name;
read_status(i, ppid, &pgrp, &name, &not_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);
}
}