307 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			307 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*-----------------------------------------------------------------------------*/
 | |
| /* Include Header Files                                                        */
 | |
| /*-----------------------------------------------------------------------------*/
 | |
| #include <linux/module.h>
 | |
| #include <asm/io.h>
 | |
| #include <linux/uaccess.h>
 | |
| #include <plat/hardware.h> //for NVT_TIMER_BASE_VIRT
 | |
| 
 | |
| #define __MODULE__    rtos_perf
 | |
| #define __DBGLVL__    8 // 0=FATAL, 1=ERR, 2=WRN, 3=UNIT, 4=FUNC, 5=IND, 6=MSG, 7=VALUE, 8=USER
 | |
| #define __DBGFLT__    "*"
 | |
| #include <kwrap/debug.h>
 | |
| #include <kwrap/cpu.h>
 | |
| #include <kwrap/dev.h>
 | |
| #include <kwrap/perf.h>
 | |
| #include <kwrap/spinlock.h>
 | |
| #include "vos_ioctl.h"
 | |
| 
 | |
| /*-----------------------------------------------------------------------------*/
 | |
| /* Local Types Declarations                                                    */
 | |
| /*-----------------------------------------------------------------------------*/
 | |
| #define RTOS_PERF_INITED_TAG    MAKEFOURCC('R', 'P', 'E', 'R') ///< a key value
 | |
| 
 | |
| #ifndef NVT_TIMER_OFS_TM0_CNT
 | |
| #if defined(_BSP_NA51055_)
 | |
| #define NVT_TIMER_OFS_TM0_CNT   0x108
 | |
| #elif defined(_BSP_NA51000_)
 | |
| #define NVT_TIMER_OFS_TM0_CNT   0x108
 | |
| #elif defined(_BSP_NA51068_)
 | |
| #define NVT_TIMER_OFS_TM0_CNT   0x108
 | |
| #elif defined(_BSP_NA51089_)
 | |
| #define NVT_TIMER_OFS_TM0_CNT   0x108
 | |
| #elif defined(_BSP_NA51090_)
 | |
| #define NVT_TIMER_OFS_TM0_CNT   0x108
 | |
| #else
 | |
| #error NVT_TIMER_OFS_TM0_CNT not defined
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| #ifndef NVT_TIMER_BASE_VIRT
 | |
| #define VOS_GET_TIMER_REG(ofs)  0 //should be fixed
 | |
| #else
 | |
| #define VOS_GET_TIMER_REG(ofs)  readl((volatile void __iomem *)(NVT_TIMER_BASE_VIRT+(ofs)))
 | |
| #endif
 | |
| 
 | |
| #define VOS_PERF_LIST_NUM       32
 | |
| #define VOS_PERF_LIST_NAME_SIZE 32
 | |
| #define VOS_GET_TM0_CNT()       (VOS_TICK)VOS_GET_TIMER_REG(NVT_TIMER_OFS_TM0_CNT)
 | |
| 
 | |
| #define VOS_STRCPY(dst, src, dst_size) do { \
 | |
| 	strncpy(dst, src, (dst_size)-1); \
 | |
| 	dst[(dst_size)-1] = '\0'; \
 | |
| } while(0)
 | |
| 
 | |
| typedef struct _VOS_PERF_LIST_ITEM {
 | |
|     CHAR name[VOS_PERF_LIST_NAME_SIZE];
 | |
|     VOS_TICK tick;
 | |
|     UINT32 line_no;
 | |
|     UINT32 cus_val;
 | |
|     UINT32 diff_time; //diff time to the first same name
 | |
|     INT column;
 | |
| } VOS_PERF_LIST_ITEM;
 | |
| 
 | |
| /*-----------------------------------------------------------------------------*/
 | |
| /* Local Global Variables                                                      */
 | |
| /*-----------------------------------------------------------------------------*/
 | |
| unsigned int rtos_perf_debug_level = NVT_DBG_WRN;
 | |
| static VK_DEFINE_SPINLOCK(g_perf_lock);
 | |
| 
 | |
| static VOS_PERF_LIST_ITEM g_perf_list[VOS_PERF_LIST_NUM] = {0};
 | |
| static UINT32 g_perf_list_idx = 0;
 | |
| static UINT32 g_perf_list_skip_count = 0;
 | |
| 
 | |
| module_param_named(rtos_perf_debug_level, rtos_perf_debug_level, int, S_IRUGO | S_IWUSR);
 | |
| MODULE_PARM_DESC(rtos_perf_debug_level, "Debug message level");
 | |
| 
 | |
| /*-----------------------------------------------------------------------------*/
 | |
| /* Interface Functions                                                         */
 | |
| /*-----------------------------------------------------------------------------*/
 | |
| void vos_perf_init(void *param)
 | |
| {
 | |
| }
 | |
| 
 | |
