debug_map_diff.py: Several improvements

Auto-detect if the TU is in main.dol or a REL. Detect this separately for the target and the base, in order to support TUs like d_a_npc_tt which are in main.dol for the debug build, but a REL for release builds. And disable linkage comparison when comparing a REL TU to a main.dol TU as the official main.dol maps do not include linkage information, but the REL maps do.
This commit is contained in:
LagoLunatic
2026-07-13 13:20:35 -04:00
parent 6f09bf28ce
commit 3cb9b3bdb4
+53 -28
View File
@@ -4,6 +4,10 @@
# Checks for missing inline usages, fake inline usages, and inlines being the wrong size.
# NOTE: You must mark the TU as "Matching" in configure.py, even if it doesn't match! Otherwise this script won't work properly.
# To use this script, pass the name of the TU as the only argument. Examples:
# ./tools/utilities/debug_map_diff.py d_a_andsw0
# ./tools/utilities/debug_map_diff.py d_a_npc_fa1
from pathlib import Path
import re
import subprocess
@@ -11,28 +15,46 @@ import argparse
from collections import defaultdict
arg_parse = argparse.ArgumentParser()
arg_parse.add_argument("object_name", help="Name of the object to compare, e.g. d_a_bridge for a REL, or main/d_a_npc_fa1 for a main.dol object")
arg_parse.add_argument("object_name", help="Name of the object to compare, e.g. d_a_bridge or d_a_npc_fa1")
args = arg_parse.parse_args()
object_name: str = args.object_name
assert "/" not in object_name and "." not in object_name, "The object name should not contain slashes or dots"
debug_maps_root_path = Path("orig/D44J01/files/maps")
decomp_root_path = Path(".")
is_rel: bool
if object_name.startswith("main/"):
is_rel = False
object_name = object_name.split("/", 1)[1]
object_name = object_name.split(".", 1)[0]
target_map_path = debug_maps_root_path / "frameworkD.map"
base_map_path = decomp_root_path / "build/D44J01/framework.elf.MAP"
else:
is_rel = True
target_map_path = debug_maps_root_path / f"{object_name}D.map"
base_map_path = decomp_root_path / f"build/D44J01/{object_name}/{object_name}.plf.MAP"
assert "/" not in object_name and "." not in object_name
retcode = subprocess.call(["python", "configure.py", "--version", "D44J01", "--debug", "--map", "--non-matching"], cwd=decomp_root_path)
assert retcode == 0, "Failed to configure"
all_ninja_outputs = []
for ninja_target in subprocess.check_output(["ninja", "-t", "targets", "all"]).decode("utf-8").splitlines():
ninja_output, ninja_rule = ninja_target.split(":", 1)
all_ninja_outputs.append(ninja_output)
target_map_path_dol = debug_maps_root_path / "frameworkD.map"
target_map_path_rel = debug_maps_root_path / f"{object_name}D.map"
if target_map_path_rel.exists():
target_is_rel = True
target_map_path = target_map_path_rel
else:
assert target_map_path_dol.exists()
target_is_rel = False
target_map_path = target_map_path_dol
del target_map_path_dol
del target_map_path_rel
base_map_path_dol = decomp_root_path / "build/D44J01/framework.elf.MAP"
base_map_path_rel = decomp_root_path / f"build/D44J01/{object_name}/{object_name}.plf.MAP"
if str(base_map_path_rel) in all_ninja_outputs:
base_is_rel = True
base_map_path = base_map_path_rel
else:
assert str(base_map_path_dol) in all_ninja_outputs
base_is_rel = False
base_map_path = base_map_path_dol
del base_map_path_dol
del base_map_path_rel
retcode = subprocess.call(["ninja", base_map_path.relative_to(decomp_root_path)], cwd=decomp_root_path)
assert retcode == 0, "Ninja build call failed"
@@ -61,7 +83,7 @@ def get_main_symbols(framework_map_contents: str, valid_obj_names = None):
symbols[name] = Symbol(name, size)
if len(symbols) == 0:
raise Exception("Failed to find object matching the given name")
raise Exception("Failed to find object matching the given name (check for typos)")
return symbols
def get_rel_symbols(rel_map_data: str):
@@ -157,13 +179,15 @@ def is_debug_only_symbol(symbol_name: str):
return True
return False
if is_rel:
if target_is_rel:
target_symbols = get_rel_symbols(target_map_path.read_text())
else:
target_symbols = get_main_symbols(target_map_path.read_text(), valid_obj_names=[object_name])
if base_is_rel:
base_symbols = get_rel_symbols(base_map_path.read_text())
else:
obj_names = [object_name]
target_symbols = get_main_symbols(target_map_path.read_text(), valid_obj_names=obj_names)
base_symbols = get_main_symbols(base_map_path.read_text(), valid_obj_names=obj_names)
base_symbols = get_main_symbols(base_map_path.read_text(), valid_obj_names=[object_name])
print(len(target_symbols), len(base_symbols))
@@ -219,15 +243,16 @@ for symbol_name, target_size, base_size, ratio in symbol_size_diffs:
total_missing += 1
print(prefix + symbol_name, "0x%X" % target_size)
for symbol_name, target_symbol in target_symbols.items():
if target_symbol.size == 0:
continue
if symbol_name not in base_symbols:
continue
base_symbol = base_symbols[symbol_name]
if target_symbol.linkage != base_symbol.linkage:
total_wrong_linkage += 1
print(f"LINKAGE: {symbol_name} (should be {target_symbol.linkage}, is {base_symbol.linkage})")
if target_is_rel:
for symbol_name, target_symbol in target_symbols.items():
if target_symbol.size == 0:
continue
if symbol_name not in base_symbols:
continue
base_symbol = base_symbols[symbol_name]
if target_symbol.linkage != base_symbol.linkage:
total_wrong_linkage += 1
print(f"LINKAGE: {symbol_name} (should be {target_symbol.linkage}, is {base_symbol.linkage})")
print("==================================================")
print(f"Total right size: {total_right_size}")