AP_Scheduler: add per-task performance information accesible from @SYS/tasks.txt
print fully qualified name on most boards remove old task slip message Don't die on allocation failure. dynamically allocation/free task info based on SCHED_OPTIONS dynamically enable task info on ftp get
This commit is contained in:
parent
f70f13e620
commit
5b5b9d57a1
@ -57,6 +57,13 @@ const AP_Param::GroupInfo AP_Scheduler::var_info[] = {
|
|||||||
// @User: Advanced
|
// @User: Advanced
|
||||||
AP_GROUPINFO("LOOP_RATE", 1, AP_Scheduler, _loop_rate_hz, SCHEDULER_DEFAULT_LOOP_RATE),
|
AP_GROUPINFO("LOOP_RATE", 1, AP_Scheduler, _loop_rate_hz, SCHEDULER_DEFAULT_LOOP_RATE),
|
||||||
|
|
||||||
|
// @Param: OPTIONS
|
||||||
|
// @DisplayName: Scheduling options
|
||||||
|
// @Description: This controls optional aspects of the scheduler.
|
||||||
|
// @Bitmask: 0:Enable per-task perf info
|
||||||
|
// @User: Advanced
|
||||||
|
AP_GROUPINFO("OPTIONS", 2, AP_Scheduler, _options, 0),
|
||||||
|
|
||||||
AP_GROUPEND
|
AP_GROUPEND
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -114,6 +121,10 @@ void AP_Scheduler::init(const AP_Scheduler::Task *tasks, uint8_t num_tasks, uint
|
|||||||
perf_info.set_loop_rate(get_loop_rate_hz());
|
perf_info.set_loop_rate(get_loop_rate_hz());
|
||||||
perf_info.reset();
|
perf_info.reset();
|
||||||
|
|
||||||
|
if (_options & uint8_t(Options::RECORD_TASK_INFO)) {
|
||||||
|
perf_info.allocate_task_info(_num_tasks);
|
||||||
|
}
|
||||||
|
|
||||||
_log_performance_bit = log_performance_bit;
|
_log_performance_bit = log_performance_bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,13 +184,7 @@ void AP_Scheduler::run(uint32_t time_available)
|
|||||||
_task_time_allowed = task.max_time_micros;
|
_task_time_allowed = task.max_time_micros;
|
||||||
|
|
||||||
if (dt >= interval_ticks*2) {
|
if (dt >= interval_ticks*2) {
|
||||||
// we've slipped a whole run of this task!
|
perf_info.task_slipped(i);
|
||||||
debug(2, "Scheduler slip task[%u-%s] (%u/%u/%u)\n",
|
|
||||||
(unsigned)i,
|
|
||||||
task.name,
|
|
||||||
(unsigned)dt,
|
|
||||||
(unsigned)interval_ticks,
|
|
||||||
(unsigned)_task_time_allowed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dt >= interval_ticks*max_task_slowdown) {
|
if (dt >= interval_ticks*max_task_slowdown) {
|
||||||
@ -216,8 +221,9 @@ void AP_Scheduler::run(uint32_t time_available)
|
|||||||
// work out how long the event actually took
|
// work out how long the event actually took
|
||||||
now = AP_HAL::micros();
|
now = AP_HAL::micros();
|
||||||
uint32_t time_taken = now - _task_time_started;
|
uint32_t time_taken = now - _task_time_started;
|
||||||
|
bool overrun = false;
|
||||||
if (time_taken > _task_time_allowed) {
|
if (time_taken > _task_time_allowed) {
|
||||||
|
overrun = true;
|
||||||
// the event overran!
|
// the event overran!
|
||||||
debug(3, "Scheduler overrun task[%u-%s] (%u/%u)\n",
|
debug(3, "Scheduler overrun task[%u-%s] (%u/%u)\n",
|
||||||
(unsigned)i,
|
(unsigned)i,
|
||||||
@ -225,6 +231,9 @@ void AP_Scheduler::run(uint32_t time_available)
|
|||||||
(unsigned)time_taken,
|
(unsigned)time_taken,
|
||||||
(unsigned)_task_time_allowed);
|
(unsigned)_task_time_allowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
perf_info.update_task_info(i, time_taken, overrun);
|
||||||
|
|
||||||
if (time_taken >= time_available) {
|
if (time_taken >= time_available) {
|
||||||
time_available = 0;
|
time_available = 0;
|
||||||
break;
|
break;
|
||||||
@ -365,6 +374,12 @@ void AP_Scheduler::update_logging()
|
|||||||
}
|
}
|
||||||
perf_info.set_loop_rate(get_loop_rate_hz());
|
perf_info.set_loop_rate(get_loop_rate_hz());
|
||||||
perf_info.reset();
|
perf_info.reset();
|
||||||
|
// dynamically update the per-task perf counter
|
||||||
|
if (!(_options & uint8_t(Options::RECORD_TASK_INFO)) && perf_info.has_task_info()) {
|
||||||
|
perf_info.free_task_info();
|
||||||
|
} else if ((_options & uint8_t(Options::RECORD_TASK_INFO)) && !perf_info.has_task_info()) {
|
||||||
|
perf_info.allocate_task_info(_num_tasks);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a performance monitoring packet
|
// Write a performance monitoring packet
|
||||||
@ -389,6 +404,61 @@ void AP_Scheduler::Log_Write_Performance()
|
|||||||
AP::logger().WriteCriticalBlock(&pkt, sizeof(pkt));
|
AP::logger().WriteCriticalBlock(&pkt, sizeof(pkt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// display task statistics as text buffer for @SYS/tasks.txt
|
||||||
|
size_t AP_Scheduler::task_info(char *buf, size_t bufsize)
|
||||||
|
{
|
||||||
|
size_t total = 0;
|
||||||
|
|
||||||
|
// a header to allow for machine parsers to determine format
|
||||||
|
int n = hal.util->snprintf(buf, bufsize, "TasksV1\n");
|
||||||
|
|
||||||
|
if (n <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dynamically enable statistics collection
|
||||||
|
if (!(_options & uint8_t(Options::RECORD_TASK_INFO))) {
|
||||||
|
_options |= uint8_t(Options::RECORD_TASK_INFO);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (perf_info.get_task_info(0) == nullptr) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf += n;
|
||||||
|
bufsize -= n;
|
||||||
|
total += n;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < _num_tasks; i++) {
|
||||||
|
const AP_Scheduler::Task& task = (i < _num_unshared_tasks) ? _tasks[i] : _common_tasks[i - _num_unshared_tasks];
|
||||||
|
const AP::PerfInfo::TaskInfo* ti = perf_info.get_task_info(i);
|
||||||
|
|
||||||
|
uint16_t avg = 0;
|
||||||
|
if (ti->tick_count > 0) {
|
||||||
|
avg = MIN(uint16_t(ti->elapsed_time_us / ti->tick_count), 999);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAL_MINIMIZE_FEATURES
|
||||||
|
const char* fmt = "%-16.16s MIN=%3u MAX=%3u AVG=%3u OVR=%3u SLP=%3u\n";
|
||||||
|
#else
|
||||||
|
const char* fmt = "%-32.32s MIN=%3u MAX=%3u AVG=%3u OVR=%3u SLP=%3u\n";
|
||||||
|
#endif
|
||||||
|
n = hal.util->snprintf(buf, bufsize, fmt, task.name,
|
||||||
|
unsigned(MIN(ti->min_time_us, 999)), unsigned(MIN(ti->max_time_us, 999)), unsigned(avg),
|
||||||
|
unsigned(MIN(ti->overrun_count, 999)), unsigned(MIN(ti->slip_count, 999)));
|
||||||
|
|
||||||
|
if (n <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf += n;
|
||||||
|
bufsize -= n;
|
||||||
|
total += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
namespace AP {
|
namespace AP {
|
||||||
|
|
||||||
AP_Scheduler &scheduler()
|
AP_Scheduler &scheduler()
|
||||||
|
@ -22,10 +22,15 @@
|
|||||||
|
|
||||||
#include <AP_Param/AP_Param.h>
|
#include <AP_Param/AP_Param.h>
|
||||||
#include <AP_HAL/Util.h>
|
#include <AP_HAL/Util.h>
|
||||||
|
#include <AP_HAL/AP_HAL.h>
|
||||||
#include <AP_Math/AP_Math.h>
|
#include <AP_Math/AP_Math.h>
|
||||||
#include "PerfInfo.h" // loop perf monitoring
|
#include "PerfInfo.h" // loop perf monitoring
|
||||||
|
|
||||||
#define AP_SCHEDULER_NAME_INITIALIZER(_name) .name = #_name,
|
#if HAL_MINIMIZE_FEATURES
|
||||||
|
#define AP_SCHEDULER_NAME_INITIALIZER(_clazz,_name) .name = #_name,
|
||||||
|
#else
|
||||||
|
#define AP_SCHEDULER_NAME_INITIALIZER(_clazz,_name) .name = #_clazz "::" #_name,
|
||||||
|
#endif
|
||||||
#define LOOP_RATE 0
|
#define LOOP_RATE 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -33,7 +38,7 @@
|
|||||||
*/
|
*/
|
||||||
#define SCHED_TASK_CLASS(classname, classptr, func, _rate_hz, _max_time_micros) { \
|
#define SCHED_TASK_CLASS(classname, classptr, func, _rate_hz, _max_time_micros) { \
|
||||||
.function = FUNCTOR_BIND(classptr, &classname::func, void),\
|
.function = FUNCTOR_BIND(classptr, &classname::func, void),\
|
||||||
AP_SCHEDULER_NAME_INITIALIZER(func)\
|
AP_SCHEDULER_NAME_INITIALIZER(classname, func)\
|
||||||
.rate_hz = _rate_hz,\
|
.rate_hz = _rate_hz,\
|
||||||
.max_time_micros = _max_time_micros\
|
.max_time_micros = _max_time_micros\
|
||||||
}
|
}
|
||||||
@ -74,6 +79,10 @@ public:
|
|||||||
uint16_t max_time_micros;
|
uint16_t max_time_micros;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Options : uint8_t {
|
||||||
|
RECORD_TASK_INFO = 1 << 0
|
||||||
|
};
|
||||||
|
|
||||||
// initialise scheduler
|
// initialise scheduler
|
||||||
void init(const Task *tasks, uint8_t num_tasks, uint32_t log_performance_bit);
|
void init(const Task *tasks, uint8_t num_tasks, uint32_t log_performance_bit);
|
||||||
|
|
||||||
@ -147,6 +156,8 @@ public:
|
|||||||
|
|
||||||
HAL_Semaphore &get_semaphore(void) { return _rsem; }
|
HAL_Semaphore &get_semaphore(void) { return _rsem; }
|
||||||
|
|
||||||
|
size_t task_info(char *buf, size_t bufsize);
|
||||||
|
|
||||||
static const struct AP_Param::GroupInfo var_info[];
|
static const struct AP_Param::GroupInfo var_info[];
|
||||||
|
|
||||||
// loop performance monitoring:
|
// loop performance monitoring:
|
||||||
@ -164,6 +175,9 @@ private:
|
|||||||
|
|
||||||
// loop rate in Hz as set at startup
|
// loop rate in Hz as set at startup
|
||||||
AP_Int16 _active_loop_rate_hz;
|
AP_Int16 _active_loop_rate_hz;
|
||||||
|
|
||||||
|
// scheduler options
|
||||||
|
AP_Int8 _options;
|
||||||
|
|
||||||
// calculated loop period in usec
|
// calculated loop period in usec
|
||||||
uint16_t _loop_period_us;
|
uint16_t _loop_period_us;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <AP_Logger/AP_Logger.h>
|
#include <AP_Logger/AP_Logger.h>
|
||||||
#include <GCS_MAVLink/GCS.h>
|
#include <GCS_MAVLink/GCS.h>
|
||||||
|
#include <AP_InternalError/AP_InternalError.h>
|
||||||
#include "AP_Scheduler.h"
|
#include "AP_Scheduler.h"
|
||||||
|
|
||||||
extern const AP_HAL::HAL& hal;
|
extern const AP_HAL::HAL& hal;
|
||||||
@ -29,6 +30,50 @@ void AP::PerfInfo::ignore_this_loop()
|
|||||||
ignore_loop = true;
|
ignore_loop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// allocate the array of task statistics for use by @SYS/tasks.txt
|
||||||
|
void AP::PerfInfo::allocate_task_info(uint8_t num_tasks)
|
||||||
|
{
|
||||||
|
_task_info = new TaskInfo[num_tasks];
|
||||||
|
if (_task_info == nullptr) {
|
||||||
|
hal.console->printf("Unable to allocate scheduler TaskInfo\n");
|
||||||
|
_num_tasks = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_num_tasks = num_tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AP::PerfInfo::free_task_info()
|
||||||
|
{
|
||||||
|
delete[] _task_info;
|
||||||
|
_task_info = nullptr;
|
||||||
|
_num_tasks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// called after each run of a task to update its statistics based on measurements taken by the scheduler
|
||||||
|
void AP::PerfInfo::update_task_info(uint8_t task_index, uint16_t task_time_us, bool overrun)
|
||||||
|
{
|
||||||
|
if (_task_info == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task_index >= _num_tasks) {
|
||||||
|
INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TaskInfo& ti = _task_info[task_index];
|
||||||
|
ti.max_time_us = MAX(ti.max_time_us, task_time_us);
|
||||||
|
if (ti.min_time_us == 0) {
|
||||||
|
ti.min_time_us = task_time_us;
|
||||||
|
} else {
|
||||||
|
ti.min_time_us = MIN(ti.min_time_us, task_time_us);
|
||||||
|
}
|
||||||
|
ti.elapsed_time_us += task_time_us;
|
||||||
|
ti.tick_count++;
|
||||||
|
if (overrun) {
|
||||||
|
ti.overrun_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check_loop_time - check latest loop time vs min, max and overtime threshold
|
// check_loop_time - check latest loop time vs min, max and overtime threshold
|
||||||
void AP::PerfInfo::check_loop_time(uint32_t time_in_micros)
|
void AP::PerfInfo::check_loop_time(uint32_t time_in_micros)
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,16 @@ class PerfInfo {
|
|||||||
public:
|
public:
|
||||||
PerfInfo() {}
|
PerfInfo() {}
|
||||||
|
|
||||||
|
// per-task timing information
|
||||||
|
struct TaskInfo {
|
||||||
|
uint16_t min_time_us;
|
||||||
|
uint16_t max_time_us;
|
||||||
|
uint32_t elapsed_time_us;
|
||||||
|
uint32_t tick_count;
|
||||||
|
uint16_t slip_count;
|
||||||
|
uint16_t overrun_count;
|
||||||
|
};
|
||||||
|
|
||||||
/* Do not allow copies */
|
/* Do not allow copies */
|
||||||
PerfInfo(const PerfInfo &other) = delete;
|
PerfInfo(const PerfInfo &other) = delete;
|
||||||
PerfInfo &operator=(const PerfInfo&) = delete;
|
PerfInfo &operator=(const PerfInfo&) = delete;
|
||||||
@ -26,6 +36,24 @@ public:
|
|||||||
|
|
||||||
void update_logging();
|
void update_logging();
|
||||||
|
|
||||||
|
// allocate the array of task statistics for use by @SYS/tasks.txt
|
||||||
|
void allocate_task_info(uint8_t num_tasks);
|
||||||
|
void free_task_info();
|
||||||
|
// whether or not we have task info allocated
|
||||||
|
bool has_task_info() { return _task_info != nullptr; }
|
||||||
|
// return a task info
|
||||||
|
const TaskInfo* get_task_info(uint8_t task_index) const {
|
||||||
|
return (_task_info && task_index < _num_tasks) ? &_task_info[task_index] : nullptr;
|
||||||
|
}
|
||||||
|
// called after each run of a task to update its statistics based on measurements taken by the scheduler
|
||||||
|
void update_task_info(uint8_t task_index, uint16_t task_time_us, bool overrun);
|
||||||
|
// record that a task slipped
|
||||||
|
void task_slipped(uint8_t task_index) {
|
||||||
|
if (_task_info && task_index < _num_tasks) {
|
||||||
|
_task_info[task_index].overrun_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint16_t loop_rate_hz;
|
uint16_t loop_rate_hz;
|
||||||
uint16_t overtime_threshold_micros;
|
uint16_t overtime_threshold_micros;
|
||||||
@ -38,7 +66,9 @@ private:
|
|||||||
uint32_t last_check_us;
|
uint32_t last_check_us;
|
||||||
float filtered_loop_time;
|
float filtered_loop_time;
|
||||||
bool ignore_loop;
|
bool ignore_loop;
|
||||||
|
// performance monitoring
|
||||||
|
uint8_t _num_tasks;
|
||||||
|
TaskInfo* _task_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user