⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 profile.c

📁 开放源码实时操作系统源码.
💻 C
📖 第 1 页 / 共 2 页
字号:
    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 + -