📄 profile.c
字号:
if (profile_tftp_is_open) {
return -1;
}
// Only read-only access is supported.
if ((0 != (flags & ~O_RDONLY)) || (0 == (flags & O_RDONLY))) {
return -1;
}
// Only gmon.out can be retrieved using this tftp daemon
if (0 != strcmp(filename, "gmon.out")) {
return -1;
}
// Everything is in order. Prepare for the first read. Profiling
// is suspended while the tftp transfer is in progress to avoid
// inconsistent results.
profile_enabled = 0;
profile_tftp_is_open = 1;
profile_tftp_next_index = 0;
profile_tftp_current_len = 0;
// Report any callgraph overflows. This is best done when retrieving
// the results, either in the gdb script or at tftp open time.
#ifdef CYGPKG_PROFILE_CALLGRAPH
if (profile_arc_overflow) {
diag_printf("Profiling: warning, the table of callgraph arcs has overflowed\n");
diag_printf("This can be avoided by increasing CYGNUM_PROFILE_CALLGRAPH_ARC_PERCENTAGE\n");
}
#endif
return 1;
}
static int
profile_tftp_close(int fd)
{
if (! profile_tftp_is_open) {
return -1;
}
profile_tftp_is_open = 0;
// The histogram counters are only 16 bits, so can easily overflow
// during a long run. Resetting the counters here makes it possible
// to examine profile data during different parts of the run with
// a reduced risk of overflow.
profile_reset();
// Profiling was disabled in the open() call
profile_enabled = 1;
return 0;
}
// gmon.out can only be read, not written.
static int
profile_tftp_write(int fd, const void *buf, int len)
{
return -1;
}
// The data that should go into gmon.out is spread all over memory.
// This utility is used to move from one block to the next.
static void
profile_tftp_read_next(void)
{
switch (profile_tftp_next_index) {
case 0 : // The current block is the gmon hdr
profile_tftp_current_block = (unsigned char*) &profile_gmon_hdr;
profile_tftp_current_len = sizeof(struct gmon_hdr);
break;
case 1 : // The histogram tag
profile_tftp_current_block = &(profile_tags[0]);
profile_tftp_current_len = 1;
break;
case 2 : // The histogram header
profile_tftp_current_block = (unsigned char*) &profile_hist_hdr;
profile_tftp_current_len = sizeof(struct gmon_hist_hdr);
break;
case 3 : // The histogram data
profile_tftp_current_block = (unsigned char*) profile_hist_data;
profile_tftp_current_len = profile_hist_hdr.hist_size * sizeof(cyg_uint16);
break;
default : // One of the arc records. These start at array offset 1.
{
int arc_index = profile_tftp_next_index - 3;
if (arc_index >= profile_arc_next) {
profile_tftp_current_block = (unsigned char*) 0;
profile_tftp_current_len = 0;
} else {
// gmon.out should contain a 1 byte tag followed by each
// arc record.
profile_tftp_current_block = (unsigned char*) &(profile_arc_records[arc_index].tags[3]);
profile_tftp_current_len = sizeof(struct gmon_cg_arc_record) + 1;
}
break;
}
}
profile_tftp_next_index++;
}
// Read the next block of data. There is no seek operation so no need
// to worry about the current position. State from the previous reads
// is held in profile_tftp_current_block and profile_tftp_current_len
static int
profile_tftp_read(int fd, void *buf_arg, int len)
{
unsigned char* buf = (unsigned char*) buf_arg;
int read = 0;
if ( ! profile_tftp_is_open ) {
return -1;
}
while (len > 0) {
if (0 == profile_tftp_current_len) {
profile_tftp_read_next();
if (0 == profile_tftp_current_len) {
break;
}
}
if (profile_tftp_current_len >= len) {
// The request can be satisfied by the current block
memcpy(&(buf[read]), profile_tftp_current_block, len);
profile_tftp_current_block += len;
profile_tftp_current_len -= len;
read += len;
break;
} else {
memcpy(&(buf[read]), profile_tftp_current_block, profile_tftp_current_len);
len -= profile_tftp_current_len;
read += profile_tftp_current_len;
profile_tftp_current_len = 0;
}
}
return read;
}
static struct tftpd_fileops profile_tftp_fileops = {
&profile_tftp_open,
&profile_tftp_close,
&profile_tftp_write,
&profile_tftp_read
};
#endif
// ----------------------------------------------------------------------------
// stop profiling
void
profile_off(void)
{
// suspend currently running profiling
profile_enabled = 0;
// Clear all pre-existing profile data
profile_reset();
if (profile_hist_data) {
free(profile_hist_data);
profile_hist_data = NULL;
}
#ifdef CYGPKG_PROFILE_CALLGRAPH
if (profile_arc_hashtable) {
free(profile_arc_hashtable);
profile_arc_hashtable=NULL;
}
if (profile_arc_records) {
free(profile_arc_records);
profile_arc_records=NULL;
}
#endif
}
// ----------------------------------------------------------------------------
// profile_on() has to be called by application code to start profiling.
// Application code will determine the start and end addresses, usually
// _stext and _etext, but it is possible to limit profiling to only
// some of the code. The bucket size controls how many PC addresses
// will be treated as a single hit: a smaller bucket increases precision
// but requires more memory. The resolution is used to initialize the
// profiling timer: more frequent interrupts means more accurate results
// but increases the risk of an overflow.
//
// profile_on() can be invoked multiple times. If invoked a second time
// it will stop the current profiling run and create a new profiling
// range.
void
profile_on(void *_start, void *_end, int _bucket_size, int resolution)
{
int bucket_size;
cyg_uint32 version = GMON_VERSION;
CYG_ADDRWORD text_size = (CYG_ADDRWORD)_end - (CYG_ADDRWORD)_start;
if (profile_enabled)
{
// invoking profile_on a second time
profile_off();
}
// Initialize statics. This also ensures that they won't be
// garbage collected by the linker so a gdb script can safely
// reference them.
memcpy(profile_gmon_hdr.cookie, GMON_MAGIC, 4);
memcpy(profile_gmon_hdr.version, &version, 4);
profile_tags[0] = GMON_TAG_TIME_HIST;
profile_tags[1] = GMON_TAG_CG_ARC;
profile_tags[2] = GMON_TAG_BB_COUNT;
strcpy(profile_hist_hdr.dimen, "seconds");
profile_hist_hdr.dimen_abbrev = 's';
// The actual bucket size. For efficiency this should be a power of 2.
bucket_size = 1;
bucket_shift = 0;
while (bucket_size < _bucket_size) {
bucket_size <<= 1;
bucket_shift += 1;
}
// The gprof documentation claims that this should be the size in
// bytes. The implementation treats it as a count.
profile_hist_hdr.hist_size = (cyg_uint32) ((text_size + bucket_size - 1) / bucket_size);
profile_hist_hdr.low_pc = _start;
profile_hist_hdr.high_pc = (void*)((cyg_uint8*)_end - 1);
// The prof_rate is the frequency in hz. The resolution argument is
// an interval in microseconds.
profile_hist_hdr.prof_rate = 1000000 / resolution;
// Now allocate a buffer for the histogram data.
profile_hist_data = (cyg_uint16*) malloc(profile_hist_hdr.hist_size * sizeof(cyg_uint16));
if ((cyg_uint16*)0 == profile_hist_data) {
diag_printf("profile_on(): cannot allocate histogram buffer - ignored\n");
return;
}
memset(profile_hist_data, 0, profile_hist_hdr.hist_size * sizeof(cyg_uint16));
#ifdef CYGPKG_PROFILE_CALLGRAPH
// Two arrays are needed for keeping track of the callgraph. The
// first is a hash table. The second holds the arc data. The
// latter array contains an extra 50 slots to cope with degenerate
// programs (including testcases).
{
int i;
profile_arc_hash_count = (int) ((text_size + (0x01 << CYGNUM_PROFILE_CALLGRAPH_HASH_SHIFT) - 1)
>> CYGNUM_PROFILE_CALLGRAPH_HASH_SHIFT);
profile_arc_records_count = (int)
(CYGNUM_PROFILE_CALLGRAPH_ARC_PERCENTAGE * (text_size / 100)) /
sizeof(struct profile_arc)
+ 50;
profile_arc_hashtable = (int*) malloc(profile_arc_hash_count * sizeof(int));
if ((int*)0 == profile_arc_hashtable) {
diag_printf("profile_on(): cannot allocate call graph hash table\n call graph profiling disabled\n");
} else {
memset(profile_arc_hashtable, 0, profile_arc_hash_count * sizeof(int));
profile_arc_records = (struct profile_arc*) malloc(profile_arc_records_count * sizeof(struct profile_arc));
if ((struct profile_arc*)0 == profile_arc_records) {
diag_printf("profile_on(): cannot allocate call graph arc table\n call graph profiling disabled\n");
free(profile_arc_hashtable);
profile_arc_hashtable = (int*) 0;
} else {
memset(profile_arc_records, 0, profile_arc_records_count * sizeof(struct profile_arc));
for (i = 0; i < profile_arc_records_count; i++) {
profile_arc_records[i].tags[3] = GMON_TAG_CG_ARC;
}
profile_arc_next = 1; // slot 0 cannot be used because 0 marks an unused hash slot.
}
}
}
#else
profile_arc_records = (struct profile_arc*) 0;
#endif
diag_printf("Profile from %p..%p in %d buckets of size %d\n",
profile_hist_hdr.low_pc, profile_hist_hdr.high_pc,
profile_hist_hdr.hist_size, bucket_size);
// Activate the profiling timer, which is usually provided by the
// variant or target HAL. The requested resolution may not be
// possible on the current hardware, so the HAL is allowed to
// tweak it.
resolution = hal_enable_profile_timer(resolution);
profile_hist_hdr.prof_rate = 1000000 / resolution;
profile_enabled = 1;
#ifdef CYGPKG_PROFILE_TFTP
// Create a TFTP server to provide the data
// invoking this a second time is harmless
(void) tftpd_start(CYGNUM_PROFILE_TFTP_PORT, &profile_tftp_fileops);
#endif
}
// EOF profile.c
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -