blob: e1286e2c95ea48f183638313fdc8cbfe900b7394 [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
milind-ua313a5b2023-02-24 15:17:45 -080015#include <bitset>
Tyler Chatowbf0609c2021-07-31 16:13:27 -070016#include <cstdint>
17#include <cstdio>
18#include <cstdlib>
milind-uf963ee92021-11-06 16:10:07 -070019#include <set>
Brian Silverman70f88512014-12-27 19:27:35 -080020#include <string>
21
Philipp Schrader790cb542023-07-05 21:06:52 -070022#include "glog/logging.h"
23
Austin Schuh14056182023-01-03 21:19:38 -080024#include "aos/events/shm_event_loop.h"
25#include "aos/init.h"
John Park33858a32018-09-28 23:05:48 -070026#include "aos/time/time.h"
milind-ueb075d22023-02-24 14:57:43 -080027#include "aos/util/top.h"
Brian Silverman70f88512014-12-27 19:27:35 -080028
Austin Schuh14056182023-01-03 21:19:38 -080029DEFINE_string(config, "aos_config.json", "File path of aos configuration");
30
Brian Silverman70f88512014-12-27 19:27:35 -080031namespace {
32
33const char *policy_string(uint32_t policy) {
34 switch (policy) {
35 case SCHED_OTHER:
36 return "OTHER";
37 case SCHED_BATCH:
38 return "BATCH";
39 case SCHED_IDLE:
40 return "IDLE";
41 case SCHED_FIFO:
42 return "FIFO";
43 case SCHED_RR:
44 return "RR";
45#ifdef SCHED_DEADLINE
46 case SCHED_DEADLINE:
47 return "DEADLINE";
48#endif
49 default:
50 return "???";
51 }
52}
53
54::std::string strip_string_prefix(size_t length, ::std::string str) {
55 str = str.substr(length);
56 while (str[0] == ' ' || str[0] == '\t') {
57 str = str.substr(1);
58 }
59 return str.substr(0, str.size() - 1);
60}
61
Austin Schuh14056182023-01-03 21:19:38 -080062cpu_set_t FindAllCpus() {
Brian Silverman70f88512014-12-27 19:27:35 -080063 long nproc = sysconf(_SC_NPROCESSORS_CONF);
Brian Silverman3eb60d22021-11-04 19:06:47 -070064 PCHECK(nproc != -1);
Brian Silverman70f88512014-12-27 19:27:35 -080065 cpu_set_t r;
66 CPU_ZERO(&r);
67 for (long i = 0; i < nproc; ++i) {
68 CPU_SET(i, &r);
69 }
70 return r;
71}
72
73cpu_set_t find_cpu_mask(int process, bool *not_there) {
74 cpu_set_t r;
75 const int result = sched_getaffinity(process, sizeof(r), &r);
76 if (result == -1 && errno == ESRCH) {
77 *not_there = true;
78 return cpu_set_t();
79 }
Brian Silverman3eb60d22021-11-04 19:06:47 -070080 PCHECK(result == 0) << ": sched_getaffinity of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -080081 return r;
82}
83
84sched_param find_sched_param(int process, bool *not_there) {
85 sched_param r;
86 const int result = sched_getparam(process, &r);
87 if (result == -1 && errno == ESRCH) {
88 *not_there = true;
89 return sched_param();
90 }
Brian Silverman3eb60d22021-11-04 19:06:47 -070091 PCHECK(result == 0) << ": sched_getparam of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -080092 return r;
93}
94
95int find_scheduler(int process, bool *not_there) {
96 int scheduler = sched_getscheduler(process);
97 if (scheduler == -1 && errno == ESRCH) {
98 *not_there = true;
99 return 0;
100 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700101 PCHECK(scheduler != -1) << ": sched_getscheduler of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -0800102 return scheduler;
103}
104
105::std::string find_exe(int process, bool *not_there) {
106 ::std::string exe_filename = "/proc/" + ::std::to_string(process) + "/exe";
107 char exe_buffer[1024];
108 ssize_t exe_size =
109 readlink(exe_filename.c_str(), exe_buffer, sizeof(exe_buffer));
110 if (exe_size == -1 && errno == ENOENT) {
111 return "ENOENT";
112 } else {
113 if (exe_size == -1 && errno == ESRCH) {
114 *not_there = true;
115 return "";
116 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700117 PCHECK(exe_size != -1) << ": readlink " << exe_filename
118 << " into buffer of size " << sizeof(exe_buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800119 return ::std::string(exe_buffer, exe_size);
120 }
121}
122
123int find_nice_value(int process, bool *not_there) {
124 errno = 0;
125 int nice_value = getpriority(PRIO_PROCESS, process);
126 if (errno == ESRCH) {
127 *not_there = true;
128 return 0;
129 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700130 PCHECK(errno == 0) << "getpriority of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -0800131 return nice_value;
132}
133
134void read_stat(int process, int *ppid, int *sid, bool *not_there) {
135 ::std::string stat_filename = "/proc/" + ::std::to_string(process) + "/stat";
136 FILE *stat = fopen(stat_filename.c_str(), "r");
137 if (stat == nullptr && errno == ENOENT) {
138 *not_there = true;
139 return;
140 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700141 PCHECK(stat != nullptr) << ": Failed to open " << stat_filename;
Brian Silverman70f88512014-12-27 19:27:35 -0800142
143 char buffer[2048];
144 if (fgets(buffer, sizeof(buffer), stat) == nullptr) {
145 if (ferror(stat)) {
146 if (errno == ESRCH) {
147 *not_there = true;
148 return;
149 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700150 PLOG(FATAL) << "reading from " << stat_filename << " into buffer of size "
151 << sizeof(buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800152 }
153 }
154
155 int pid = 0;
156
157 int field = 0;
158 size_t field_start = 0;
159 int parens = 0;
160 for (size_t i = 0; i < sizeof(buffer); ++i) {
161 if (buffer[i] == '\0') break;
162 if (buffer[i] == '(') ++parens;
163 if (parens > 0) {
164 if (buffer[i] == ')') --parens;
165 } else if (buffer[i] == ' ') {
166 ::std::string field_string(buffer, field_start, i - field_start);
167 switch (field) {
168 case 0:
169 pid = ::std::stoi(field_string);
170 break;
171 case 3:
172 *ppid = ::std::stoi(field_string);
173 break;
174 case 4:
175 *sid = ::std::stoi(field_string);
176 break;
177 default:
178 break;
179 }
180 ++field;
181 field_start = i + 1;
182 }
183 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700184 PCHECK(fclose(stat) == 0);
Brian Silverman70f88512014-12-27 19:27:35 -0800185
186 if (field < 4) {
Brian Silverman3eb60d22021-11-04 19:06:47 -0700187 LOG(FATAL) << "couldn't get fields from /proc/" << process << "/stat";
Brian Silverman70f88512014-12-27 19:27:35 -0800188 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700189 CHECK_EQ(pid, process);
Brian Silverman70f88512014-12-27 19:27:35 -0800190}
191
192void read_status(int process, int ppid, int *pgrp, ::std::string *name,
193 bool *not_there) {
194 ::std::string status_filename =
195 "/proc/" + ::std::to_string(process) + "/status";
196 FILE *status = fopen(status_filename.c_str(), "r");
197 if (status == nullptr && errno == ENOENT) {
198 *not_there = true;
199 return;
200 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700201 PCHECK(status != nullptr) << ": Failed to open " << status_filename;
Brian Silverman70f88512014-12-27 19:27:35 -0800202
203 int pid = 0, status_ppid = 0;
204 while (true) {
205 char buffer[1024];
206 if (fgets(buffer, sizeof(buffer), status) == nullptr) {
207 if (ferror(status)) {
Brian Silverman3eb60d22021-11-04 19:06:47 -0700208 PLOG(FATAL) << "reading from " << status_filename
209 << " into buffer of size " << sizeof(buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800210 } else {
211 break;
212 }
213 }
214 ::std::string line(buffer);
215 if (line.substr(0, 5) == "Name:") {
216 *name = strip_string_prefix(5, line);
217 } else if (line.substr(0, 4) == "Pid:") {
218 pid = ::std::stoi(strip_string_prefix(4, line));
219 } else if (line.substr(0, 5) == "PPid:") {
220 status_ppid = ::std::stoi(strip_string_prefix(5, line));
221 } else if (line.substr(0, 5) == "Tgid:") {
222 *pgrp = ::std::stoi(strip_string_prefix(5, line));
223 }
224 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700225 PCHECK(fclose(status) == 0);
226 CHECK_EQ(pid, process);
227 CHECK_EQ(status_ppid, ppid);
Brian Silverman70f88512014-12-27 19:27:35 -0800228}
229
milind-uf963ee92021-11-06 16:10:07 -0700230struct Thread {
231 uint32_t policy;
232 std::string exe, name, cpu_mask;
233 int nice_value, sched_priority, tid, pid, ppid, sid;
234
235 void Print() const {
236 printf("%s,%s,%s,%s,%d,%d,%d,%d,%d,%d\n", exe.c_str(), name.c_str(),
237 cpu_mask.c_str(), policy_string(policy), nice_value, sched_priority,
238 tid, pid, ppid, sid);
239 }
240
241 // Make threads with SCHED_FIFO or SCHED_RR show up before SCHED_OTHER, and
242 // make higher priority threads show up first. Higher priority threads are
243 // less than lower priority so that they appear first.
244 bool operator<(const Thread &t) const {
245 return (((((policy == SCHED_FIFO) || (policy == SCHED_RR))) &&
246 (t.policy == SCHED_OTHER)) ||
247 (sched_priority > t.sched_priority));
248 }
249};
250
Brian Silverman70f88512014-12-27 19:27:35 -0800251} // namespace
252
Austin Schuh14056182023-01-03 21:19:38 -0800253int main(int argc, char **argv) {
254 aos::InitGoogle(&argc, &argv);
255 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
256 aos::configuration::ReadConfig(FLAGS_config);
Brian Silverman70f88512014-12-27 19:27:35 -0800257
Austin Schuh14056182023-01-03 21:19:38 -0800258 aos::ShmEventLoop event_loop(&config.message());
259 event_loop.SkipTimingReport();
260 event_loop.SkipAosLog();
milind-ueb075d22023-02-24 14:57:43 -0800261 aos::util::Top top(&event_loop, true);
Austin Schuh14056182023-01-03 21:19:38 -0800262 top.set_track_top_processes(true);
milind-uf963ee92021-11-06 16:10:07 -0700263
Austin Schuh14056182023-01-03 21:19:38 -0800264 const cpu_set_t all_cpus = FindAllCpus();
Brian Silverman70f88512014-12-27 19:27:35 -0800265
Austin Schuh14056182023-01-03 21:19:38 -0800266 top.set_on_reading_update([&]() {
267 std::multiset<Thread> threads;
Brian Silverman70f88512014-12-27 19:27:35 -0800268
Austin Schuh14056182023-01-03 21:19:38 -0800269 for (const std::pair<const pid_t, aos::util::Top::ProcessReadings>
270 &reading : top.readings()) {
271 pid_t tid = reading.first;
272 bool not_there = false;
Brian Silverman70f88512014-12-27 19:27:35 -0800273
Austin Schuh14056182023-01-03 21:19:38 -0800274 const cpu_set_t cpu_mask = find_cpu_mask(tid, &not_there);
275 const sched_param param = find_sched_param(tid, &not_there);
276 const int scheduler = find_scheduler(tid, &not_there);
277 const ::std::string exe = find_exe(tid, &not_there);
278 const int nice_value = find_nice_value(tid, &not_there);
Brian Silverman70f88512014-12-27 19:27:35 -0800279
Austin Schuh14056182023-01-03 21:19:38 -0800280 int ppid = 0, sid = 0;
281 read_stat(tid, &ppid, &sid, &not_there);
Brian Silverman70f88512014-12-27 19:27:35 -0800282
Austin Schuh14056182023-01-03 21:19:38 -0800283 int pgrp = 0;
284 ::std::string name;
285 read_status(tid, ppid, &pgrp, &name, &not_there);
Brian Silverman70f88512014-12-27 19:27:35 -0800286
Austin Schuh14056182023-01-03 21:19:38 -0800287 if (not_there) continue;
milind-uf963ee92021-11-06 16:10:07 -0700288
milind-ua313a5b2023-02-24 15:17:45 -0800289 std::string_view cpu_mask_string;
290 if (CPU_EQUAL(&cpu_mask, &all_cpus)) {
291 cpu_mask_string = "all";
292 } else {
293 // Convert the CPU mask to a bitset
294 std::bitset<CPU_SETSIZE> bitset;
295 for (size_t i = 0; i < CPU_SETSIZE; i++) {
296 bitset[i] = CPU_ISSET(i, &cpu_mask);
297 }
298 std::stringstream ss;
299 ss << std::hex << bitset.to_ulong();
300 cpu_mask_string = ss.str();
301 }
Austin Schuh14056182023-01-03 21:19:38 -0800302
303 threads.emplace(Thread{.policy = static_cast<uint32_t>(scheduler),
304 .exe = exe,
305 .name = name,
milind-ua313a5b2023-02-24 15:17:45 -0800306 .cpu_mask = cpu_mask_string.data(),
Austin Schuh14056182023-01-03 21:19:38 -0800307 .nice_value = nice_value,
308 .sched_priority = param.sched_priority,
309 .tid = tid,
310 .pid = pgrp,
311 .ppid = ppid,
312 .sid = sid});
313 }
314
315 printf("exe,name,cpumask,policy,nice,priority,tid,pid,ppid,sid\n");
316 for (const auto &t : threads) {
317 t.Print();
318 }
319 event_loop.Exit();
320 });
321
322 event_loop.Run();
Brian Silverman70f88512014-12-27 19:27:35 -0800323}