Imported Upstream version 1.25.0
[platform/core/ml/nnfw.git] / compiler / one-cmds / one-profile
1 #!/usr/bin/env bash
2 ''''export SCRIPT_PATH="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" # '''
3 ''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python                                       # '''
4 ''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@"                                     # '''
5 ''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # '''
6 ''''exit 255                                                                            # '''
7
8 # Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved
9 #
10 # Licensed under the Apache License, Version 2.0 (the "License");
11 # you may not use this file except in compliance with the License.
12 # You may obtain a copy of the License at
13 #
14 #    http://www.apache.org/licenses/LICENSE-2.0
15 #
16 # Unless required by applicable law or agreed to in writing, software
17 # distributed under the License is distributed on an "AS IS" BASIS,
18 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 # See the License for the specific language governing permissions and
20 # limitations under the License.
21
22 import argparse
23 import copy
24 import glob
25 import itertools
26 import ntpath
27 import os
28 import sys
29 from types import SimpleNamespace
30
31 import onelib.utils as oneutils
32
33 # TODO Find better way to suppress trackback on error
34 sys.tracebacklimit = 0
35
36
37 def _get_backends_list():
38     """
39     [one hierarchy]
40     one
41     ├── backends
42     ├── bin
43     ├── doc
44     ├── include
45     ├── lib
46     └── test
47
48     The list where `one-profile` finds its backends
49     - `bin` folder where `one-profile` exists
50     - `backends` folder
51
52     NOTE If there are backends of the same name in different places,
53      the closer to the top in the list, the higher the priority.
54     """
55     dir_path = os.path.dirname(os.path.realpath(__file__))
56     backend_set = set()
57
58     # bin folder
59     files = [f for f in glob.glob(dir_path + '/*-profile')]
60     # backends folder
61     files += [
62         f for f in glob.glob(dir_path + '/../backends/**/*-profile', recursive=True)
63     ]
64     # TODO find backends in `$PATH`
65
66     backends_list = []
67     for cand in files:
68         base = ntpath.basename(cand)
69         if not base in backend_set and os.path.isfile(cand) and os.access(cand, os.X_OK):
70             backend_set.add(base)
71             backends_list.append(cand)
72
73     return backends_list
74
75
76 def _get_parser(backends_list):
77     profile_usage = 'one-profile [-h] [-v] [-C CONFIG] [-b BACKEND] [--] [COMMANDS FOR BACKEND]'
78     parser = argparse.ArgumentParser(
79         description='command line tool for profiling backend model', usage=profile_usage)
80
81     oneutils.add_default_arg(parser)
82
83     # get backend list in the directory
84     backends_name = [ntpath.basename(f) for f in backends_list]
85     if not backends_name:
86         backends_name_message = '(There is no available backend drivers)'
87     else:
88         backends_name_message = '(available backend drivers: ' + ', '.join(
89             backends_name) + ')'
90     backend_help_message = 'backend name to use ' + backends_name_message
91     parser.add_argument('-b', '--backend', type=str, help=backend_help_message)
92
93     return parser
94
95
96 def _verify_arg(parser, args, cfg_args, backend_args, unknown_args):
97     """verify given arguments"""
98     cmd_backend_exist = oneutils.is_valid_attr(args, 'backend')
99     cfg_backend_exist = oneutils.is_valid_attr(cfg_args, 'backend')
100     cfg_backends_exist = oneutils.is_valid_attr(cfg_args, 'backends')
101
102     # check if required arguments is given
103     missing = []
104     if not cmd_backend_exist and not cfg_backend_exist and not cfg_backends_exist:
105         missing.append('-b/--backend')
106     if len(missing):
107         parser.error('the following arguments are required: ' + ' '.join(missing))
108
109     if not oneutils.is_valid_attr(args, 'config'):
110         if not backend_args and not unknown_args:
111             parser.error('commands for the backend is missing.')
112
113     if cfg_backend_exist and cfg_backends_exist:
114         parser.error(
115             '\'backend\' option and \'backends\' option cannot be used simultaneously.')
116
117     # Check if given backend from command line exists in the configuration file
118     if cmd_backend_exist and cfg_backend_exist:
119         if args.backend != cfg_args.backend:
120             parser.error('Not found the command of given backend')
121
122     if cfg_backend_exist and not oneutils.is_valid_attr(cfg_args, 'command'):
123         parser.error('\'command\' key is missing in the configuration file.')
124
125     if cfg_backends_exist:
126         cfg_backends = getattr(cfg_args, 'backends').split(',')
127         # check if commands of given backends exist
128         for b in cfg_backends:
129             if not oneutils.is_valid_attr(cfg_args, b):
130                 parser.error('Not found the command for ' + b)
131         # Check if given backend from command line exists in the configuration file
132         if cmd_backend_exist:
133             if args.backend not in cfg_backends:
134                 parser.error('Not found the command of given backend')
135
136
137 def _parse_arg(parser):
138     profile_args = []
139     backend_args = []
140     unknown_args = []
141     argv = copy.deepcopy(sys.argv)
142     # delete file name
143     del argv[0]
144     # split by '--'
145     args = [list(y) for x, y in itertools.groupby(argv, lambda z: z == '--') if not x]
146     if len(args) == 0:
147         profile_args = parser.parse_args(profile_args)
148     # one-profile has two interfaces
149     # 1. one-profile [-h] [-v] [-C CONFIG] [-b BACKEND] [COMMANDS FOR BACKEND]
150     if len(args) == 1:
151         profile_args = args[0]
152         profile_args, unknown_args = parser.parse_known_args(profile_args)
153     # 2. one-profile [-h] [-v] [-C CONFIG] [-b BACKEND] -- [COMMANDS FOR BACKEND]
154     if len(args) == 2:
155         profile_args = args[0]
156         backend_args = args[1]
157         profile_args = parser.parse_args(profile_args)
158     # print version
159     if len(args) and profile_args.version:
160         oneutils.print_version_and_exit(__file__)
161
162     return profile_args, backend_args, unknown_args
163
164
165 def main():
166     # get backend list
167     backends_list = _get_backends_list()
168
169     # parse arguments
170     parser = _get_parser(backends_list)
171     args, backend_args, unknown_args = _parse_arg(parser)
172
173     # parse configuration file
174     cfg_args = SimpleNamespace()
175     oneutils.parse_cfg(args.config, 'one-profile', cfg_args)
176
177     # verify arguments
178     _verify_arg(parser, args, cfg_args, backend_args, unknown_args)
179     '''
180     one-profile defines its behavior for below cases.
181
182     [1] one-profile -h
183     [2] one-profile -v
184     [3] one-profile -C ${cfg} (backend, command key in cfg)
185     [4] one-profile -C ${cfg} (backends key in cfg)
186     [5] one-profile -b ${backend} ${command}
187     [6] one-profile -b ${backend} -- ${command}
188     [7] one-profile -b ${backend} -C {cfg} (backend, command key in cfg)
189     [8] one-profile -b ${backend} -C {cfg} (backends key in cfg) (Only 'backend' is invoked, 
190          even though cfg file has multiple backends)
191     [9] one-profile -b ${backend} -C ${cfg} -- ${command} (backend, command key in cfg) 
192     [10] one-profile -b ${backend} -C ${cfg} -- ${command} (backends key in cfg) (Only 'backend' is invoked, 
193          even though cfg file has multiple backends)
194
195     All other cases are not allowed or an undefined behavior.
196     '''
197     cmd_overwrite = False
198     if oneutils.is_valid_attr(args, 'config'):
199         # [9], [10]
200         if backend_args and not unknown_args:
201             given_backends = [args.backend]
202             cmd_overwrite = True
203         else:
204             # [7], [8]
205             if oneutils.is_valid_attr(args, 'backend'):
206                 given_backends = [args.backend]
207                 if oneutils.is_valid_attr(cfg_args, 'backend'):
208                     assert (oneutils.is_valid_attr(cfg_args, 'command'))
209                     setattr(cfg_args, args.backend, cfg_args.command)
210             else:
211                 # [3]
212                 if oneutils.is_valid_attr(cfg_args, 'backend'):
213                     assert (oneutils.is_valid_attr(cfg_args, 'command'))
214                     given_backends = [cfg_args.backend]
215                     setattr(cfg_args, cfg_args.backend, cfg_args.command)
216                 # [4]
217                 if oneutils.is_valid_attr(cfg_args, 'backends'):
218                     given_backends = cfg_args.backends.split(',')
219     # [5], [6]
220     else:
221         assert (backend_args or unknown_args)
222         given_backends = [args.backend]
223
224     for given_backend in given_backends:
225         # make a command to run given backend driver
226         profile_path = None
227         backend_base = given_backend + '-profile'
228         for cand in backends_list:
229             if ntpath.basename(cand) == backend_base:
230                 profile_path = cand
231         if not profile_path:
232             raise FileNotFoundError(backend_base + ' not found')
233
234         profile_cmd = [profile_path]
235         if not cmd_overwrite and oneutils.is_valid_attr(cfg_args, given_backend):
236             profile_cmd += getattr(cfg_args, given_backend).split()
237         else:
238             profile_cmd += backend_args
239             profile_cmd += unknown_args
240
241         # run backend driver
242         oneutils.run(profile_cmd, err_prefix=backend_base)
243
244
245 if __name__ == '__main__':
246     main()