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

📄 profile.c

📁 开放源码实时操作系统源码.
💻 C
📖 第 1 页 / 共 2 页
字号:
//==========================================================================
//
//      profile.c
//
//      Application profiling support
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 2003 eCosCentric Ltd.
// Copyright (C) 2002 Gary Thomas
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    Gary Thomas
// Contributors: Bart Veer
// Date:         2002-11-14
// Purpose:      Application profiling support
// Description:  
//
//####DESCRIPTIONEND####
//
//===========================================================================

#include <pkgconf/system.h>
#include <pkgconf/profile_gprof.h>

#include <stdlib.h>
#include <string.h>
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/diag.h>
#include <cyg/profile/profile.h>
#include <cyg/profile/gmon_out.h>

#ifdef CYGPKG_PROFILE_TFTP
# include <network.h>
# include <tftp_support.h>
#endif

// ----------------------------------------------------------------------------
// A gmon.out file starts with a struct gmon_hdr containing a cookie
// "gmon", a format version number, and some spare bytes. The structure
// is initialized by the profile_on() entry point so that it does not
// get garbage collected by the collector and hence a gdb script can
// always access it.
static struct gmon_hdr  profile_gmon_hdr;

// The header is followed by data blocks. Each data block consists of a
// one-byte tag (HIST, ARC, or BB_COUNT), followed by data in a specific
// format.
static unsigned char    profile_tags[3];

// The profiling data always contains histogram data. Typically an
// extra hardware timer is made to interrupt at the desired rate
// and stores the interrupted pc.
static struct gmon_hist_hdr    profile_hist_hdr;

// The actual histogram counts. The file format only allows for 16-bit
// counts, which means overflow is a real possibility.
static cyg_uint16*      profile_hist_data;

// Each slot in the histogram data covers a range of pc addresses,
// allowing a trade off between memory requirements and precision.
static int              bucket_shift;

// Profiling is disabled on start-up and while a tftp transfer takes place.
static int              profile_enabled;

// This is used by the gdb script to reset the profile data.
static int              profile_reset_pending;

// The callgraph data. There is no header for this. Instead each non-zero
// entry is output separately, prefixed by an ARC tag. The data is accessed
// via a hash table/linked list combination. The tag is part of the
// structure to reduce the number of I/O operations needed for writing
// gmon.out.
struct profile_arc {
    cyg_uint32                  next;
    unsigned char               tags[4];
    struct gmon_cg_arc_record   record;
};

static struct profile_arc*  profile_arc_records;

// The next free slot in the arc_records table.
static int                  profile_arc_next    = 1;

#ifdef CYGPKG_PROFILE_CALLGRAPH
// The callgraph is accessed via a hash table. The hashing function is
// trivial, it just involves shifting an address an appropriate number
// of places.
static int*         profile_arc_hashtable;

// The sizes of these tables
static int          profile_arc_hash_count;
static int          profile_arc_records_count;

// Is the hashtable too small? Used for diagnostics.
static int          profile_arc_overflow;
#endif

// Reset current profiling data.
static void
profile_reset(void)
{
    memset(profile_hist_data, 0, profile_hist_hdr.hist_size * sizeof(cyg_uint16));

#ifdef CYGPKG_PROFILE_CALLGRAPH
    // Zeroing the callgraph can be achieved by zeroing the hash
    // table and resetting the next field used for indexing into
    // the arc data itself. Whenever an arc data slot is allocated
    // the count and addresses are reset.
    memset(profile_arc_hashtable, 0, profile_arc_hash_count * sizeof(int));
    profile_arc_next     = 1;
    profile_arc_overflow = 0;
#endif
}

// ----------------------------------------------------------------------------
// Accumulate profiling data.

// __profile_hit() will be called by HAL-specific code, typically in an ISR
// associated with a timer.

void
__profile_hit(CYG_ADDRWORD pc)
{
    int bucket;
    if (! profile_enabled ) {
        if (! profile_reset_pending) {
            return;
        }
        // reset_pending can be set by the gdb script to request resetting
        // the data. It avoids having to do lots of memory updates via the
        // gdb protocol, which is too slow.
        profile_reset_pending   = 0;
        profile_reset();
        profile_enabled         = 1;
    }
    
    if ((pc >= (CYG_ADDRWORD)profile_hist_hdr.low_pc) && (pc <= (CYG_ADDRWORD)profile_hist_hdr.high_pc)) {
        bucket = (pc - (CYG_ADDRWORD)profile_hist_hdr.low_pc) >> bucket_shift;
        if (profile_hist_data[bucket] < (unsigned short)0xFFFF) {
            profile_hist_data[bucket]++;
        }
    }
}

