blob: 5d9a0b49646f42b0c9d8f8beb75499e8b4eda2a1 [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>
10#include <stdlib.h>
11#include <stdio.h>
12#include <stdint.h>
13#include <sys/time.h>
14#include <sys/resource.h>
15#include <unistd.h>
16
17#include <string>
18
John Park33858a32018-09-28 23:05:48 -070019#include "aos/logging/logging.h"
20#include "aos/logging/implementations.h"
21#include "aos/time/time.h"
Brian Silverman70f88512014-12-27 19:27:35 -080022
23namespace {
24
25const char *policy_string(uint32_t policy) {
26 switch (policy) {
27 case SCHED_OTHER:
28 return "OTHER";
29 case SCHED_BATCH:
30 return "BATCH";
31 case SCHED_IDLE:
32 return "IDLE";
33 case SCHED_FIFO:
34 return "FIFO";
35 case SCHED_RR:
36 return "RR";
37#ifdef SCHED_DEADLINE
38 case SCHED_DEADLINE:
39 return "DEADLINE";
40#endif
41 default:
42 return "???";
43 }
44}
45
46::std::string strip_string_prefix(size_t length, ::std::string str) {
47 str = str.substr(length);
48 while (str[0] == ' ' || str[0] == '\t') {
49 str = str.substr(1);
50 }
51 return str.substr(0, str.size() - 1);
52}
53
54int find_pid_max() {
55 int r;
56 FILE *pid_max_file = fopen("/proc/sys/kernel/pid_max", "r");
57 if (pid_max_file == nullptr) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070058 AOS_PLOG(FATAL, "fopen(\"/proc/sys/kernel/pid_max\")");
Brian Silverman70f88512014-12-27 19:27:35 -080059 }
Austin Schuhf257f3c2019-10-27 21:00:43 -070060 AOS_CHECK_EQ(1, fscanf(pid_max_file, "%d", &r));
61 AOS_PCHECK(fclose(pid_max_file));
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);
67 if (nproc == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070068 AOS_PLOG(FATAL, "sysconf(_SC_NPROCESSORS_CONF)");
Brian Silverman70f88512014-12-27 19:27:35 -080069 }
70 cpu_set_t r;
71 CPU_ZERO(&r);
72 for (long i = 0; i < nproc; ++i) {
73 CPU_SET(i, &r);
74 }
75 return r;
76}
77
78cpu_set_t find_cpu_mask(int process, bool *not_there) {
79 cpu_set_t r;
80 const int result = sched_getaffinity(process, sizeof(r), &r);
81 if (result == -1 && errno == ESRCH) {
82 *not_there = true;
83 return cpu_set_t();
84 }
85 if (result != 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070086 AOS_PLOG(FATAL, "sched_getaffinity(%d, %zu, %p)", process, sizeof(r), &r);
Brian Silverman70f88512014-12-27 19:27:35 -080087 }
88 return r;
89}
90
91sched_param find_sched_param(int process, bool *not_there) {
92 sched_param r;
93 const int result = sched_getparam(process, &r);
94 if (result == -1 && errno == ESRCH) {
95 *not_there = true;
96 return sched_param();
97 }
98 if (result != 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070099 AOS_PLOG(FATAL, "sched_getparam(%d)", process);
Brian Silverman70f88512014-12-27 19:27:35 -0800100 }
101 return r;
102}
103
104int find_scheduler(int process, bool *not_there) {
105 int scheduler = sched_getscheduler(process);
106 if (scheduler == -1 && errno == ESRCH) {
107 *not_there = true;
108 return 0;
109 }
110 if (scheduler == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700111 AOS_PLOG(FATAL, "sched_getscheduler(%d)", process);
Brian Silverman70f88512014-12-27 19:27:35 -0800112 }
113 return scheduler;
114}
115
116::std::string find_exe(int process, bool *not_there) {
117 ::std::string exe_filename = "/proc/" + ::std::to_string(process) + "/exe";
118 char exe_buffer[1024];
119 ssize_t exe_size =
120 readlink(exe_filename.c_str(), exe_buffer, sizeof(exe_buffer));
121 if (exe_size == -1 && errno == ENOENT) {
122 return "ENOENT";
123 } else {
124 if (exe_size == -1 && errno == ESRCH) {
125 *not_there = true;
126 return "";
127 }
128 if (exe_size == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700129 AOS_PLOG(FATAL, "readlink(%s, %p, %zu)", exe_filename.c_str(), exe_buffer,
130 sizeof(exe_buffer));
Brian Silverman70f88512014-12-27 19:27:35 -0800131 }
132 return ::std::string(exe_buffer, exe_size);
133 }
134}
135
136int find_nice_value(int process, bool *not_there) {
137 errno = 0;
138 int nice_value = getpriority(PRIO_PROCESS, process);
139 if (errno == ESRCH) {
140 *not_there = true;
141 return 0;
142 }
143 if (errno != 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700144 AOS_PLOG(FATAL, "getpriority(PRIO_PROCESS, %d)", process);
Brian Silverman70f88512014-12-27 19:27:35 -0800145 }
146 return nice_value;
147}
148
149void read_stat(int process, int *ppid, int *sid, bool *not_there) {
150 ::std::string stat_filename = "/proc/" + ::std::to_string(process) + "/stat";
151 FILE *stat = fopen(stat_filename.c_str(), "r");
152 if (stat == nullptr && errno == ENOENT) {
153 *not_there = true;
154 return;
155 }
156 if (stat == nullptr) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700157 AOS_PLOG(FATAL, "fopen(%s, \"r\")", stat_filename.c_str());
Brian Silverman70f88512014-12-27 19:27:35 -0800158 }
159
160 char buffer[2048];
161 if (fgets(buffer, sizeof(buffer), stat) == nullptr) {
162 if (ferror(stat)) {
163 if (errno == ESRCH) {
164 *not_there = true;
165 return;
166 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700167 AOS_PLOG(FATAL, "fgets(%p, %zu, %p)", buffer, sizeof(buffer), stat);
Brian Silverman70f88512014-12-27 19:27:35 -0800168 }
169 }
170
171 int pid = 0;
172
173 int field = 0;
174 size_t field_start = 0;
175 int parens = 0;
176 for (size_t i = 0; i < sizeof(buffer); ++i) {
177 if (buffer[i] == '\0') break;
178 if (buffer[i] == '(') ++parens;
179 if (parens > 0) {
180 if (buffer[i] == ')') --parens;
181 } else if (buffer[i] == ' ') {
182 ::std::string field_string(buffer, field_start, i - field_start);
183 switch (field) {
184 case 0:
185 pid = ::std::stoi(field_string);
186 break;
187 case 3:
188 *ppid = ::std::stoi(field_string);
189 break;
190 case 4:
191 *sid = ::std::stoi(field_string);
192 break;
193 default:
194 break;
195 }
196 ++field;
197 field_start = i + 1;
198 }
199 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700200 AOS_PCHECK(fclose(stat));
Brian Silverman70f88512014-12-27 19:27:35 -0800201
202 if (field < 4) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700203 AOS_LOG(FATAL, "couldn't get fields from /proc/%d/stat\n", process);
Brian Silverman70f88512014-12-27 19:27:35 -0800204 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700205 AOS_CHECK_EQ(pid, process);
Brian Silverman70f88512014-12-27 19:27:35 -0800206}
207
208void read_status(int process, int ppid, int *pgrp, ::std::string *name,
209 bool *not_there) {
210 ::std::string status_filename =
211 "/proc/" + ::std::to_string(process) + "/status";
212 FILE *status = fopen(status_filename.c_str(), "r");
213 if (status == nullptr && errno == ENOENT) {
214 *not_there = true;
215 return;
216 }
217 if (status == nullptr) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700218 AOS_PLOG(FATAL, "fopen(%s, \"r\")", status_filename.c_str());
Brian Silverman70f88512014-12-27 19:27:35 -0800219 }
220
221 int pid = 0, status_ppid = 0;
222 while (true) {
223 char buffer[1024];
224 if (fgets(buffer, sizeof(buffer), status) == nullptr) {
225 if (ferror(status)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700226 AOS_PLOG(FATAL, "fgets(%p, %zu, %p)", buffer, sizeof(buffer), status);
Brian Silverman70f88512014-12-27 19:27:35 -0800227 } else {
228 break;
229 }
230 }
231 ::std::string line(buffer);
232 if (line.substr(0, 5) == "Name:") {
233 *name = strip_string_prefix(5, line);
234 } else if (line.substr(0, 4) == "Pid:") {
235 pid = ::std::stoi(strip_string_prefix(4, line));
236 } else if (line.substr(0, 5) == "PPid:") {
237 status_ppid = ::std::stoi(strip_string_prefix(5, line));
238 } else if (line.substr(0, 5) == "Tgid:") {
239 *pgrp = ::std::stoi(strip_string_prefix(5, line));
240 }
241 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700242 AOS_PCHECK(fclose(status));
243 AOS_CHECK_EQ(pid, process);
244 AOS_CHECK_EQ(status_ppid, ppid);
Brian Silverman70f88512014-12-27 19:27:35 -0800245}
246
247} // namespace
248
249int main() {
250 ::aos::logging::Init();
Tyler Chatow4b471e12020-01-05 20:19:36 -0800251 ::aos::logging::SetImplementation(
Brian Silverman70f88512014-12-27 19:27:35 -0800252 new ::aos::logging::StreamLogImplementation(stdout));
253
254 const int pid_max = find_pid_max();
255 const cpu_set_t all_cpus = find_all_cpus();
256
257 for (int i = 0; i < pid_max; ++i) {
258 bool not_there = false;
259
260 const cpu_set_t cpu_mask = find_cpu_mask(i, &not_there);
261 const sched_param param = find_sched_param(i, &not_there);
262 const int scheduler = find_scheduler(i, &not_there);
263 const ::std::string exe = find_exe(i, &not_there);
264 const int nice_value = find_nice_value(i, &not_there);
265
266 int ppid = 0, sid = 0;
267 read_stat(i, &ppid, &sid, &not_there);
268
269 int pgrp = 0;
270 ::std::string name;
271 read_status(i, ppid, &pgrp, &name, &not_there);
272
273 if (not_there) continue;
274
275 const char *cpu_mask_string =
276 CPU_EQUAL(&cpu_mask, &all_cpus) ? "all" : "???";
277
278 printf("%s,%s,%s,%s,%d,%d,%d,%d,%d,%d\n", exe.c_str(), name.c_str(),
279 cpu_mask_string, policy_string(scheduler), nice_value,
280 param.sched_priority, i, pgrp, ppid, sid);
281 }
282}