reccmp: template compare annotations (#88)

* reccmp: Add ability to compare template instantiations

* Add example of template instantiation comparison.

* merge

* Add template compare annotations for MxList instances

---------

Co-authored-by: Christian Semmler <mail@csemmler.com>
This commit is contained in:
pewpew 2023-09-29 13:40:46 -05:00 committed by GitHub
parent f7743c51fb
commit b77cd067d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 57 additions and 23 deletions

View file

@ -25,4 +25,13 @@ class MxDSActionList : public MxList<MxDSAction>
typedef MxListCursorChild<MxDSAction> MxDSActionListCursor; typedef MxListCursorChild<MxDSAction> MxDSActionListCursor;
// OFFSET: LEGO1 0x100c9d20 TEMPLATE
// MxListParent<MxDSAction>::Destroy
// OFFSET: LEGO1 0x100c9cd0 TEMPLATE
// MxListParent<MxDSAction>::~MxListParent<MxDSAction>
// OFFSET: LEGO1 0x100c9d30 TEMPLATE
// MxList<MxDSAction>::~MxList<MxDSAction>
#endif // MXDSACTIONLIST_H #endif // MXDSACTIONLIST_H

View file

@ -31,15 +31,11 @@ class MxListParent : public MxCore
m_count = 0; m_count = 0;
m_customDestructor = Destroy; m_customDestructor = Destroy;
} }
// OFFSET: LEGO1 0x1001cdd0
virtual ~MxListParent() {} virtual ~MxListParent() {}
// OFFSET: LEGO1 0x1001cd30
static void Destroy(T *) {};
// OFFSET: LEGO1 0x1001cd20
virtual MxS8 Compare(T *, T *) = 0; virtual MxS8 Compare(T *, T *) = 0;
static void Destroy(T *) {};
protected: protected:
MxU32 m_count; // +0x8 MxU32 m_count; // +0x8
void (*m_customDestructor)(T *); // +0xc void (*m_customDestructor)(T *); // +0xc
@ -114,7 +110,6 @@ class MxListCursorChildChild : public MxListCursorChild<T>
}; };
template <class T> template <class T>
// OFFSET: LEGO1 0x1001ce20
MxList<T>::~MxList() MxList<T>::~MxList()
{ {
DeleteAll(); DeleteAll();

View file

@ -182,4 +182,4 @@ MxResult MxNotificationManager::Send(MxCore *p_listener, MxParam *p_param)
} }
return FAILURE; return FAILURE;
} }

View file

@ -26,4 +26,13 @@ class MxPresenterList : public MxPresenterListParent
typedef MxListCursorChildChild<MxPresenter> MxPresenterListCursor; typedef MxListCursorChildChild<MxPresenter> MxPresenterListCursor;
// OFFSET: LEGO1 0x1001cd30 TEMPLATE
// MxListParent<MxPresenter>::Destroy
// OFFSET: LEGO1 0x1001cdd0 TEMPLATE
// MxListParent<MxPresenter>::~MxListParent<MxPresenter>
// OFFSET: LEGO1 0x1001ce20 TEMPLATE
// MxList<MxPresenter>::~MxList<MxPresenter>
#endif // MXPRESENTERLIST_H #endif // MXPRESENTERLIST_H

View file

