blob: 4ed9415fbd4d67a58654daf9f0e9224d3ff70c2a [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
Austin Schuh14056182023-01-03 21:19:38 -080022#include "aos/events/shm_event_loop.h"
23#include "aos/init.h"
John Park33858a32018-09-28 23:05:48 -070024#include "aos/time/time.h"
milind-ueb075d22023-02-24 14:57:43 -080025#include "aos/util/top.h"
Brian Silverman3eb60d22021-11-04 19:06:47 -070026#include "glog/logging.h"
Brian Silverman70f88512014-12-27 19:27:35 -080027
Austin Schuh14056182023-01-03 21:19:38 -080028DEFINE_string(config, "aos_config.json", "File path of aos configuration");
29
Brian Silverman70f88512014-12-27 19:27:35 -080030namespace {
31
32const char *policy_string(uint32_t policy) {
33 switch (policy) {
34 case SCHED_OTHER:
35 return "OTHER";
36 case SCHED_BATCH:
37 return "BATCH";
38 case SCHED_IDLE:
39 return "IDLE";
40 case SCHED_FIFO:
41 return "FIFO";
42 case SCHED_RR:
43 return "RR";
44#ifdef SCHED_DEADLINE
45 case SCHED_DEADLINE:
46 return "DEADLINE";
47#endif
48 default:
49 return "???";
50 }
51}
52
53::std::string strip_string_prefix(size_t length, ::std::string str) {
54 str = str.substr(length);
55 while (str[0] == ' ' || str[0] == '\t') {
56 str = str.substr(1);
57 }
58 return str.substr(0, str.size() - 1);
59}
60
Austin Schuh14056182023-01-03 21:19:38 -080061cpu_set_t FindAllCpus() {
Brian Silverman70f88512014-12-27 19:27:35 -080062 long nproc = sysconf(_SC_NPROCESSORS_CONF);
Brian Silverman3eb60d22021-11-04 19:06:47 -070063 PCHECK(nproc != -1);
Brian Silverman70f88512014-12-27 19:27:35 -080064 cpu_set_t r;
65 CPU_ZERO(&r);
66 for (long i = 0; i < nproc; ++i) {
67 CPU_SET(i, &r);
68 }
69 return r;
70}
71
72cpu_set_t find_cpu_mask(int process, bool *not_there) {
73 cpu_set_t r;
74 const int result = sched_getaffinity(process, sizeof(r), &r);
75 if (result == -1 && errno == ESRCH) {
76 *not_there = true;
77 return cpu_set_t();
78 }
Brian Silverman3eb60d22021-11-04 19:06:47 -070079 PCHECK(result == 0) << ": sched_getaffinity of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -080080 return r;
81}
82
83sched_param find_sched_param(int process, bool *not_there) {
84 sched_param r;
85 const int result = sched_getparam(process, &r);
86 if (result == -1 && errno == ESRCH) {
87 *not_there = true;
88 return sched_param();
89 }
Brian Silverman3eb60d22021-11-04 19:06:47 -070090 PCHECK(result == 0) << ": sched_getparam of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -080091 return r;
92}
93
94int find_scheduler(int process, bool *not_there) {
95 int scheduler = sched_getscheduler(process);
96 if (scheduler == -1 && errno == ESRCH) {
97 *not_there = true;
98 return 0;
99 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700100 PCHECK(scheduler != -1) << ": sched_getscheduler of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -0800101 return scheduler;
102}
103
104::std::string find_exe(int process, bool *not_there) {
105 ::std::string exe_filename = "/proc/" + ::std::to_string(process) + "/exe";
106 char exe_buffer[1024];
107 ssize_t exe_size =
108 readlink(exe_filename.c_str(), exe_buffer, sizeof(exe_buffer));
109 if (exe_size == -1 && errno == ENOENT) {
110 return "ENOENT";
111 } else {
112 if (exe_size == -1 && errno == ESRCH) {
113 *not_there = true;
114 return "";
115 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700116 PCHECK(exe_size != -1) << ": readlink " << exe_filename
117 << " into buffer of size " << sizeof(exe_buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800118 return ::std::string(exe_buffer, exe_size);
119 }
120}
121
122int find_nice_value(int process, bool *not_there) {
123 errno = 0;
124 int nice_value = getpriority(PRIO_PROCESS, process);
125 if (errno == ESRCH) {
126 *not_there = true;
127 return 0;
128 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700129 PCHECK(errno == 0) << "getpriority of " << process;
Brian Silverman70f88512014-12-27 19:27:35 -0800130 return nice_value;
131}
132
133void read_stat(int process, int *ppid, int *sid, bool *not_there) {
134 ::std::string stat_filename = "/proc/" + ::std::to_string(process) + "/stat";
135 FILE *stat = fopen(stat_filename.c_str(), "r");
136 if (stat == nullptr && errno == ENOENT) {
137 *not_there = true;
138 return;
139 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700140 PCHECK(stat != nullptr) << ": Failed to open " << stat_filename;
Brian Silverman70f88512014-12-27 19:27:35 -0800141
142 char buffer[2048];
143 if (fgets(buffer, sizeof(buffer), stat) == nullptr) {
144 if (ferror(stat)) {
145 if (errno == ESRCH) {
146 *not_there = true;
147 return;
148 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700149 PLOG(FATAL) << "reading from " << stat_filename << " into buffer of size "
150 << sizeof(buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800151 }
152 }
153
154 int pid = 0;
155
156 int field = 0;
157 size_t field_start = 0;
158 int parens = 0;
159 for (size_t i = 0; i < sizeof(buffer); ++i) {
160 if (buffer[i] == '\0') break;
161 if (buffer[i] == '(') ++parens;
162 if (parens > 0) {
163 if (buffer[i] == ')') --parens;
164 } else if (buffer[i] == ' ') {
165 ::std::string field_string(buffer, field_start, i - field_start);
166 switch (field) {
167 case 0:
168 pid = ::std::stoi(field_string);
169 break;
170 case 3:
171 *ppid = ::std::stoi(field_string);
172 break;
173 case 4:
174 *sid = ::std::stoi(field_string);
175 break;
176 default:
177 break;
178 }
179 ++field;
180 field_start = i + 1;
181 }
182 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700183 PCHECK(fclose(stat) == 0);
Brian Silverman70f88512014-12-27 19:27:35 -0800184
185 if (field < 4) {
Brian Silverman3eb60d22021-11-04 19:06:47 -0700186 LOG(FATAL) << "couldn't get fields from /proc/" << process << "/stat";
Brian Silverman70f88512014-12-27 19:27:35 -0800187 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700188 CHECK_EQ(pid, process);
Brian Silverman70f88512014-12-27 19:27:35 -0800189}
190
191void read_status(int process, int ppid, int *pgrp, ::std::string *name,
192 bool *not_there) {
193 ::std::string status_filename =
194 "/proc/" + ::std::to_string(process) + "/status";
195 FILE *status = fopen(status_filename.c_str(), "r");
196 if (status == nullptr && errno == ENOENT) {
197 *not_there = true;
198 return;
199 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700200 PCHECK(status != nullptr) << ": Failed to open " << status_filename;
Brian Silverman70f88512014-12-27 19:27:35 -0800201
202 int pid = 0, status_ppid = 0;
203 while (true) {
204 char buffer[1024];
205 if (fgets(buffer, sizeof(buffer), status) == nullptr) {
206 if (ferror(status)) {
Brian Silverman3eb60d22021-11-04 19:06:47 -0700207 PLOG(FATAL) << "reading from " << status_filename
208 << " into buffer of size " << sizeof(buffer);
Brian Silverman70f88512014-12-27 19:27:35 -0800209 } else {
210 break;
211 }
212 }
213 ::std::string line(buffer);
214 if (line.substr(0, 5) == "Name:") {
215 *name = strip_string_prefix(5, line);
216 } else if (line.substr(0, 4) == "Pid:") {
217 pid = ::std::stoi(strip_string_prefix(4, line));
218 } else if (line.substr(0, 5) == "PPid:") {
219 status_ppid = ::std::stoi(strip_string_prefix(5, line));
220 } else if (line.substr(0, 5) == "Tgid:") {
221 *pgrp = ::std::stoi(strip_string_prefix(5, line));
222 }
223 }
Brian Silverman3eb60d22021-11-04 19:06:47 -0700224 PCHECK(fclose(status) == 0);
225 CHECK_EQ(pid, process);
226 CHECK_EQ(status_ppid, ppid);
Brian Silverman70f88512014-12-27 19:27:35 -0800227}
228
milind-uf963ee92021-11-06 16:10:07 -0700229struct Thread {
230 uint32_t policy;
231 std::string exe, name, cpu_mask;
232 int nice_value, sched_priority, tid, pid, ppid, sid;
233
234 void Print() const {
235 printf("%s,%s,%s,%s,%d,%d,%d,%d,%d,%d\n", exe.c_str(), name.c_str(),
236 cpu_mask.c_str(), policy_string(policy), nice_value, sched_priority,
237 tid, pid, ppid, sid);
238 }
239
240 // Make threads with SCHED_FIFO or SCHED_RR show up before SCHED_OTHER, and
241 // make higher priority threads show up first. Higher priority threads are
242 // less than lower priority so that they appear first.
243 bool operator<(const Thread &t) const {
244 return (((((policy == SCHED_FIFO) || (policy == SCHED_RR))) &&
245 (t.policy == SCHED_OTHER)) ||
246 (sched_priority > t.sched_priority));
247 }
248};
249
Brian Silverman70f88512014-12-27 19:27:35 -0800250} // namespace
251
Austin Schuh14056182023-01-03 21:19:38 -0800252int main(int argc, char **argv) {
253 aos::InitGoogle(&argc, &argv);
254 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
255 aos::configuration::ReadConfig(FLAGS_config);
Brian Silverman70f88512014-12-27 19:27:35 -0800256
Austin Schuh14056182023-01-03 21:19:38 -0800257 aos::ShmEventLoop event_loop(&config.message());
258 event_loop.SkipTimingReport();
259 event_loop.SkipAosLog();
milind-ueb075d22023-02-24 14:57:43 -0800260 aos::util::Top top(&event_loop, true);
Austin Schuh14056182023-01-03 21:19:38 -0800261 top.set_track_top_processes(true);
milind-uf963ee92021-11-06 16:10:07 -0700262
Austin Schuh14056182023-01-03 21:19:38 -0800263 const cpu_set_t all_cpus = FindAllCpus();
Brian Silverman70f88512014-12-27 19:27:35 -0800264
Austin Schuh14056182023-01-03 21:19:38 -0800265 top.set_on_reading_update([&]() {
266 std::multiset<Thread> threads;
Brian Silverman70f88512014-12-27 19:27:35 -0800267
Austin Schuh14056182023-01-03 21:19:38 -0800268 for (const std::pair<const pid_t, aos::util::Top::ProcessReadings>
269 &reading : top.readings()) {
270 pid_t tid = reading.first;
271 bool not_there = false;
Brian Silverman70f88512014-12-27 19:27:35 -0800272
Austin Schuh14056182023-01-03 21:19:38 -0800273 const cpu_set_t cpu_mask = find_cpu_mask(tid, &not_there);
274 const sched_param param = find_sched_param(tid, &not_there);
275 const int scheduler = find_scheduler(tid, &not_there);
276 const ::std::string exe = find_exe(tid, &not_there);
277 const int nice_value = find_nice_value(tid, &not_there);
Brian Silverman70f88512014-12-27 19:27:35 -0800278
Austin Schuh14056182023-01-03 21:19:38 -0800279 int ppid = 0, sid = 0;
280 read_stat(tid, &ppid, &sid, &not_there);
Brian Silverman70f88512014-12-27 19:27:35 -0800281
Austin Schuh14056182023-01-03 21:19:38 -0800282 int pgrp = 0;
283 ::std::string name;
284 read_status(tid, ppid, &pgrp, &name, &not_there);
Brian Silverman70f88512014-12-27 19:27:35 -0800285
Austin Schuh14056182023-01-03 21:19:38 -0800286 if (not_there) continue;
milind-uf963ee92021-11-06 16:10:07 -0700287
milind-ua313a5b2023-02-24 15:17:45 -0800288 std::string_view cpu_mask_string;
289 if (CPU_EQUAL(&cpu_mask, &all_cpus)) {
290 cpu_mask_string = "all";
291 } else {
292 // Convert the CPU mask to a bitset
293 std::bitset<CPU_SETSIZE> bitset;
294 for (size_t i = 0; i < CPU_SETSIZE; i++) {
295 bitset[i] = CPU_ISSET(i, &cpu_mask);
296 }
297 std::stringstream ss;
298 ss << std::hex << bitset.to_ulong();
299 cpu_mask_string = ss.str();
300 }
Austin Schuh14056182023-01-03 21:19:38 -0800301
302 threads.emplace(Thread{.policy = static_cast<uint32_t>(scheduler),
303 .exe = exe,
304 .name = name,
milind-ua313a5b2023-02-24 15:17:45 -0800305 .cpu_mask = cpu_mask_string.data(),
Austin Schuh14056182023-01-03 21:19:38 -0800306 .nice_value = nice_value,
307 .sched_priority = param.sched_priority,
308 .tid = tid,
309 .pid = pgrp,
310 .ppid = ppid,
311 .sid = sid});
312 }
313
314 printf("exe,name,cpumask,policy,nice,priority,tid,pid,ppid,sid\n");
315 for (const auto &t : threads) {
316 t.Print();
317 }
318 event_loop.Exit();
319 });
320
321 event_loop.Run();
Brian Silverman70f88512014-12-27 19:27:35 -0800322}