util: Remove homegrown Windows KM profiler.

It's not sampling based so its results are biased towards functions called
many times.
This commit is contained in:
José Fonseca 2009-11-03 19:47:51 +00:00
parent 767bc8eb5a
commit 0b4ea45e8a
6 changed files with 0 additions and 653 deletions

View File

@ -1,309 +0,0 @@
#!/usr/bin/env python
##########################################################################
#
# Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
# All Rights Reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sub license, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice (including the
# next paragraph) shall be included in all copies or substantial portions
# of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
# IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
##########################################################################
import sys
import optparse
import re
import struct
from gprof2dot import Call, Function, Profile
from gprof2dot import CALLS, SAMPLES, TIME, TIME_RATIO, TOTAL_TIME, TOTAL_TIME_RATIO
from gprof2dot import DotWriter, TEMPERATURE_COLORMAP
__version__ = '0.1'
class ParseError(Exception):
pass
class MsvcDemangler:
# http://www.kegel.com/mangle.html
def __init__(self, symbol):
self._symbol = symbol
self._pos = 0
def lookahead(self):
return self._symbol[self._pos]
def consume(self):
ret = self.lookahead()
self._pos += 1
return ret
def match(self, c):
if self.lookahead() != c:
raise ParseError
self.consume()
def parse(self):
self.match('?')
name = self.parse_name()
qualifications = self.parse_qualifications()
return '::'.join(qualifications + [name])
def parse_name(self):
if self.lookahead() == '?':
return self.consume() + self.consume()
else:
name = self.parse_id()
self.match('@')
return name
def parse_qualifications(self):
qualifications = []
while self.lookahead() != '@':
name = self.parse_id()
qualifications.append(name)
self.match('@')
return qualifications
def parse_id(self):
s = ''
while True:
c = self.lookahead()
if c.isalnum() or c in '_':
s += c
self.consume()
else:
break
return s
def demangle(name):
if name.startswith('_'):
name = name[1:]
idx = name.rfind('@')
if idx != -1 and name[idx+1:].isdigit():
name = name[:idx]
return name
if name.startswith('?'):
demangler = MsvcDemangler(name)
return demangler.parse()
return name
class Reader:
def __init__(self):
self.symbols = []
self.symbol_cache = {}
self.base_addr = None
def read_map(self, mapfile):
# See http://msdn.microsoft.com/en-us/library/k7xkk3e2.aspx
last_addr = 0
last_name = 0
for line in file(mapfile, "rt"):
fields = line.split()
try:
section_offset, name, addr, type, lib_object = fields
except ValueError:
continue
if type != 'f':
continue
section, offset = section_offset.split(':')
addr = int(offset, 16)
self.symbols.append((addr, name))
last_addr = addr
last_name = name
# sort symbols
self.symbols.sort(key = lambda (addr, name): addr)
def lookup_addr(self, addr):
try:
return self.symbol_cache[addr]
except KeyError:
pass
tolerance = 4196
s, e = 0, len(self.symbols)
while s != e:
i = (s + e)//2
start_addr, name = self.symbols[i]
try:
end_addr, next_name = self.symbols[i + 1]
except IndexError:
end_addr = start_addr + tolerance
if addr < start_addr:
e = i
continue
if addr == end_addr:
return next_name, addr - start_addr
if addr > end_addr:
s = i
continue
return name, addr - start_addr
raise ValueError
def lookup_symbol(self, name):
for symbol_addr, symbol_name in self.symbols:
if name == symbol_name:
return symbol_addr
return 0
def read_data(self, data):
profile = Profile()
fp = file(data, "rb")
entry_format = "IIII"
entry_size = struct.calcsize(entry_format)
caller = None
caller_stack = []
while True:
entry = fp.read(entry_size)
if len(entry) < entry_size:
break
caller_addr, callee_addr, samples_lo, samples_hi = struct.unpack(entry_format, entry)
if caller_addr == 0 and callee_addr == 0:
continue
if self.base_addr is None:
ref_addr = self.lookup_symbol('___debug_profile_reference@0')
if ref_addr:
self.base_addr = (caller_addr - ref_addr) & ~(options.align - 1)
else:
self.base_addr = 0
sys.stderr.write('Base addr: %08x\n' % self.base_addr)
samples = (samples_hi << 32) | samples_lo
try:
caller_raddr = caller_addr - self.base_addr
caller_sym, caller_ofs = self.lookup_addr(caller_raddr)
try:
caller = profile.functions[caller_sym]
except KeyError:
caller_name = demangle(caller_sym)
caller = Function(caller_sym, caller_name)
profile.add_function(caller)
caller[CALLS] = 0
caller[SAMPLES] = 0
except ValueError:
caller = None
if not callee_addr:
if caller:
caller[SAMPLES] += samples
else:
callee_raddr = callee_addr - self.base_addr
callee_sym, callee_ofs = self.lookup_addr(callee_raddr)
try:
callee = profile.functions[callee_sym]
except KeyError:
callee_name = demangle(callee_sym)
callee = Function(callee_sym, callee_name)
profile.add_function(callee)
callee[CALLS] = samples
callee[SAMPLES] = 0
else:
callee[CALLS] += samples
if caller is not None:
try:
call = caller.calls[callee.id]
except KeyError:
call = Call(callee.id)
call[CALLS] = samples
caller.add_call(call)
else:
call[CALLS] += samples
if options.verbose:
if not callee_addr:
sys.stderr.write('%s+%u: %u\n' % (caller_sym, caller_ofs, samples))
else:
sys.stderr.write('%s+%u -> %s+%u: %u\n' % (caller_sym, caller_ofs, callee_sym, callee_ofs, samples))
# compute derived data
profile.validate()
profile.find_cycles()
profile.aggregate(SAMPLES)
profile.ratio(TIME_RATIO, SAMPLES)
profile.call_ratios(CALLS)
profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO)
return profile
def main():
parser = optparse.OptionParser(
usage="\n\t%prog [options] [file] ...",
version="%%prog %s" % __version__)
parser.add_option(
'-a', '--align', metavar='NUMBER',
type="int", dest="align", default=16,
help="section alignment")
parser.add_option(
'-m', '--map', metavar='FILE',
type="string", dest="map",
help="map file")
parser.add_option(
'-b', '--base', metavar='FILE',
type="string", dest="base",
help="base addr")
parser.add_option(
'-n', '--node-thres', metavar='PERCENTAGE',
type="float", dest="node_thres", default=0.5,
help="eliminate nodes below this threshold [default: %default]")
parser.add_option(
'-e', '--edge-thres', metavar='PERCENTAGE',
type="float", dest="edge_thres", default=0.1,
help="eliminate edges below this threshold [default: %default]")
parser.add_option(
'-v', '--verbose',
action="count",
dest="verbose", default=0,
help="verbose output")
global options
(options, args) = parser.parse_args(sys.argv[1:])
reader = Reader()
if options.base is not None:
reader.base_addr = int(options.base, 16)
if options.map is not None:
reader.read_map(options.map)
for arg in args:
profile = reader.read_data(arg)
profile.prune(options.node_thres/100.0, options.edge_thres/100.0)
output = sys.stdout
dot = DotWriter(output)
colormap = TEMPERATURE_COLORMAP
dot.graph(profile, colormap)
if __name__ == '__main__':
main()