| void vos_perf_exit(void)
 | |
| {
 | |
| }
 | |
| 
 | |
| void vos_perf_mark(VOS_TICK *p_tick)
 | |
| {
 | |
| 	if (NULL == p_tick) {
 | |
| 		DBG_ERR("p_tick is NULL\r\n");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	*p_tick = VOS_GET_TM0_CNT();
 | |
| }
 | |
| 
 | |
| VOS_TICK vos_perf_duration(VOS_TICK t_begin, VOS_TICK t_end)
 | |
| {
 | |
| 	return (t_end - t_begin);
 | |
| }
 | |
| 
 | |
| void vos_perf_list_reset(void)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	vk_spin_lock_irqsave(&g_perf_lock, flags);
 | |
| 
 | |
| 	g_perf_list_idx = 0;
 | |
| 	g_perf_list_skip_count = 0;
 | |
| 
 | |
| 	vk_spin_unlock_irqrestore(&g_perf_lock, flags);
 | |
| }
 | |
| 
 | |
| void vos_perf_list_mark(const CHAR *p_name, UINT32 line_no, UINT32 cus_val)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	vk_spin_lock_irqsave(&g_perf_lock, flags);
 | |
| 
 | |
| 	if (g_perf_list_idx < VOS_PERF_LIST_NUM) {
 | |
| 		VOS_STRCPY(g_perf_list[g_perf_list_idx].name, p_name, sizeof(g_perf_list[g_perf_list_idx].name));
 | |
| 		g_perf_list[g_perf_list_idx].tick = VOS_GET_TM0_CNT();
 | |
| 		g_perf_list[g_perf_list_idx].line_no = line_no;
 | |
| 		g_perf_list[g_perf_list_idx].cus_val = cus_val;
 | |
| 		g_perf_list_idx++;
 | |
| 	} else {
 | |
| 		g_perf_list_skip_count++;
 | |
| 	}
 | |
| 
 | |
| 	vk_spin_unlock_irqrestore(&g_perf_lock, flags);
 | |
| }
 | |
| 
 | |
| static void vos_perf_list_dump_by_idx(void)
 | |
