blob: 4ea540dc5ffa2ab466b15c7934d6393c711677b7 [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 Schuh99f7c6a2024-06-25 22:07:44 -070022#include "absl/flags/flag.h"
23#include "absl/log/check.h"
24#include "absl/log/log.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070025
Austin Schuh14056182023-01-03 21:19:38 -080026#include "aos/events/shm_event_loop.h"
27#include "aos/init.h"
John Park33858a32018-09-28 23:05:48 -070028#include "aos/time/time.h"
milind-ueb075d22023-02-24 14:57:43 -080029#include "aos/util/top.h"
Brian Silverman70f88512014-12-27 19:27:35 -080030
Austin Schuh99f7c6a2024-06-25 22:07:44 -070031ABSL_FLAG(std::string, config, "aos_config.json",
32 "File path of aos configuration");
Austin Schuh14056182023-01-03 21:19:38 -080033
Brian Silverman70f88512014-12-27 19:27:35 -080034namespace {
35
36const char *policy_string(uint32_t policy) {
37 switch (policy) {
38 case SCHED_OTHER:
39 return "OTHER";
40 case SCHED_BATCH:
41 return "BATCH";
42 case SCHED_IDLE:
43 return "IDLE";
44 case SCHED_FIFO:
45 return "FIFO";
46 case SCHED_RR:
47 return "RR";
48#ifdef SCHED_DEADLINE
49 case SCHED_DEADLINE:
50 return "DEADLINE";
51#endif
52 default:
53 return "???";
54 }
55}
56
57::std::string strip_string_prefix(size_t length, ::std::string str) {
58 str = str.substr(length);
59 while (str[0] == ' ' || str[0] == '\t') {
60 str = str.substr(1);
61 }
62 return str.substr(0, str.size() - 1);
63}
64
Austin Schuh14056182023-01-03 21:19:38 -080065cpu_set_t FindAllCpus() {
Brian Silverman70f88512014-12-27 19:27:35 -080066 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
Austin Schuh14056182023-01-03 21:19:38 -0800256int main(int argc, char **argv) {
257 aos::InitGoogle(&argc, &argv);
258 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700259 aos::configuration::ReadConfig(absl::GetFlag(FLAGS_config));
Brian Silverman70f88512014-12-27 19:27:35 -0800260
Austin Schuh14056182023-01-03 21:19:38 -0800261 aos::ShmEventLoop event_loop(&config.message());
262 event_loop.SkipTimingReport();
263 event_loop.SkipAosLog();
Maxwell Gumleyb27245f2024-04-11 15:46:22 -0600264 aos::util::Top top(&event_loop, aos::util::Top::TrackThreadsMode::kEnabled,
265 aos::util::Top::TrackPerThreadInfoMode::kDisabled);
Austin Schuh14056182023-01-03 21:19:38 -0800266 top.set_track_top_processes(true);
milind-uf963ee92021-11-06 16:10:07 -0700267
Austin Schuh14056182023-01-03 21:19:38 -0800268 const cpu_set_t all_cpus = FindAllCpus();
Brian Silverman70f88512014-12-27 19:27:35 -0800269
Austin Schuh14056182023-01-03 21:19:38 -0800270 top.set_on_reading_update([&]() {
271 std::multiset<Thread> threads;
Brian Silverman70f88512014-12-27 19:27:35 -0800272
Austin Schuh14056182023-01-03 21:19:38 -0800273 for (const std::pair<const pid_t, aos::util::Top::ProcessReadings>
274 &reading : top.readings()) {
275 pid_t tid = reading.first;
276 bool not_there = false;
Brian Silverman70f88512014-12-27 19:27:35 -0800277
Austin Schuh14056182023-01-03 21:19:38 -0800278 const cpu_set_t cpu_mask = find_cpu_mask(tid, &not_there);
279 const sched_param param = find_sched_param(tid, &not_there);
280 const int scheduler = find_scheduler(tid, &not_there);
281 const ::std::string exe = find_exe(tid, &not_there);
282 const int nice_value = find_nice_value(tid, &not_there);
Brian Silverman70f88512014-12-27 19:27:35 -0800283
Austin Schuh14056182023-01-03 21:19:38 -0800284 int ppid = 0, sid = 0;
285 read_stat(tid, &ppid, &sid, &not_there);
Brian Silverman70f88512014-12-27 19:27:35 -0800286
Austin Schuh14056182023-01-03 21:19:38 -0800287 int pgrp = 0;
288 ::std::string name;
289 read_status(tid, ppid, &pgrp, &name, &not_there);
Brian Silverman70f88512014-12-27 19:27:35 -0800290
Austin Schuh14056182023-01-03 21:19:38 -0800291 if (not_there) continue;
milind-uf963ee92021-11-06 16:10:07 -0700292
milind-ua313a5b2023-02-24 15:17:45 -0800293 std::string_view cpu_mask_string;
294 if (CPU_EQUAL(&cpu_mask, &all_cpus)) {
295 cpu_mask_string = "all";
296 } else {
297 // Convert the CPU mask to a bitset
298 std::bitset<CPU_SETSIZE> bitset;
299 for (size_t i = 0; i < CPU_SETSIZE; i++) {
300 bitset[i] = CPU_ISSET(i, &cpu_mask);
301 }
302 std::stringstream ss;
303 ss << std::hex << bitset.to_ulong();
304 cpu_mask_string = ss.str();
305 }
Austin Schuh14056182023-01-03 21:19:38 -0800306
307 threads.emplace(Thread{.policy = static_cast<uint32_t>(scheduler),
308 .exe = exe,
309 .name = name,
milind-ua313a5b2023-02-24 15:17:45 -0800310 .cpu_mask = cpu_mask_string.data(),
Austin Schuh14056182023-01-03 21:19:38 -0800311 .nice_value = nice_value,
312 .sched_priority = param.sched_priority,
313 .tid = tid,
314 .pid = pgrp,
315 .ppid = ppid,
316 .sid = sid});
317 }
318
319 printf("exe,name,cpumask,policy,nice,priority,tid,pid,ppid,sid\n");
320 for (const auto &t : threads) {
321 t.Print();
322 }
323 event_loop.Exit();
324 });
325
326 event_loop.Run();
Brian Silverman70f88512014-12-27 19:27:35 -0800327}