#include #include #include "task_stats.h" using namespace roro; void setUp() {} void tearDown() {} static TaskSample task(uint32_t id, const char* name, uint32_t runtime, uint16_t stackFree = 2000, int core = 1) { TaskSample t; t.id = id; std::snprintf(t.name, sizeof t.name, "%s", name); t.runtime = runtime; t.stackFree = stackFree; t.core = static_cast(core); t.state = 2; // blocked, unless a test says it's the one running return t; } static const TaskRow* find(const std::vector& rows, const char* name) { for (auto& r : rows) if (r.name == name) return &r; return nullptr; } // Q119: a task's share is its run time over the interval between two samples, in tenths of a percent. void test_shares_over_the_interval() { std::vector before = {task(1, "loopTask", 5000000), task(2, "IDLE1", 1000000), task(3, "IDLE0", 4000000, 240, 0), task(4, "wifi", 900000, 4000, 0)}; std::vector now = {task(1, "loopTask", 5810000), task(2, "IDLE1", 1170000), task(3, "IDLE0", 4780000, 240, 0), task(4, "wifi", 1090000, 4000, 0)}; auto rows = taskRows(before, 10000000, now, 11000000); // one second later TEST_ASSERT_EQUAL_size_t(4, rows.size()); TEST_ASSERT_EQUAL_UINT16(810, find(rows, "loopTask")->permille); TEST_ASSERT_EQUAL_UINT16(170, find(rows, "IDLE1")->permille); TEST_ASSERT_EQUAL_UINT16(780, find(rows, "IDLE0")->permille); TEST_ASSERT_EQUAL_UINT16(190, find(rows, "wifi")->permille); // A core's load is what its idle task didn't use. TEST_ASSERT_EQUAL_INT(22, coreLoad(rows, 0)); TEST_ASSERT_EQUAL_INT(83, coreLoad(rows, 1)); TEST_ASSERT_EQUAL_INT(-1, coreLoad(rows, 2)); } // FreeRTOS adds to a task's run time when it's switched out. The task doing the sampling is // running: if nothing else wanted its core, it was never switched out and its counter hardly // moved. Its real share is what's left of its core. void test_the_running_task_gets_what_is_left_of_its_core() { auto running = [](TaskSample t) { t.state = 0; // eRunning return t; }; std::vector before = {running(task(1, "loopTask", 5000000)), task(2, "IDLE1", 1000000), task(3, "radio", 100), task(4, "IDLE0", 4000000, 240, 0), task(5, "wifi", 900000, 4000, 0)}; std::vector now = {running(task(1, "loopTask", 5020000)), task(2, "IDLE1", 1000000), task(3, "radio", 30100), task(4, "IDLE0", 4980000, 240, 0), task(5, "wifi", 910000, 4000, 0)}; auto rows = taskRows(before, 0, now, 1000000); TEST_ASSERT_EQUAL_UINT16(970, find(rows, "loopTask")->permille); // not the 2 % its counter says TEST_ASSERT_EQUAL_UINT16(30, find(rows, "radio")->permille); TEST_ASSERT_EQUAL_UINT16(10, find(rows, "wifi")->permille); // another core: untouched TEST_ASSERT_EQUAL_INT(100, coreLoad(rows, 1)); TEST_ASSERT_EQUAL_INT(2, coreLoad(rows, 0)); } // When its counter did keep up (it was switched out often), nothing is added. void test_the_running_task_keeps_its_own_count_when_it_is_higher() { std::vector before = {task(1, "loopTask", 0), task(2, "IDLE1", 0)}; std::vector now = {task(1, "loopTask", 600000), task(2, "IDLE1", 400000)}; now[0].state = before[0].state = 0; auto rows = taskRows(before, 0, now, 1000000); TEST_ASSERT_EQUAL_UINT16(600, find(rows, "loopTask")->permille); } // The counters are 32-bit microseconds: they wrap every 71 minutes. void test_counters_wrap() { std::vector before = {task(1, "loopTask", 0xFFFFF000u)}; std::vector now = {task(1, "loopTask", 0x0007A120u - 0x1000u)}; // 500 ms of run time later auto rows = taskRows(before, 0xFFFF0000u, now, 0xFFFF0000u + 1000000u); TEST_ASSERT_EQUAL_UINT16(500, rows[0].permille); } void test_tasks_come_and_go() { std::vector before = {task(1, "loopTask", 100), task(7, "gemini", 50000)}; std::vector now = {task(1, "loopTask", 400100), task(9, "gemini", 30000)}; // a new gemini task: another id auto rows = taskRows(before, 0, now, 1000000); TEST_ASSERT_EQUAL_size_t(2, rows.size()); TEST_ASSERT_EQUAL_UINT16(400, find(rows, "loopTask")->permille); TEST_ASSERT_EQUAL_UINT16(30, find(rows, "gemini")->permille); // all its run time is inside the interval } void test_no_time_passed() { std::vector one = {task(1, "loopTask", 100)}; auto rows = taskRows(one, 500, one, 500); TEST_ASSERT_EQUAL_UINT16(0, rows[0].permille); TEST_ASSERT_EQUAL_INT(-1, coreLoad(rows, 1)); // no idle task in the sample } // Q122: under 512 bytes of stack left is flagged. void test_low_stack() { std::vector s = {task(1, "spk_task", 0, 264), task(2, "IDLE0", 0, 240, 0), task(3, "radio", 0, 2360), task(4, "edge", 0, 512)}; auto rows = taskRows(s, 0, s, 1000); TEST_ASSERT_TRUE(find(rows, "spk_task")->lowStack); TEST_ASSERT_TRUE(find(rows, "IDLE0")->lowStack); TEST_ASSERT_FALSE(find(rows, "radio")->lowStack); TEST_ASSERT_FALSE(find(rows, "edge")->lowStack); } void test_sorting() { std::vector before = {task(1, "wifi", 0, 4000), task(2, "loopTask", 0, 2800), task(3, "IDLE0", 0, 240), task(4, "irc", 0, 3600)}; std::vector now = {task(1, "wifi", 190000, 4000), task(2, "loopTask", 810000, 2800), task(3, "IDLE0", 780000, 240), task(4, "irc", 0, 3600)}; auto rows = taskRows(before, 0, now, 1000000); sortTasks(rows, TaskSort::Share); // busiest first, idle tasks last: they're what's left over TEST_ASSERT_EQUAL_STRING("loopTask", rows[0].name.c_str()); TEST_ASSERT_EQUAL_STRING("wifi", rows[1].name.c_str()); TEST_ASSERT_EQUAL_STRING("irc", rows[2].name.c_str()); TEST_ASSERT_EQUAL_STRING("IDLE0", rows[3].name.c_str()); sortTasks(rows, TaskSort::Stack); // least stack left first TEST_ASSERT_EQUAL_STRING("IDLE0", rows[0].name.c_str()); TEST_ASSERT_EQUAL_STRING("loopTask", rows[1].name.c_str()); sortTasks(rows, TaskSort::Name); // letters only, whatever the case TEST_ASSERT_EQUAL_STRING("IDLE0", rows[0].name.c_str()); TEST_ASSERT_EQUAL_STRING("irc", rows[1].name.c_str()); TEST_ASSERT_EQUAL_STRING("loopTask", rows[2].name.c_str()); TEST_ASSERT_EQUAL_STRING("wifi", rows[3].name.c_str()); } // Q120: two minutes of history, the oldest sample dropped first. void test_history() { History h; TEST_ASSERT_EQUAL_size_t(0, h.size()); for (uint8_t v : {10, 20, 30}) h.push(v); TEST_ASSERT_EQUAL_size_t(3, h.size()); TEST_ASSERT_EQUAL_UINT8(10, h.at(0)); TEST_ASSERT_EQUAL_UINT8(30, h.at(2)); for (uint8_t v : {40, 50, 60}) h.push(v); TEST_ASSERT_EQUAL_size_t(4, h.size()); TEST_ASSERT_EQUAL_UINT8(30, h.at(0)); // oldest kept TEST_ASSERT_EQUAL_UINT8(60, h.at(3)); // newest h.clear(); TEST_ASSERT_EQUAL_size_t(0, h.size()); } int main() { UNITY_BEGIN(); RUN_TEST(test_shares_over_the_interval); RUN_TEST(test_the_running_task_gets_what_is_left_of_its_core); RUN_TEST(test_the_running_task_keeps_its_own_count_when_it_is_higher); RUN_TEST(test_counters_wrap); RUN_TEST(test_tasks_come_and_go); RUN_TEST(test_no_time_passed); RUN_TEST(test_low_stack); RUN_TEST(test_sorting); RUN_TEST(test_history); return UNITY_END(); }