| {
 | |
| 	INT list_idx;
 | |
| 	INT max_idx;
 | |
| 
 | |
| 	DBG_DUMP("\r\n===== %s =====\r\n", __func__);
 | |
| 
 | |
| 	max_idx = g_perf_list_idx;
 | |
| 
 | |
| 	for(list_idx = 0; list_idx < max_idx; list_idx++) {
 | |
| 		DBG_DUMP("[%02d] %s() ln %d ts %d df %d cus %d\r\n",
 | |
| 			list_idx,
 | |
| 			g_perf_list[list_idx].name,
 | |
| 			(UINT)g_perf_list[list_idx].line_no,
 | |
| 			(UINT)g_perf_list[list_idx].tick,
 | |
| 			(list_idx < 1) ? 0 : (UINT)vos_perf_duration(g_perf_list[list_idx-1].tick, g_perf_list[list_idx].tick),
 | |
| 			(UINT)g_perf_list[list_idx].cus_val);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void vos_perf_list_dump_by_name(void)
 | |
| {
 | |
| 	INT item_idx, item_idx2;
 | |
| 	INT column_cur, column_max;
 | |
| 	INT row_idx;
 | |
| 	INT max_idx;
 | |
| 
 | |
| 	pr_cont("\r\n===== %s =====\r\n", __func__);
 | |
| 
 | |
| 	max_idx = g_perf_list_idx;
 | |
| 
 | |
| 	//reset the column to recount the newest column
 | |
| 	for (item_idx = 0; item_idx < max_idx; item_idx++) {
 | |
| 		g_perf_list[item_idx].column = 0;
 | |
| 	}
 | |
| 
 | |
| 	//setup the column of each item
 | |
| 	column_cur = 0;
 | |
| 	for (item_idx = 0; item_idx < max_idx; item_idx++) {
 | |
| 		if (0 != g_perf_list[item_idx].column) {
 | |
| 			continue;
 | |
| 		}
 | |
| 		column_cur++;
 | |
| 		g_perf_list[item_idx].column = column_cur;
 | |
| 		g_perf_list[item_idx].diff_time = 0;
 | |
| 
 | |
| 		for (item_idx2 = item_idx + 1; item_idx2 < max_idx; item_idx2++) {
 | |
| 			if (!strcmp(g_perf_list[item_idx2].name, g_perf_list[item_idx].name)) {
 | |
| 				g_perf_list[item_idx2].column = column_cur;
 | |
| 				g_perf_list[item_idx2].diff_time = g_perf_list[item_idx2].tick - g_perf_list[item_idx].tick;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	column_max = column_cur;
 | |
| 
 | |
| #if 0 //for debug
 | |
| 	DBG_DUMP("max_idx %d, column_max %d\r\n", max_idx, column_max);
 | |
| 	for (item_idx = 0; item_idx < max_idx; item_idx++) {
 | |
| 		DBG_DUMP("item[%d], column %d, df %u, %u, %s\r\n",
 | |
| 			item_idx,
 | |
| 			g_perf_list[item_idx].column,
 | |
| 			g_perf_list[item_idx].tick,
 | |
| 			g_perf_list[item_idx].diff_time,
 | |
| 			g_perf_list[item_idx].name);
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	//print column name
 | |
| 	pr_cont("Name");
 | |
| 	for (column_cur = 1; column_cur <= column_max; column_cur++) {
 | |
| 		for (item_idx = 0; item_idx < max_idx; item_idx++) {
 | |
| 			if (column_cur == g_perf_list[item_idx].column) {
 | |
| 				pr_cont(" %10s", g_perf_list[item_idx].name);
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	pr_cont("\n");
 | |
| 
 | |
| 	//print item data by column
 | |
| 	for (row_idx = 0; row_idx < max_idx; row_idx++) {
 | |
| 		pr_cont("[%02d]", row_idx);
 | |
| 		for (column_cur = 1; column_cur <= column_max; column_cur++) {
 | |
| 			if (column_cur == g_perf_list[row_idx].column) {
 | |
| 				pr_cont(" %10u", g_perf_list[row_idx].tick);
 | |
| 			} else {
 | |
| 				pr_cont(" %10c", '-');
 | |
| 			}
 | |
| 		}
 | |
| 		pr_cont("\n");
 | |
| 	}
 | |
| 
 | |
| 	//print diff time
 | |
| 	pr_cont("-----------------------------------------------\n");
 | |
| 	pr_cont("Diff");
 | |
| 	for (column_cur = 1; column_cur <= column_max; column_cur++) {
 | |
| 		for (item_idx = (max_idx-1); item_idx >= 0; item_idx--) {
 | |
| 			if (column_cur == g_perf_list[item_idx].column) {
 | |
| 				pr_cont(" %10u", g_perf_list[item_idx].diff_time);
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	pr_cont("\n");
 | |
| }
 | |
| 
 | |
| void vos_perf_list_dump(void)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	vk_spin_lock_irqsave(&g_perf_lock, flags);
 | |
| 
 | |
| 	vos_perf_list_dump_by_idx();
 | |
| 	vos_perf_list_dump_by_name();
 | |
| 
 | |
| 	if (g_perf_list_skip_count) {
 | |
| 		DBG_WRN("skip count %d\r\n", (UINT)g_perf_list_skip_count);
 | |
| 	}
 | |
| 
 | |
| 	vk_spin_unlock_irqrestore(&g_perf_lock, flags);
 | |
| }
 | |
| 
 | |
| int _IOFUNC_PERF_IOCMD_MARK(unsigned long arg)
 | |
| {
 | |
| 	VOS_PERF_IOARG ioarg = {0};
 | |
| 	VOS_TICK tmp_tick;
 | |
| 
 | |
| 	//-------------------
 | |
| 	//skip copy_from_user
 | |
| 	//-------------------
 | |
| 
 | |
| 	vos_perf_mark(&tmp_tick);
 | |
| 	ioarg.tick = tmp_tick;
 | |
| 
 | |
| 	if (copy_to_user((void *)arg, (void *)&ioarg, sizeof(VOS_PERF_IOARG))) {
 | |
| 		DBG_ERR("copy_to_user failed\n");
 | |
| 		return -EFAULT;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int _IOFUNC_PERF_IOCMD_LIST_MARK(unsigned long arg)
 | |
| {
 | |
| 	VOS_PERF_IOARG ioarg = {0};
 | |
| 
 | |
| 	if (copy_from_user((void *)&ioarg, (void *)arg, sizeof(VOS_PERF_IOARG))) {
 | |
| 		DBG_ERR("copy_from_user failed\n");
 | |
| 		return -EFAULT;
 | |
| 	}
 | |
| 
 | |
| 	vos_perf_list_mark(ioarg.name, ioarg.line_no, ioarg.cus_val);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int _IOFUNC_PERF_IOCMD_LIST_DUMP(unsigned long arg)
 | |
| {
 | |
| 	vos_perf_list_dump();
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int _IOFUNC_PERF_IOCMD_LIST_RESET(unsigned long arg)
 | |
| {
 | |
| 	vos_perf_list_reset();
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| EXPORT_SYMBOL(vos_perf_mark);
 | |
| EXPORT_SYMBOL(vos_perf_duration);
 | |
| EXPORT_SYMBOL(vos_perf_list_reset);
 | |
| EXPORT_SYMBOL(vos_perf_list_mark);
 | |
| EXPORT_SYMBOL(vos_perf_list_dump);
 | 
