📄 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];voidprofiler(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;}intprof_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);}voidprof_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(" %-12s: %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 + -