324 lines
6.6 KiB
Python
Executable File
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()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|