mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
3e3779498e
Add a python interface (`revng.profile`) for interacting with the rev.ng infrastructure as a whole; either through the CLI (`CLIProject`) or the GraphQL API (`DaemonProject`).
167 lines
5.7 KiB
Python
Executable File
167 lines
5.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# This file is distributed under the MIT License. See LICENSE.md for details.
|
|
#
|
|
|
|
import argparse
|
|
import re
|
|
from typing import Callable, Dict, List, Mapping
|
|
|
|
from revng.model import MetaAddress, MetaAddressType # type: ignore[attr-defined]
|
|
from revng.project import CLIProject, LocalDaemonProject, Project
|
|
from revng.support.artifacts import PTMLArtifact
|
|
|
|
|
|
def parse_args():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--resume", help="Path to the resume directory", required=True)
|
|
parser.add_argument("--binary", help="Path to the Binary", required=True)
|
|
group = parser.add_mutually_exclusive_group()
|
|
group.add_argument("--cli", help="Run revng cli", action="store_true")
|
|
group.add_argument("--daemon", help="Run revng daemon", action="store_true")
|
|
return parser.parse_args()
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
|
|
if args.cli:
|
|
|
|
def project_getter():
|
|
return CLIProject(args.resume)
|
|
|
|
elif args.daemon:
|
|
|
|
def project_getter():
|
|
return LocalDaemonProject(args.resume, connection_retries=100)
|
|
|
|
else:
|
|
raise ValueError("The script expects either --cli or --daemon")
|
|
|
|
run_test(project_getter, args.binary)
|
|
|
|
|
|
def run_test(project_getter: Callable[[], Project], binary: str):
|
|
project = project_getter()
|
|
project.import_and_analyze(binary)
|
|
|
|
# Get the first function with `Name`
|
|
function_original_name = None
|
|
function_idx = 0
|
|
function_entry = None
|
|
for idx, function in enumerate(project.model.Functions):
|
|
if function.Name != "":
|
|
function_original_name = function.Name
|
|
function_idx = idx
|
|
function_entry = function.Entry
|
|
break
|
|
assert function_original_name is not None
|
|
|
|
# Run an artefact on `TypeDefinitions`
|
|
project.model.TypeDefinitions[1].get_artifact("emit-type-definitions")
|
|
|
|
all_functions_entry = [function.Entry for function in project.model.Functions]
|
|
# Assert that when we get the artifact for a single function we get
|
|
# the result of only that function
|
|
result1 = project.model.Functions[function_idx].get_artifact("disassemble")
|
|
assert isinstance(result1, PTMLArtifact)
|
|
parsed1 = result1.parse()
|
|
re_match = re.search(function_original_name, parsed1.text)
|
|
assert re_match is not None
|
|
for entry in all_functions_entry:
|
|
if entry == function_entry:
|
|
continue
|
|
re_match = re.search(str(entry), parsed1.text)
|
|
assert re_match is None
|
|
|
|
# Check that when we get the artifact of all the function we get
|
|
# the result for all the functions
|
|
result2 = project.get_artifact("disassemble")
|
|
assert isinstance(result2, Mapping)
|
|
assert set(result2.keys()) == set(map(str, all_functions_entry))
|
|
for value in result2.values():
|
|
assert isinstance(value, PTMLArtifact)
|
|
value.parse()
|
|
|
|
# Change the function name, get an artifact and make sure that the
|
|
# parsed result contains the new name
|
|
function_new_name1 = "lol"
|
|
project.model.Functions[function_idx].Name = function_new_name1
|
|
project.commit()
|
|
result3 = project.model.Functions[function_idx].get_artifact("disassemble")
|
|
assert isinstance(result3, PTMLArtifact)
|
|
parsed3 = result3.parse()
|
|
re_match = re.search(function_new_name1, parsed3.text)
|
|
assert re_match is not None
|
|
|
|
# Change function name again and commit
|
|
function_new_name2 = "wow"
|
|
project.model.Functions[function_idx].Name = function_new_name2
|
|
project.commit()
|
|
|
|
# Test revert
|
|
function_new_name3 = "heh"
|
|
project.model.Functions[function_idx].Name = function_new_name3
|
|
project.revert()
|
|
assert project.model.Functions[function_idx].Name == function_new_name2
|
|
|
|
# Stop daemon if `DaemonProject` otherwise the daemon will
|
|
# not persist the data on disk
|
|
if isinstance(project, LocalDaemonProject):
|
|
project.stop_daemon()
|
|
|
|
# Load the project again, this should also load the model and
|
|
# pipeline description
|
|
project = project_getter()
|
|
project.set_binary_path(binary)
|
|
|
|
# make sure that the second name changes was commit
|
|
assert project.model.Functions[function_idx].Name == function_new_name2
|
|
|
|
# Get the function by passing a MetaAddress
|
|
function = str(project.model.Functions[0].Entry).split(":")
|
|
address = MetaAddress(int(function[0], 16), MetaAddressType[function[1]])
|
|
|
|
# Get multiple artifacts
|
|
result = project.get_artifacts(
|
|
{
|
|
"disassemble": [project.model.Functions[address], project.model.Functions[1]],
|
|
"decompile": None,
|
|
}
|
|
)
|
|
assert isinstance(result["disassemble"], Mapping)
|
|
for value in result["disassemble"].values():
|
|
assert isinstance(value, PTMLArtifact)
|
|
value.parse()
|
|
|
|
assert isinstance(result["decompile"], Mapping)
|
|
for value in result["decompile"].values():
|
|
assert isinstance(value, PTMLArtifact)
|
|
value.parse()
|
|
|
|
# Run an analysis with targets
|
|
analysis_name = "detect-stack-size"
|
|
targets: Dict[str, List[str]] = {}
|
|
for input_, kinds in project.get_analysis_inputs(analysis_name).items():
|
|
targets[input_] = []
|
|
for kind in kinds:
|
|
if kind == "root":
|
|
targets[input_].append(f":{kind}")
|
|
else:
|
|
targets[input_].extend(
|
|
f"{_target.key()}:{kind}" for _target in project.model.Functions
|
|
)
|
|
|
|
if isinstance(project, CLIProject):
|
|
# TODO: drop this if when CLI supports passing targets for analyses
|
|
project.analyze(analysis_name, {})
|
|
else:
|
|
project.analyze(analysis_name, targets)
|
|
|
|
# Run an analysis without targets
|
|
project.analyze(analysis_name)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|