#ifdef CYGPKG_PROFILE_CALLGRAPH
// __profile_mcount() will be called by the HAL-specific mcount() routine.
// When code is compiled with -pg the compiler inserts calls to mcount()
// at the start of each function. Typically mcount() will not use standard
// calling conventions so it has to be provided by the HAL.
//
// The from_pc/to_pc data should end up in profile_arc_records. A hash table
// maps a PC into a list chained through the records array. The hash function
// is a simple shift, so a range of PC addresses (usually 256 bytes) map
// onto a single linked list of arc records.
//
// We can hash on either the caller_pc, the callee_pc, or some combination.
// The caller PC will typically be in the middle of some function. The
// number of arcs that hash into the same list will depend on the number of
// function calls within a 256-byte region of code, multiplied by the
// number of different functions called at each location. The latter will
// be 1 unless the code uses changing function pointers. The callee pc
// is near the start of a function, and the number of hash collisions will
// depend on the number of places that function is called from. Usually this
// will be small, but some utility functions may be called from many different
// places.
//
// Hashing on the caller PC should give more deterministic results.
//
// On some targets the compiler does additional work. For example on
// the 68K in theory there is no need for a hash table because the
// compiler provides a word with each callee for the head of the
// linked list. It is not easy to cope with that in generic code, so
// for now this code ignores such compiler assistance.
//
// It is assumed that __profile_mcount() will be called with interrupts
// disabled. 

void
__profile_mcount(CYG_ADDRWORD caller_pc, CYG_ADDRWORD callee_pc)
{
    int                 hash_index;
    struct profile_arc* current;

    // mcount() may be called at any time, even before profile_arc_records
    // is enabled. There is an assumption here that .bss has been zeroed
    // before the first call into C code, i.e. by the initial assembler
    // start-up.
    if (!profile_enabled) {
        if (! profile_reset_pending) {
            return;
        }
        profile_reset_pending   = 0;
        profile_reset();
        profile_enabled         = 1;
    }

    // Check the caller_pc because that is what is used to index the
    // hash table. Checking the callee_pc is optional and depends on
    // exactly how you interpret the start and end addresses passed to
    // profile_on().
    if ((caller_pc < (CYG_ADDRWORD)profile_hist_hdr.low_pc) ||
        (caller_pc > (CYG_ADDRWORD)profile_hist_hdr.high_pc)) {
        return;
    }

    hash_index = (int) ((caller_pc - (CYG_ADDRWORD)profile_hist_hdr.low_pc) >> CYGNUM_PROFILE_CALLGRAPH_HASH_SHIFT);
    if (0 == profile_arc_hashtable[hash_index]) {
        if (profile_arc_next == profile_arc_records_count) {
            profile_arc_overflow = 1;
        } else {
            profile_arc_hashtable[hash_index]   = profile_arc_next;
            current = &(profile_arc_records[profile_arc_next]);
            profile_arc_next++;
            current->next   = 0;
            current->record.from_pc = (void*) caller_pc;
            current->record.self_pc = (void*) callee_pc;
            current->record.count   = 1;
        }
    } else {
        current = &(profile_arc_records[profile_arc_hashtable[hash_index]]);
        while (1) {
            if ((current->record.from_pc == (void*) caller_pc) && (current->record.self_pc == (void*) callee_pc)) {
                current->record.count++;
                break;
            } else if (0 == current->next) {
                if (profile_arc_next == profile_arc_records_count) {
                    profile_arc_overflow = 1;
                } else {
                    current->next   = profile_arc_next;
                    current         = &(profile_arc_records[profile_arc_next]);
                    profile_arc_next++;
                    current->next   = 0;
                    current->record.from_pc = (void*) caller_pc;
                    current->record.self_pc = (void*) callee_pc;
                    current->record.count   = 1;
                }
                break;
            } else {
                current = &(profile_arc_records[current->next]);
            }
        }
    }
}
#endif

#ifdef CYGPKG_PROFILE_TFTP
// ----------------------------------------------------------------------------
// TFTP support
//
// To keep things simple this code only supports one open file at a time,
// and only gmon.out is supported.

static int              profile_tftp_next_index     = 0;
static unsigned char*   profile_tftp_current_block  = (unsigned char*) 0;
static int              profile_tftp_current_len    = 0;
static int              profile_tftp_is_open        = 0;

static int
profile_tftp_open(const char *filename, int flags)
{
    // Only allow one open file for now.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -