###################################### # # MEM VISUALIZE PARSER # Eric J Anderson # # cd X:\payne\src\dev\rage\base\tools\misctools # python mem_visualize_parser.py
[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 [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()