profilog.c
来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 416 行
C
416 行
/****************************************************************************
*
* Open Watcom Project
*
* Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
* ========================================================================
*
* This file contains Original Code and/or Modifications of Original
* Code as defined in and that are subject to the Sybase Open Watcom
* Public License version 1.0 (the 'License'). You may not use this file
* except in compliance with the License. BY USING THIS FILE YOU AGREE TO
* ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
* provided with the Original Code and Modifications, and is also
* available at www.sybase.com/developer/opensource.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
* ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
* NON-INFRINGEMENT. Please see the License for the specific language
* governing rights and limitations under the License.
*
* ========================================================================
*
* Description: Pentium RDTSC-based profiling support.
*
****************************************************************************/
#include "variety.h"
#include "rtinit.h"
#include "p5prof.h"
#include "ljmphdl.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#ifdef __NT__
#include <windows.h>
#endif
#include "widechar.h"
#include "initarg.h"
#define info new_P5_timing_info
char _data_ProfEnable;
__int64 _data_P5_overhead;
_WCRTDATA extern pfun __longjmp_handler;
pfun __prof_old_longjmp_handler;
#ifdef __NT__
#if 0 // not currently in use
HANDLE hDevice = INVALID_HANDLE_VALUE;
#endif
CRITICAL_SECTION __ProfCriticalSection;
void __ProfUnwind( void _WCFAR * ); // forward decl
static void profilog_init( void )
{
InitializeCriticalSection( &__ProfCriticalSection );
if( __prof_old_longjmp_handler == NULL ) {
__prof_old_longjmp_handler = __longjmp_handler;
__longjmp_handler = __ProfUnwind;
}
}
static void profilog_fini( void )
{
DeleteCriticalSection( &__ProfCriticalSection );
#if 0 // not currently in use
if (hDevice != INVALID_HANDLE_VALUE) {
long *pcounter = NULL;
DeviceIoControl(hDevice, 2, &pcounter, sizeof(pcounter), NULL,
0, NULL, NULL);
CloseHandle(hDevice);
}
#endif
}
XI( __new_p5_profilog_init, profilog_init, INIT_PRIORITY_LIBRARY-1 )
YI( __new_p5_profilog_fini, profilog_fini, INIT_PRIORITY_LIBRARY-1 )
#endif
#ifdef __NT__
#define ENTER_CRIT_SECTION() ( EnterCriticalSection( &__ProfCriticalSection ) )
#define EXIT_CRIT_SECTION() ( LeaveCriticalSection( &__ProfCriticalSection ) )
#else
#define ENTER_CRIT_SECTION()
#define EXIT_CRIT_SECTION()
#endif
_WCRTLINK void __ProfEnterCriticalSection( void )
{
ENTER_CRIT_SECTION();
}
_WCRTLINK void __ProfExitCriticalSection( void )
{
EXIT_CRIT_SECTION();
}
_WCRTLINK void __ProfEnable( void )
{
__ProfEnterCriticalSection();
_data_ProfEnable = 1;
__ProfExitCriticalSection();
}
_WCRTLINK void __ProfDisable( void )
{
__ProfEnterCriticalSection();
_data_ProfEnable = 0;
__ProfExitCriticalSection();
}
_WCRTLINK __int64 __P5_overhead( void )
{
return _data_P5_overhead;
}
extern __int64 rdtsc( void );
#pragma aux rdtsc = "rdtsc" value [ edx eax ];
#ifndef __SW_OF
#error "Must be compiled with /of to find return address"
#endif
extern reg_32 findLongJmpReturn( void );
#pragma aux findLongJmpReturn = \
"mov eax,[ebp]" \
"mov eax,8[eax]" \
value [ eax ] modify [ esp ];
extern reg_32 findReturn( void );
#pragma aux findReturn = \
"mov eax,+4[ebp]" \
value [ eax ] modify [ eax ];
extern reg_32 findStack( void );
#pragma aux findStack = \
"lea eax,+12[ebp]" \
value [ eax ] modify [ eax ];
extern reg_32 findSecondReturn( void );
#pragma aux findSecondReturn = \
"mov eax,+12[ebp]" \
value [ eax ] modify [ eax ];
extern void push_eax( void );
#pragma aux push_eax = \
"push eax" \
modify [ esp ];
extern void pop_eax( void );
#pragma aux pop_eax = \
"pop eax" \
modify [ esp ];
#define DIRECT_CALL_INDICATOR (unsigned short)0xc4f7 // test esp,offset(info)
#define INDIRECT_CALL_INDICATOR (unsigned short)0xc5f7 // test ebp,offset(info)
#define BLOCK_OFFSET 2 // offset of offset(info) in instruction
#define PON_CALL_SIZE 10 // push L1; call __PON
#define NUM_ALLOC 10
static info *FindOrAllocInfo( info *callerBlock, reg_32 callee )
{
int i;
info *newBlock;
for( ;; ) {
if( callerBlock->callee == callee ) break;
if( callerBlock->callee == 0 ) break;
if( callerBlock->flag[0] != 0 ) {
if( callerBlock->dynamic == 0 ) {
callerBlock->flag[0] = PROFILE_FLAG_DYNAMIC;
newBlock = (info*)__ProfAlloc( NUM_ALLOC*sizeof( info ) );
callerBlock->dynamic = newBlock;
callerBlock = newBlock;
for( i = 0; i < NUM_ALLOC; ++i ) {
newBlock->flag[0] = 0;
newBlock->dynamic = 0;
newBlock->start_time = 0;
newBlock->semaphore = 0;
newBlock->count = 0;
newBlock->stack = callerBlock->stack;
newBlock->cycles = 0;
newBlock->caller = 0;
newBlock->call_ins = 0;
newBlock->callee = 0;
++newBlock;
}
--newBlock;
newBlock->flag[0] = PROFILE_FLAG_END_GROUP;
break;
} else {
callerBlock = callerBlock->dynamic;
}
} else {
++callerBlock;
}
}
return( callerBlock );
}
void __ProfUnwind( void _WCFAR *ss_esp )
{
reg_32 returnAddress;
reg_32 esp;
info *block,*next;
__int64 start,startLessOverhead;
info *callerBlock;
push_eax();
if( _data_ProfEnable ) {
ENTER_CRIT_SECTION();
start = rdtsc();
startLessOverhead = start - _data_P5_overhead;
_data_P5_overhead -= start;
returnAddress = findLongJmpReturn();
if( *(unsigned short*)returnAddress == DIRECT_CALL_INDICATOR ) {
// get call site block for longjmp call
block = *(info**)( returnAddress + BLOCK_OFFSET );
// get function block from call site block
block = block->stack;
esp = (reg_32)ss_esp;
while( block != 0 && esp >= block->esp ) {
if( --block->semaphore == 0 ) {
if( block->start_time != 0 ) {
block->cycles -= block->start_time;
block->cycles += startLessOverhead;
block->start_time = 0;
}
}
callerBlock = block->stack;
block->stack = 0;
if( callerBlock != 0 ) {
if( --callerBlock->semaphore == 0 ) {
if( callerBlock->start_time != 0 ) {
callerBlock->cycles += startLessOverhead;
callerBlock->cycles -= callerBlock->start_time;
callerBlock->start_time = 0;
}
}
next = callerBlock->stack; // next function block
} else {
next = 0;
}
block = next;
}
}
_data_P5_overhead += rdtsc();
EXIT_CRIT_SECTION();
}
pop_eax();
__prof_old_longjmp_handler( ss_esp );
}
_WCRTLINK void __ProfProlog( info *block )
{
__int64 start,startLessOverhead;
reg_32 returnAddress;
reg_32 secondReturnAddress;
info *callerBlock;
reg_32 callee,caller,esp;
push_eax();
if( _data_ProfEnable ) {
ENTER_CRIT_SECTION();
start = rdtsc();
startLessOverhead = start - _data_P5_overhead;
_data_P5_overhead -= start;
returnAddress = findReturn();
esp = findStack();
callee = returnAddress - PON_CALL_SIZE;
if( *(unsigned short*)returnAddress != DIRECT_CALL_INDICATOR ) {
secondReturnAddress = findSecondReturn();
callerBlock = *(info**)( secondReturnAddress + BLOCK_OFFSET );
if( *(unsigned short*)secondReturnAddress == DIRECT_CALL_INDICATOR ) {
// nothing more to do right here
} else if( *(unsigned short*)secondReturnAddress == INDIRECT_CALL_INDICATOR ) {
caller = callerBlock->caller;
callerBlock = FindOrAllocInfo( callerBlock, callee );
callerBlock->caller = caller;
} else {
callerBlock = 0;
}
if( callerBlock != 0 ) {
callerBlock->count++;
if( callerBlock->semaphore++ == 0 ) {
callerBlock->callee = callee;
callerBlock->start_time = startLessOverhead;
callerBlock->call_ins = secondReturnAddress - 1;
}
}
}
block->count++;
if( block->semaphore++ == 0 ) { // no current invocation
block->callee = callee;
block->start_time = startLessOverhead;
block->esp = esp; // for unwind from longjmp
block->stack = callerBlock; // for unwind from longjmp
}
_data_P5_overhead += rdtsc();
EXIT_CRIT_SECTION();
}
pop_eax();
}
_WCRTLINK void __ProfEpilog( info *block )
{
__int64 start,startLessOverhead;
reg_32 secondReturnAddress;
info *callerBlock;
push_eax();
if( _data_ProfEnable ) {
ENTER_CRIT_SECTION();
start = rdtsc();
startLessOverhead = start - _data_P5_overhead;
_data_P5_overhead -= start;
if( --block->semaphore == 0 ) {
if( block->start_time != 0 ) {
block->cycles -= block->start_time;
block->cycles += startLessOverhead;
block->start_time = 0;
}
}
secondReturnAddress = findSecondReturn();
callerBlock = *(info**)( secondReturnAddress + BLOCK_OFFSET );
if( *(unsigned short*)secondReturnAddress == DIRECT_CALL_INDICATOR ) {
// nothing more to do right here
} else if( *(unsigned short*)secondReturnAddress == INDIRECT_CALL_INDICATOR ) {
callerBlock = FindOrAllocInfo( callerBlock, block->callee );
} else {
callerBlock = 0;
}
if( callerBlock != 0 ) {
if( --callerBlock->semaphore == 0 ) {
if( callerBlock->start_time != 0 ) {
callerBlock->cycles += startLessOverhead;
callerBlock->cycles -= callerBlock->start_time;
callerBlock->start_time = 0;
}
}
}
_data_P5_overhead += rdtsc();
EXIT_CRIT_SECTION();
}
pop_eax();
}
#if 0 // not currently in use
#ifdef __NT__
#define VXDNAME "P5PROF"
#define VXDEXT ".VXD"
static int findVXD( char *buff )
{
char fname[ _MAX_PATH2 ];
char *drive,*dir;
_splitpath2( _LpPgmName, &fname, &drive, &dir, NULL, NULL );
_makepath( buff, drive, dir, VXDNAME, VXDEXT );
if( access( buff, R_OK ) == 0 ) return( TRUE );
strcpy( buff, ".\\" VXDNAME VXDEXT );
if( access( buff, R_OK ) == 0 ) return( TRUE );
GetSystemDirectory( buff, _MAX_PATH );
strcat( buff, "\\" );
strcat( buff, VXDNAME VXDEXT );
if( access( buff, R_OK ) == 0 ) return( TRUE );
GetWindowsDirectory( buff, _MAX_PATH );
strcat( buff, "\\" );
strcat( buff, VXDNAME VXDEXT );
if( access( buff, R_OK ) == 0 ) return( TRUE );
_searchenv( VXDNAME VXDEXT, "PATH", buff );
if( buff[0] != '\0' ) return( TRUE );
return( FALSE );
}
#endif
_WCRTLINK void __ProfInit( void )
{
#ifdef __NT__
char buff[_MAX_PATH];
if( __prof_old_longjmp_handler == NULL ) {
__prof_old_longjmp_handler = __longjmp_handler;
__longjmp_handler = __ProfUnwind;
strcpy( buff, "\\\\.\\" );
if( findVXD( buff + strlen( buff ) ) ) {
hDevice = CreateFile("\\\\.\\c:\\dev\\prof\\VXD\\P5PROF.VXD", 0, 0, NULL, 0,
FILE_FLAG_DELETE_ON_CLOSE, NULL);
if (hDevice != INVALID_HANDLE_VALUE) {
DWORD version;
DWORD nret;
int *pcounter = (int*)&_data_P5_overhead;
DeviceIoControl(hDevice, 1, NULL, 0, &version, sizeof(version),
&nret, NULL);
printf("P5PROF.VXD is version %d.%02d\n", HIBYTE(version),
LOBYTE(version));
// nyi - virtualize 2 dwords!
DeviceIoControl(hDevice, 2, &pcounter, sizeof(pcounter), NULL, 0,
NULL, NULL);
}
}
}
#endif
}
#endif
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?