View File

@ -392,11 +392,6 @@ def generate(env):
'/O2', # optimize for speed
#'/fp:fast', # fast floating point
]
if env['profile']:
ccflags += [
'/Gh', # enable _penter hook function
'/GH', # enable _pexit hook function
]
ccflags += [
'/W3', # warning level
#'/Wp64', # enable 64 bit porting warnings

View File

@ -406,8 +406,6 @@ def generate(env):
ccflags += ['-O0', '-g3'] # mingw 4.2.1 optimizer is broken
else:
ccflags += ['-O3', '-g0']
if env['profile']:
ccflags += ['-pg']
if env['machine'] == 'x86':
ccflags += [
'-m32',
@ -450,11 +448,6 @@ def generate(env):
'/Ot', # favor code speed
#'/fp:fast', # fast floating point
]
if env['profile']:
ccflags += [
'/Gh', # enable _penter hook function
'/GH', # enable _pexit hook function
]
ccflags += [
'/W3', # warning level
#'/Wp64', # enable 64 bit porting warnings

View File

@ -28,7 +28,6 @@ util = env.ConvenienceLibrary(
'u_debug.c',
'u_debug_dump.c',
'u_debug_memory.c',
'u_debug_profile.c',
'u_debug_stack.c',
'u_debug_symbol.c',
'u_draw_quad.c',

View File

@ -351,17 +351,6 @@ void
debug_memory_end(unsigned long beginning);
#if defined(PROFILE) && defined(PIPE_SUBSYSTEM_WINDOWS_DISPLAY)
void
debug_profile_start(void);
void
debug_profile_stop(void);
#endif
#ifdef DEBUG
struct pipe_surface;
struct pipe_transfer;

View File

@ -1,320 +0,0 @@
/**************************************************************************
*
* Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**************************************************************************/
/**
* @file
* Poor-man profiling.
*
* @author José Fonseca <jrfonseca@tungstengraphics.com>
*
* @sa http://blogs.msdn.com/joshpoley/archive/2008/03/12/poor-man-s-profiler.aspx
* @sa http://www.johnpanzer.com/aci_cuj/index.html
*/
#include "pipe/p_config.h"
#if defined(PROFILE) && defined(PIPE_SUBSYSTEM_WINDOWS_DISPLAY)
#include <windows.h>
#include <winddi.h>
#include "util/u_debug.h"
#include "util/u_string.h"
#define PROFILE_TABLE_SIZE (1024*1024)
#define FILE_NAME_SIZE 256
struct debug_profile_entry
{
uintptr_t caller;
uintptr_t callee;
uint64_t samples;
};
static unsigned long enabled = 0;
static WCHAR wFileName[FILE_NAME_SIZE] = L"\\??\\c:\\00000000.prof";
static ULONG_PTR iFile = 0;
static struct debug_profile_entry *table = NULL;
static unsigned long free_table_entries = 0;
static unsigned long max_table_entries = 0;
uint64_t start_stamp = 0;
uint64_t end_stamp = 0;
static void
debug_profile_entry(uintptr_t caller, uintptr_t callee, uint64_t samples)
{
unsigned hash = ( caller + callee ) & PROFILE_TABLE_SIZE - 1;
while(1) {
if(table[hash].caller == 0 && table[hash].callee == 0) {
table[hash].caller = caller;
table[hash].callee = callee;
table[hash].samples = samples;
--free_table_entries;
break;
}
else if(table[hash].caller == caller && table[hash].callee == callee) {
table[hash].samples += samples;
break;
}
else {
++hash;
}
}
}
static uintptr_t caller_stack[1024];
static unsigned last_caller = 0;
static int64_t delta(void) {
int64_t result = end_stamp - start_stamp;
if(result > UINT64_C(0xffffffff))
result = 0;
return result;
}
static void __cdecl
debug_profile_enter(uintptr_t callee)
{
uintptr_t caller = last_caller ? caller_stack[last_caller - 1] : 0;
if (caller)
debug_profile_entry(caller, 0, delta());
debug_profile_entry(caller, callee, 1);
caller_stack[last_caller++] = callee;
}
static void __cdecl
debug_profile_exit(uintptr_t callee)
{
debug_profile_entry(callee, 0, delta());
if(last_caller)
--last_caller;
}
/**
* Called at the start of every method or function.
*
* @sa http://msdn.microsoft.com/en-us/library/c63a9b7h.aspx
*/
void __declspec(naked) __cdecl
_penter(void) {
_asm {
push eax
mov eax, [enabled]
test eax, eax
jz skip
push edx
rdtsc
mov dword ptr [end_stamp], eax
mov dword ptr [end_stamp+4], edx
xor eax, eax
mov [enabled], eax
mov eax, [esp+8]
push ebx
push ecx
push ebp
push edi
push esi
push eax
call debug_profile_enter
add esp, 4
pop esi
pop edi
pop ebp
pop ecx
pop ebx
mov eax, 1
mov [enabled], eax
rdtsc
mov dword ptr [start_stamp], eax
mov dword ptr [start_stamp+4], edx
pop edx
skip:
pop eax
ret
}
}
/**
* Called at the end of Calls the end of every method or function.
*
* @sa http://msdn.microsoft.com/en-us/library/xc11y76y.aspx
*/
void __declspec(naked) __cdecl
_pexit(void) {
_asm {
push eax
mov eax, [enabled]
test eax, eax
jz skip
push edx
rdtsc
mov dword ptr [end_stamp], eax
mov dword ptr [end_stamp+4], edx
xor eax, eax
mov [enabled], eax
mov eax, [esp+8]
push ebx
push ecx
push ebp
push edi
push esi
push eax
call debug_profile_exit
add esp, 4
pop esi
pop edi
pop ebp
pop ecx
pop ebx
mov eax, 1
mov [enabled], eax
rdtsc
mov dword ptr [start_stamp], eax
mov dword ptr [start_stamp+4], edx
pop edx
skip:
pop eax
ret
}
}
/**
* Reference function for calibration.
*/
void __declspec(naked)
__debug_profile_reference(void) {
_asm {
call _penter
call _pexit
ret
}
}
void
debug_profile_start(void)
{
WCHAR *p;
/* increment starting from the less significant digit */
p = &wFileName[14];
while(1) {
if(*p == '9') {
*p-- = '0';
}
else {
*p += 1;
break;
}
}
table = EngMapFile(wFileName,
PROFILE_TABLE_SIZE*sizeof(struct debug_profile_entry),
&iFile);
if(table) {
unsigned i;
free_table_entries = max_table_entries = PROFILE_TABLE_SIZE;
memset(table, 0, PROFILE_TABLE_SIZE*sizeof(struct debug_profile_entry));
table[0].caller = (uintptr_t)&__debug_profile_reference;
table[0].callee = 0;
table[0].samples = 0;
--free_table_entries;
_asm {
push edx
push eax
rdtsc
mov dword ptr [start_stamp], eax
mov dword ptr [start_stamp+4], edx
pop edx
pop eax
}
last_caller = 0;
enabled = 1;
for(i = 0; i < 8; ++i) {
_asm {
call __debug_profile_reference
}
}
}
}
void
debug_profile_stop(void)
{
enabled = 0;
if(iFile)
EngUnmapFile(iFile);
iFile = 0;
table = NULL;
free_table_entries = max_table_entries = 0;
}
#endif /* PROFILE */