/* * Copyright (c) 2013-2019 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * 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 * * 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. * * ----------------------------------------------------------------------------- * * Project: CMSIS-RTOS RTX * Title: Thread functions * * ----------------------------------------------------------------------------- */ #include "hal_location.h" #include "hal_timer.h" #include "hal_trace.h" #include "plat_addr_map.h" #include "rtx_lib.h" #define RTX_DUMP_VERBOSE struct IRQ_STACK_FRAME_T { uint32_t r0; uint32_t r1; uint32_t r2; uint32_t r3; uint32_t r12; uint32_t lr; uint32_t pc; uint32_t xpsr; }; extern uint32_t __StackTop[]; static inline uint32_t get_IPSR(void) { uint32_t result; asm volatile("MRS %0, ipsr" : "=r"(result)); return (result); } static inline uint32_t get_PSP(void) { uint32_t result; asm volatile("MRS %0, psp" : "=r"(result)); return (result); } static inline struct IRQ_STACK_FRAME_T * _rtx_get_irq_stack_frame(const os_thread_t *thread) { uint32_t sp; if (thread == NULL) { return NULL; } if (thread == osRtxThreadGetRunning() && get_IPSR() == 0) { return NULL; } if (thread == osRtxThreadGetRunning()) { sp = get_PSP(); } else { sp = thread->sp; } if ((sp & 3) || !hal_trace_address_writable(sp)) { return NULL; } if (thread != osRtxThreadGetRunning()) { // r4-r11 sp += 4 * 8; if ((thread->stack_frame & 0x10) == 0) { // s16-s31 sp += 4 * 16; } } return (struct IRQ_STACK_FRAME_T *)sp; } FLASH_TEXT_LOC void rtx_thread_show(const os_thread_t *thread) { const char *thread_st_str; struct IRQ_STACK_FRAME_T *frame; if (thread) { switch (thread->state) { case osRtxThreadInactive: thread_st_str = "INACTIVE"; break; case osRtxThreadReady: thread_st_str = "READY"; break; case osRtxThreadRunning: thread_st_str = "RUNNING"; break; case osRtxThreadTerminated: thread_st_str = "TERMINAT"; break; case osRtxThreadWaitingDelay: thread_st_str = "WAIT_DLY"; break; case osRtxThreadWaitingJoin: thread_st_str = "WAIT_JOIN"; break; case osRtxThreadWaitingThreadFlags: thread_st_str = "WAIT_FLAG"; break; case osRtxThreadWaitingEventFlags: thread_st_str = "WAIT_EVE"; break; case osRtxThreadWaitingMutex: thread_st_str = "WAIT_MUT"; break; case osRtxThreadWaitingSemaphore: thread_st_str = "WAIT_SEM"; break; case osRtxThreadWaitingMemoryPool: thread_st_str = "WAIT_MEM"; break; case osRtxThreadWaitingMessageGet: thread_st_str = "WAIT_MGET"; break; case osRtxThreadWaitingMessagePut: thread_st_str = "WAIT_MPUT"; break; default: thread_st_str = "BAD"; break; } REL_TRACE_NOCRLF_NOTS(0, "--- Thread "); REL_TRACE_NOCRLF_NOTS(1, "name=%s", thread->name ? thread->name : "NULL"); REL_TRACE_NOTS(4, " thread=0x%x, prio=%u state=%-9s thread_addr=0x%08X", (uint32_t)thread, thread->priority, thread_st_str, thread->thread_addr); #ifdef RTX_DUMP_VERBOSE REL_TRACE_NOTS(4, " thread_next=0x%08X thread_prev=0x%08X " "delay_next=0x%08X delay_prev=0x%08X", (uint32_t)thread->thread_next, (uint32_t)thread->thread_prev, (uint32_t)thread->delay_next, (uint32_t)thread->delay_prev); REL_TRACE_NOTS( 4, " thread_join=0x%08X flags_options=%u wait_flags=%u thread_flags=%u", (uint32_t)thread->thread_join, thread->flags_options, thread->wait_flags, thread->thread_flags); REL_TRACE_NOTS(3, " stack_mem=0x%08X stack_size=%u sp:0x%04x", (uint32_t)thread->stack_mem, thread->stack_size, thread->sp); #ifdef __RTX_CPU_STATISTICS__ REL_TRACE_NOTS(2, " swap_in_time=%u swap_out_time=%u", thread->swap_in_time, thread->swap_out_time); REL_TRACE_NOCRLF_NOTS(0, " after last switch "); if (thread->swap_in_time <= thread->swap_out_time) REL_TRACE_NOTS(1, "thread runtime %ums", thread->swap_out_time - thread->swap_in_time); else REL_TRACE_NOTS(1, "thread still runing, now %d", HWTICKS_TO_MS(rtx_get_hwticks())); #endif #endif /*RTX_DUMP_VERBOSE*/ frame = _rtx_get_irq_stack_frame(thread); if (frame) { uint32_t stack_end; uint32_t search_cnt, print_cnt; REL_TRACE_NOTS(1, " frame:0x%08X", (uint32_t)frame); REL_TRACE_NOTS(4, " R0 =0x%08X R1=0x%08X R2=0x%08X R3 =0x%08X", frame->r0, frame->r1, frame->r2, frame->r3); REL_TRACE_NOTS(4, " R12=0x%08X LR=0x%08X PC=0x%08X XPSR=0x%08X", frame->r12, frame->lr, frame->pc, frame->xpsr); stack_end = (uint32_t)thread->stack_mem + thread->stack_size; if (stack_end > thread->sp) { search_cnt = (stack_end - thread->sp) / 4; if (search_cnt > 512) { search_cnt = 512; } print_cnt = 10; hal_trace_print_backtrace(thread->sp, search_cnt, print_cnt); } } } else { REL_TRACE_NOTS(0, "--- Thread NONE"); } REL_TRACE_IMM_NOTS(0, " "); #if (defined(DEBUG) || defined(REL_TRACE_ENABLE)) if (hal_trace_crash_dump_onprocess()) { hal_sys_timer_delay(MS_TO_TICKS(500)); } #endif } void rtx_show_all_threads(void) { int i; const os_thread_t *thread; #if (defined(DEBUG) || defined(REL_TRACE_ENABLE)) if (hal_trace_crash_dump_onprocess()) { for (i = 0; i < 10; i++) { REL_TRACE_IMM_NOTS(0, " " " "); REL_TRACE_IMM_NOTS(0, " " " \n"); hal_sys_timer_delay(MS_TO_TICKS(200)); } } #endif REL_TRACE_NOTS(0, "Thread List:"); // Current List REL_TRACE_NOTS(0, "Current List"); rtx_thread_show(osRtxInfo.thread.run.curr); // Next List if (osRtxInfo.thread.run.next != osRtxInfo.thread.run.curr) { REL_TRACE_NOTS(0, "Next List"); rtx_thread_show(osRtxInfo.thread.run.next); } // Ready List REL_TRACE_NOTS(0, "Ready List"); for (thread = osRtxInfo.thread.ready.thread_list; thread != NULL; thread = thread->thread_next) { rtx_thread_show(thread); } // Delay List REL_TRACE_NOTS(0, "Delay List"); for (thread = osRtxInfo.thread.delay_list; thread != NULL; thread = thread->delay_next) { rtx_thread_show(thread); } // Wait List REL_TRACE_NOTS(0, "Wait List"); for (thread = osRtxInfo.thread.wait_list; thread != NULL; thread = thread->delay_next) { rtx_thread_show(thread); } // Terminate List REL_TRACE_NOTS(0, "Terminate List"); for (thread = osRtxInfo.thread.terminate_list; thread != NULL; thread = thread->thread_next) { rtx_thread_show(thread); } REL_TRACE_IMM_NOTS(0, " "); } #if __RTX_CPU_STATISTICS__ #if TASK_HUNG_CHECK_ENABLED FLASH_TEXT_LOC NOINLINE static void print_hung_task(const os_thread_t *thread, U32 curr_time) { REL_TRACE_IMM_NOTS(2, "Thread \"%s\" blocked for %dms", thread->name == NULL ? "NULL" : (char *)thread->name, curr_time - thread->swap_out_time); ASSERT(0, "Find thread hung "); } static void check_hung_thread(const os_thread_t *thread) { uint32_t curr_hwticks, curr_time; if (!thread->hung_check) return; curr_hwticks = hal_sys_timer_get(); curr_time = HWTICKS_TO_MS(curr_hwticks); if ((curr_time - thread->swap_out_time) > thread->hung_check_timeout) { print_hung_task(thread, curr_time); } } void check_hung_threads(void) { const os_thread_t *thread; // Current List check_hung_thread(osRtxInfo.thread.run.curr); // Next List if (osRtxInfo.thread.run.next != osRtxInfo.thread.run.curr) check_hung_thread(osRtxInfo.thread.run.next); // Ready List for (thread = osRtxInfo.thread.ready.thread_list; thread != NULL; thread = thread->thread_next) { check_hung_thread(thread); } // Delay List for (thread = osRtxInfo.thread.delay_list; thread != NULL; thread = thread->delay_next) { check_hung_thread(thread); } // Wait List for (thread = osRtxInfo.thread.wait_list; thread != NULL; thread = thread->delay_next) { check_hung_thread(thread); } // Terminate List for (thread = osRtxInfo.thread.terminate_list; thread != NULL; thread = thread->thread_next) { check_hung_thread(thread); } } #endif static inline void print_thread_sw_statitics(const os_thread_t *thread) { /* REL_TRACE_NOTS(3,"--- Thread swap in:%d out=%d runings %d", thread->swap_in_time, thread->swap_out_time, thread->rtime); */ } FLASH_TEXT_LOC static void _rtx_show_thread_usage(const os_thread_t *thread, uint32_t sample_time) { if (thread) { if (thread->thread_addr && thread->stack_mem) { REL_TRACE_NOTS(4, "--- Thread name=%s cpu=%%%d", thread->name == NULL ? "null" : (char *)thread->name, sample_time != 0 ? ((thread->rtime - thread->step_rtime) * 100 / sample_time) : 0); print_thread_sw_statitics(thread); ((os_thread_t *)thread)->step_rtime = thread->rtime; } else { REL_TRACE_NOTS(0, "--- Thread BAD"); } } else { REL_TRACE_NOTS(0, "--- Thread NONE"); } } FLASH_TEXT_LOC void rtx_show_all_threads_usage(void) { const os_thread_t *thread; static bool first_time = 1; uint32_t sample_time; static uint32_t start_sample_time = 0; if (first_time) { start_sample_time = rtx_get_hwticks(); first_time = 0; return; } sample_time = HWTICKS_TO_MS(rtx_get_hwticks() - start_sample_time); REL_TRACE_IMM_NOTS(0, " "); REL_TRACE_NOTS(0, "Thread List:"); // Current List _rtx_show_thread_usage(osRtxInfo.thread.run.curr, sample_time); // Next List if (osRtxInfo.thread.run.next != osRtxInfo.thread.run.curr) _rtx_show_thread_usage(osRtxInfo.thread.run.next, sample_time); // Ready List for (thread = osRtxInfo.thread.ready.thread_list; thread != NULL; thread = thread->thread_next) { _rtx_show_thread_usage(thread, sample_time); } // Delay List for (thread = osRtxInfo.thread.delay_list; thread != NULL; thread = thread->delay_next) { _rtx_show_thread_usage(thread, sample_time); } // Wait List for (thread = osRtxInfo.thread.wait_list; thread != NULL; thread = thread->delay_next) { _rtx_show_thread_usage(thread, sample_time); } // Terminate List for (thread = osRtxInfo.thread.terminate_list; thread != NULL; thread = thread->thread_next) { _rtx_show_thread_usage(thread, sample_time); } start_sample_time = rtx_get_hwticks(); REL_TRACE_IMM_NOTS(0, " "); } #endif