blob: d36a1c02ed41deec50275aa976728d76a67b3f78 [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
Austin Schuh14056182023-01-03 21:19:38 -080021#include "aos/events/shm_event_loop.h"
22#include "aos/init.h"
23#include "aos/util/top.h"
John Park33858a32018-09-28 23:05:48 -070024#include "aos/time/time.h"
Brian Silverman3eb60d22021-11-04 19:06:47 -070025#include "glog/logging.h"
Brian Silverman70f88512014-12-27 19:27:35 -080026
Austin Schuh14056182023-01-03 21:19:38 -080027DEFINE_string(config, "aos_config.json", "File path of aos configuration");
28
Brian Silverman70f88512014-12-27 19:27:35 -080029namespace {
30
31const char *policy_string(uint32_t policy) {
32 switch (policy) {
33 case SCHED_OTHER:
34 return "OTHER";
35 case SCHED_BATCH:
36 return "BATCH";
37 case SCHED_IDLE:
38 return "IDLE";
39 case SCHED_FIFO:
40 return "FIFO";
41 case SCHED_RR:
42 return "RR";
43#ifdef SCHED_DEADLINE
44 case SCHED_DEADLINE:
45 return "DEADLINE";
46#endif
47 default:
48 return "???";
49 }
50}
51
52::std::string strip_string_prefix(size_t length, ::std::string str) {
53 str = str.substr(length);
54 while (str[0] == ' ' || str[0] == '\t') {
55 str = str.substr(1);
56 }
57 return str.substr(0, str.size() - 1);
58}
59
Austin Schuh14056182023-01-03 21:19:38 -080060cpu_set_t FindAllCpus() {
Brian Silverman70f88512014-12-27 19:27:35 -080061 long nproc = sysconf(_SC_NPROCESSORS_CONF);
Brian Silverman3eb60d22021-11-04 19:06:47 -070062 PCHECK(nproc != -1);
Brian Silverman70f88512014-12-27 19:27:35 -080063 cpu_set_t r;
64 CPU_ZERO(&r);
65 for (long i = 0; i < nproc; ++i) {
66 CPU_SET(i, &r);
67 }
68 return r;
69}
70
71cpu_set_t find_cpu_mask(int process, bool *not_there) {
72 cpu_set_t r;
73 const int result = sched_getaffinity(process, sizeof(r), &r);
74 if (result == -1 && errno == ESRCH) {
75 *not_there = true;
76 return cpu_set_t();
77 }
Brian Silverman3eb60d22021-11-04 19:06:47 -070078 PCHECK(result == 0) << ": sched_getaffinity of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -080079 return r;
80}
81
82sched_param find_sched_param(int process, bool *not_there) {
83 sched_param r;
84 const int result = sched_getparam(process, &r);
85 if (result == -1 && errno == ESRCH) {
86 *not_there = true;
87 return sched_param();
88 }
Brian Silverman3eb60d22021-11-04 19:06:47 -070089 PCHECK(result == 0) << ": sched_getparam of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -080090 return r;
91}
92
93int find_scheduler(int process, bool *not_there) {
94 int scheduler = sched_getscheduler(process);
95 if (scheduler == -1 && errno == ESRCH) {
96 *not_there = true;
97 return 0;
98 }
Brian Silverman3eb60d22021-11-04 19:06:47 -070099 PCHECK(scheduler != -1) << ": sched_getscheduler of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -0800100 return scheduler;
101}
102
103::std::string find_exe(int process, bool *not_there) {
104 ::std::string exe_filename = "/proc/" + ::std::to_string(process) + "/exe";
105 char exe_buffer[1024];
106 ssize_t exe_size =
107 readlink(exe_filename.c_str(), exe_buffer, sizeof(exe_buffer));
108 if (exe_size == -1 && errno == ENOENT) {
109 return "ENOENT";
110 } else {
111 if (exe_size == -1 && errno == ESRCH) {
112 *not_there = true;
113 return "";
114 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700115 PCHECK(exe_size != -1) << ": readlink " << exe_filename
116 << " into buffer of size " << sizeof(exe_buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800117 return ::std::string(exe_buffer, exe_size);
118 }
119}
120
121int find_nice_value(int process, bool *not_there) {
122 errno = 0;
123 int nice_value = getpriority(PRIO_PROCESS, process);
124 if (errno == ESRCH) {
125 *not_there = true;
126 return 0;
127 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700128 PCHECK(errno == 0) << "getpriority of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -0800129 return nice_value;
130}
131
132void read_stat(int process, int *ppid, int *sid, bool *not_there) {
133 ::std::string stat_filename = "/proc/" + ::std::to_string(process) + "/stat";
134 FILE *stat = fopen(stat_filename.c_str(), "r");
135 if (stat == nullptr && errno == ENOENT) {
136 *not_there = true;
137 return;
138 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700139 PCHECK(stat != nullptr) << ": Failed to open " << stat_filename;
Brian Silverman70f88512014-12-27 19:27:35 -0800140
141 char buffer[2048];
142 if (fgets(buffer, sizeof(buffer), stat) == nullptr) {
143 if (ferror(stat)) {
144 if (errno == ESRCH) {
145 *not_there = true;
146 return;
147 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700148 PLOG(FATAL) << "reading from " << stat_filename << " into buffer of size "
149 << sizeof(buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800150 }
151 }
152
153 int pid = 0;
154
155 int field = 0;
156 size_t field_start = 0;
157 int parens = 0;
158 for (size_t i = 0; i < sizeof(buffer); ++i) {
159 if (buffer[i] == '\0') break;
160 if (buffer[i] == '(') ++parens;
161 if (parens > 0) {
162 if (buffer[i] == ')') --parens;
163 } else if (buffer[i] == ' ') {
164 ::std::string field_string(buffer, field_start, i - field_start);
165 switch (field) {
166 case 0:
167 pid = ::std::stoi(field_string);
168 break;
169 case 3:
170 *ppid = ::std::stoi(field_string);
171 break;
172 case 4:
173 *sid = ::std::stoi(field_string);
174 break;
175 default:
176 break;
177 }
178 ++field;
179 field_start = i + 1;
180 }
181 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700182 PCHECK(fclose(stat) == 0);
Brian Silverman70f88512014-12-27 19:27:35 -0800183
184 if (field < 4) {
Brian Silverman3eb60d22021-11-04 19:06:47 -0700185 LOG(FATAL) << "couldn't get fields from /proc/" << process << "/stat";
Brian Silverman70f88512014-12-27 19:27:35 -0800186 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700187 CHECK_EQ(pid, process);
Brian Silverman70f88512014-12-27 19:27:35 -0800188}
189
190void read_status(int process, int ppid, int *pgrp, ::std::string *name,
191 bool *not_there) {
192 ::std::string status_filename =
193 "/proc/" + ::std::to_string(process) + "/status";
194 FILE *status = fopen(status_filename.c_str(), "r");
195 if (status == nullptr && errno == ENOENT) {
196 *not_there = true;
197 return;
198 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700199 PCHECK(status != nullptr) << ": Failed to open " << status_filename;
Brian Silverman70f88512014-12-27 19:27:35 -0800200
201 int pid = 0, status_ppid = 0;
202 while (true) {
203 char buffer[1024];
204 if (fgets(buffer, sizeof(buffer), status) == nullptr) {
205 if (ferror(status)) {
Brian Silverman3eb60d22021-11-04 19:06:47 -0700206 PLOG(FATAL) << "reading from " << status_filename
207 << " into buffer of size " << sizeof(buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800208 } else {
209 break;
210 }
211 }
212 ::std::string line(buffer);
213 if (line.substr(0, 5) == "Name:") {
214 *name = strip_string_prefix(5, line);
215 } else if (line.substr(0, 4) == "Pid:") {
216 pid = ::std::stoi(strip_string_prefix(4, line));
217 } else if (line.substr(0, 5) == "PPid:") {
218 status_ppid = ::std::stoi(strip_string_prefix(5, line));
219 } else if (line.substr(0, 5) == "Tgid:") {
220 *pgrp = ::std::stoi(strip_string_prefix(5, line));
221 }
222 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700223 PCHECK(fclose(status) == 0);
224 CHECK_EQ(pid, process);
225 CHECK_EQ(status_ppid, ppid);
Brian Silverman70f88512014-12-27 19:27:35 -0800226}
227
milind-uf963ee92021-11-06 16:10:07 -0700228struct Thread {
229 uint32_t policy;
230 std::string exe, name, cpu_mask;
231 int nice_value, sched_priority, tid, pid, ppid, sid;
232
233 void Print() const {
234 printf("%s,%s,%s,%s,%d,%d,%d,%d,%d,%d\n", exe.c_str(), name.c_str(),
235 cpu_mask.c_str(), policy_string(policy), nice_value, sched_priority,
236 tid, pid, ppid, sid);
237 }
238
239 // Make threads with SCHED_FIFO or SCHED_RR show up before SCHED_OTHER, and
240 // make higher priority threads show up first. Higher priority threads are
241 // less than lower priority so that they appear first.
242 bool operator<(const Thread &t) const {
243 return (((((policy == SCHED_FIFO) || (policy == SCHED_RR))) &&
244 (t.policy == SCHED_OTHER)) ||
245 (sched_priority > t.sched_priority));
246 }
247};
248
Brian Silverman70f88512014-12-27 19:27:35 -0800249} // namespace
250
Austin Schuh14056182023-01-03 21:19:38 -0800251int main(int argc, char **argv) {
252 aos::InitGoogle(&argc, &argv);
253 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
254 aos::configuration::ReadConfig(FLAGS_config);
Brian Silverman70f88512014-12-27 19:27:35 -0800255
Austin Schuh14056182023-01-03 21:19:38 -0800256 aos::ShmEventLoop event_loop(&config.message());
257 event_loop.SkipTimingReport();
258 event_loop.SkipAosLog();
259 aos::util::Top top(&event_loop);
260 top.set_track_top_processes(true);
milind-uf963ee92021-11-06 16:10:07 -0700261
Austin Schuh14056182023-01-03 21:19:38 -0800262 const cpu_set_t all_cpus = FindAllCpus();
Brian Silverman70f88512014-12-27 19:27:35 -0800263
Austin Schuh14056182023-01-03 21:19:38 -0800264 top.set_on_reading_update([&]() {
265 std::multiset<Thread> threads;
Brian Silverman70f88512014-12-27 19:27:35 -0800266
Austin Schuh14056182023-01-03 21:19:38 -0800267 for (const std::pair<const pid_t, aos::util::Top::ProcessReadings>
268 &reading : top.readings()) {
269 pid_t tid = reading.first;
270 bool not_there = false;
Brian Silverman70f88512014-12-27 19:27:35 -0800271
Austin Schuh14056182023-01-03 21:19:38 -0800272 const cpu_set_t cpu_mask = find_cpu_mask(tid, &not_there);
273 const sched_param param = find_sched_param(tid, &not_there);
274 const int scheduler = find_scheduler(tid, &not_there);
275 const ::std::string exe = find_exe(tid, &not_there);
276 const int nice_value = find_nice_value(tid, &not_there);
Brian Silverman70f88512014-12-27 19:27:35 -0800277
Austin Schuh14056182023-01-03 21:19:38 -0800278 int ppid = 0, sid = 0;
279 read_stat(tid, &ppid, &sid, &not_there);
Brian Silverman70f88512014-12-27 19:27:35 -0800280
Austin Schuh14056182023-01-03 21:19:38 -0800281 int pgrp = 0;
282 ::std::string name;
283 read_status(tid, ppid, &pgrp, &name, &not_there);
Brian Silverman70f88512014-12-27 19:27:35 -0800284
Austin Schuh14056182023-01-03 21:19:38 -0800285 if (not_there) continue;
milind-uf963ee92021-11-06 16:10:07 -0700286
Austin Schuh14056182023-01-03 21:19:38 -0800287 const char *cpu_mask_string =
288 CPU_EQUAL(&cpu_mask, &all_cpus) ? "all" : "???";
289
290 threads.emplace(Thread{.policy = static_cast<uint32_t>(scheduler),
291 .exe = exe,
292 .name = name,
293 .cpu_mask = cpu_mask_string,
294 .nice_value = nice_value,
295 .sched_priority = param.sched_priority,
296 .tid = tid,
297 .pid = pgrp,
298 .ppid = ppid,
299 .sid = sid});
300 }
301
302 printf("exe,name,cpumask,policy,nice,priority,tid,pid,ppid,sid\n");
303 for (const auto &t : threads) {
304 t.Print();
305 }
306 event_loop.Exit();
307 });
308
309 event_loop.Run();
Brian Silverman70f88512014-12-27 19:27:35 -0800310}