// SPDX-License-Identifier: Apache-2.0 // Copyright 2015-2022 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "stats.h" #include #include "esp_log.h" #include "esp_hosted_transport_init.h" #if TEST_RAW_TP || ESP_PKT_STATS static const char TAG[] = "stats"; #endif #if ESP_PKT_STATS || TEST_RAW_TP #define ESP_PKT_STATS_REPORT_INTERVAL 10 struct pkt_stats_t pkt_stats; #endif #ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS /* These functions are only for debugging purpose * Please do not enable in production environments */ static esp_err_t log_real_time_stats(TickType_t xTicksToWait) { TaskStatus_t *start_array = NULL, *end_array = NULL; UBaseType_t start_array_size, end_array_size; uint32_t start_run_time, end_run_time; esp_err_t ret; /*Allocate array to store current task states*/ start_array_size = uxTaskGetNumberOfTasks() + ARRAY_SIZE_OFFSET; start_array = malloc(sizeof(TaskStatus_t) * start_array_size); if (start_array == NULL) { ret = ESP_ERR_NO_MEM; goto exit; } /*Get current task states*/ start_array_size = uxTaskGetSystemState(start_array, start_array_size, &start_run_time); if (start_array_size == 0) { ret = ESP_ERR_INVALID_SIZE; goto exit; } vTaskDelay(xTicksToWait); /*Allocate array to store tasks states post delay*/ end_array_size = uxTaskGetNumberOfTasks() + ARRAY_SIZE_OFFSET; end_array = malloc(sizeof(TaskStatus_t) * end_array_size); if (end_array == NULL) { ret = ESP_ERR_NO_MEM; goto exit; } /*Get post delay task states*/ end_array_size = uxTaskGetSystemState(end_array, end_array_size, &end_run_time); if (end_array_size == 0) { ret = ESP_ERR_INVALID_SIZE; goto exit; } /*Calculate total_elapsed_time in units of run time stats clock period.*/ uint32_t total_elapsed_time = (end_run_time - start_run_time); if (total_elapsed_time == 0) { ret = ESP_ERR_INVALID_STATE; goto exit; } ESP_LOGI(TAG,"| Task | Run Time | Percentage\n"); /*Match each task in start_array to those in the end_array*/ for (int i = 0; i < start_array_size; i++) { int k = -1; for (int j = 0; j < end_array_size; j++) { if (start_array[i].xHandle == end_array[j].xHandle) { k = j; /*Mark that task have been matched by overwriting their handles*/ start_array[i].xHandle = NULL; end_array[j].xHandle = NULL; break; } } /*Check if matching task found*/ if (k >= 0) { uint32_t task_elapsed_time = end_array[k].ulRunTimeCounter - start_array[i].ulRunTimeCounter; uint32_t percentage_time = (task_elapsed_time * 100UL) / (total_elapsed_time * portNUM_PROCESSORS); ESP_LOGI(TAG,"| %s | %d | %d%%\n", start_array[i].pcTaskName, task_elapsed_time, percentage_time); } } /*Print unmatched tasks*/ for (int i = 0; i < start_array_size; i++) { if (start_array[i].xHandle != NULL) { ESP_LOGI(TAG,"| %s | Deleted\n", start_array[i].pcTaskName); } } for (int i = 0; i < end_array_size; i++) { if (end_array[i].xHandle != NULL) { ESP_LOGI(TAG,"| %s | Created\n", end_array[i].pcTaskName); } } ret = ESP_OK; exit: /*Common return path*/ if (start_array) free(start_array); if (end_array) free(end_array); return ret; } static void log_runtime_stats_task(void* pvParameters) { while (1) { ESP_LOGI(TAG,"\n\nGetting real time stats over %d ticks\n", STATS_TICKS); if (log_real_time_stats(STATS_TICKS) == ESP_OK) { ESP_LOGI(TAG,"Real time stats obtained\n"); } else { ESP_LOGE(TAG,"Error getting real time stats\n"); } vTaskDelay(pdMS_TO_TICKS(1000*2)); } } #endif #if TEST_RAW_TP uint64_t test_raw_tp_rx_len; uint64_t test_raw_tp_tx_len; void debug_update_raw_tp_rx_count(uint16_t len) { test_raw_tp_rx_len += (len); } // static buffer to hold tx data during test DMA_ATTR static uint8_t tx_buf[TEST_RAW_TP__BUF_SIZE]; extern volatile uint8_t datapath; static void raw_tp_tx_task(void* pvParameters) { int ret; interface_buffer_handle_t buf_handle = {0}; uint8_t *raw_tp_tx_buf = NULL; uint32_t *ptr = NULL; uint16_t i = 0; sleep(5); // initialise the static buffer raw_tp_tx_buf = tx_buf; ptr = (uint32_t*)raw_tp_tx_buf; // initialise the tx buffer for (i=0; i<(TEST_RAW_TP__BUF_SIZE/4-1); i++, ptr++) *ptr = 0xdeadbeef; for (;;) { if (!datapath) { sleep(1); continue; } buf_handle.if_type = ESP_TEST_IF; buf_handle.if_num = 0; buf_handle.payload = raw_tp_tx_buf; buf_handle.payload_len = TEST_RAW_TP__BUF_SIZE; // free the buffer after it has been sent buf_handle.free_buf_handle = NULL; buf_handle.priv_buffer_handle = buf_handle.payload; ret = send_to_host_queue(&buf_handle, PRIO_Q_OTHERS); if (ret) { ESP_LOGE(TAG,"Failed to send to queue\n"); continue; } test_raw_tp_tx_len += (TEST_RAW_TP__BUF_SIZE); } } #endif #if TEST_RAW_TP || ESP_PKT_STATS static void stats_timer_func(void* arg) { #if TEST_RAW_TP static int32_t cur = 0; double actual_bandwidth_rx = 0; double actual_bandwidth_tx = 0; int32_t div = 1024; actual_bandwidth_tx = (test_raw_tp_tx_len*8)/TEST_RAW_TP__TIMEOUT; actual_bandwidth_rx = (test_raw_tp_rx_len*8)/TEST_RAW_TP__TIMEOUT; #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) ESP_LOGI(TAG,"%lu-%lu sec Rx: %.2f Tx: %.2f kbps", cur, cur + TEST_RAW_TP__TIMEOUT, actual_bandwidth_rx/div, actual_bandwidth_tx/div); #else ESP_LOGI(TAG,"%u-%u sec Rx: %.2f Tx: %.2f kbps", cur, cur + TEST_RAW_TP__TIMEOUT, actual_bandwidth_rx/div, actual_bandwidth_tx/div); #endif cur += TEST_RAW_TP__TIMEOUT; test_raw_tp_rx_len = test_raw_tp_tx_len = 0; #endif #if ESP_PKT_STATS ESP_LOGI(TAG, "slave StaRxQLoad: %8lu \nH=>S: sta: in: %8lu out: %8lu fail: %8lu \nH<=S: sta: in: %8lu out: %8lu \n serial : in: %8lu rsp: %8lu evt: %8lu", pkt_stats.slave_wifi_rx_msg_loaded, pkt_stats.hs_bus_sta_in,pkt_stats.hs_bus_sta_out, pkt_stats.hs_bus_sta_fail, pkt_stats.sta_sh_in,pkt_stats.sta_sh_out, pkt_stats.serial_rx, pkt_stats.serial_tx_total, pkt_stats.serial_tx_evt); #endif } static void start_timer_to_display_stats(int periodic_time_sec) { test_args_t args = {0}; esp_timer_handle_t raw_tp_timer = {0}; esp_timer_create_args_t create_args = { .callback = &stats_timer_func, .arg = &args, .name = "raw_tp_timer", }; ESP_ERROR_CHECK(esp_timer_create(&create_args, &raw_tp_timer)); args.timer = raw_tp_timer; ESP_ERROR_CHECK(esp_timer_start_periodic(raw_tp_timer, SEC_TO_USEC(periodic_time_sec))); } #endif #if TEST_RAW_TP void process_test_capabilities(uint8_t capabilities) { ESP_LOGD(TAG, "capabilites: %d", capabilities); if ((capabilities & ESP_TEST_RAW_TP__ESP_TO_HOST) || (capabilities & ESP_TEST_RAW_TP__BIDIRECTIONAL)) { assert(xTaskCreate(raw_tp_tx_task , "raw_tp_tx_task", CONFIG_ESP_DEFAULT_TASK_STACK_SIZE, NULL , CONFIG_ESP_DEFAULT_TASK_PRIO, NULL) == pdTRUE); } } #endif void create_debugging_tasks(void) { #ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS assert(xTaskCreate(log_runtime_stats_task, "log_runtime_stats_task", CONFIG_ESP_DEFAULT_TASK_STACK_SIZE, NULL, CONFIG_ESP_DEFAULT_TASK_PRIO, NULL) == pdTRUE); #endif #if TEST_RAW_TP || ESP_PKT_STATS start_timer_to_display_stats(ESP_PKT_STATS_REPORT_INTERVAL); #endif } uint8_t debug_get_raw_tp_conf(void) { uint8_t raw_tp_cap = 0; #if TEST_RAW_TP raw_tp_cap |= ESP_TEST_RAW_TP; ESP_LOGI(TAG, "\n\n***** Slave: Raw Throughput testing (Report per %u sec)*****\n", CONFIG_ESP_RAW_TP_REPORT_INTERVAL); #endif return raw_tp_cap; }