summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLagoLunatic <LagoLunatic@users.noreply.github.com>2026-07-13 13:20:35 -0400
committerLagoLunatic <LagoLunatic@users.noreply.github.com>2026-07-13 13:20:35 -0400
commit3cb9b3bdb49c76e71c06ab2cd873ed5130864afb (patch)
treefe1867bc5d14726cc73166ec54d93e502ee790e2
parent6f09bf28ce1a2f1cdea62b931cbbdb40e7f821ec (diff)
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.
-rwxr-xr-xtools/utilities/debug_map_diff.py81
1 files changed, 53 insertions, 28 deletions
diff --git a/tools/utilities/debug_map_diff.py b/tools/utilities/debug_map_diff.py
index bec1e986..12d29331 100755
--- a/tools/utilities/debug_map_diff.py
+++ b/tools/utilities/debug_map_diff.py
@@ -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}")