mirror of
https://github.com/isledecomp/isle.git
synced 2026-01-31 12:11:15 +00:00
roadmap: Suggest order of modules
This commit is contained in:
parent
96234ddc23
commit
8a9809814e
@ -58,6 +58,9 @@ def print_sections(sections):
|
|||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
ALLOWED_TYPE_ABBREVIATIONS = ["fun", "dat", "poi", "str", "vta"]
|
||||||
|
|
||||||
|
|
||||||
def match_type_abbreviation(mtype: Optional[SymbolType]) -> str:
|
def match_type_abbreviation(mtype: Optional[SymbolType]) -> str:
|
||||||
"""Return abbreviation of the given SymbolType name"""
|
"""Return abbreviation of the given SymbolType name"""
|
||||||
if mtype is None:
|
if mtype is None:
|
||||||
@ -66,6 +69,20 @@ def match_type_abbreviation(mtype: Optional[SymbolType]) -> str:
|
|||||||
return mtype.name.lower()[:3]
|
return mtype.name.lower()[:3]
|
||||||
|
|
||||||
|
|
||||||
|
def truncate_module_name(prefix: str, module: str) -> str:
|
||||||
|
"""Remove the CMakeFiles prefix and the .obj suffix for the given module.
|
||||||
|
Input: CMakeFiles/lego1.dir/, CMakeFiles/lego1.dir/LEGO1/define.cpp.obj
|
||||||
|
Output: LEGO1/define.cpp"""
|
||||||
|
|
||||||
|
if module.startswith(prefix):
|
||||||
|
module = module[len(prefix) :]
|
||||||
|
|
||||||
|
if module.endswith(".obj"):
|
||||||
|
module = module[:-4]
|
||||||
|
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
RoadmapRow = namedtuple(
|
RoadmapRow = namedtuple(
|
||||||
"RoadmapRow",
|
"RoadmapRow",
|
||||||
[
|
[
|
||||||
@ -82,6 +99,94 @@ def match_type_abbreviation(mtype: Optional[SymbolType]) -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DeltaCollector:
|
||||||
|
"""Reads each row of the results and aggregates information about the
|
||||||
|
placement of each module."""
|
||||||
|
|
||||||
|
def __init__(self, match_type: str = "fun"):
|
||||||
|
self.disp_map = {}
|
||||||
|
self.earliest = {}
|
||||||
|
self.seen = set()
|
||||||
|
self.match_type = "fun"
|
||||||
|
|
||||||
|
match_type = str(match_type).strip().lower()[:3]
|
||||||
|
if match_type in ALLOWED_TYPE_ABBREVIATIONS:
|
||||||
|
self.match_type = match_type
|
||||||
|
|
||||||
|
def read_row(self, row: RoadmapRow):
|
||||||
|
if row.module is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.seen.add(row.module)
|
||||||
|
if row.sym_type != self.match_type:
|
||||||
|
return
|
||||||
|
|
||||||
|
if row.orig_addr is not None:
|
||||||
|
if row.orig_addr < self.earliest.get(row.module, 0xFFFFFFFFF):
|
||||||
|
self.earliest[row.module] = row.orig_addr
|
||||||
|
|
||||||
|
if row.displacement is not None:
|
||||||
|
if row.module not in self.disp_map:
|
||||||
|
self.disp_map[row.module] = []
|
||||||
|
|
||||||
|
self.disp_map[row.module].append(row.displacement)
|
||||||
|
|
||||||
|
|
||||||
|
def suggest_order(results: List[RoadmapRow], match_type: str):
|
||||||
|
"""Suggest the order of modules for CMakeLists.txt"""
|
||||||
|
|
||||||
|
dc = DeltaCollector(match_type)
|
||||||
|
for row in results:
|
||||||
|
dc.read_row(row)
|
||||||
|
|
||||||
|
leftover_modules = set(mod for mod in dc.seen if mod.startswith("CMakeFiles"))
|
||||||
|
|
||||||
|
# A little convoluted, but we want to take the first two tokens
|
||||||
|
# of the string with '/' as the delimiter.
|
||||||
|
# i.e. CMakeFiles/isle.dir/
|
||||||
|
# The idea is to print exactly what appears in CMakeLists.txt.
|
||||||
|
cmake_prefixes = sorted(
|
||||||
|
set("/".join(mod.split("/", 2)[:2]) + "/" for mod in leftover_modules)
|
||||||
|
)
|
||||||
|
|
||||||
|
# These may already be sorted by earliest, but make sure
|
||||||
|
first_function = [(earliest, module) for (module, earliest) in dc.earliest.items()]
|
||||||
|
first_function.sort()
|
||||||
|
|
||||||
|
for prefix in cmake_prefixes:
|
||||||
|
print(prefix)
|
||||||
|
# Show modules ordered by the first appearance of whichever symbol type.
|
||||||
|
for start, module in first_function:
|
||||||
|
if not module.startswith(prefix):
|
||||||
|
continue
|
||||||
|
|
||||||
|
leftover_modules.remove(module)
|
||||||
|
|
||||||
|
avg_displacement = None
|
||||||
|
displacements = dc.disp_map.get(module)
|
||||||
|
if displacements is not None and len(displacements) > 0:
|
||||||
|
avg_displacement = int(sum(displacements) / len(displacements))
|
||||||
|
|
||||||
|
code_file = truncate_module_name(prefix, module)
|
||||||
|
print(f"0x{start:08x} {or_blank(avg_displacement):10} {code_file}")
|
||||||
|
|
||||||
|
# These modules are included in the final binary (in some form) but
|
||||||
|
# they are not represented by whichever type of symbol we were checking.
|
||||||
|
# n.b. There could still be other modules that are part of CMakeLists.txt
|
||||||
|
# but are not included in the pdb for whatever reason.
|
||||||
|
# In other words: don't take the list we provide as the final word on what
|
||||||
|
# should or should not be included. This is merely a suggestion of the order.
|
||||||
|
for module in leftover_modules:
|
||||||
|
if not module.startswith(prefix):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# aligned with previous print
|
||||||
|
code_file = truncate_module_name(prefix, module)
|
||||||
|
print(f" no suggestion {code_file}")
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
def print_text_report(results: List[RoadmapRow]):
|
def print_text_report(results: List[RoadmapRow]):
|
||||||
"""Print the result with original and recomp addresses."""
|
"""Print the result with original and recomp addresses."""
|
||||||
for row in results:
|
for row in results:
|
||||||
@ -150,6 +255,13 @@ def parse_args() -> argparse.Namespace:
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--verbose", "-v", action="store_true", help="Show recomp addresses in output"
|
"--verbose", "-v", action="store_true", help="Show recomp addresses in output"
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--order",
|
||||||
|
const="fun",
|
||||||
|
nargs="?",
|
||||||
|
type=str,
|
||||||
|
help="Show suggested order of modules (using the specified symbol type)",
|
||||||
|
)
|
||||||
|
|
||||||
(args, _) = parser.parse_known_args()
|
(args, _) = parser.parse_known_args()
|
||||||
|
|
||||||
@ -245,6 +357,10 @@ def to_roadmap_row(match):
|
|||||||
|
|
||||||
results = list(map(to_roadmap_row, engine.get_all()))
|
results = list(map(to_roadmap_row, engine.get_all()))
|
||||||
|
|
||||||
|
if args.order is not None:
|
||||||
|
suggest_order(results, args.order)
|
||||||
|
return
|
||||||
|
|
||||||
if args.csv is None:
|
if args.csv is None:
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
print("ORIG sections:")
|
print("ORIG sections:")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user