blob: 80208574657e9e7196be843e248b6781e2831354 [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:
milind-uf963ee92021-11-06 16:10:07 -07007// exe,name,cpumask,policy,nice,priority,tid,pid,ppid,sid
8// The threads in the output are sorted by realtime priority.
Brian Silverman70f88512014-12-27 19:27:35 -08009
10#include <sched.h>
Brian Silverman70f88512014-12-27 19:27:35 -080011#include <sys/resource.h>
Austin Schuha0c41ba2020-09-10 22:59:14 -070012#include <sys/time.h>
Brian Silverman70f88512014-12-27 19:27:35 -080013#include <unistd.h>
14
Tyler Chatowbf0609c2021-07-31 16:13:27 -070015#include <cstdint>
16#include <cstdio>
17#include <cstdlib>
milind-uf963ee92021-11-06 16:10:07 -070018#include <set>
Brian Silverman70f88512014-12-27 19:27:35 -080019#include <string>
20
John Park33858a32018-09-28 23:05:48 -070021#include "aos/time/time.h"
Brian Silverman3eb60d22021-11-04 19:06:47 -070022#include "glog/logging.h"
Brian Silverman70f88512014-12-27 19:27:35 -080023
24namespace {
25
26const char *policy_string(uint32_t policy) {
27 switch (policy) {
28 case SCHED_OTHER:
29 return "OTHER";
30 case SCHED_BATCH:
31 return "BATCH";
32 case SCHED_IDLE:
33 return "IDLE";
34 case SCHED_FIFO:
35 return "FIFO";
36 case SCHED_RR:
37 return "RR";
38#ifdef SCHED_DEADLINE
39 case SCHED_DEADLINE:
40 return "DEADLINE";
41#endif
42 default:
43 return "???";
44 }
45}
46
47::std::string strip_string_prefix(size_t length, ::std::string str) {
48 str = str.substr(length);
49 while (str[0] == ' ' || str[0] == '\t') {
50 str = str.substr(1);
51 }
52 return str.substr(0, str.size() - 1);
53}
54
55int find_pid_max() {
56 int r;
57 FILE *pid_max_file = fopen("/proc/sys/kernel/pid_max", "r");
Brian Silverman3eb60d22021-11-04 19:06:47 -070058 PCHECK(pid_max_file != nullptr)
59 << ": Failed to open /proc/sys/kernel/pid_max";
60 CHECK_EQ(1, fscanf(pid_max_file, "%d", &r));
61 PCHECK(fclose(pid_max_file) == 0);
Brian Silverman70f88512014-12-27 19:27:35 -080062 return r;
63}
64
65cpu_set_t find_all_cpus() {
66 long nproc = sysconf(_SC_NPROCESSORS_CONF);
Brian Silverman3eb60d22021-11-04 19:06:47 -070067 PCHECK(nproc != -1);
Brian Silverman70f88512014-12-27 19:27:35 -080068 cpu_set_t r;
69 CPU_ZERO(&r);
70 for (long i = 0; i < nproc; ++i) {
71 CPU_SET(i, &r);
72 }
73 return r;
74}
75
76cpu_set_t find_cpu_mask(int process, bool *not_there) {
77 cpu_set_t r;
78 const int result = sched_getaffinity(process, sizeof(r), &r);
79 if (result == -1 && errno == ESRCH) {
80 *not_there = true;
81 return cpu_set_t();
82 }
Brian Silverman3eb60d22021-11-04 19:06:47 -070083 PCHECK(result == 0) << ": sched_getaffinity of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -080084 return r;
85}
86
87sched_param find_sched_param(int process, bool *not_there) {
88 sched_param r;
89 const int result = sched_getparam(process, &r);
90 if (result == -1 && errno == ESRCH) {
91 *not_there = true;
92 return sched_param();
93 }
Brian Silverman3eb60d22021-11-04 19:06:47 -070094 PCHECK(result == 0) << ": sched_getparam of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -080095 return r;
96}
97
98int find_scheduler(int process, bool *not_there) {
99 int scheduler = sched_getscheduler(process);
100 if (scheduler == -1 && errno == ESRCH) {
101 *not_there = true;
102 return 0;
103 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700104 PCHECK(scheduler != -1) << ": sched_getscheduler of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -0800105 return scheduler;
106}
107
108::std::string find_exe(int process, bool *not_there) {
109 ::std::string exe_filename = "/proc/" + ::std::to_string(process) + "/exe";
110 char exe_buffer[1024];
111 ssize_t exe_size =
112 readlink(exe_filename.c_str(), exe_buffer, sizeof(exe_buffer));
113 if (exe_size == -1 && errno == ENOENT) {
114 return "ENOENT";
115 } else {
116 if (exe_size == -1 && errno == ESRCH) {
117 *not_there = true;
118 return "";
119 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700120 PCHECK(exe_size != -1) << ": readlink " << exe_filename
121 << " into buffer of size " << sizeof(exe_buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800122 return ::std::string(exe_buffer, exe_size);
123 }
124}
125
126int find_nice_value(int process, bool *not_there) {
127 errno = 0;
128 int nice_value = getpriority(PRIO_PROCESS, process);
129 if (errno == ESRCH) {
130 *not_there = true;
131 return 0;
132 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700133 PCHECK(errno == 0) << "getpriority of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -0800134 return nice_value;
135}
136
137void read_stat(int process, int *ppid, int *sid, bool *not_there) {
138 ::std::string stat_filename = "/proc/" + ::std::to_string(process) + "/stat";
139 FILE *stat = fopen(stat_filename.c_str(), "r");
140 if (stat == nullptr && errno == ENOENT) {
141 *not_there = true;
142 return;
143 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700144 PCHECK(stat != nullptr) << ": Failed to open " << stat_filename;
Brian Silverman70f88512014-12-27 19:27:35 -0800145
146 char buffer[2048];
147 if (fgets(buffer, sizeof(buffer), stat) == nullptr) {
148 if (ferror(stat)) {
149 if (errno == ESRCH) {
150 *not_there = true;
151 return;
152 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700153 PLOG(FATAL) << "reading from " << stat_filename << " into buffer of size "
154 << sizeof(buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800155 }
156 }
157
158 int pid = 0;
159
160 int field = 0;
161 size_t field_start = 0;
162 int parens = 0;
163 for (size_t i = 0; i < sizeof(buffer); ++i) {
164 if (buffer[i] == '\0') break;
165 if (buffer[i] == '(') ++parens;
166 if (parens > 0) {
167 if (buffer[i] == ')') --parens;
168 } else if (buffer[i] == ' ') {
169 ::std::string field_string(buffer, field_start, i - field_start);
170 switch (field) {
171 case 0:
172 pid = ::std::stoi(field_string);
173 break;
174 case 3:
175 *ppid = ::std::stoi(field_string);
176 break;
177 case 4:
178 *sid = ::std::stoi(field_string);
179 break;
180 default:
181 break;
182 }
183 ++field;
184 field_start = i + 1;
185 }
186 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700187 PCHECK(fclose(stat) == 0);
Brian Silverman70f88512014-12-27 19:27:35 -0800188
189 if (field < 4) {
Brian Silverman3eb60d22021-11-04 19:06:47 -0700190 LOG(FATAL) << "couldn't get fields from /proc/" << process << "/stat";
Brian Silverman70f88512014-12-27 19:27:35 -0800191 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700192 CHECK_EQ(pid, process);
Brian Silverman70f88512014-12-27 19:27:35 -0800193}
194
195void read_status(int process, int ppid, int *pgrp, ::std::string *name,
196 bool *not_there) {
197 ::std::string status_filename =
198 "/proc/" + ::std::to_string(process) + "/status";
199 FILE *status = fopen(status_filename.c_str(), "r");
200 if (status == nullptr && errno == ENOENT) {
201 *not_there = true;
202 return;
203 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700204 PCHECK(status != nullptr) << ": Failed to open " << status_filename;
Brian Silverman70f88512014-12-27 19:27:35 -0800205
206 int pid = 0, status_ppid = 0;
207 while (true) {
208 char buffer[1024];
209 if (fgets(buffer, sizeof(buffer), status) == nullptr) {
210 if (ferror(status)) {
Brian Silverman3eb60d22021-11-04 19:06:47 -0700211 PLOG(FATAL) << "reading from " << status_filename
212 << " into buffer of size " << sizeof(buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800213 } else {
214 break;
215 }
216 }
217 ::std::string line(buffer);
218 if (line.substr(0, 5) == "Name:") {
219 *name = strip_string_prefix(5, line);
220 } else if (line.substr(0, 4) == "Pid:") {
221 pid = ::std::stoi(strip_string_prefix(4, line));
222 } else if (line.substr(0, 5) == "PPid:") {
223 status_ppid = ::std::stoi(strip_string_prefix(5, line));
224 } else if (line.substr(0, 5) == "Tgid:") {
225 *pgrp = ::std::stoi(strip_string_prefix(5, line));
226 }
227 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700228 PCHECK(fclose(status) == 0);
229 CHECK_EQ(pid, process);
230 CHECK_EQ(status_ppid, ppid);
Brian Silverman70f88512014-12-27 19:27:35 -0800231}
232
milind-uf963ee92021-11-06 16:10:07 -0700233struct Thread {
234 uint32_t policy;
235 std::string exe, name, cpu_mask;
236 int nice_value, sched_priority, tid, pid, ppid, sid;
237
238 void Print() const {
239 printf("%s,%s,%s,%s,%d,%d,%d,%d,%d,%d\n", exe.c_str(), name.c_str(),
240 cpu_mask.c_str(), policy_string(policy), nice_value, sched_priority,
241 tid, pid, ppid, sid);
242 }
243
244 // Make threads with SCHED_FIFO or SCHED_RR show up before SCHED_OTHER, and
245 // make higher priority threads show up first. Higher priority threads are
246 // less than lower priority so that they appear first.
247 bool operator<(const Thread &t) const {
248 return (((((policy == SCHED_FIFO) || (policy == SCHED_RR))) &&
249 (t.policy == SCHED_OTHER)) ||
250 (sched_priority > t.sched_priority));
251 }
252};
253
Brian Silverman70f88512014-12-27 19:27:35 -0800254} // namespace
255
256int main() {
Brian Silverman70f88512014-12-27 19:27:35 -0800257 const int pid_max = find_pid_max();
258 const cpu_set_t all_cpus = find_all_cpus();
259
milind-uf963ee92021-11-06 16:10:07 -0700260 std::multiset<Thread> threads;
261
Brian Silverman70f88512014-12-27 19:27:35 -0800262 for (int i = 0; i < pid_max; ++i) {
263 bool not_there = false;
264
265 const cpu_set_t cpu_mask = find_cpu_mask(i, &not_there);
266 const sched_param param = find_sched_param(i, &not_there);
267 const int scheduler = find_scheduler(i, &not_there);
268 const ::std::string exe = find_exe(i, &not_there);
269 const int nice_value = find_nice_value(i, &not_there);
270
271 int ppid = 0, sid = 0;
272 read_stat(i, &ppid, &sid, &not_there);
273
274 int pgrp = 0;
275 ::std::string name;
276 read_status(i, ppid, &pgrp, &name, &not_there);
277
278 if (not_there) continue;
279
280 const char *cpu_mask_string =
281 CPU_EQUAL(&cpu_mask, &all_cpus) ? "all" : "???";
282
milind-uf963ee92021-11-06 16:10:07 -0700283 threads.emplace(Thread{.policy = static_cast<uint32_t>(scheduler),
284 .exe = exe,
285 .name = name,
286 .cpu_mask = cpu_mask_string,
287 .nice_value = nice_value,
288 .sched_priority = param.sched_priority,
289 .tid = i,
290 .pid = pgrp,
291 .ppid = ppid,
292 .sid = sid});
293 }
294
295 printf("exe,name,cpumask,policy,nice,priority,tid,pid,ppid,sid\n");
296 for (const auto &t : threads) {
297 t.Print();
Brian Silverman70f88512014-12-27 19:27:35 -0800298 }
299}