blob: 163b11f34cb8a9a307a4cf6cc13fb1ef50143fb3 [file] [log] [blame]
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
import argparse
import asyncio
import os
from typing import Mapping, Optional, Union
from executorch.sdk.edir.et_schema import (
FXOperatorGraph,
InferenceRun,
OperatorGraphWithStats,
)
from executorch.sdk.etdb.etdb import debug_graphs
from executorch.sdk.etdb.inspector import Inspector
from executorch.sdk.etrecord import ETRecord, parse_etrecord
"""
Private Lib Helpers
"""
def _gen_graphs_from_etrecord(
etrecord: ETRecord,
) -> Mapping[str, OperatorGraphWithStats]:
if etrecord.graph_map is None:
return {}
return {
name: FXOperatorGraph.gen_operator_graph(exported_program.graph_module)
for name, exported_program in etrecord.graph_map.items()
}
def _gen_and_attach_metadata(
op_graph_dict: Mapping[str, OperatorGraphWithStats], et_dump_path: str
) -> None:
"""
(!!) Note: Currently we only support attaching etdump data to the
et_dialect_graph_module.
Attach metadata in ETDump under path et_dump_path to the given op_graph.
To visualize op_graph without ETDump metadata, this function can be skipped.
Args:
op_graph (ExportedETOperatorGraph): operator graph to visualize
et_dump_path (str): local or Manifold path to the ETDump
"""
op_graph = op_graph_dict["et_dialect_graph_module/forward"]
if os.path.exists(et_dump_path):
op_graph.attach_metadata(
inference_run=InferenceRun.extract_runs_from_path(file_path=et_dump_path)[0]
)
else:
raise Exception("Invalid ET Dump path")
"""
SDK Entry Points
"""
def debug_etrecord(
etrecord: ETRecord, et_dump_path: Optional[str] = None, verbose: bool = False
):
"""
Given an ETRecord, kick off ETDB
"""
op_graph_dict: Mapping[str, OperatorGraphWithStats] = _gen_graphs_from_etrecord(
etrecord
)
if et_dump_path is not None:
_gen_and_attach_metadata(op_graph_dict, et_dump_path)
debug_graphs(op_graph_dict, verbose)
def debug_etrecord_path(
etrecord_path: str, et_dump_path: Optional[str] = None, verbose: bool = False
):
"""
Given a path to an ETRecord, kick off ETDB
"""
debug_etrecord(parse_etrecord(etrecord_path), et_dump_path, verbose)
def gen_inspector_from_etrecord(
etrecord: Union[str, ETRecord],
etdump_path: Optional[str] = None,
show_stack_trace: Optional[bool] = False,
verbose: Optional[bool] = False,
) -> Inspector:
"""
API that creates an Inspector instance based on a file path to an ETRecord instance
or an ETRecord instance and optional parameters including a file path to an ETDump
"""
if isinstance(etrecord, str):
etrecord = parse_etrecord(etrecord_path=str(etrecord))
op_graph_dict: Mapping[str, OperatorGraphWithStats] = _gen_graphs_from_etrecord(
etrecord=etrecord
)
if etdump_path is not None:
_gen_and_attach_metadata(op_graph_dict=op_graph_dict, et_dump_path=etdump_path)
return Inspector(
op_graph_dict=op_graph_dict, show_stack_trace=show_stack_trace, verbose=verbose
)
"""
SDK Binary
"""
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument("et_record", help="Path to ETRecord")
parser.add_argument("--et_dump", help="Path to ET Dump")
parser.add_argument(
"--verbose",
action="store_true",
help="Whether the terminal should display in verbose mode",
)
parser.add_argument(
"--show_stack_trace",
help="Whether to show stack trace in the output tables",
)
return parser.parse_args()
async def main() -> int:
"""
Simple CLI wrapper for triggering ETDB
Only required argument is an et_record path
"""
args = parse_args()
et_inspector = gen_inspector_from_etrecord(
etrecord=args.et_record,
etdump_path=args.et_dump,
show_stack_trace=args.show_stack_trace,
verbose=args.verbose,
)
et_inspector.cli_flow()
return 0
if __name__ == "__main__":
asyncio.run(main())