Files
2025-09-29 00:52:08 +02:00

324 lines
6.6 KiB
Python
Executable File

######################################
#
# MEM VISUALIZE PARSER
# Eric J Anderson
#
# cd X:\payne\src\dev\rage\base\tools\misctools
# python mem_visualize_parser.py <pre> <post> [output_dir]
#
# Example: X:\payne\src\dev\rage\base\tools\misctools>python mem_visualize_parser.py pre.txt post.txt
#
######################################
import os
import os.path
import re
import sys
######################################
# GLOBAL
#
g_stack = []
g_window = 50
g_pre_path = ""
g_pre_lines = []
g_pre_data = {}
g_post_path = ""
g_post_lines = []
g_post_data = {}
g_output_dir = "X:\\"
g_pre_output_data = []
g_post_output_data = []
g_leak_overview = []
######################################
# USAGE
#
def get_filename(path):
# Directory
filename = path
index = filename.rfind('\\')
if index > -1:
filename = filename[index+1:len(filename)]
else:
index = filename.rfind('/')
if index > -1:
filename = filename[index+1:len(filename)]
# Extension
#index = filename.rfind('.')
#if index > -1:
# filename = filename[0:index]
return filename
def usage():
if len(sys.argv) < 3:
print 'USAGE: python mem_visualize_parser.py <pre> <post> [output_dir]'
exit(-1)
global g_pre_path
g_pre_path = sys.argv[1]
print "Pre Path:\t", g_pre_path
global g_post_path
g_post_path = sys.argv[2]
print "Post Path:\t", g_post_path
global g_output_dir
if len(sys.argv) > 3 and os.path.isdir(sys.argv[3]):
g_output_dir = sys.argv[3]
if not (g_output_dir.endswith('\\') or g_output_dir.endswith('/')):
g_output_dir = g_output_dir + '\\'
print "Output Dir:\t", g_output_dir
######################################
# PARSE
#
def get_space(line):
spaces = 0
for ch in line:
if ch != ' ':
break
spaces = spaces + 1
return spaces
def get_key(line):
key = ""
index = line.find('|')
if index > -1:
key = line[0:index + 1]
return key
def get_size(line):
num = 0
end = line.find("(in ")
if end > -1:
begin = line.rfind(" | ", 0, end)
if begin > -1:
num = int(line[begin+3:end-1])
return num
def get_allocs(line):
num = 0
index = line.find("(in ")
if index > -1:
num = int(line[index+4:line.find(" allocations")])
return num
def get_prefix(line):
data = ""
index = line.find(" | ")
if index > -1:
data = line[0:index]
return data
def get_postfix(line):
data = ""
index = line.rfind(" | ")
if index > -1:
data = line[index+3:len(line)]
return data
######################################
# UTILITY
#
def progress(value):
if 0 == (value % 20):
sys.stdout.write("\b|")
elif 0 == (value % 15):
sys.stdout.write("\b\\")
elif 0 == (value % 10):
sys.stdout.write("\b-")
elif 0 == (value % 5):
sys.stdout.write("\b/")
def print_stack():
for line in g_stack:
print line
def create_key():
data = ""
for line in g_stack:
data = data + line + '\n'
return data
######################################
# READ
#
def create_data(line_array):
if len(line_array) == 0:
return {}
global g_stack
g_stack = []
line_map = {}
i = 0
for line in line_array:
prefix = get_prefix(line)
spaces = get_space(line)
if i == 0:
g_stack.append(prefix)
else:
unwind = len(g_stack)
if spaces < unwind:
# Save the callstack
prev_line = line_array[i - 1]
line_map[create_key()] = [get_size(prev_line), get_allocs(prev_line)]
while spaces < unwind:
g_stack.pop()
spaces += 1
g_stack.append(prefix)
i += 1
# Save the callstack
line_map[create_key()] = [get_size(line), get_allocs(line)]
return line_map
def read():
# Pre
global g_pre_lines
global g_pre_data
fin = open(g_pre_path, "r")
g_pre_lines = fin.readlines()
g_pre_data = create_data(g_pre_lines)
fin.close()
'''
for key, data in g_pre_data.items():
print key, " - ", "Size: ", data[0]
'''
# Post
global g_post_lines
global g_post_data
fin = open(g_post_path, "r")
g_post_lines = fin.readlines()
g_post_data = create_data(g_post_lines)
fin.close()
######################################
# WRITE
#
def write():
pre_filename = get_filename(g_pre_path)
post_filename = get_filename(g_post_path)
# Pre
i = 0
fout = open(g_output_dir + pre_filename + "__" + post_filename, "w")
for data in g_pre_output_data:
fout.write(str(i) + " | " + str(data[1]) + " bytes | " + str(data[2]) + " allocations\n")
fout.write(data[0] + "\n")
i += 1
fout.close()
# Post
i = 0
fout = open(g_output_dir + post_filename + "__" + pre_filename, "w")
for data in g_post_output_data:
fout.write(str(i) + " | " + str(data[1]) + " (+" + str(data[1] - g_pre_output_data[i][1]) + ") bytes | " + str(data[2]) + " (+" + str(data[2] - g_pre_output_data[i][2]) + ") allocations\n")
fout.write(data[0] + "\n")
i += 1
fout.close()
# Leak
fout = open(g_output_dir + post_filename + "__" + pre_filename + "__overview.txt", "w")
fout.write("[MEMORY LEAKS]\n")
for item in g_leak_overview:
fout.write(item[0] + " | " + str(item[1]) + " | " + str(item[2]) + '\n')
fout.close()
######################################
# COMPARE
#
def insensitive_compare(x, y):
return cmp(x.lower(), y.lower())
def insensitive_compare_array(x, y):
return cmp(x[0].lower(), y[0].lower())
def compare():
global g_leak_overview
global g_pre_output_data
global g_post_output_data
i = 1
# Sort the
post_data_keys = []
for key in g_post_data.keys():
post_data_keys.append(key)
post_data_keys.sort(cmp = insensitive_compare)
# Debug
for post_key in post_data_keys:
progress(i)
post_data = g_post_data[post_key]
post_size = post_data[0]
post_allocs = post_data[1]
if g_pre_data.has_key(post_key):
pre_data = g_pre_data[post_key]
pre_size = pre_data[0]
pre_allocs = pre_data[1]
if (post_allocs > pre_allocs) and (post_size > pre_size):
index = post_key.rfind(" ")
function = post_key[index+2:len(post_key)-1]
g_leak_overview.append([function, post_size - pre_size, post_allocs - pre_allocs])
g_pre_output_data.append([post_key, pre_size, pre_allocs])
g_post_output_data.append([post_key, post_size, post_allocs])
i += 1
# Debug
if len(g_leak_overview) > 0:
print "\n[MEMORY LEAKS]"
g_leak_overview.sort(cmp = insensitive_compare_array)
for item in g_leak_overview:
print item[0], " | ", item[1], " | ", item[2]
######################################
# MAIN
#
def main():
usage()
read()
compare()
write()
main()