code: def _obtain_health_pills_at_step(self, events_directory, node_names, step):
      
      # Obtain all files with debugger-related events.
      pattern = os.path.join(events_directory, _DEBUGGER_EVENTS_GLOB_PATTERN)
      file_paths = glob.glob(pattern)
  
      if not file_paths:
        raise IOError(
            'No events files found that matches the pattern %r.' % pattern)
  
      # Sort by name (and thus by timestamp).
      file_paths.sort()
  
      mapping = collections.defaultdict(list)
      node_name_set = frozenset(node_names)
  
      for file_path in file_paths:
        should_stop = self._process_health_pill_event(
            node_name_set, mapping, step, file_path)
        if should_stop:
          break
  
      return mapping
docs: Reads disk to obtain the health pills for a run at a specific step.
  
      This could be much slower than the alternative path of just returning all
      health pills sampled by the event multiplexer. It could take tens of minutes
      to complete this call for large graphs for big step values (in the
      thousands).
  
      Args:
        events_directory: The directory containing events for the desired run.
        node_names: A list of node names for which to retrieve health pills.
        step: The step to obtain health pills for.
  
      Returns:
        A dictionary mapping from node name to a list of health pill objects (see
        docs for _serve_health_pills_handler for properties of those objects).
  
      Raises:
        IOError: If no files with health pill events could be found.