Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
CpuLoad.hxx
Go to the documentation of this file.
1
34#include "openmrn_features.h"
35
36#ifdef OPENMRN_FEATURE_THREAD_FREERTOS
37
38#ifndef _OS_CPULOAD_HXX_
39#define _OS_CPULOAD_HXX_
40
41#include <algorithm>
42
44#include "utils/SimpleQueue.hxx"
45#include "utils/Singleton.hxx"
47#include "freertos_includes.h"
48
49extern "C" {
54 void cpuload_tick(unsigned irq);
55}
56
68class CpuLoad : public Singleton<CpuLoad> {
69public:
70 CpuLoad() {}
71
72 ~CpuLoad()
73 {
74 while (!perKeyCost_.empty())
75 {
76 delete perKeyCost_.pop_front();
77 }
78 }
79
82 uint8_t get_load();
83
86 uint8_t get_max_consecutive() {
87 return maxConsecutive_;
88 }
89
91 void clear_max_consecutive() {
92 maxConsecutive_ = 0;
93 }
94
96 uint8_t get_peak_over_16_counts() {
97 return peakOver16Counts_;
98 }
99
101 void clear_peak_over_16_counts() {
102 peakOver16Counts_ = 0;
103 }
104
107 uintptr_t new_key()
108 {
109 auto k = newKey_;
110 newKey_ = 0;
111 return k;
112 }
113
115 void set_key_description(uintptr_t key, string description)
116 {
117 auto it = perKeyCost_.begin();
118 for (; it != perKeyCost_.end(); ++it)
119 {
120 if (it->key == key)
121 {
122 it->description = std::move(description);
123 return;
124 }
125 }
126 auto *kk = new KeyInfo;
127 kk->key = key;
128 kk->description = std::move(description);
129 perKeyCost_.insert(it, kk);
130 }
131
135 uint32_t get_utilization_delta(
136 std::vector<pair<unsigned, string *>> *output)
137 {
138 HASSERT(output);
139 output->clear();
140 for (auto it = perKeyCost_.begin(); it != perKeyCost_.end(); ++it)
141 {
142 volatile unsigned curr = it->rolling_count;
143 unsigned diff = curr - it->last_count;
144 it->last_count = curr;
145 if (diff > 0)
146 {
147 output->emplace_back(diff, &it->description);
148 }
149 }
150 uint32_t ret = countSinceUpdate_;
151 countSinceUpdate_ = 0;
152 return ret;
153 }
154
155private:
156 friend void cpuload_tick(unsigned);
161 inline void record_value(bool busy, uintptr_t key);
162
166 uint32_t avg_{0};
167
169 uint32_t countSinceUpdate_{0};
170
172 uintptr_t newKey_{0};
173
174 struct KeyInfo : public QMember
175 {
177 uintptr_t key;
179 string description;
181 uint32_t last_count{0};
183 uint32_t rolling_count{0};
184 };
185
187 TypedQueue<KeyInfo> perKeyCost_;
188
190 uint8_t consecutive_{0};
192 uint8_t maxConsecutive_{0};
194 uint16_t last16Bits_{0};
196 uint8_t peakOver16Counts_{0};
197};
198
199class CpuLoadLog : public StateFlowBase
200{
201public:
202 CpuLoadLog(Service *service)
203 : StateFlowBase(service)
204 {
205 start_flow(STATE(log_and_wait));
206 }
207
208private:
209 Action log_and_wait()
210 {
211 auto *l = CpuLoad::instance();
212 uint32_t ex_count = service()->executor()->sequence();
213 LOG(INFO,
214 "Ex %d|FreeHeap %d|Buf %d|Load: avg %3d max streak %d max of 16 %d",
215 (int)(ex_count - executorLastCount_), (int)os_get_free_heap(),
216 (int)mainBufferPool->total_size(), l->get_load(),
217 l->get_max_consecutive(), l->get_peak_over_16_counts());
218 executorLastCount_ = ex_count;
219 l->clear_max_consecutive();
220 l->clear_peak_over_16_counts();
221 vector<pair<unsigned, string *>> per_task_ticks;
222 unsigned c = l->get_utilization_delta(&per_task_ticks);
223 std::sort(per_task_ticks.begin(), per_task_ticks.end(),
224 std::greater<decltype(per_task_ticks)::value_type>());
225 string details;
226 for (volatile auto it : per_task_ticks)
227 {
228 int perc = it.first * 1000 / c;
229 details += StringPrintf(" | %d.%d:", perc / 10, perc % 10);
230 details += *it.second;
231 }
232 log_output((char *)details.data(), details.size());
233 auto k = l->new_key();
234 if (k < 300)
235 {
236 l->set_key_description(k, StringPrintf("irq-%u", (unsigned)k));
237 }
238 else if (k & 1)
239 {
240 l->set_key_description(
241 k, StringPrintf("ex 0x%x", (unsigned)(k & ~1)));
242 }
243 else
244 {
245#if tskKERNEL_VERSION_MAJOR < 9
246 char *name = pcTaskGetTaskName((TaskHandle_t)k);
247#else
248 char *name = pcTaskGetName((TaskHandle_t)k);
249#endif
250 l->set_key_description(k, name);
251 }
252 return sleep_and_call(&timer_, MSEC_TO_NSEC(2000), STATE(log_and_wait));
253 }
254
255 uint32_t executorLastCount_{0};
256 StateFlowTimer timer_{this};
257};
258
259#endif // _OS_CPULOAD_HXX_
260#endif // OPENMRN_FEATURE_THREAD_FREERTOS
DynamicPool * mainBufferPool
main buffer pool instance
Definition Buffer.cxx:37
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
virtual uint32_t sequence()=0
size_t total_size()
Definition Buffer.hxx:281
Essentially a "next" pointer container.
Definition QMember.hxx:42
Collection of related state machines that pend on incoming messages.
ExecutorBase * executor()
Singleton class.
Definition Singleton.hxx:65
Base class for state machines.
Service * service()
Return a pointer to the service I am bound to.
void start_flow(Callback c)
Resets the flow to the specified state and starts it.
Action sleep_and_call(::Timer *timer, long long timeout_nsec, Callback c)
Suspends execution of this control flow for a specified time.
A simple, fast, type-safe single-linked queue class with non-virtual methods.
void log_output(char *buf, int size)
Prints a line of log to the log output destination.
Definition logging.cxx:73
#define LOG(level, message...)
Conditionally write a message to the logging output.
Definition logging.h:99
static const int INFO
Loglevel that is printed by default, reporting some status information.
Definition logging.h:57
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
ssize_t os_get_free_heap()
Definition os.c:914
#define MSEC_TO_NSEC(_msec)
Convert a millisecond value to a nanosecond value.
Definition os.h:268