add code for examining realtime priorities etc
Change-Id: Ia0b815337a8c315f3ec83e49139ec0ee6ff3b00f
diff --git a/aos/linux_code/dump_rtprio.cc b/aos/linux_code/dump_rtprio.cc
new file mode 100644
index 0000000..cae69f1
--- /dev/null
+++ b/aos/linux_code/dump_rtprio.cc
@@ -0,0 +1,282 @@
+// 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/common/logging/logging.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/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) {
+ PLOG(FATAL, "fopen(\"/proc/sys/kernel/pid_max\")");
+ }
+ CHECK_EQ(1, fscanf(pid_max_file, "%d", &r));
+ PCHECK(fclose(pid_max_file));
+ return r;
+}
+
+cpu_set_t find_all_cpus() {
+ long nproc = sysconf(_SC_NPROCESSORS_CONF);
+ if (nproc == -1) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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;
+ }
+ 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;
+ }
+ }
+ PCHECK(fclose(stat));
+
+ if (field < 4) {
+ LOG(FATAL, "couldn't get fields from /proc/%d/stat\n", process);
+ }
+ 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) {
+ 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)) {
+ 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));
+ }
+ }
+ PCHECK(fclose(status));
+ CHECK_EQ(pid, process);
+ CHECK_EQ(status_ppid, ppid);
+}
+
+} // namespace
+
+int main() {
+ ::aos::logging::Init();
+ ::aos::logging::AddImplementation(
+ 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);
+ }
+}