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:
parent
767bc8eb5a
commit
0b4ea45e8a
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
Loading…
Reference in New Issue