blob: a017137760e3e232baecfd3a2e92f6533c7e04cc [file] [log] [blame]
Brian Silverman70f88512014-12-27 19:27:35 -08001// This is a utility program that prints out realtime priorities for processes
2// on a system. It is useful both because the standard tools don't format that
3// information very well and the roboRIO's busybox ones don't seem to do it at
4// all.
5//
6// The output format is the following comma-separated columns:
7// exe,name,cpumask,policy,nice,priority,tid,pid,ppid,sid,cpu
8
9#include <sched.h>
Brian Silverman70f88512014-12-27 19:27:35 -080010#include <sys/resource.h>
Austin Schuha0c41ba2020-09-10 22:59:14 -070011#include <sys/time.h>
Brian Silverman70f88512014-12-27 19:27:35 -080012#include <unistd.h>
13
Tyler Chatowbf0609c2021-07-31 16:13:27 -070014#include <cstdint>
15#include <cstdio>
16#include <cstdlib>
Brian Silverman70f88512014-12-27 19:27:35 -080017#include <string>
18
John Park33858a32018-09-28 23:05:48 -070019#include "aos/time/time.h"
Brian Silverman3eb60d22021-11-04 19:06:47 -070020#include "glog/logging.h"
Brian Silverman70f88512014-12-27 19:27:35 -080021
22namespace {
23
24const char *policy_string(uint32_t policy) {
25 switch (policy) {
26 case SCHED_OTHER:
27 return "OTHER";
28 case SCHED_BATCH:
29 return "BATCH";
30 case SCHED_IDLE:
31 return "IDLE";
32 case SCHED_FIFO:
33 return "FIFO";
34 case SCHED_RR:
35 return "RR";
36#ifdef SCHED_DEADLINE
37 case SCHED_DEADLINE:
38 return "DEADLINE";
39#endif
40 default:
41 return "???";
42 }
43}
44
45::std::string strip_string_prefix(size_t length, ::std::string str) {
46 str = str.substr(length);
47 while (str[0] == ' ' || str[0] == '\t') {
48 str = str.substr(1);
49 }
50 return str.substr(0, str.size() - 1);
51}
52
53int find_pid_max() {
54 int r;
55 FILE *pid_max_file = fopen("/proc/sys/kernel/pid_max", "r");
Brian Silverman3eb60d22021-11-04 19:06:47 -070056 PCHECK(pid_max_file != nullptr)
57 << ": Failed to open /proc/sys/kernel/pid_max";
58 CHECK_EQ(1, fscanf(pid_max_file, "%d", &r));
59 PCHECK(fclose(pid_max_file) == 0);
Brian Silverman70f88512014-12-27 19:27:35 -080060 return r;
61}
62
63cpu_set_t find_all_cpus() {
64 long nproc = sysconf(_SC_NPROCESSORS_CONF);
Brian Silverman3eb60d22021-11-04 19:06:47 -070065 PCHECK(nproc != -1);
Brian Silverman70f88512014-12-27 19:27:35 -080066 cpu_set_t r;
67 CPU_ZERO(&r);
68 for (long i = 0; i < nproc; ++i) {
69 CPU_SET(i, &r);
70 }
71 return r;
72}
73
74cpu_set_t find_cpu_mask(int process, bool *not_there) {
75 cpu_set_t r;
76 const int result = sched_getaffinity(process, sizeof(r), &r);
77 if (result == -1 && errno == ESRCH) {
78 *not_there = true;
79 return cpu_set_t();
80 }
Brian Silverman3eb60d22021-11-04 19:06:47 -070081 PCHECK(result == 0) << ": sched_getaffinity of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -080082 return r;
83}
84
85sched_param find_sched_param(int process, bool *not_there) {
86 sched_param r;
87 const int result = sched_getparam(process, &r);
88 if (result == -1 && errno == ESRCH) {
89 *not_there = true;
90 return sched_param();
91 }
Brian Silverman3eb60d22021-11-04 19:06:47 -070092 PCHECK(result == 0) << ": sched_getparam of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -080093 return r;
94}
95
96int find_scheduler(int process, bool *not_there) {
97 int scheduler = sched_getscheduler(process);
98 if (scheduler == -1 && errno == ESRCH) {
99 *not_there = true;
100 return 0;
101 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700102 PCHECK(scheduler != -1) << ": sched_getscheduler of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -0800103 return scheduler;
104}
105
106::std::string find_exe(int process, bool *not_there) {
107 ::std::string exe_filename = "/proc/" + ::std::to_string(process) + "/exe";
108 char exe_buffer[1024];
109 ssize_t exe_size =
110 readlink(exe_filename.c_str(), exe_buffer, sizeof(exe_buffer));
111 if (exe_size == -1 && errno == ENOENT) {
112 return "ENOENT";
113 } else {
114 if (exe_size == -1 && errno == ESRCH) {
115 *not_there = true;
116 return "";
117 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700118 PCHECK(exe_size != -1) << ": readlink " << exe_filename
119 << " into buffer of size " << sizeof(exe_buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800120 return ::std::string(exe_buffer, exe_size);
121 }
122}
123
124int find_nice_value(int process, bool *not_there) {
125 errno = 0;
126 int nice_value = getpriority(PRIO_PROCESS, process);
127 if (errno == ESRCH) {
128 *not_there = true;
129 return 0;
130 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700131 PCHECK(errno == 0) << "getpriority of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -0800132 return nice_value;
133}
134
135void read_stat(int process, int *ppid, int *sid, bool *not_there) {
136 ::std::string stat_filename = "/proc/" + ::std::to_string(process) + "/stat";
137 FILE *stat = fopen(stat_filename.c_str(), "r");
138 if (stat == nullptr && errno == ENOENT) {
139 *not_there = true;
140 return;
141 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700142 PCHECK(stat != nullptr) << ": Failed to open " << stat_filename;
Brian Silverman70f88512014-12-27 19:27:35 -0800143
144 char buffer[2048];
145 if (fgets(buffer, sizeof(buffer), stat) == nullptr) {
146 if (ferror(stat)) {
147 if (errno == ESRCH) {
148 *not_there = true;
149 return;
150 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700151 PLOG(FATAL) << "reading from " << stat_filename << " into buffer of size "
152 << sizeof(buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800153 }
154 }
155
156 int pid = 0;
157
158 int field = 0;
159 size_t field_start = 0;
160 int parens = 0;
161 for (size_t i = 0; i < sizeof(buffer); ++i) {
162 if (buffer[i] == '\0') break;
163 if (buffer[i] == '(') ++parens;
164 if (parens > 0) {
165 if (buffer[i] == ')') --parens;
166 } else if (buffer[i] == ' ') {
167 ::std::string field_string(buffer, field_start, i - field_start);
168 switch (field) {
169 case 0:
170 pid = ::std::stoi(field_string);
171 break;
172 case 3:
173 *ppid = ::std::stoi(field_string);
174 break;
175 case 4:
176 *sid = ::std::stoi(field_string);
177 break;
178 default:
179 break;
180 }
181 ++field;
182 field_start = i + 1;
183 }
184 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700185 PCHECK(fclose(stat) == 0);
Brian Silverman70f88512014-12-27 19:27:35 -0800186
187 if (field < 4) {
Brian Silverman3eb60d22021-11-04 19:06:47 -0700188 LOG(FATAL) << "couldn't get fields from /proc/" << process << "/stat";
Brian Silverman70f88512014-12-27 19:27:35 -0800189 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700190 CHECK_EQ(pid, process);
Brian Silverman70f88512014-12-27 19:27:35 -0800191}
192
193void read_status(int process, int ppid, int *pgrp, ::std::string *name,
194 bool *not_there) {
195 ::std::string status_filename =
196 "/proc/" + ::std::to_string(process) + "/status";
197 FILE *status = fopen(status_filename.c_str(), "r");
198 if (status == nullptr && errno == ENOENT) {
199 *not_there = true;
200 return;
201 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700202 PCHECK(status != nullptr) << ": Failed to open " << status_filename;
Brian Silverman70f88512014-12-27 19:27:35 -0800203
204 int pid = 0, status_ppid = 0;
205 while (true) {
206 char buffer[1024];
207 if (fgets(buffer, sizeof(buffer), status) == nullptr) {
208 if (ferror(status)) {
Brian Silverman3eb60d22021-11-04 19:06:47 -0700209 PLOG(FATAL) << "reading from " << status_filename
210 << " into buffer of size " << sizeof(buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800211 } else {
212 break;
213 }
214 }
215 ::std::string line(buffer);
216 if (line.substr(0, 5) == "Name:") {
217 *name = strip_string_prefix(5, line);
218 } else if (line.substr(0, 4) == "Pid:") {
219 pid = ::std::stoi(strip_string_prefix(4, line));
220 } else if (line.substr(0, 5) == "PPid:") {
221 status_ppid = ::std::stoi(strip_string_prefix(5, line));
222 } else if (line.substr(0, 5) == "Tgid:") {
223 *pgrp = ::std::stoi(strip_string_prefix(5, line));
224 }
225 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700226 PCHECK(fclose(status) == 0);
227 CHECK_EQ(pid, process);
228 CHECK_EQ(status_ppid, ppid);
Brian Silverman70f88512014-12-27 19:27:35 -0800229}
230
231} // namespace
232
233int main() {
Brian Silverman70f88512014-12-27 19:27:35 -0800234 const int pid_max = find_pid_max();
235 const cpu_set_t all_cpus = find_all_cpus();
236
237 for (int i = 0; i < pid_max; ++i) {
238 bool not_there = false;
239
240 const cpu_set_t cpu_mask = find_cpu_mask(i, &not_there);
241 const sched_param param = find_sched_param(i, &not_there);
242 const int scheduler = find_scheduler(i, &not_there);
243 const ::std::string exe = find_exe(i, &not_there);
244 const int nice_value = find_nice_value(i, &not_there);
245
246 int ppid = 0, sid = 0;
247 read_stat(i, &ppid, &sid, &not_there);
248
249 int pgrp = 0;
250 ::std::string name;
251 read_status(i, ppid, &pgrp, &name, &not_there);
252
253 if (not_there) continue;
254
255 const char *cpu_mask_string =
256 CPU_EQUAL(&cpu_mask, &all_cpus) ? "all" : "???";
257
258 printf("%s,%s,%s,%s,%d,%d,%d,%d,%d,%d\n", exe.c_str(), name.c_str(),
259 cpu_mask_string, policy_string(scheduler), nice_value,
260 param.sched_priority, i, pgrp, ppid, sid);
261 }
262}