📄 monprof.c
字号:
/*
* monprof.c:
* This command and support code allow the monitor to provide an application
* with some system profiling capabilities. The function "profiler"
* is part of the API to allow the application to take advantage of this.
*
* The basic assumption is that the application has the ability to insert
* a call to mon_profiler() in its system tick handler (actually the call
* can be put anywhere in the application, but this is the most logical
* place to put it). The content of the structure passed in to
* mon_profiler() depends on what kind of profiling the application wants
* to do. The tool works partially through calls made by the application
* into the monitor's api (mon_profiler()) and partially through
* interaction with the user at the monitor command line. The runtime
* profiling calls are done in application space, but the initial setup of
* the profiling session is done at the CLI.
*
* There are a few different profiling mechanisms in place...
*
* FUNCTION LOGGING:
* This capability makes the same assumption regarding the symtbl file
* as does strace... That the symbols are listed in the file in ascending
* address order. Ideally, the only symbols in the file would be the
* functions, so some processing can be done on the symtbl file prior to
* putting it on the target, this would help (not mandatory). At the CLI,
* the user issues the "prof finit [address]" command. This initializes
* a table in the monitor that contains one pdata structure for each entry
* in the symtbl file. The pdata structure simply contains the starting
* address and a pass count for each entry in symtbl. After this
* initialization, runtime profiling can start. This is used by setting
* the MONPROF_FUNCLOG (see monprof.h) flag in the 'type' member of the
* monprof structure. Upon entry into the profiler, the 'pc' member of
* the incoming structure is assumed to contain the address that was
* executing at the time of the interrupt. The profiler uses a simple
* binary search to scan through all entries in the table to find the
* symbol (function) that the address falls within. When this is found,
* that entry's pass count variable is incremented. At some point later,
* profiling is completed and the user can dump the results to show the
* user what functions were hogging the CPU during the profiling session.
*
* Example setup:
* prof init # Initialize internals.
* prof funccfg # Configure function logging using symtbl.
* prof on # Enable profiling.
*
*
* PC LOGGING:
* This capability assumes that every instruction is the same size (typical
* for RISC CPUs). It uses a block of memory equal in size to the .text
* section of the application and treats that block as a table of counters.
* Each time the profiler is called, the PC (along with some delta) is
* used as an offset into the table and that offset is incremented.
* This may be used as a better alternative to the FUNCTION LOGGING
* mechanism described above; but it does have some additional requirements,
* (fixed-width instruction & extra ram space == .text size) so it may not
* be an option.
*
* Example setup:
* prof init # Initialize internals.
* prof pccfg 4 0x22000 0x10000 # Configure pc logging for 32-bit
* # instructions. The .text space of
* # the application starts at 0x22000
* # with a size of 0x10000 bytes.
* prof on # Enable profiling.
*
* TASK ID LOGGING:
* Similar to function logging except that the MONPROF_TIDLOG flag is set
* in the 'type' member, and the 'tid' member is used...
* The user initializes with the "prof tinit {count}" command. The count
* is the maximum number of unique task ids expected. The monitor builds
* another table and as each tid comes in through mon_profiler, if this is
* the first time mon_profiler() is being called with the specific incoming
* tid value, then it is added to the list of pdata structures and the pass
* count is set to 1; otherwise if it has already been logged, only the
* pass count is incremented.
*
* Example setup:
* prof init # Initialize internals.
* prof tidcfg 16 # Configure tid logging for 16 unique tids.
* prof on # Enable profiling.
*
*
* General notice:
* This code is part of a boot-monitor package developed as a generic base
* platform for embedded system designs. As such, it is likely to be
* distributed to various projects beyond the control of the original
* author. Please notify the author of any enhancements made or bugs found
* so that all may benefit from the changes. In addition, notification back
* to the author will allow the new user to pick up changes that may have
* been made by other users after this version of the code was distributed.
*
* Note1: the majority of this code was edited with 4-space tabs.
* Note2: as more and more contributions are accepted, the term "author"
* is becoming a mis-representation of credit.
*
* Original author: Ed Sutter
* Email: esutter@lucent.com
* Phone: 908-582-2351
*/
#include "config.h"
#include "monprof.h"
#if INCLUDE_PROFILER
#include "stddefs.h"
#include "genlib.h"
#include "ctype.h"
#include "cli.h"
#include "tfs.h"
#include "tfsprivate.h"
#define HALF(m) (m >> 1)
/* pdata:
* One of these represents each symbol (or tid) in the profiling session.
* For MONPROF_FUNCLOG, the data member is the starting address of the symbol
* and for MONPROF_TIDLOG, the data member is the tid value.
*/
struct pdata {
ulong data; /* Start of symbol or tid. */
int pcount; /* Pass count. */
};
static int prof_Enabled; /* If set, profiler runs; else return. */
static int prof_BadSymCnt; /* Number of hits not within a symbol. */
static int prof_CallCnt; /* Number of times profiler was called. */
static int prof_FuncTot; /* Number of functions being profiled. */
static int prof_TidTot; /* Number of TIDs being profiled. */
static int prof_TidTally; /* Number of unique TIDs logged so far. */
static int prof_TidOverflow; /* More TIDs than the table was built for. */
static int prof_PcTot; /* Number of instructions being profiled. */
static int prof_PcDelta; /* Delta between .text space and PC table. */
static int prof_PcWidth; /* Width (2 or 4) of PC table elements. */
static int prof_PcOORCnt; /* Out-of-range hit count for PC profiler */
static ulong prof_PcTxtEnd; /* End of .text area being profiled */
static ulong prof_PcTxtBase; /* Base of .text area being profiled */
static struct pdata *prof_FuncTbl;
static struct pdata *prof_TidTbl;
static uchar *prof_PcTbl;
static char prof_SymFile[TFSNAMESIZE+1];
void
profiler(struct monprof *mpp)
{
struct pdata *current, *base;
int nmem;
if (prof_Enabled == 0)
return;
if (mpp->type & MONPROF_FUNCLOG) {
nmem = prof_FuncTot;
base = prof_FuncTbl;
while(nmem) {
current = &base[HALF(nmem)];
if (mpp->pc < current->data)
nmem = HALF(nmem);
else if (mpp->pc > current->data) {
if (mpp->pc < (current+1)->data) {
current->pcount++;
goto tidlog;
}
else {
base = current + 1;
nmem = (HALF(nmem)) - (nmem ? 0 : 1);
}
}
else {
current->pcount++;
goto tidlog;
}
}
prof_BadSymCnt++;
}
tidlog:
if (mpp->type & MONPROF_TIDLOG) {
/* First see if the tid is already in the table. If it is,
* increment the pcount. If it isn't add it to the table.
*/
nmem = prof_TidTally;
base = prof_TidTbl;
while(nmem) {
current = &base[HALF(nmem)];
if (mpp->tid < current->data)
nmem = HALF(nmem);
else if (mpp->tid > current->data) {
base = current + 1;
nmem = (HALF(nmem)) - (nmem ? 0 : 1);
}
else {
current->pcount++;
goto pclog;
}
}
/* Since we got here, the tid must not be in the table, so
* do an insertion into the table. Items are in the table in
* ascending order.
*/
if (prof_TidTally == 0) {
prof_TidTbl->pcount = 1;
prof_TidTbl->data = mpp->tid;
prof_TidTally++;
}
else if (prof_TidTally >= prof_TidTot) {
prof_TidOverflow++;
}
else {
current = prof_TidTbl + prof_TidTally - 1;
while(current >= prof_TidTbl) {
if (mpp->tid > current->data) {
current++;
current->pcount = 1;
current->data = mpp->tid;
break;
}
else {
*(current+1) = *current;
if (current == prof_TidTbl) {
current->pcount = 1;
current->data = mpp->tid;
break;
}
}
current--;
}
prof_TidTally++;
}
}
pclog:
if (mpp->type & MONPROF_PCLOG) {
ulong offset;
if ((mpp->pc > prof_PcTxtEnd) || (mpp->pc < prof_PcTxtBase)) {
prof_PcOORCnt++;
}
else {
offset = mpp->pc - prof_PcDelta;
switch(prof_PcWidth) {
case 2:
(*(ushort *)offset)++;
break;
case 4:
(*(ulong *)offset)++;
break;
}
}
}
prof_CallCnt++;
return;
}
int
prof_GetSymFile(void)
{
int tfd;
if (prof_SymFile[0] == 0) {
tfd = SymFileFd(1);
}
else {
tfd = tfsopen(prof_SymFile,TFS_RDONLY,0);
if (tfd < 0) {
printf("%s: %s\n",prof_SymFile,(char *)tfsctrl(TFS_ERRMSG,tfd,0));
}
}
return(tfd);
}
void
prof_ShowStats(int minhit, int more)
{
int i, tfd, linecount;
ulong notused;
char symname[64];
struct pdata *pptr;
printf("FuncCount Cfg: tbl: 0x%08lx, size: 0x%x\n",
(ulong)prof_FuncTbl, prof_FuncTot);
printf("TidCount Cfg: tbl: 0x%08lx, size: 0x%x\n",
(ulong)prof_TidTbl, prof_TidTot);
printf("PcCount Cfg: tbl: 0x%08lx, size: 0x%x\n",
(ulong)prof_PcTbl, prof_PcTot*prof_PcWidth);
if (prof_CallCnt == 0) {
printf("No data collected%s",
prof_Enabled == 0 ? " (profiling disabled)\n" : "\n");
return;
}
linecount = 0;
tfd = prof_GetSymFile();
if ((prof_FuncTbl) && (prof_FuncTot > 0)) {
printf("\nFUNC_PROF stats:\n");
pptr = prof_FuncTbl;
for(i=0;i<prof_FuncTot;pptr++,i++) {
if (pptr->pcount < minhit)
continue;
if ((tfd < 0) ||
(AddrToSym(tfd,pptr->data,symname,¬used) == 0)) {
printf(" %08lx : %d\n",pptr->data,pptr->pcount);
}
else {
printf(" %-25s: %d\n",symname,pptr->pcount);
}
if ((more) && (++linecount >= more)) {
linecount = 0;
if (More() == 0)
goto showdone;
}
}
}
if ((prof_TidTbl) && (prof_TidTot > 0)) {
printf("\nTID_PROF stats:\n");
pptr = prof_TidTbl;
for(i=0;i<prof_TidTot;pptr++,i++) {
if (pptr->pcount < minhit)
continue;
printf(" %08lx : %d\n",pptr->data,pptr->pcount);
if ((more) && (++linecount >= more)) {
linecount = 0;
if (More() == 0)
goto showdone;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -