diff --git a/python/paddle/__init__.py b/python/paddle/__init__.py index 7cac26bda3790e..a0456225aef02a 100644 --- a/python/paddle/__init__.py +++ b/python/paddle/__init__.py @@ -1223,3 +1223,13 @@ 'pi', 'e', ] + +import os + +FLAGS_trace_api = os.environ.get("FLAGS_trace_api", None) +if FLAGS_trace_api is not None and FLAGS_trace_api != "": + from .api_tracer import start_api_tracer + + api_path = FLAGS_trace_api.split(",")[0] + save_config_path = FLAGS_trace_api.split(",")[1] + start_api_tracer(api_path, save_config_path) diff --git a/python/paddle/api_tracer/__init__.py b/python/paddle/api_tracer/__init__.py new file mode 100644 index 00000000000000..f735ef05036c2a --- /dev/null +++ b/python/paddle/api_tracer/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .api_tracer import start_api_tracer + +__all__ = [ + 'api_tracer', + 'start_api_tracer', +] diff --git a/python/paddle/api_tracer/api_tracer.py b/python/paddle/api_tracer/api_tracer.py new file mode 100644 index 00000000000000..2ca550f16fd9df --- /dev/null +++ b/python/paddle/api_tracer/api_tracer.py @@ -0,0 +1,197 @@ +# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math + +import numpy as np +import yaml + + +class HookAPIMap: + pass + + +class ConfigDump: + def __init__(self): + pass + + def open_file(self, path): + self.file = open(path, "a+") + + def dump_config(self, api, input_args, input_kwargs, outputs): + result = api + "(" + for value in input_args: + tmp = self.dump_item_str(api, value) + if tmp == "": + return + result = result + tmp + ", " + for key, value in input_kwargs.items(): + tmp = self.dump_item_str(api, value) + if tmp == "": + return + result = result + key + "=" + tmp + ", " + + result = result + ")" + # self.file.write(") -> ") + # if isinstance(outputs, (list, tuple)): + # for output in outputs: + # self.file.write(self.dump_item_str(api, output) + ", ") + # else: + # self.file.write(self.dump_item_str(api, outputs) + ", ") + + self.file.write(result) + self.file.write("\n") + self.file.flush() + + def dump_item_str(self, api, item): + import paddle + + type_mapping = { + np.int16: int, + np.int32: int, + np.int64: int, + np.float16: float, + np.float32: float, + np.float64: float, + np.integer: int, + np.floating: float, + np.bool_: bool, + np.complexfloating: complex, + np.str_: str, + np.bytes_: bytes, + # np.unicode_: str, + } + for numpy_type, builtin_type in type_mapping.items(): + if isinstance(item, numpy_type): + item = builtin_type(item) + break + + if isinstance(item, paddle.Tensor): + return ( + "Tensor(" + str(item.shape) + ',"' + str(item.dtype)[7:] + '")' + ) + elif isinstance(item, paddle.base.core.DataType): + return "Dtype(" + str(item)[7:] + ")" + elif isinstance(item, paddle.base.core.VarDesc.VarType): + return "VarType(" + str(item)[7:] + ")" + elif isinstance(item, list): + result = "list[" + for sub_item in item: + tmp = self.dump_item_str(api, sub_item) + if tmp == "": + return "" + result = result + tmp + "," + result = result + "]" + return result + elif isinstance(item, tuple): + result = "tuple(" + for sub_item in item: + tmp = self.dump_item_str(api, sub_item) + if tmp == "": + return "" + result = result + tmp + "," + result = result + ")" + return result + elif isinstance(item, slice): + return ( + "slice(" + + str(item.start) + + "," + + str(item.stop) + + "," + + str(item.step) + + ")" + ) + elif isinstance(item, complex): + return ( + "complex(" + + self.dump_item_str(api, item.real) + + "," + + self.dump_item_str(api, item.imag) + + ")" + ) + elif item is None: + return "None" + elif isinstance( + item, (paddle.base.Variable, paddle.base.libpaddle.pir.Value) + ): + return "" + elif item == math.inf: + return "math.inf" + elif item == -math.inf: + return "-math.inf" + elif item == math.nan: + return "math.nan" + elif item == -math.nan: + return "-math.nan" + elif isinstance(item, (bool, int, float)): + return str(item) + elif isinstance(item, str): + return '"' + item + '"' + elif isinstance(item, type): + return ( + "type(" + + str(item)[str(item).index("'") + 1 : str(item).rindex("'")] + + ")" + ) + else: + print("[api_tracer error] : dump_item_str ", api, ", item = ", item) + return "" + + +config_dump = ConfigDump() + + +class APITemplate: + def __init__(self, api_name): + self.api_name = api_name + + def __call__(self, *args, **kwargs): + output = getattr(HookAPIMap, self.api_name)(*args, **kwargs) + try: + config_dump.dump_config(self.api_name, args, kwargs, output) + except Exception as err: + print( + "[api_tracer error] : config_dump.dump_config ", + self.api_name, + str(err), + ) + return output + + +def wrapped_api(api_name): + def api_template(*args, **kwargs): + return APITemplate(api_name)(*args, **kwargs) + + return api_template + + +def start_api_tracer(api_path, save_config_path): + import paddle + + print(paddle.__version__) + with open(api_path, "r") as f: + apis = yaml.safe_load(f) + sample_apis = apis.get("apis") + f.close() + + for api in sample_apis: + parent_package, method_name = api.rsplit(".", maxsplit=1) + try: + setattr(HookAPIMap, api, getattr(eval(parent_package), method_name)) + setattr(eval(parent_package), method_name, wrapped_api(api)) + except Exception as err: + print("[api_tracer error] : start_api_tracer ", api, str(err)) + + config_dump.open_file(save_config_path) diff --git a/python/setup.py.in b/python/setup.py.in index 1fbaa439e0ccec..dd0f46821df3ae 100644 --- a/python/setup.py.in +++ b/python/setup.py.in @@ -821,6 +821,7 @@ packages=['paddle', 'paddle.decomposition', 'paddle._typing', 'paddle._typing.libs', + 'paddle.api_tracer', ] if '@WITH_PIP_TENSORRT@' =='ON': diff --git a/setup.py b/setup.py index 644b8dbe7d4032..c0094cdda978a7 100644 --- a/setup.py +++ b/setup.py @@ -2080,6 +2080,7 @@ def get_setup_parameters(): 'paddle.decomposition', 'paddle._typing', 'paddle._typing.libs', + 'paddle.api_tracer', ] if env_dict.get("WITH_PIP_TENSORRT") == 'ON':