@ -10,6 +10,7 @@
import os import os
import sys import sys
import colorama import colorama
import html
import re import re
parser = argparse.ArgumentParser(allow_abbrev=False, parser = argparse.ArgumentParser(allow_abbrev=False,
@ -43,7 +44,7 @@
verbose = int(args.verbose, 16) verbose = int(args.verbose, 16)
except ValueError: except ValueError:
parser.error('invalid verbose argument') parser.error('invalid verbose argument')
html = args.html html_path = args.html
plain = args.no_color plain = args.no_color
@ -140,6 +141,7 @@ def get_file_in_script_dir(fn):
class SymInfo: class SymInfo:
funcs = {} funcs = {}
lines = {} lines = {}
names = {}
def __init__(self, pdb, file, wine_path_converter): def __init__(self, pdb, file, wine_path_converter):
call = [get_file_in_script_dir('cvdump.exe'), '-l', '-s'] call = [get_file_in_script_dir('cvdump.exe'), '-l', '-s']
@ -183,6 +185,7 @@ def __init__(self, pdb, file, wine_path_converter):
info.name = line[77:] info.name = line[77:]
self.names[info.name] = info
self.funcs[addr] = info self.funcs[addr] = info
elif current_section == 'LINES' and line.startswith(' ') and not line.startswith(' '): elif current_section == 'LINES' and line.startswith(' ') and not line.startswith(' '):
sourcepath = line.split()[0] sourcepath = line.split()[0]
@ -238,6 +241,14 @@ def get_recompiled_address(self, filename, line):
else: else:
logger.error('Failed to find function symbol with filename and line: %s:%d', filename, line) logger.error('Failed to find function symbol with filename and line: %s:%d', filename, line)
def get_recompiled_address_from_name(self, name):
logger.debug('Looking for %s', name)
if name in self.names:
return self.names[name]
else:
logger.error('Failed to find function symbol with name: %s', name)
wine_path_converter = None wine_path_converter = None
if os.name != 'nt': if os.name != 'nt':
wine_path_converter = WinePathConverter(source) wine_path_converter = WinePathConverter(source)
@ -419,14 +430,24 @@ def can_resolve_register_differences(original_asm, new_asm):
else: else:
continue continue
find_open_bracket = line if line.endswith("TEMPLATE"):
while '{' not in find_open_bracket: line = srcfile.readline()
find_open_bracket = srcfile.readline() line_no += 1
line_no += 1 # Name comes after // comment
name = line[2:].strip()
recinfo = syminfo.get_recompiled_address(srcfilename, line_no) recinfo = syminfo.get_recompiled_address_from_name(name)
if not recinfo: if not recinfo:
continue continue
else:
find_open_bracket = line
while '{' not in find_open_bracket:
find_open_bracket = srcfile.readline()
line_no += 1
recinfo = syminfo.get_recompiled_address(srcfilename, line_no)
if not recinfo:
continue
# The effective_ratio is the ratio when ignoring differing register # The effective_ratio is the ratio when ignoring differing register
# allocation vs the ratio is the true ratio. # allocation vs the ratio is the true ratio.
@ -511,14 +532,14 @@ def can_resolve_register_differences(original_asm, new_asm):
print("\n%s is only %s similar to the original, diff above" % (recinfo.name, percenttext)) print("\n%s is only %s similar to the original, diff above" % (recinfo.name, percenttext))
# If html, record the diffs to an HTML file # If html, record the diffs to an HTML file
if html: if html_path:
escaped = '\\n'.join(udiff).replace('"', '\\"').replace('\n', '\\n').replace('<', '&lt;').replace('>', '&gt;') escaped = '\\n'.join(udiff).replace('"', '\\"').replace('\n', '\\n').replace('<', '&lt;').replace('>', '&gt;')
htmlinsert.append('{address: "%s", name: "%s", matching: %s, diff: "%s"}' % (hex(addr), recinfo.name, str(effective_ratio), escaped)) htmlinsert.append('{address: "%s", name: "%s", matching: %s, diff: "%s"}' % (hex(addr), html.escape(recinfo.name), str(effective_ratio), escaped))
except UnicodeDecodeError: except UnicodeDecodeError:
break break
def gen_html(html, data): def gen_html(html_path, data):
templatefile = open(get_file_in_script_dir('template.html'), 'r') templatefile = open(get_file_in_script_dir('template.html'), 'r')
if not templatefile: if not templatefile:
print('Failed to find HTML template file, can\'t generate HTML summary') print('Failed to find HTML template file, can\'t generate HTML summary')
@ -529,9 +550,9 @@ def gen_html(html, data):
templatedata = templatedata.replace('/* INSERT DATA HERE */', ','.join(data), 1) templatedata = templatedata.replace('/* INSERT DATA HERE */', ','.join(data), 1)
htmlfile = open(html, 'w') htmlfile = open(html_path, 'w')
if not htmlfile: if not htmlfile:
print('Failed to write to HTML file %s' % html) print('Failed to write to HTML file %s' % html_path)
return return
htmlfile.write(templatedata) htmlfile.write(templatedata)
@ -580,8 +601,8 @@ def gen_svg(svg, name, icon, implemented_funcs, total_funcs, raw_accuracy):
svgfile.write(templatedata) svgfile.write(templatedata)
svgfile.close() svgfile.close()
if html: if html_path:
gen_html(html, htmlinsert) gen_html(html_path, htmlinsert)
if verbose: if verbose:
if not found_verbose_target: if not found_verbose_target: