975 lines
27 KiB
C
975 lines
27 KiB
C
/*
|
|
******************************************************************************
|
|
* SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipcom_prof.c,v $ $Revision: 1.20.154.6 $
|
|
* $Source: /home/interpeak/CVSRoot/ipcom/src/ipcom_prof.c,v $
|
|
* $Author: hxie $ $Date: 2013-08-06 08:37:51 $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include "ipcom_config.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include "ipcom_clib.h"
|
|
#include "ipcom_clib2.h"
|
|
#include "ipcom_getopt.h"
|
|
#include "ipcom_list.h"
|
|
#include "ipcom_os.h"
|
|
#include "ipcom_prof.h"
|
|
#include "ipcom_shell.h"
|
|
#include "ipcom_time.h"
|
|
#include "ipcom_err.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPCOM_PROF_ENTRIES 0x10000
|
|
|
|
/* Set to ipcom_printf for shell use, ipcom_debug_printf for stand-alone use */
|
|
#define PRINTF ipcom_printf
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
typedef struct Ipcom_prof_listentry_struct
|
|
{
|
|
Ipcom_list head;
|
|
Ipcom_prof_entry entry;
|
|
Ip_u64 us;
|
|
Ip_u32 max;
|
|
Ip_u32 min;
|
|
Ip_u32 count;
|
|
}
|
|
Ipcom_prof_listentry;
|
|
|
|
typedef struct Ipcom_prof_listhead_struct
|
|
{
|
|
Ipcom_list head;
|
|
Ipcom_list list;
|
|
Ip_u64 us;
|
|
Ip_u32 max;
|
|
Ip_u32 min;
|
|
Ip_u32 count;
|
|
}
|
|
Ipcom_prof_listhead;
|
|
|
|
typedef struct Ipcom_prof_data_struct
|
|
{
|
|
volatile Ip_s32 indx;
|
|
volatile int stopped;
|
|
Ip_u32 overhead;
|
|
Ipcom_prof_entry data[IPCOM_PROF_ENTRIES];
|
|
}
|
|
Ipcom_prof_data;
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_STATIC Ipcom_prof_data ipcom_prof_data;
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipcom_prof_init
|
|
*===========================================================================
|
|
* Description: Init profiler.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipcom_prof_init(void *ununsed)
|
|
{
|
|
(void)ununsed;
|
|
|
|
ipcom_prof_clear();
|
|
ipcom_prof_data.overhead = 0;
|
|
|
|
(void)ipcom_shell_add_cmd("prof", "prof [options] command",
|
|
"Interpeak profiler",
|
|
ipcom_cmd_prof,
|
|
IPCOM_PRIORITY_DEFAULT, IPCOM_PROC_STACK_DEFAULT);
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipcom_prof_clear
|
|
*===========================================================================
|
|
* Description: Clear profiler data.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipcom_prof_clear(void)
|
|
{
|
|
Ip_u32 overhead = ipcom_prof_data.overhead;
|
|
|
|
ipcom_memset(&ipcom_prof_data, 0, sizeof ipcom_prof_data);
|
|
|
|
ipcom_prof_data.overhead = overhead;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipcom_prof_stopped
|
|
*===========================================================================
|
|
* Description: Enable/disable profiling.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipcom_prof_stopped(int new_prof)
|
|
{
|
|
int stopped = ipcom_prof_data.stopped;
|
|
|
|
ipcom_prof_data.stopped = new_prof;
|
|
|
|
return stopped;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipcom_prof_enter
|
|
*===========================================================================
|
|
* Description: Enter profiler data.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipcom_prof_enter(const char *msg, const char *file, int line, int flags)
|
|
{
|
|
static Ipcom_once_t once = IPCOM_ONCE_INIT;
|
|
#ifdef IPCOM_USE_TIME
|
|
struct Ip_timeval tv;
|
|
#endif
|
|
|
|
(void)ipcom_once(&once, ipcom_prof_init, IP_NULL);
|
|
|
|
if (ipcom_prof_data.stopped == 0 && msg != IP_NULL)
|
|
{
|
|
Ip_s32 indx = ipcom_prof_data.indx;
|
|
|
|
ipcom_prof_data.indx++;
|
|
|
|
if (indx >= IPCOM_PROF_ENTRIES)
|
|
ipcom_prof_data.indx--;
|
|
else
|
|
{
|
|
Ipcom_prof_entry *pe = &ipcom_prof_data.data[indx];
|
|
|
|
#if defined(IPCOM_USE_INTERRUPT_TIME)
|
|
{
|
|
typedef struct TimeStruct
|
|
{
|
|
Ip_s32 Seconds;
|
|
Ip_u32 Fraction;
|
|
} Time;
|
|
extern void INTERRUPT_GetHighResTime(Time *time);
|
|
Time time;
|
|
INTERRUPT_GetHighResTime(&time);
|
|
pe->us = time.Fraction / 4295;
|
|
}
|
|
#elif defined(IPCOM_USE_TIME)
|
|
ipcom_microtime(&tv);
|
|
pe->us = (Ip_u32)tv.tv_usec;
|
|
#else
|
|
pe->us = 0;
|
|
#endif
|
|
pe->msg = msg;
|
|
pe->file = file;
|
|
pe->line = (Ip_u16)line;
|
|
pe->type = (Ip_u8) ((msg[1] == ':' ? msg[0] : 0));
|
|
pe->flags = (Ip_u8)flags;
|
|
|
|
if (ipcom_prof_data.overhead == 0 && indx > 0 &&
|
|
pe->type == 'R' && (pe - 1)->type == 'R')
|
|
{
|
|
ipcom_prof_data.overhead = pe->us - (pe - 1)->us;
|
|
}
|
|
|
|
if (pe->type == 'C')
|
|
{
|
|
ipcom_prof_clear();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipcom_prof_push
|
|
*===========================================================================
|
|
* Description: Enter profiler data.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipcom_prof_push(Ipcom_prof_entry *e)
|
|
{
|
|
Ip_s32 indx;
|
|
|
|
if ((indx = ipcom_prof_data.indx) <= 0)
|
|
{
|
|
ipcom_memset(e, 0, sizeof(*e));
|
|
}
|
|
else
|
|
{
|
|
*e = ipcom_prof_data.data[indx - 1];
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipcom_prof_pop
|
|
*===========================================================================
|
|
* Description: Enter profiler data.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipcom_prof_pop(Ipcom_prof_entry *e)
|
|
{
|
|
ipcom_prof_enter(e->msg, e->file, e->line, IPCOM_PROF_CONTINUED);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipcom_prof_dump
|
|
*===========================================================================
|
|
* Description: Dump profiler data.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipcom_prof_dump(int start, int stop, int flags)
|
|
{
|
|
int stopped = ipcom_prof_stopped(1);
|
|
int i;
|
|
|
|
if (stop < 0 || stop > ipcom_prof_data.indx)
|
|
{
|
|
stop = ipcom_prof_data.indx;
|
|
}
|
|
|
|
/* Plain dump of all entries */
|
|
for (i = start; i < stop; i++)
|
|
{
|
|
Ipcom_prof_entry *pe = &ipcom_prof_data.data[i]; /*lint !e676 */
|
|
Ip_u32 us;
|
|
|
|
if ((flags & IPCOM_PROF_ABSOLUTE) == 0 && i < (stop - 1))
|
|
{
|
|
us = (pe + 1)->us - pe->us;
|
|
if (us & 0x80000000)
|
|
{
|
|
us = 0;
|
|
}
|
|
else if ((flags & IPCOM_PROF_OVERHEAD) == 0)
|
|
{
|
|
if ((us -= ipcom_prof_data.overhead) & 0x80000000)
|
|
{
|
|
us = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
us = pe->us;
|
|
}
|
|
|
|
if (flags & IPCOM_PROF_LINENUM)
|
|
{
|
|
PRINTF("%s(%d): ", pe->file, pe->line);
|
|
}
|
|
|
|
PRINTF("%lu us %s\n", (unsigned long)us, pe->msg);
|
|
}
|
|
|
|
(void)ipcom_prof_stopped(stopped);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipcom_prof_summary
|
|
*===========================================================================
|
|
* Description: Print summary of profiler data.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipcom_prof_summary(int start, int stop, int flags)
|
|
{
|
|
int stopped = ipcom_prof_stopped(1);
|
|
Ipcom_prof_listentry *le;
|
|
Ipcom_prof_listentry *le2;
|
|
Ipcom_prof_listentry *next;
|
|
Ipcom_list list;
|
|
Ip_u64 totus;
|
|
int i;
|
|
|
|
IP_U64_CLEAR(totus);
|
|
|
|
if (stop < 0 || stop > ipcom_prof_data.indx)
|
|
{
|
|
stop = ipcom_prof_data.indx;
|
|
}
|
|
|
|
ipcom_list_init(&list);
|
|
|
|
/* Gather data for all entries */
|
|
for (i = start; i < stop; i++)
|
|
{
|
|
Ipcom_prof_entry *pe = &ipcom_prof_data.data[i]; /*lint !e676 */
|
|
Ip_u32 us = (i < (stop - 1) ? (pe + 1)->us - pe->us : pe->us);
|
|
|
|
/* Handle special entries */
|
|
if (pe->type == 'E' || pe->type == 'C')
|
|
{
|
|
/* Ignore endmarks and cyber marks */
|
|
continue;
|
|
}
|
|
|
|
/* Handle out-of-order stuff */
|
|
if (us & 0x80000000)
|
|
{
|
|
us = 0;
|
|
}
|
|
else if ((flags & IPCOM_PROF_OVERHEAD) == 0)
|
|
{
|
|
if ((us -= ipcom_prof_data.overhead) & 0x80000000)
|
|
{
|
|
us = 0;
|
|
}
|
|
}
|
|
|
|
/* Check if already in list */
|
|
for (le = IPCOM_LIST_FIRST(&list);
|
|
le != IP_NULL;
|
|
le = IPCOM_LIST_NEXT(&le->head))
|
|
{
|
|
if (le->entry.msg == pe->msg)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (le == IP_NULL)
|
|
{
|
|
/* Init new listentry */
|
|
le = ipcom_malloc(sizeof(*le));
|
|
if (le == IP_NULL)
|
|
goto errout;
|
|
ipcom_memset(le, 0, sizeof(*le));
|
|
le->entry = *pe;
|
|
ipcom_list_insert_first(&list, &le->head);
|
|
}
|
|
|
|
/* Enter min/max/count */
|
|
if (le->min == 0 || us < le->min)
|
|
{
|
|
le->min = us;
|
|
}
|
|
if (us > le->max)
|
|
{
|
|
le->max = us;
|
|
}
|
|
IP_U64_ADD32(le->us, us);
|
|
IP_U64_ADD32(totus, us);
|
|
if ((pe->flags & IPCOM_PROF_CONTINUED) == 0)
|
|
{
|
|
le->count++;
|
|
}
|
|
}
|
|
|
|
/* Sort list by moving all entries to front of list */
|
|
for (le = IPCOM_LIST_FIRST(&list); le != IP_NULL; le = next)
|
|
{
|
|
next = IPCOM_LIST_NEXT(&le->head);
|
|
|
|
/* Find location to insert new entry */
|
|
for (le2 = IPCOM_LIST_FIRST(&list);
|
|
le2 != le;
|
|
le2 = IPCOM_LIST_NEXT(&le2->head))
|
|
{
|
|
ip_assert(le2 != IP_NULL);
|
|
if (flags & IPCOM_PROF_COUNT)
|
|
{
|
|
if (le2->count < le->count)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if (IP_U64_GETHI(le2->us) < IP_U64_GETHI(le->us) ||
|
|
(IP_U64_GETHI(le2->us) == IP_U64_GETHI(le->us) &&
|
|
IP_U64_GETLO(le2->us) < IP_U64_GETLO(le->us)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Don't bother if already last in sorted list */
|
|
if (le != le2)
|
|
{
|
|
ipcom_list_remove(&le->head);
|
|
ipcom_list_insert_before(&le2->head, &le->head);
|
|
}
|
|
}
|
|
|
|
/* Print entries */
|
|
while ((le = IPCOM_LIST_FIRST(&list)) != IP_NULL)
|
|
{
|
|
Ip_u64 a = le->us;
|
|
Ip_u64 b = totus;
|
|
|
|
if (flags & IPCOM_PROF_LINENUM)
|
|
{
|
|
PRINTF("%s(%d): ", le->entry.file, le->entry.line);
|
|
}
|
|
|
|
/* Shift until able to compute percentage in 32 bits */
|
|
while ((IP_U64_GETHI(a) | IP_U64_GETHI(b)) != 0 ||
|
|
((IP_U64_GETLO(a) | IP_U64_GETLO(b)) & 0xff000000) != 0)
|
|
{
|
|
/* 64-bit shift... */
|
|
IP_U64_SETLO(a, (IP_U64_GETLO(a) >> 1) | (IP_U64_GETHI(a) << 31));
|
|
IP_U64_SETHI(a, IP_U64_GETHI(a) >> 1);
|
|
IP_U64_SETLO(b, (IP_U64_GETLO(b) >> 1) | (IP_U64_GETHI(b) << 31));
|
|
IP_U64_SETHI(b, IP_U64_GETHI(b) >> 1);
|
|
}
|
|
|
|
PRINTF("%lu%% %lu(%lu-%lu) us, %lu times, %s\n",
|
|
(unsigned long)((IP_U64_GETLO(a) * 100 + 50) /
|
|
IP_U64_GETLO(b)),
|
|
(unsigned long)(IP_U64_GETLO(le->us) / le->count),
|
|
(unsigned long)le->min, (unsigned long)le->max,
|
|
(unsigned long)le->count, le->entry.msg); /*lint !e414 */
|
|
|
|
ipcom_list_remove(&le->head);
|
|
ipcom_free(le);
|
|
}
|
|
|
|
(void)ipcom_prof_stopped(stopped);
|
|
return;
|
|
|
|
errout:
|
|
while ((le = IPCOM_LIST_FIRST(&list)) != IP_NULL)
|
|
{
|
|
ipcom_list_remove(&le->head);
|
|
ipcom_free(le);
|
|
}
|
|
(void)ipcom_prof_stopped(stopped);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipcom_prof_sequence
|
|
*===========================================================================
|
|
* Description: Print sequences in profiler data.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipcom_prof_sequence(int start, int stop, int flags)
|
|
{
|
|
int stopped = ipcom_prof_stopped(1);
|
|
Ipcom_list list;
|
|
Ipcom_prof_listhead *head = IP_NULL;
|
|
Ipcom_prof_listhead *seq;
|
|
Ipcom_prof_listhead *next;
|
|
Ipcom_prof_listentry *le;
|
|
Ip_u64 totus;
|
|
int i;
|
|
|
|
IP_U64_CLEAR(totus);
|
|
|
|
if (stop < 0 || stop > ipcom_prof_data.indx)
|
|
{
|
|
stop = ipcom_prof_data.indx;
|
|
}
|
|
|
|
ipcom_list_init(&list);
|
|
|
|
/* Gather data for all entries */
|
|
for (i = start; i < stop; i++)
|
|
{
|
|
Ipcom_prof_entry *pe = &ipcom_prof_data.data[i]; /*lint !e676 */
|
|
Ip_u32 us = (i < (stop - 1) ? (pe + 1)->us - pe->us : pe->us);
|
|
|
|
/* Allocate new list? */
|
|
if (pe->type == 'B' && head == IP_NULL)
|
|
{
|
|
head = ipcom_malloc(sizeof(*head));
|
|
if (head == IP_NULL)
|
|
goto errout;
|
|
ipcom_memset(head, 0, sizeof(*head));
|
|
ipcom_list_init(&head->list);
|
|
head->count = 1;
|
|
}
|
|
|
|
/* Handle special entries */
|
|
if (pe->type == 'C' || head == IP_NULL)
|
|
{
|
|
/* Ignore cyber marks and entries outside begin/end */
|
|
continue;
|
|
}
|
|
else if (pe->type == 'E')
|
|
{
|
|
/* Enter min/max for sequence */
|
|
head->min = head->max = IP_U64_GETLO(head->us);
|
|
|
|
/* Loop through all sequences and check for match */
|
|
for (seq = IPCOM_LIST_FIRST(&list);
|
|
seq != IP_NULL;
|
|
seq = IPCOM_LIST_NEXT(&seq->head))
|
|
{
|
|
Ipcom_prof_listentry *le1;
|
|
Ipcom_prof_listentry *le2;
|
|
|
|
/* Check one sequence */
|
|
for (le1 = IPCOM_LIST_FIRST(&head->list),
|
|
le2 = IPCOM_LIST_FIRST(&seq->list);
|
|
le1 != IP_NULL && le2 != IP_NULL;
|
|
le1 = IPCOM_LIST_NEXT(&le1->head),
|
|
le2 = IPCOM_LIST_NEXT(&le2->head))
|
|
{
|
|
if (le1->entry.msg != le2->entry.msg)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Match? */
|
|
if (le1 == IP_NULL && le2 == IP_NULL)
|
|
{
|
|
/* The two sequences match, merge */
|
|
for (le1 = IPCOM_LIST_FIRST(&head->list),
|
|
le2 = IPCOM_LIST_FIRST(&seq->list);
|
|
le1 != IP_NULL && le2 != IP_NULL;
|
|
le1 = IPCOM_LIST_NEXT(&le1->head),
|
|
le2 = IPCOM_LIST_NEXT(&le2->head))
|
|
{
|
|
/* Enter min/max/count */
|
|
if (IP_U64_GETLO(le1->us) < le2->min)
|
|
{
|
|
le2->min = IP_U64_GETLO(le1->us);
|
|
}
|
|
if (IP_U64_GETLO(le1->us) > le2->max)
|
|
{
|
|
le2->max = IP_U64_GETLO(le1->us);
|
|
}
|
|
IP_U64_ADD32(le2->us, IP_U64_GETLO(le1->us));
|
|
le2->count += 1;
|
|
}
|
|
|
|
/* One more occurrence of this sequence */
|
|
if (IP_U64_GETLO(head->us) < seq->min)
|
|
{
|
|
seq->min = IP_U64_GETLO(head->us);
|
|
}
|
|
if (IP_U64_GETLO(head->us) > seq->max)
|
|
{
|
|
seq->max = IP_U64_GETLO(head->us);
|
|
}
|
|
IP_U64_ADD32(seq->us, IP_U64_GETLO(head->us));
|
|
seq->count++;
|
|
|
|
/* Now release the merged sequence */
|
|
while ((le1 = IPCOM_LIST_FIRST(&head->list)) != IP_NULL)
|
|
{
|
|
ipcom_list_remove(&le1->head);
|
|
ipcom_free(le1);
|
|
}
|
|
ipcom_free(head);
|
|
|
|
/* Done checking sequences */
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* No match, enter new sequence in list */
|
|
ipcom_list_insert_first(&list, &head->head);
|
|
done:
|
|
/* Indicate end of sequence */
|
|
head = IP_NULL;
|
|
}
|
|
else
|
|
{
|
|
/* Init new listentry */
|
|
le = ipcom_malloc(sizeof(*le));
|
|
if (le == IP_NULL)
|
|
goto errout;
|
|
ipcom_memset(le, 0, sizeof(*le));
|
|
le->entry = *pe;
|
|
ipcom_list_insert_last(&head->list, &le->head);
|
|
|
|
/* Handle out-of-order stuff */
|
|
if (us & 0x80000000)
|
|
{
|
|
us = 0;
|
|
}
|
|
else if ((flags & IPCOM_PROF_OVERHEAD) == 0)
|
|
{
|
|
if ((us -= ipcom_prof_data.overhead) & 0x80000000)
|
|
{
|
|
us = 0;
|
|
}
|
|
}
|
|
|
|
/* Enter min/max/count */
|
|
le->min = us;
|
|
le->max = us;
|
|
le->count = 1;
|
|
IP_U64_SET(le->us, 0, us);
|
|
IP_U64_ADD32(head->us, us);
|
|
IP_U64_ADD32(totus, us);
|
|
}
|
|
}
|
|
|
|
/* Sort list by moving all entries to front of list */
|
|
for (seq = IPCOM_LIST_FIRST(&list); seq != IP_NULL; seq = next)
|
|
{
|
|
Ipcom_prof_listhead *seq2;
|
|
|
|
next = IPCOM_LIST_NEXT(&seq->head);
|
|
|
|
/* Find location to insert new entry */
|
|
for (seq2 = IPCOM_LIST_FIRST(&list);
|
|
seq2 != seq;
|
|
seq2 = IPCOM_LIST_NEXT(&seq2->head))
|
|
{
|
|
ip_assert(seq2 != IP_NULL);
|
|
if (flags & IPCOM_PROF_COUNT)
|
|
{
|
|
if (seq2->count < seq->count)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if (IP_U64_GETHI(seq2->us) < IP_U64_GETHI(seq->us) ||
|
|
(IP_U64_GETHI(seq2->us) == IP_U64_GETHI(seq->us) &&
|
|
IP_U64_GETLO(seq2->us) < IP_U64_GETLO(seq->us)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Don't bother if already last in sorted list */
|
|
if (seq != seq2)
|
|
{
|
|
ipcom_list_remove(&seq->head);
|
|
ipcom_list_insert_before(&seq2->head, &seq->head);
|
|
}
|
|
}
|
|
|
|
/* Print sequences */
|
|
while ((seq = IPCOM_LIST_FIRST(&list)) != IP_NULL)
|
|
{
|
|
Ip_u64 a = seq->us;
|
|
Ip_u64 b = totus;
|
|
|
|
/* Shift until able to compute percentage in 32 bits */
|
|
while ((IP_U64_GETHI(a) | IP_U64_GETHI(b)) != 0 ||
|
|
((IP_U64_GETLO(a) | IP_U64_GETLO(b)) & 0xff000000) != 0)
|
|
{
|
|
/* 64-bit shift... */
|
|
IP_U64_SETLO(a, (IP_U64_GETLO(a) >> 1) | (IP_U64_GETHI(a) << 31));
|
|
IP_U64_SETHI(a, IP_U64_GETHI(a) >> 1);
|
|
IP_U64_SETLO(b, (IP_U64_GETLO(b) >> 1) | (IP_U64_GETHI(b) << 31));
|
|
IP_U64_SETHI(b, IP_U64_GETHI(b) >> 1);
|
|
}
|
|
|
|
if (seq->count)
|
|
PRINTF("\nSEQUENCE: %lu%% %lu(%lu-%lu) us, %lu times\n\n",
|
|
(unsigned long)((IP_U64_GETLO(a) * 100 + 50) /
|
|
IP_U64_GETLO(b)),
|
|
(unsigned long)(IP_U64_GETLO(seq->us) / seq->count),
|
|
(unsigned long)seq->min, (unsigned long)seq->max,
|
|
(unsigned long)seq->count); /*lint !e414 */
|
|
|
|
/* Print one sequence */
|
|
while ((le = IPCOM_LIST_FIRST(&seq->list)) != IP_NULL)
|
|
{
|
|
a = le->us;
|
|
b = seq->us;
|
|
|
|
if (flags & IPCOM_PROF_LINENUM)
|
|
{
|
|
PRINTF("%s(%d): ", le->entry.file, le->entry.line);
|
|
}
|
|
|
|
/* Shift until able to compute percentage in 32 bits */
|
|
while ((IP_U64_GETHI(a) | IP_U64_GETHI(b)) != 0 ||
|
|
((IP_U64_GETLO(a) | IP_U64_GETLO(b)) & 0xff000000) != 0)
|
|
{
|
|
/* 64-bit shift... */
|
|
IP_U64_SETLO(a, (IP_U64_GETLO(a) >> 1) |
|
|
(IP_U64_GETHI(a) << 31));
|
|
IP_U64_SETHI(a, IP_U64_GETHI(a) >> 1);
|
|
IP_U64_SETLO(b, (IP_U64_GETLO(b) >> 1) |
|
|
(IP_U64_GETHI(b) << 31));
|
|
IP_U64_SETHI(b, IP_U64_GETHI(b) >> 1);
|
|
}
|
|
|
|
PRINTF("%lu%% %lu(%lu-%lu) us, %lu times, %s\n",
|
|
(unsigned long)((IP_U64_GETLO(a) * 100 + 50) /
|
|
IP_U64_GETLO(b)),
|
|
(unsigned long)(IP_U64_GETLO(le->us) / le->count),
|
|
(unsigned long)le->min, (unsigned long)le->max,
|
|
(unsigned long)le->count, le->entry.msg);
|
|
|
|
ipcom_list_remove(&le->head);
|
|
ipcom_free(le);
|
|
}
|
|
|
|
ipcom_list_remove(&seq->head);
|
|
ipcom_free(seq);
|
|
}
|
|
|
|
(void)ipcom_prof_stopped(stopped);
|
|
return;
|
|
|
|
errout:
|
|
while ((seq = IPCOM_LIST_FIRST(&list)) != IP_NULL)
|
|
{
|
|
while ((le = IPCOM_LIST_FIRST(&seq->list)) != IP_NULL)
|
|
{
|
|
ipcom_list_remove(&le->head);
|
|
ipcom_free(le);
|
|
}
|
|
ipcom_list_remove(&seq->head);
|
|
ipcom_free(seq);
|
|
}
|
|
if (head != IP_NULL)
|
|
{
|
|
while ((le = IPCOM_LIST_FIRST(&head->list)) != IP_NULL)
|
|
{
|
|
ipcom_list_remove(&le->head);
|
|
ipcom_free(le);
|
|
}
|
|
ipcom_free(head);
|
|
head = IP_NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipcom_prof_void
|
|
*===========================================================================
|
|
* Description: Void command.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipcom_prof_void(void)
|
|
{
|
|
IPCOM_PROF("Start print");
|
|
PRINTF("void\n");
|
|
IPCOM_PROF("Stop print");
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipcom_cmd_prof
|
|
*===========================================================================
|
|
* Description: Profiler shell command.
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_PUBLIC int
|
|
ipcom_cmd_prof(int argc, char *argv[])
|
|
{
|
|
Ipcom_getopt o;
|
|
int flags = 0;
|
|
int begin = 0;
|
|
int end = -1;
|
|
int opt;
|
|
|
|
ipcom_getopt_clear_r(&o);
|
|
|
|
/* Get options */
|
|
while ((opt = ipcom_getopt_r(argc, argv, "b:e:acloh", &o)) != -1)
|
|
{
|
|
switch (opt)
|
|
{
|
|
case 'b':
|
|
begin = ipcom_atoi(o.optarg);
|
|
break;
|
|
case 'e':
|
|
end = ipcom_atoi(o.optarg);
|
|
break;
|
|
case 'a':
|
|
flags |= IPCOM_PROF_ABSOLUTE;
|
|
break;
|
|
case 'c':
|
|
flags |= IPCOM_PROF_COUNT;
|
|
break;
|
|
case 'l':
|
|
flags |= IPCOM_PROF_LINENUM;
|
|
break;
|
|
case 'o':
|
|
flags |= IPCOM_PROF_OVERHEAD;
|
|
break;
|
|
case 'h':
|
|
PRINTF("Usage: prof [options] command\n");
|
|
PRINTF("Commands:\n");
|
|
PRINTF(" clear Clear profiler data\n");
|
|
PRINTF(" start Start profiling\n");
|
|
PRINTF(" stop Stop profiling\n");
|
|
PRINTF(" info Show profiling info\n");
|
|
PRINTF(" dump Print plain dump of profiler data\n");
|
|
PRINTF(" sum Print summary of profiler data\n");
|
|
PRINTF(" seq Print sequence info of profiler data\n");
|
|
PRINTF(" void Void command for shell profiling \n");
|
|
PRINTF("Options:\n");
|
|
PRINTF(" -a Use absolute times where applicable\n");
|
|
PRINTF(" -c Sort output on count instead of time\n");
|
|
PRINTF(" -l Print linenumber for each entry\n");
|
|
PRINTF(" -o Include profiler overhead\n");
|
|
PRINTF(" -h Print this message\n");
|
|
return 0;
|
|
default:
|
|
PRINTF("Invalid profiler option\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Check command */
|
|
if (argc <= o.optind)
|
|
{
|
|
PRINTF("Please enter a profiler command\n");
|
|
return 1;
|
|
}
|
|
|
|
if (ipcom_strcmp(argv[o.optind], "clear") == 0)
|
|
{
|
|
ipcom_prof_clear();
|
|
}
|
|
else if (ipcom_strcmp(argv[o.optind], "start") == 0)
|
|
{
|
|
(void)ipcom_prof_stopped(0);
|
|
}
|
|
else if (ipcom_strcmp(argv[o.optind], "stop") == 0)
|
|
{
|
|
(void)ipcom_prof_stopped(1);
|
|
}
|
|
else if (ipcom_strcmp(argv[o.optind], "info") == 0)
|
|
{
|
|
PRINTF("%s, got %lu samples\n",
|
|
(ipcom_prof_data.stopped ? "Stopped" : "Running"),
|
|
ipcom_prof_data.indx);
|
|
PRINTF("%lu us overhead/sample\n", ipcom_prof_data.overhead);
|
|
}
|
|
else if (ipcom_strcmp(argv[o.optind], "dump") == 0)
|
|
{
|
|
ipcom_prof_dump(begin, end, flags);
|
|
}
|
|
else if (ipcom_strcmp(argv[o.optind], "sum") == 0)
|
|
{
|
|
ipcom_prof_summary(begin, end, flags);
|
|
}
|
|
else if (ipcom_strcmp(argv[o.optind], "seq") == 0)
|
|
{
|
|
ipcom_prof_sequence(begin, end, flags);
|
|
}
|
|
else if (ipcom_strcmp(argv[o.optind], "void") == 0)
|
|
{
|
|
ipcom_prof_void();
|
|
}
|
|
else
|
|
{
|
|
PRINTF("Invalid profiler command\n");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|