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);
 |