code: def _build_tree_relationship(  # noqa: C901
      self, host_event_nodes, runtime_event_nodes, mem_event_nodes
  ):
      # root node
      root_node = HostNode()
      (
          root_node.name,
          root_node.type,
          root_node.start_ns,
          root_node.end_ns,
      ) = ("root node", "UserDefined", 0, sys.maxsize)
      # push root node into node_stack
      node_stack = []
      node_stack.append(root_node)
      # handle host_event_nodes
      for host_node in host_event_nodes:
          while True:
              stack_top_node = node_stack[-1]
              if host_node.start_ns < stack_top_node.end_ns:
                  stack_top_node.children_node.append(host_node)
                  node_stack.append(host_node)
                  break
              else:
                  node_stack.pop()
                  # insert runtime node
                  # select runtime nodes which time range within stack_top_node
                  hasenter = False
                  firstposition = 0
                  lastposition = len(runtime_event_nodes)
                  for i, runtimenode in enumerate(runtime_event_nodes):
                      if (
                          runtimenode.start_ns
                          >= stack_top_node.start_ns
                          and runtimenode.end_ns
                          <= stack_top_node.end_ns
                      ):
                          if not hasenter:
                              firstposition = i
                              hasenter = True
                          stack_top_node.runtime_node.append(
                              runtimenode
                          )
                      else:
                          # from this runtime node, not within stack_top_node, erase the
                          # nodes from runtime_event_nodes
                          if (
                              runtimenode.start_ns
                              > stack_top_node.end_ns
                          ):
                              lastposition = i
                              break
                  if hasenter:
                      del runtime_event_nodes[
                          firstposition:lastposition
                      ]
      # to insert left runtimenode into host_event_nodes
      while node_stack:
          stack_top_node = node_stack.pop()
          # insert runtime node
          # select runtime nodes which time range within stack_top_node
          firstposition = 0
          lastposition = len(runtime_event_nodes)
          hasenter = False
          for i, runtimenode in enumerate(runtime_event_nodes):
              if (
                  runtimenode.start_ns >= stack_top_node.start_ns
                  and runtimenode.end_ns <= stack_top_node.end_ns
              ):
                  if not hasenter:
                      firstposition = i
                      hasenter = True
                  stack_top_node.runtime_node.append(runtimenode)
              else:
                  # from this runtime node, not within stack_top_node, erase the
                  # nodes from runtime_event_nodes
                  if runtimenode.start_ns > stack_top_node.end_ns:
                      lastposition = i
                      break
          if hasenter:
              del runtime_event_nodes[firstposition:lastposition]
  
      # build relationship between host event node and mem event node
      # First, post-order traverse the tree. Then, insert the memory and op
      # supplement node into correct host nodes.
      stack = []
      flag_stack = []
      post_order_nodes = []
      stack.append(root_node)
      flag_stack.append(0)
      while stack:
          current_node = stack.pop()
          flag = flag_stack.pop()
          if flag == 0:
              stack.append(current_node)
              flag_stack.append(1)
              for child in current_node.children_node[::-1]:
                  stack.append(child)
                  flag_stack.append(0)
          else:
              post_order_nodes.append(current_node)
      for node in post_order_nodes:
          hasenter = False
          firstposition = 0
          lastposition = len(mem_event_nodes)
          for i, mem_node in enumerate(mem_event_nodes):
              if (
                  mem_node.timestamp_ns >= node.start_ns
                  and mem_node.timestamp_ns <= node.end_ns
              ):
                  node.mem_node.append(mem_node)
                  if not hasenter:
                      firstposition = i
                      hasenter = True
              else:
                  if mem_node.timestamp_ns > node.end_ns:
                      lastposition = i
                      break
          if hasenter:
              del mem_event_nodes[firstposition:lastposition]
  
      return root_node
  
imports: import collections
  import functools
  import json
  import re
  import sys
  import tempfile
  from .utils import traverse_tree
label_desc: Use image node_id 5 for calling the ProfilerResult obj's underlying member method code with example usage: obj._build_tree_relationship(host_event_nodes, runtime_event_nodes, mem_event_nodes) and returns: root_node