📄 profile.c
字号:
//==========================================================================
//
// 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 + -