blob: 32ff65d8d35a3fc698f9c4dfd0c21d3fdbf753ab [file] [log] [blame]
James Kuszmaul418fd062022-03-22 15:22:27 -07001#ifndef AOS_UTIL_TOP_H_
2#define AOS_UTIL_TOP_H_
3
4#include <map>
5#include <string>
6
7#include "aos/containers/ring_buffer.h"
8#include "aos/events/event_loop.h"
9#include "aos/util/process_info_generated.h"
10
11namespace aos::util {
12
13// ProcStat is a struct to hold all the fields available in /proc/[pid]/stat.
14// Currently we only use a small subset of the feilds. See man 5 proc for
15// details on what the fields are--these are in the same order as they appear in
16// the stat file.
17//
18// Things are signed or unsigned based on whether they are listed
19// as signed/unsigned in man 5 proc. We just make everything 64 bits wide
20// because otherwise we have to write out way too many casts everywhere.
21struct ProcStat {
22 int pid;
23 std::string name;
24 char state;
25 int64_t parent_pid;
26 int64_t group_id;
27 int64_t session_id;
28 int64_t tty;
29 int64_t tpgid;
30 uint64_t kernel_flags;
31 uint64_t minor_faults;
32 uint64_t children_minor_faults;
33 uint64_t major_faults;
34 uint64_t children_major_faults;
35 uint64_t user_mode_ticks;
36 uint64_t kernel_mode_ticks;
37 int64_t children_user_mode_ticks;
38 int64_t children_kernel_mode_ticks;
39 int64_t priority;
40 int64_t nice;
41 int64_t num_threads;
42 int64_t itrealvalue; // always zero.
43 uint64_t start_time_ticks;
44 uint64_t virtual_memory_size;
45 // Number of pages in real memory.
46 int64_t resident_set_size;
47 uint64_t rss_soft_limit;
48 uint64_t start_code_address;
49 uint64_t end_code_address;
50 uint64_t start_stack_address;
51 uint64_t stack_pointer;
52 uint64_t instruction_pointer;
53 uint64_t signal_bitmask;
54 uint64_t blocked_signals;
55 uint64_t ignored_signals;
56 uint64_t caught_signals;
57 uint64_t wchan;
58 // swap_pages fields are not maintained.
59 uint64_t swap_pages;
60 uint64_t children_swap_pages;
61 int64_t exit_signal;
62 // CPU number last exitted on.
63 int64_t processor;
64 // Zero for non-realtime processes.
65 uint64_t rt_priority;
66 uint64_t scheduling_policy;
67 // Aggregated block I/O delay.
68 uint64_t block_io_delay_ticks;
69 uint64_t guest_ticks;
70 uint64_t children_guest_ticks;
71 uint64_t start_data_address;
72 uint64_t end_data_address;
73 uint64_t start_brk_address;
74 uint64_t start_arg_address;
75 uint64_t end_arg_address;
76 uint64_t start_env_address;
77 uint64_t end_env_address;
78 int64_t exit_code;
79};
80
81// Retrieves the stats for a particular process (note that there also exists a
82// /proc/[pid]/task/[tid]/stat with the same format for per-thread information;
83// we currently do not read that).
84// Returns nullopt if unable to read/parse the file.
85std::optional<ProcStat> ReadProcStat(int pid);
86
87// This class provides a basic utility for retrieving general performance
88// information on running processes (named after the top utility). It can either
89// be used to directly get information on individual processes (via
90// set_track_pids()) or used to track a list of the top N processes with the
91// highest CPU usage.
92// Note that this currently relies on sampling processes in /proc every second
93// and using the differences between the two readings to calculate CPU usage.
94// For crash-looping processees or other situations with highly variable or
95// extremely short-lived loads, this may do a poor job of capturing information.
96class Top {
97 public:
98 Top(aos::EventLoop *event_loop);
99
100 // Set whether to track all the top processes (this will result in us having
101 // to track every single process on the system, so that we can sort them).
102 void set_track_top_processes(bool track_all) { track_all_ = track_all; }
103
104 // Specify a set of individual processes to track statistics for.
105 // This can be changed at run-time, although it may take up to kSamplePeriod
106 // to have full statistics on all the relevant processes, since we need at
107 // least two samples to estimate CPU usage.
108 void set_track_pids(const std::set<pid_t> &pids) { pids_to_track_ = pids; }
109
110 // Retrieve statistics for the specified process. Will return the null offset
111 // of no such pid is being tracked.
112 flatbuffers::Offset<ProcessInfo> InfoForProcess(
113 flatbuffers::FlatBufferBuilder *fbb, pid_t pid);
114
115 // Returns information on up to n processes, sorted by CPU usage.
116 flatbuffers::Offset<TopProcessesFbs> TopProcesses(
117 flatbuffers::FlatBufferBuilder *fbb, int n);
118
119 private:
120 // Rate at which to sample /proc/[pid]/stat.
121 static constexpr std::chrono::seconds kSamplePeriod{1};
122
123 struct Reading {
124 aos::monotonic_clock::time_point reading_time;
125 std::chrono::nanoseconds total_run_time;
126 uint64_t memory_usage;
127 };
128
129 struct ProcessReadings {
130 std::string name;
131 aos::monotonic_clock::time_point start_time;
132 // CPU usage is based on the past two readings.
133 double cpu_percent;
134 aos::RingBuffer<Reading, 2> readings;
135 };
136
137 std::chrono::nanoseconds TotalProcessTime(const ProcStat &proc_stat);
138 aos::monotonic_clock::time_point ProcessStartTime(const ProcStat &proc_stat);
139 uint64_t RealMemoryUsage(const ProcStat &proc_stat);
140 void UpdateReadings();
141
142 aos::EventLoop *event_loop_;
143
144 // Length of a clock tick (used to convert from raw numbers in /proc to actual
145 // times).
146 const std::chrono::nanoseconds clock_tick_;
147 // Page size, in bytes, on the current system.
148 const long page_size_;
149
150 std::set<pid_t> pids_to_track_;
151 bool track_all_ = false;
152
153 std::map<pid_t, ProcessReadings> readings_;
154};
155
156} // namespace aos::util
157#endif // AOS_UTIL_TOP_H_