mirror of
https://github.com/isledecomp/isle-portable.git
synced 2024-11-22 15:37:55 -05:00
f26c30974a
* Add draft for Ghidra function import script * feature: Basic PDB analysis [skip ci] This is a draft with a lot of open questions left. Please do not merge * Refactor: Introduce submodules and reload remedy * refactor types and make them Python 3.9 compatible * run black * WIP: save progress * fix types and small type safety violations * fix another Python 3.9 syntax incompatibility * Implement struct imports [skip ci] - This code is still in dire need of refactoring and tests - There are only single-digit issues left, and 2600 functions can be imported - The biggest remaining error is mismatched stacks * Refactor, implement enums, fix lots of bugs * fix Python 3.9 issue * refactor: address review comments Not sure why VS Code suddenly decides to remove some empty spaces, but they don't make sense anyway * add unit tests for new type parsers, fix linter issue * refactor: db access from pdb_extraction.py * Fix stack layout offset error * fix: Undo incorrect reference change * Fix CI issue * Improve READMEs (fix typos, add information) --------- Co-authored-by: jonschz <jonschz@users.noreply.github.com>
68 lines
2.5 KiB
Python
68 lines
2.5 KiB
Python
from dataclasses import dataclass, field
|
|
import logging
|
|
|
|
from lego_util.exceptions import (
|
|
TypeNotFoundInGhidraError,
|
|
ClassOrNamespaceNotFoundInGhidraError,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@dataclass
|
|
class Statistics:
|
|
functions_changed: int = 0
|
|
successes: int = 0
|
|
failures: dict[str, int] = field(default_factory=dict)
|
|
known_missing_types: dict[str, int] = field(default_factory=dict)
|
|
known_missing_namespaces: dict[str, int] = field(default_factory=dict)
|
|
|
|
def track_failure_and_tell_if_new(self, error: Exception) -> bool:
|
|
"""
|
|
Adds the error to the statistics. Returns `False` if logging the error would be redundant
|
|
(e.g. because it is a `TypeNotFoundInGhidraError` with a type that has been logged before).
|
|
"""
|
|
error_type_name = error.__class__.__name__
|
|
self.failures[error_type_name] = (
|
|
self.failures.setdefault(error_type_name, 0) + 1
|
|
)
|
|
|
|
if isinstance(error, TypeNotFoundInGhidraError):
|
|
return self._add_occurence_and_check_if_new(
|
|
self.known_missing_types, error.args[0]
|
|
)
|
|
|
|
if isinstance(error, ClassOrNamespaceNotFoundInGhidraError):
|
|
return self._add_occurence_and_check_if_new(
|
|
self.known_missing_namespaces, error.get_namespace_str()
|
|
)
|
|
|
|
# We do not have detailed tracking for other errors, so we want to log them every time
|
|
return True
|
|
|
|
def _add_occurence_and_check_if_new(self, target: dict[str, int], key: str) -> bool:
|
|
old_count = target.setdefault(key, 0)
|
|
target[key] = old_count + 1
|
|
return old_count == 0
|
|
|
|
def log(self):
|
|
logger.info("Statistics:\n~~~~~")
|
|
logger.info(
|
|
"Missing types (with number of occurences): %s\n~~~~~",
|
|
self.format_statistics(self.known_missing_types),
|
|
)
|
|
logger.info(
|
|
"Missing classes/namespaces (with number of occurences): %s\n~~~~~",
|
|
self.format_statistics(self.known_missing_namespaces),
|
|
)
|
|
logger.info("Successes: %d", self.successes)
|
|
logger.info("Failures: %s", self.failures)
|
|
logger.info("Functions changed: %d", self.functions_changed)
|
|
|
|
def format_statistics(self, stats: dict[str, int]) -> str:
|
|
if len(stats) == 0:
|
|
return "<none>"
|
|
return ", ".join(
|
|
f"{entry[0]} ({entry[1]})"
|
|
for entry in sorted(stats.items(), key=lambda x: x[1], reverse=True)
|
|
)
|