You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

graph_handler.py 26 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. # Copyright 2020 Huawei Technologies Co., Ltd
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. # ============================================================================
  15. """Define the graph stream handler."""
  16. from mindinsight.debugger.conditionmgr.common.utils import NodeBasicInfo
  17. from mindinsight.debugger.conditionmgr.condition import TargetTypeEnum as CategoryTypeEnum
  18. from mindinsight.debugger.common.exceptions.exceptions import DebuggerParamValueError, \
  19. DebuggerNodeNotInGraphError, DebuggerGraphNotExistError
  20. from mindinsight.debugger.common.log import LOGGER as log
  21. from mindinsight.debugger.common.utils import is_scope_type
  22. from mindinsight.debugger.stream_cache.debugger_graph import DebuggerGraph
  23. from mindinsight.debugger.stream_cache.debugger_multigraph import DebuggerMultiGraph
  24. from mindinsight.debugger.stream_handler.base_handler import StreamHandlerBase
  25. class GraphHandler(StreamHandlerBase):
  26. """Metadata Handler."""
  27. def __init__(self):
  28. # dict of <graph_name, GraphProto object>
  29. self._graph_proto = {}
  30. # dict of <graph_name, DebuggerGraph object>
  31. self._graph = {}
  32. self._searched_node_list = {}
  33. # list of node names in bfs order
  34. self.bfs_order = []
  35. # dict of <node full name, graph_name>
  36. self.graph_node_map = {}
  37. # dict of <node ui name, Node object> for all graphs
  38. self._all_leaf_nodes = {}
  39. # the whole graph
  40. self._whole_graph = None
  41. @property
  42. def whole_graph(self):
  43. """The property of whole_graph."""
  44. return self._whole_graph
  45. @property
  46. def graph(self):
  47. """The property of graph."""
  48. return self._graph_proto
  49. @property
  50. def graph_names(self):
  51. """The property of graph names."""
  52. return list(self._graph)
  53. @property
  54. def debugger_graph_obj(self):
  55. """The property of graph object."""
  56. return self._graph
  57. def put(self, value):
  58. """
  59. Put value into graph cache. Called by grpc server.
  60. Args:
  61. value (GraphProto): The Graph proto message.
  62. """
  63. log.info("Put graph into cache.")
  64. sorted_value_list = self._sort_graph(value)
  65. for graph_name, graph_value in sorted_value_list:
  66. self._graph_proto[graph_name] = graph_value
  67. # build sub graph
  68. graph = DebuggerGraph()
  69. graph.build_graph(graph_value)
  70. self._graph[graph_name] = graph
  71. self.bfs_order.extend(graph.get_bfs_order())
  72. leaf_nodes = graph.leaf_nodes
  73. self._all_leaf_nodes.update(leaf_nodes)
  74. for _, node in leaf_nodes.items():
  75. self.graph_node_map[node.full_name] = graph_name
  76. # build whole graph
  77. graph = DebuggerMultiGraph()
  78. graph.add_graph(self._graph)
  79. self._whole_graph = graph
  80. def get(self, filter_condition=None):
  81. """
  82. Get the graph of specific node.
  83. Args:
  84. filter_condition (dict):
  85. - name (str): The full debug node name.
  86. - graph_name (str): The relative graph_name of the node.
  87. - single_node (bool): If True, return the graph from root
  88. to the specific node; else, return the sublayer of the
  89. graph. Default: False.
  90. Returns:
  91. dict, the metadata.
  92. """
  93. try:
  94. self._graph_exists()
  95. except DebuggerGraphNotExistError:
  96. log.warning('The graph is empty. To view a graph, '
  97. 'please start the training script first.')
  98. return {'graph': {}}
  99. graph = {}
  100. if filter_condition is None:
  101. filter_condition = {}
  102. graph = {'graph_names': self.graph_names}
  103. single_node = filter_condition.get('single_node', False)
  104. name = filter_condition.get('name')
  105. graph_name = filter_condition.get('graph_name')
  106. if single_node is True:
  107. nodes = self._get_single_node(name, graph_name)
  108. else:
  109. nodes = self._list_nodes(name, graph_name)
  110. graph.update(nodes)
  111. return {'graph': graph}
  112. def _get_single_node(self, name, graph_name=None):
  113. """
  114. Search node, and return every layer nodes until this node.
  115. Args:
  116. graph_name(str): The graph_name.
  117. name (str): The name of node.
  118. Returns:
  119. dict, every layer nodes until this node.
  120. """
  121. if graph_name:
  122. graph = self._get_graph(graph_name=graph_name)
  123. searched_graph = graph.search_single_node(name)
  124. else:
  125. searched_graph = self._whole_graph.search_single_node(name)
  126. return searched_graph
  127. def _list_nodes(self, scope, graph_name):
  128. """
  129. Get the nodes of every layer in graph.
  130. Args:
  131. scope (str): The name of a scope.
  132. graph_name(str): The graph name.
  133. Returns:
  134. TypedDict{'nodes': ['Node_1', ...], 'graph_names': ['graph_name_1', ...]},
  135. format is {'nodes': [<NodeObject>], 'graph_names': [<str>]}.
  136. example:
  137. {
  138. "nodes" : [
  139. {
  140. "attr" :
  141. {
  142. "index" : "i: 0\n"
  143. },
  144. "input" : {},
  145. "name" : "input_tensor",
  146. "output" :
  147. {
  148. "Default/TensorAdd-op17" :
  149. {
  150. "edge_type" : "data",
  151. "scope" : "name_scope",
  152. "shape" : [1, 16, 128, 128]
  153. }
  154. },
  155. "output_i" : -1,
  156. "proxy_input" : {},
  157. "proxy_output" : {},
  158. "independent_layout" : False,
  159. "subnode_count" : 0,
  160. "type" : "Data"
  161. }
  162. ]
  163. }
  164. """
  165. if graph_name:
  166. graph = self._get_graph(graph_name, scope)
  167. nodes = graph.list_node_by_scope(scope=scope)
  168. res = {'nodes': nodes}
  169. else:
  170. nodes = self._whole_graph.list_node_by_scope(scope=scope)
  171. res = {'nodes': nodes}
  172. return res
  173. def get_tensor_history(self, node_name, graph_name=None, depth=0):
  174. """
  175. Get the tensor history of a specified node.
  176. Args:
  177. node_name (str): The debug name of the node.
  178. graph_name (str): The graph_name. Default: None.
  179. depth (int): The number of layers the user
  180. wants to trace. Default is 0.
  181. Returns:
  182. dict, basic tensor history, only including tensor name and tensor type and node type.
  183. """
  184. graph_name, node_name = self._parse_node_name(node_name, graph_name)
  185. graph = self._get_graph(graph_name=graph_name, node_name=node_name)
  186. # validate node type, scope node has no tensor history
  187. node_type = graph.get_node_type(node_name)
  188. if is_scope_type(node_type):
  189. log.error("Scope type node has no tensor history.")
  190. raise DebuggerParamValueError("Invalid leaf node name.")
  191. # get tensor history
  192. tensor_history, cur_outputs_nums = graph.get_tensor_history(node_name, depth)
  193. # add the tensor type for tensor history
  194. self._update_tensor_history(tensor_history[0:cur_outputs_nums], 'output', graph_name)
  195. self._update_tensor_history(tensor_history[cur_outputs_nums:], 'input', graph_name)
  196. log.debug("Get %d tensors in tensor history for node <%s>.", len(tensor_history), node_name)
  197. return {'tensor_history': tensor_history}
  198. @staticmethod
  199. def _update_tensor_history(tensor_history, tensor_type, graph_name):
  200. """
  201. Add tensor source type for tensor history.
  202. Args:
  203. tensor_history (list[dict]): Tensor history from Graph stream. Each element has two
  204. keys: `node_type` and `name`. `node_type` refers to the type of the node which
  205. the tensor come from. `name` refers to the tensor name.
  206. tensor_type (str): The source type of the tensor. `input` or `output`.
  207. graph_name (str): The graph name.
  208. """
  209. for single_tensor_info in tensor_history:
  210. single_tensor_info['type'] = tensor_type
  211. single_tensor_info['graph_name'] = graph_name
  212. def search_nodes(self, pattern):
  213. """
  214. Search nodes by given pattern.
  215. Args:
  216. pattern (dict): Filter condition.
  217. - name (str): The name pattern.
  218. - graph_name (str): The graph name.
  219. - node_category (str): The node_category. Default: None
  220. - condition (dict): The additional filter condition.
  221. Returns:
  222. dict, the searched node.
  223. """
  224. graph_name = pattern.pop('graph_name', None)
  225. search_nodes = self.get_searched_nodes(pattern, graph_name)
  226. # construct to search tree
  227. if not self._has_graph_scope(graph_name):
  228. for graph_name, searched_node_list in search_nodes.items():
  229. graph = self._get_graph(graph_name=graph_name)
  230. format_nodes = graph.get_nodes(searched_node_list)
  231. return {'nodes': format_nodes}
  232. # deal with graph_name is None
  233. res = []
  234. for graph_name, graph in self._graph.items():
  235. format_nodes = graph.get_nodes(search_nodes.get(graph_name, []))
  236. if not format_nodes:
  237. continue
  238. self._add_graph_scope_for_nodes(format_nodes, graph_name)
  239. search_graph = {
  240. 'name': graph_name,
  241. 'type': 'name_scope',
  242. 'nodes': format_nodes
  243. }
  244. res.append(search_graph)
  245. return {'nodes': res}
  246. def get_searched_node_list(self, pattern, graph_name):
  247. """Get searched node list in single graph."""
  248. searched_nodes = self.get_searched_nodes(pattern, graph_name)
  249. return searched_nodes.get(graph_name, [])
  250. def get_searched_nodes(self, pattern, graph_name=None):
  251. """
  252. Search nodes by given pattern.
  253. Args:
  254. pattern (dict): Filter condition.
  255. - name (str): The name pattern.
  256. - node_category (str): The node_category. Default: None
  257. - condition (dict): The additional filter condition.
  258. graph_name (str): The graph name. If not given, search in all sub graphs. Default: None.
  259. Returns:
  260. dict, the searched nodes. The format is dict of <graph_name, list[Node]>.
  261. """
  262. if not graph_name:
  263. graph_names = self.graph_names
  264. else:
  265. graph_names = [graph_name]
  266. search_nodes = {}
  267. for sub_graph_name in graph_names:
  268. search_nodes[sub_graph_name] = self._search_in_single_graph(pattern, sub_graph_name)
  269. return search_nodes
  270. def _search_in_single_graph(self, pattern, graph_name=None):
  271. """
  272. Search nodes by given pattern.
  273. Args:
  274. pattern (dict): Filter condition.
  275. - name (str): The name pattern.
  276. - node_category (str): The node_category. Default: None.
  277. - condition (dict): The additional filter condition.
  278. graph_name (str): The graph name.
  279. Returns:
  280. list, the searched node list.
  281. """
  282. temp_node_list = []
  283. node_category = pattern.get('node_category')
  284. graph = self._get_graph(graph_name=graph_name)
  285. # filter nodes by name
  286. if pattern.get('name'):
  287. if node_category:
  288. # get leaf nodes for forward filter
  289. temp_node_list = graph.search_leaf_nodes_by_pattern(pattern.get('name'))
  290. else:
  291. # optimize search nodes
  292. temp_node_list = graph.search_nodes_by_pattern(pattern.get('name'))
  293. if not temp_node_list:
  294. log.debug("No node named %s", pattern.get('name'))
  295. return []
  296. # filter nodes by category
  297. if node_category:
  298. node_category = self._get_inner_node_category(node_category)
  299. condition = pattern['condition'].copy() if pattern.get('condition') else {}
  300. condition['search_range'] = temp_node_list
  301. temp_node_list = graph.search_nodes_by_category(node_category, condition=condition)
  302. return temp_node_list
  303. @staticmethod
  304. def _get_inner_node_category(node_category):
  305. """
  306. Get inner node category.
  307. Args:
  308. node_category (str): The node category supported in
  309. mindinsight.conditionmgr.condition.TargetTypeEnum.
  310. Returns:
  311. CategoryTypeEnum, the translated value.
  312. """
  313. try:
  314. res = CategoryTypeEnum(node_category)
  315. except ValueError as err:
  316. log.error("Invalid node category. %s", err)
  317. raise DebuggerParamValueError("Invalid node_category.")
  318. return res
  319. def get_nodes_by_scope(self, scope_name, graph_name):
  320. """
  321. Get node by a given scope name.
  322. Args:
  323. scope_name (str): The name of scope.
  324. graph_name (str): The relative graph_name of the watched node. Default: None.
  325. Returns:
  326. list[Node], a list of node.
  327. """
  328. if graph_name:
  329. graph = self._get_graph(graph_name)
  330. else:
  331. graph = self._whole_graph
  332. return graph.search_leaf_nodes_by_pattern(scope_name)
  333. def get_graph_id_by_name(self, node_name):
  334. """
  335. Get graph id by full name.
  336. Args:
  337. node_name (str): The name of the node.
  338. Returns:
  339. str, the graph name of the node.
  340. Raises:
  341. DebuggerNodeNotInGraphError: If can not find the node in all graphs.
  342. """
  343. if node_name:
  344. for graph_name, sub_graph in self._graph.items():
  345. if sub_graph.exist_node(name=node_name):
  346. return graph_name
  347. log.error('Failed to find node %s in graph. Please make sure the graph has been sent and '
  348. 'the node name is correct, and try again.', node_name)
  349. raise DebuggerGraphNotExistError
  350. def get_graph_id_by_full_name(self, node_name):
  351. """
  352. Get graph id by full name.
  353. Args:
  354. node_name (str): The full name of the node.
  355. Returns:
  356. str, the graph name of the node.
  357. Raises:
  358. DebuggerNodeNotInGraphError: If can not find the node in all graphs.
  359. """
  360. graph_id = self.graph_node_map.get(node_name) if node_name else None
  361. if not graph_id:
  362. log.warning("Failed to get graph id by full name: %s", node_name)
  363. return graph_id
  364. def get_node_type(self, node_name, graph_name=None):
  365. """
  366. Get the type of the specified node.
  367. Args:
  368. node_name (str): The debug name of the node.
  369. graph_name (str): The relative graph_name of the node. Default: None.
  370. Returns:
  371. A string of the node type, name_scope or leaf.
  372. """
  373. if graph_name:
  374. graph = self._get_graph(node_name=node_name, graph_name=graph_name)
  375. else:
  376. graph = self._whole_graph
  377. node_type = graph.get_node_type(node_name)
  378. return node_type
  379. def get_full_name(self, node_name, graph_name=None):
  380. """Get full name according to ui node name."""
  381. full_name = ''
  382. if node_name:
  383. graph = self._get_graph(node_name=node_name, graph_name=graph_name)
  384. full_name = graph.get_full_name_by_node_name(node_name)
  385. return full_name
  386. def get_node_basic_info(self, node_name, graph_name):
  387. """Get node basic info with graph scope."""
  388. graph_name, node_name = self._parse_node_name(node_name=node_name, graph_name=graph_name)
  389. graph = self._get_graph(graph_name, node_name)
  390. full_name = graph.get_full_name_by_node_name(node_name)
  391. node_type = graph.get_node_type(node_name)
  392. return self.construct_node_basic_info(full_name, graph_name, node_name, node_type)
  393. def get_tensor_graph(self, tensor_name, graph_name):
  394. """
  395. Get tensor graph according to node name.
  396. Args:
  397. tensor_name (str): Tensor name from UI, format is "node_name:slot".
  398. graph_name (str): The relative graph_name of the node. Default: None.
  399. Returns:
  400. dict, relative node.
  401. """
  402. node_name, _ = tensor_name.rsplit(':', 1)
  403. graph = self._get_graph(graph_name=graph_name, node_name=node_name)
  404. tensor_graph = graph.get_tensor_graph(node_name)
  405. return {'graph': tensor_graph}
  406. @staticmethod
  407. def construct_node_basic_info(full_name, graph_name, node_name, node_type):
  408. """Construct node basic info."""
  409. node_name_with_graph_scope = '/'.join([graph_name, node_name]) if node_name else graph_name
  410. return NodeBasicInfo(name=node_name_with_graph_scope, full_name=full_name, type=node_type)
  411. def get_node_basic_info_by_scope(self, scope_name, graph_name):
  412. """
  413. Get node by a given scope name.
  414. Args:
  415. scope_name (str): The name of scope.
  416. graph_name (str): The relative graph_name of the watched node. Default: None.
  417. Returns:
  418. list[NodeBasicInfo], a list of node.
  419. """
  420. graph_name, node_name = self._parse_node_name(scope_name, graph_name)
  421. graph = self._get_graph(graph_name)
  422. nodes = graph.search_leaf_nodes_by_pattern(node_name)
  423. res = [self.construct_node_basic_info(full_name=node.full_name,
  424. graph_name=graph_name,
  425. node_name=node.name,
  426. node_type=node.type) for node in nodes]
  427. return res
  428. def get_node_name_by_full_name(self, full_name, graph_name):
  429. """Get UI node name by full name and graph name."""
  430. if graph_name and full_name:
  431. graph = self._get_graph(graph_name)
  432. node_name = graph.get_node_name_by_full_name(full_name)
  433. else:
  434. node_name = ''
  435. log.debug("Get empty full name.")
  436. return node_name
  437. def get_node_by_bfs_order(self, node_name=None, ascend=True):
  438. """
  439. Traverse the graph in order of breath-first search by given node.
  440. Args:
  441. node_name (str): The name of current chosen leaf node.
  442. ascend (bool): If True, traverse the input nodes;
  443. If False, traverse the output nodes. Default is True.
  444. Returns:
  445. Union[None, dict], the next node object in dict type or None.
  446. """
  447. bfs_order = self.bfs_order
  448. length = len(bfs_order)
  449. if not bfs_order:
  450. log.error('Cannot get the BFS order of the graph!')
  451. msg = 'Cannot get the BFS order of the graph!'
  452. raise DebuggerParamValueError(msg)
  453. if node_name is None:
  454. if ascend is False:
  455. next_node = None
  456. else:
  457. next_node = bfs_order[0]
  458. else:
  459. try:
  460. index = bfs_order.index(node_name)
  461. log.debug("The index of the node in BFS list is: %d", index)
  462. except ValueError as err:
  463. log.error('Cannot find the node: %s. Please check '
  464. 'the node name: %s', node_name, err)
  465. msg = f'Cannot find the node: {node_name}. ' \
  466. f'Please check the node name {err}.'
  467. raise DebuggerParamValueError(msg)
  468. next_node = self._get_next_node_in_bfs(index, length, ascend)
  469. return next_node
  470. def _get_next_node_in_bfs(self, index, length, ascend):
  471. """
  472. Get the next node in bfs order.
  473. Args:
  474. index (int): The current index.
  475. length (int): The number of all leaf nodes.
  476. ascend (bool): Whether get the node in ascend order or not.
  477. Returns:
  478. Union[None, dict], the next node object in dict type or None.
  479. """
  480. next_node = None
  481. if 0 <= index < length:
  482. if ascend is True and index < length - 1:
  483. next_node = self.bfs_order[index + 1]
  484. elif ascend is False and index > 0:
  485. next_node = self.bfs_order[index - 1]
  486. return next_node
  487. def _graph_exists(self):
  488. """
  489. Check if the graph has been loaded in the debugger cache.
  490. Raises:
  491. DebuggerGraphNotExistError: If the graph does not exist.
  492. """
  493. if not self._graph:
  494. log.error('The graph does not exist. Please start the '
  495. 'training script and try again.')
  496. raise DebuggerGraphNotExistError
  497. def _get_graph(self, graph_name=None, node_name=None):
  498. """
  499. Get the graph object according to graph name and node name.
  500. Args:
  501. graph_name (str): The graph name.
  502. node_name (str): The node name.
  503. Returns:
  504. DebuggerGraph, the graph object.
  505. Raises:
  506. DebuggerGraphNotExistError: If the graph does not exist.
  507. """
  508. graph = self._graph.get(graph_name) if graph_name else self._whole_graph
  509. # get graph according to graph name and check the node
  510. if graph and (not node_name or graph.exist_node(name=node_name)):
  511. return graph
  512. log.error('The graph %s does not exist node %s.', graph_name, node_name)
  513. raise DebuggerGraphNotExistError
  514. def _has_graph_scope(self, graph_name):
  515. """Check if query with graph_scope."""
  516. return bool(graph_name is None and len(self._graph) > 1)
  517. def validate_graph_name(self, graph_name):
  518. """Validate graph_name."""
  519. if graph_name and self._graph.get(graph_name) is None:
  520. log.error("No graph named %s in debugger cache.", graph_name)
  521. raise DebuggerGraphNotExistError
  522. if not graph_name and len(self._graph) == 1:
  523. graph_name = self.graph_names[0]
  524. return graph_name
  525. def _add_graph_scope_for_nodes(self, nodes, graph_name):
  526. """
  527. Add graph scope for nodes.
  528. Args:
  529. nodes (list[Node]): List of nodes object.
  530. graph_name (str): The graph name.
  531. """
  532. def _get_updated_node_info(cur_node, node_type):
  533. """Add graph scope in key."""
  534. old_node = cur_node.get(node_type)
  535. if not old_node:
  536. return
  537. new_values = {}
  538. for old_name, node_info in old_node.items():
  539. new_name = '/'.join([graph_name, old_name]) if old_name else graph_name
  540. new_values[new_name] = node_info
  541. cur_node[node_type] = new_values
  542. for node in nodes:
  543. node['name'] = '/'.join([graph_name, node['name']]) if node['name'] else graph_name
  544. _get_updated_node_info(node, 'input')
  545. _get_updated_node_info(node, 'output')
  546. if node.get('nodes'):
  547. self._add_graph_scope_for_nodes(node.get('nodes'), graph_name)
  548. def _parse_node_name(self, node_name, graph_name):
  549. """
  550. Check if the node name should have graph scope.
  551. Args:
  552. node_name (str): The ui node name.
  553. graph_name (str): The graph name.
  554. Returns:
  555. str, parsed graph name.
  556. str, parsed node name.
  557. """
  558. node_name = '' if node_name is None else node_name
  559. if self._has_graph_scope(graph_name):
  560. names = node_name.split("/", 1)
  561. graph_name = names[0]
  562. node_name = names[1] if len(names) == 2 else ''
  563. if graph_name is None and len(self._graph) == 1:
  564. graph_name = self.graph_names[0]
  565. return graph_name, node_name
  566. def validate_node_name(self, node_name, graph_name):
  567. """
  568. Validate the graph exist the specified node.
  569. Args:
  570. node_name (str): The ui node name.
  571. graph_name (str): The graph name.
  572. Raises:
  573. DebuggerNodeNotInGraphError: If can not find the node in all graphs.
  574. """
  575. graph = self._get_graph(graph_name=graph_name)
  576. if not graph.exist_node(name=node_name):
  577. log.error("graph %s doesn't find node: %s.", graph_name, node_name)
  578. raise DebuggerNodeNotInGraphError(node_name)
  579. def _sort_graph(self, graphs):
  580. """
  581. Sort graph by graph_name.
  582. Args:
  583. graphs(dict): <graph_name, GraphProto object>.
  584. """
  585. if len(graphs) == 1:
  586. return graphs.items()
  587. sorted_graphs = sorted(graphs.items(), key=lambda x: get_graph_number(x[0]))
  588. return sorted_graphs
  589. def get_graph_number(graph_name):
  590. number = graph_name.split("_")[-1]
  591. return int(number)