LCOV - code coverage report
Current view: top level - AP_Scheduler - PerfInfo.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 83 100 83.0 %
Date: 2026-09-09 11:02:08 Functions: 14 18 77.8 %

          Line data    Source code
       1             : #include "AP_Scheduler_config.h"
       2             : 
       3             : #if AP_SCHEDULER_ENABLED
       4             : 
       5             : #include "PerfInfo.h"
       6             : 
       7             : #include <AP_Logger/AP_Logger.h>
       8             : #include <GCS_MAVLink/GCS.h>
       9             : #include <AP_InternalError/AP_InternalError.h>
      10             : #include "AP_Scheduler.h"
      11             : 
      12             : extern const AP_HAL::HAL& hal;
      13             : 
      14             : //
      15             : //  high level performance monitoring
      16             : //
      17             : //  we measure the main loop time
      18             : //
      19             : 
      20             : // loops over time by this amount or more won't be counted in filtered loop time (and thus loop rate)
      21             : #ifndef AP_SCHEDULER_OVERTIME_MARGIN_US
      22             : #define AP_SCHEDULER_OVERTIME_MARGIN_US 10000UL
      23             : #endif
      24             : 
      25             : // reset - reset all records of loop time to zero
      26       21508 : void AP::PerfInfo::reset()
      27             : {
      28       21508 :     loop_count = 0;
      29       21508 :     max_time = 0;
      30       21508 :     min_time = 0;
      31       21508 :     long_running = 0;
      32       21508 :     sigma_time = 0;
      33       21508 :     sigmasquared_time = 0;
      34       21508 :     if (_task_info != nullptr) {
      35          12 :         memset(_task_info, 0, (_num_tasks) * sizeof(TaskInfo));
      36             :     }
      37       21508 : }
      38             : 
      39             : // ignore_loop - ignore this loop from performance measurements (used to reduce false positive when arming)
      40         599 : void AP::PerfInfo::ignore_this_loop()
      41             : {
      42         599 :     ignore_loop = true;
      43         599 : }
      44             : 
      45             : // allocate the array of task statistics for use by @SYS/tasks.txt
      46           1 : void AP::PerfInfo::allocate_task_info(uint8_t num_tasks)
      47             : {
      48           1 :     _task_info = NEW_NOTHROW TaskInfo[num_tasks];
      49           1 :     if (_task_info == nullptr) {
      50           0 :         DEV_PRINTF("Unable to allocate scheduler TaskInfo\n");
      51           0 :         _num_tasks = 0;
      52           0 :         return;
      53             :     }
      54           1 :     _num_tasks = num_tasks;
      55             : }
      56             : 
      57           1 : void AP::PerfInfo::free_task_info()
      58             : {
      59           1 :     delete[] _task_info;
      60           1 :     _task_info = nullptr;
      61           1 :     _num_tasks = 0;
      62           1 : }
      63             : 
      64             : // called after each run of a task to update its statistics based on measurements taken by the scheduler
      65  1133017028 : void AP::PerfInfo::update_task_info(uint8_t task_index, uint16_t task_time_us, bool overrun)
      66             : {
      67  1133017028 :     if (_task_info == nullptr) {
      68  1131646844 :         return;
      69             :     }
      70             : 
      71     1370184 :     if (task_index >= _num_tasks) {
      72           0 :         INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
      73           0 :         return;
      74             :     }
      75     1370184 :     TaskInfo& ti = _task_info[task_index];
      76     1370184 :     ti.update(task_time_us, overrun);
      77             : }
      78             : 
      79     1370184 : void AP::PerfInfo::TaskInfo::update(uint16_t task_time_us, bool overrun)
      80             : {
      81     1370184 :     max_time_us = MAX(max_time_us, task_time_us);
      82     1370184 :     if (min_time_us == 0) {
      83     1370184 :         min_time_us = task_time_us;
      84             :     } else {
      85           0 :         min_time_us = MIN(min_time_us, task_time_us);
      86             :     }
      87     1370184 :     elapsed_time_us += task_time_us;
      88     1370184 :     tick_count++;
      89     1370184 :     if (overrun) {
      90           0 :         overrun_count++;
      91             :     }
      92     1370184 : }
      93             : 
      94         170 : void AP::PerfInfo::TaskInfo::print(const char* task_name, uint32_t total_time, ExpandingString& str) const
      95             : {
      96         170 :     uint16_t avg = 0;
      97         170 :     float pct = 0.0f;
      98         170 :     if (tick_count > 0) {
      99         170 :         pct = elapsed_time_us * 100.0f / total_time;
     100         170 :         avg = MIN(uint16_t(elapsed_time_us / tick_count), 9999);
     101             :     }
     102             : #if AP_SCHEDULER_EXTENDED_TASKINFO_ENABLED
     103         170 :     const char* fmt = "%-32.32s MIN=%4u MAX=%4u AVG=%4u OVR=%3u SLP=%3u, TOT=%4.1f%%\n";
     104             : #else
     105             :     const char* fmt = "%-16.16s MIN=%4u MAX=%4u AVG=%4u OVR=%3u SLP=%3u, TOT=%4.1f%%\n";
     106             : #endif
     107         170 :     str.printf(fmt, task_name,
     108         170 :                 unsigned(MIN(min_time_us, 9999)), unsigned(MIN(max_time_us, 9999)), unsigned(avg),
     109         170 :                 unsigned(MIN(overrun_count, 999)), unsigned(MIN(slip_count, 999)), pct);
     110         170 : }
     111             : 
     112             : // check_loop_time - check latest loop time vs min, max and overtime threshold
     113    41684622 : void AP::PerfInfo::check_loop_time(uint32_t time_in_micros)
     114             : {
     115    41684622 :     loop_count++;
     116             : 
     117             :     // exit if this loop should be ignored
     118    41684622 :     if (ignore_loop) {
     119         599 :         ignore_loop = false;
     120         599 :         return;
     121             :     }
     122             : 
     123    41684023 :     if( time_in_micros > max_time) {
     124       40636 :         max_time = time_in_micros;
     125             :     }
     126    41684023 :     if( min_time == 0 || time_in_micros < min_time) {
     127       25876 :         min_time = time_in_micros;
     128             :     }
     129    41684023 :     if (time_in_micros > overtime_threshold_micros) {
     130       59673 :         long_running++;
     131             :     }
     132    41684023 :     sigma_time += time_in_micros;
     133    41684023 :     sigmasquared_time += time_in_micros * time_in_micros;
     134             : 
     135             :     /* we keep a filtered loop time for use as G_Dt which is the
     136             :        predicted time for the next loop. We remove really excessive
     137             :        times from this calculation so as not to throw it off too far
     138             :        in case we get a single long loop
     139             : 
     140             :        Note that the time we use here is the time between calls to
     141             :        check_loop_time() not the time from loop start to loop
     142             :        end. This is because we are using the time for time between
     143             :        calls to controllers, which has nothing to do with cpu speed.
     144             :     */
     145    41684023 :     const uint32_t now = AP_HAL::micros();
     146    41684023 :     const uint32_t loop_time_us = now - last_check_us;
     147    41684023 :     last_check_us = now;
     148    41684023 :     if (loop_time_us < overtime_threshold_micros + AP_SCHEDULER_OVERTIME_MARGIN_US) {
     149    41678872 :         filtered_loop_time = 0.99f * filtered_loop_time + 0.01f * loop_time_us * 1.0e-6f;
     150             :     } else {
     151             :         // esp32 is most likely to regularly trigger long loops, might be
     152             :         // helpful for bringup of other boards too
     153             : #if CONFIG_HAL_BOARD == HAL_BOARD_ESP32
     154             : #ifdef SCHEDDEBUG
     155             :         DEV_PRINTF("way overtime: %dus\n", loop_time_us);
     156             : #endif
     157             : #endif
     158             :     }
     159             : }
     160             : 
     161             : // get_num_loops: return number of loops used for recording performance
     162       18657 : uint16_t AP::PerfInfo::get_num_loops() const
     163             : {
     164       18657 :     return loop_count;
     165             : }
     166             : 
     167             : // get_max_time - return maximum loop time (in microseconds)
     168       18657 : uint32_t AP::PerfInfo::get_max_time() const
     169             : {
     170       18657 :     return max_time;
     171             : }
     172             : 
     173             : // get_min_time - return minumum loop time (in microseconds)
     174           0 : uint32_t AP::PerfInfo::get_min_time() const
     175             : {
     176           0 :     return min_time;
     177             : }
     178             : 
     179             : // get_num_long_running - get number of long running loops
     180       18657 : uint16_t AP::PerfInfo::get_num_long_running() const
     181             : {
     182       18657 :     return long_running;
     183             : }
     184             : 
     185             : // get_avg_time - return average loop time (in microseconds)
     186           0 : uint32_t AP::PerfInfo::get_avg_time() const
     187             : {
     188           0 :     return (sigma_time / loop_count);
     189             : }
     190             : 
     191             : // get_stddev_time - return stddev of average loop time (in us)
     192           0 : uint32_t AP::PerfInfo::get_stddev_time() const
     193             : {
     194           0 :     return sqrtf((sigmasquared_time - (sigma_time*sigma_time)/loop_count) / loop_count);
     195             : }
     196             : 
     197             : // get_filtered_time - return low pass filtered loop time in seconds
     198     1588453 : float AP::PerfInfo::get_filtered_time() const
     199             : {
     200     1588453 :     return filtered_loop_time;
     201             : }
     202             : 
     203             : // return low pass filtered loop rate in hz
     204     1584089 : float AP::PerfInfo::get_filtered_loop_rate_hz() const
     205             : {
     206     1584089 :     const float filt_time_s = get_filtered_time();
     207     1584089 :     if (filt_time_s <= 0) {
     208           0 :         return loop_rate_hz;
     209             :     }
     210     1584089 :     return 1.0 / filt_time_s;
     211             : }
     212             : 
     213             : 
     214           0 : void AP::PerfInfo::update_logging() const
     215             : {
     216           0 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO,
     217             :                     "PERF: %u/%u [%lu:%lu] F=%uHz sd=%lu Ex=%lu",
     218             :                     (unsigned)get_num_long_running(),
     219             :                     (unsigned)get_num_loops(),
     220             :                     (unsigned long)get_max_time(),
     221             :                     (unsigned long)get_min_time(),
     222             :                     (unsigned)(0.5+get_filtered_loop_rate_hz()),
     223             :                     (unsigned long)get_stddev_time(),
     224             :                     (unsigned long)AP::scheduler().get_extra_loop_us());
     225           0 : }
     226             : 
     227       21508 : void AP::PerfInfo::set_loop_rate(uint16_t rate_hz)
     228             : {
     229             :     // allow a 20% overrun before we consider a loop "slow":
     230       21508 :     overtime_threshold_micros = 1000000/rate_hz * 1.2f;
     231             : 
     232       21508 :     if (loop_rate_hz != rate_hz) {
     233        2094 :         loop_rate_hz = rate_hz;
     234        2094 :         filtered_loop_time = 1.0f / rate_hz;
     235             :     }
     236       21508 : }
     237             : 
     238             : #endif  // AP_SCHEDULER_ENABLED

Generated by: LCOV version 1.14