Пример #1
0
/* Examine the predicate list for instances of -execdir or -okdir
 * which have been terminated with '+' (build argument list) rather
 * than ';' (singles only).  If there are any, run them (this will
 * have no effect if there are no arguments waiting).
 */
static void
do_complete_pending_execdirs(struct predicate *p, int dir_fd)
{
  if (NULL == p)
    return;
  
  assert (state.execdirs_outstanding);
  
  do_complete_pending_execdirs(p->pred_left, dir_fd);
  
  if (pred_is(p, pred_execdir) || pred_is(p, pred_okdir))
    {
      /* It's an exec-family predicate.  p->args.exec_val is valid. */
      if (p->args.exec_vec.multiple)
	{
	  struct exec_val *execp = &p->args.exec_vec;
	  
	  /* This one was terminated by '+' and so might have some
	   * left... Run it if necessary.
	   */
	  if (execp->state.todo)
	    {
	      /* There are not-yet-executed arguments. */
	      launch (&execp->ctl, &execp->state);
	    }
	}
    }

  do_complete_pending_execdirs(p->pred_right, dir_fd);
}
Пример #2
0
/* Returns true if the specified predicate is reorderable. */
static bool
predicate_is_cost_free (const struct predicate *p)
{
  if (pred_is(p, pred_name) ||
      pred_is(p, pred_path) ||
      pred_is(p, pred_iname) ||
      pred_is(p, pred_ipath))
    {
      /* Traditionally (at least 4.1.7 through 4.2.x) GNU find always
       * optimised these cases.
       */
      return true;
    }
  else if (options.optimisation_level > 0)
    {
      if (pred_is(p, pred_and) ||
	  pred_is(p, pred_negate) ||
	  pred_is(p, pred_comma) ||
	  pred_is(p, pred_or))
	return false;
      else
	return NeedsNothing == p->p_cost;
    }
  else
    {
      return false;
    }
}
Пример #3
0
/* Examine the predicate list for instances of -exec which have been
 * terminated with '+' (build argument list) rather than ';' (singles
 * only).  If there are any, run them (this will have no effect if
 * there are no arguments waiting).
 */
void
complete_pending_execs(struct predicate *p)
{
  if (NULL == p)
    return;
  
  complete_pending_execs(p->pred_left);
  
  /* It's an exec-family predicate then p->args.exec_val is valid
   * and we can check it. 
   */
  /* XXX: what about pred_ok() ? */
  if (pred_is(p, pred_exec) && p->args.exec_vec.multiple)
    {
      struct exec_val *execp = &p->args.exec_vec;
      
      /* This one was terminated by '+' and so might have some
       * left... Run it if necessary.  Set state.exit_status if
       * there are any problems.
       */
      if (execp->state.todo)
	{
	  /* There are not-yet-executed arguments. */
	  launch (&execp->ctl, &execp->state);
	}
    }

  complete_pending_execs(p->pred_right);
}
Пример #4
0
static void
show_outstanding_execdirs (FILE *fp)
{
  if (options.debug_options & DebugExec)
    {
      int seen=0;
      struct predicate *p;
      p = get_eval_tree ();
      fprintf (fp, "Outstanding execdirs:");

      while (p)
	{
	  const char *pfx;

	  if (pred_is (p, pred_execdir))
	    pfx = "-execdir";
	  else if (pred_is (p, pred_okdir))
	    pfx = "-okdir";
	  else
	    pfx = NULL;
	  if (pfx)
	    {
	      size_t i;
	      const struct exec_val *execp = &p->args.exec_vec;
	      ++seen;

	      fprintf (fp, "%s ", pfx);
	      if (execp->multiple)
		fprintf (fp, "multiple ");
	      fprintf (fp, "%" PRIuMAX " args: ", (uintmax_t) execp->state.cmd_argc);
	      for (i=0; i<execp->state.cmd_argc; ++i)
		{
		  fprintf (fp, "%s ", execp->state.cmd_argv[i]);
		}
	      fprintf (fp, "\n");
	    }
	  p = p->pred_next;
	}
      if (!seen)
	fprintf (fp, " none\n");
    }
  else
    {
      /* No debug output is wanted. */
    }
}
Пример #5
0
static void
flush_and_close_output_files(struct predicate *p)
{
  if (pred_is(p, pred_fprint)
      || pred_is(p, pred_fprintf)
      || pred_is(p, pred_fls)
      || pred_is(p, pred_fprint0))
    {
      FILE *f = p->args.printf_vec.stream;
      bool failed;
      
      if (f == stdout || f == stderr)
	failed = fflush(p->args.printf_vec.stream) == EOF;
      else
	failed = fclose(p->args.printf_vec.stream) == EOF;
     
      if (failed)
	  nonfatal_file_error(p->args.printf_vec.filename);
    }
  else if (pred_is(p, pred_print))
    {
      if (fflush(p->args.printf_vec.stream) == EOF)
	{
	  nonfatal_file_error(p->args.printf_vec.filename);
	}
    }
  else if (pred_is(p, pred_ls) || pred_is(p, pred_print0))
    {
      if (fflush(stdout) == EOF)
	{
	  /* XXX: migrate to printf_vec. */
	  nonfatal_file_error("standard output");
	}
    }
}
Пример #6
0
/* Consider swapping p->pred_left->pred_right with p->pred_right,
 * if that yields a faster evaluation.   Normally the left predicate is
 * evaluated first.
 *
 * If the operation is an OR, we want the left predicate to be the one that
 * succeeds most often.   If it is an AND, we want it to be the predicate that
 * fails most often.
 *
 * We don't consider swapping arms of an operator where their cost is
 * different or where they have side effects.
 *
 * A viable test case for this is
 * ./find -D opt   -O3  .   \! -type f -o -type d
 * Here, the ! -type f should be evaluated first,
 * as we assume that 95% of inodes are vanilla files.
 */
static bool
consider_arm_swap (struct predicate *p)
{
  int left_cost, right_cost;
  const char *reason = NULL;
  struct predicate **pl, **pr;

  if (BI_OP != p->p_type)
    reason = "Not a binary operation";

  if (!reason)
    {
      if (NULL == p->pred_left || NULL == p->pred_right)
	reason = "Doesn't have two arms";
    }


  if (!reason)
    {
      if (NULL == p->pred_left->pred_right)
	reason = "Left arm has no child on RHS";
    }
  pr = &p->pred_right;
  pl = &p->pred_left->pred_right;

  if (!reason)
    {
      if (subtree_has_side_effects (*pl))
	reason = "Left subtree has side-effects";
    }
  if (!reason)
    {
      if (subtree_has_side_effects (*pr))
	reason = "Right subtree has side-effects";
    }

  if (!reason)
    {
      left_cost = worst_cost (*pl);
      right_cost = worst_cost (*pr);

      if (left_cost < right_cost)
	{
	  reason = "efficient as-is";
	}
    }
  if (!reason)
    {
      bool want_swap;

      if (left_cost == right_cost)
	{
	  /* it's a candidate */
	  float succ_rate_l = (*pl)->est_success_rate;
	  float succ_rate_r = (*pr)->est_success_rate;

	  if (options.debug_options & DebugTreeOpt)
	    {
	      fprintf (stderr, "Success rates: l=%f, r=%f\n", succ_rate_l, succ_rate_r);
	    }

	  if (pred_is (p, pred_or))
	    {
	      want_swap = succ_rate_r < succ_rate_l;
	      if (!want_swap)
		reason = "Operation is OR; right success rate >= left";
	    }
	  else if (pred_is (p, pred_and))
	    {
	      want_swap = succ_rate_r > succ_rate_l;
	      if (!want_swap)
		reason = "Operation is AND; right success rate <= left";
	    }
	  else
	    {
	      want_swap = false;
	      reason = "Not 'AND' or 'OR'";
	    }
	}
      else
	{
	  want_swap = true;
	}

      if (want_swap)
	{
	  if (options.debug_options & DebugTreeOpt)
	    {
	      fprintf (stderr, "Performing arm swap on:\n");
	      print_tree (stderr, p, 0);
	    }
	  perform_arm_swap (p);
	  return true;
	}
    }


  if (options.debug_options & DebugTreeOpt)
    {
      fprintf (stderr, "Not an arm swap candidate (%s):\n", reason);
      print_tree (stderr, p, 0);
    }
  return false;
}
Пример #7
0
struct predicate*
build_expression_tree (int argc, char *argv[], int end_of_leading_options)
{
  const struct parser_table *parse_entry; /* Pointer to the parsing table entry for this expression. */
  char *predicate_name;		/* Name of predicate being parsed. */
  struct predicate *cur_pred;
  const struct parser_table *entry_close, *entry_print, *entry_open;
  int i, oldi;

  predicates = NULL;

  /* Find where in ARGV the predicates begin by skipping the list of
   * start points.  As a side effect, also figure out which is the
   * first and last start point.
   */
  start_points = argv + end_of_leading_options;
  for (i = end_of_leading_options; i < argc && !looks_like_expression(argv[i], true); i++)
    {
      ++num_start_points;
    }

  /* Enclose the expression in `( ... )' so a default -print will
     apply to the whole expression. */
  entry_open  = find_parser ("(");
  entry_close = find_parser (")");
  entry_print = find_parser ("print");
  assert (entry_open  != NULL);
  assert (entry_close != NULL);
  assert (entry_print != NULL);

  parse_openparen (entry_open, argv, &argc);
  last_pred->p_name = "(";
  predicates->artificial = true;
  parse_begin_user_args (argv, argc, last_pred, predicates);
  pred_sanity_check (last_pred);

  /* Build the input order list. */
  while (i < argc )
    {
      state.already_issued_stat_error_msg = false;
      if (!looks_like_expression (argv[i], false))
	{
	  error (0, 0, _("paths must precede expression: %s"), argv[i]);
	  usage (stderr, 1, NULL);
	}

      predicate_name = argv[i];
      parse_entry = find_parser (predicate_name);
      if (parse_entry == NULL)
	{
	  /* Command line option not recognized */
	  error (EXIT_FAILURE, 0, _("unknown predicate `%s'"), predicate_name);
	}

      /* We have recognised a test of the form -foo.  Eat that,
       * unless it is a predicate like -newerXY.
       */
      if (parse_entry->type != ARG_SPECIAL_PARSE)
	{
	  i++;
	}
      oldi = i;
      if (!(*(parse_entry->parser_func)) (parse_entry, argv, &i))
	{
	  if (argv[i])
	    {
	      if ( (ARG_SPECIAL_PARSE == parse_entry->type) && (i == oldi) )
		{
		  /* The special parse function spat out the
		   * predicate.  It must be invalid, or not tasty.
		   */
		  error (EXIT_FAILURE, 0, _("invalid predicate `%s'"),
			 predicate_name);
		}
	      else
		{
		  error (EXIT_FAILURE, 0, _("invalid argument `%s' to `%s'"),
			 argv[i], predicate_name);
		}
	    }
	  else
	    {
	      /* Command line option requires an argument */
	      error (EXIT_FAILURE, 0,
		     _("missing argument to `%s'"), predicate_name);
	    }
	}
      else
	{
	  last_pred->p_name = predicate_name;

	  /* If the parser consumed an argument, save it. */
	  if (i != oldi)
	    last_pred->arg_text = argv[oldi];
	  else
	    last_pred->arg_text = NULL;
	}
      pred_sanity_check(last_pred);
      pred_sanity_check(predicates); /* XXX: expensive */
    }
  parse_end_user_args (argv, argc, last_pred, predicates);
  if (predicates->pred_next == NULL)
    {
      /* No predicates that do something other than set a global variable
	 were given; remove the unneeded initial `(' and add `-print'. */
      cur_pred = predicates;
      predicates = last_pred = predicates->pred_next;
      free (cur_pred);
      parse_print (entry_print, argv, &argc);
      last_pred->p_name = "-print";
      pred_sanity_check(last_pred);
      pred_sanity_check(predicates); /* XXX: expensive */
    }
  else if (!default_prints (predicates->pred_next))
    {
      /* One or more predicates that produce output were given;
	 remove the unneeded initial `('. */
      cur_pred = predicates;
      predicates = predicates->pred_next;
      pred_sanity_check (predicates); /* XXX: expensive */
      free (cur_pred);
    }
  else
    {
      /* `( user-supplied-expression ) -print'. */
      parse_closeparen (entry_close, argv, &argc);
      last_pred->p_name = ")";
      last_pred->artificial = true;
      pred_sanity_check (last_pred);
      parse_print (entry_print, argv, &argc);
      last_pred->p_name = "-print";
      last_pred->artificial = true;
      pred_sanity_check (last_pred);
      pred_sanity_check (predicates); /* XXX: expensive */
    }

  if (options.debug_options & (DebugExpressionTree|DebugTreeOpt))
    {
      fprintf (stderr, "Predicate List:\n");
      print_list (stderr, predicates);
    }

  /* do a sanity check */
  check_option_combinations (predicates);
  pred_sanity_check (predicates);

  /* Done parsing the predicates.  Build the evaluation tree. */
  cur_pred = predicates;
  eval_tree = get_expr (&cur_pred, NO_PREC, NULL);
  calculate_derived_rates (eval_tree);

  /* Check if we have any left-over predicates (this fixes
   * Debian bug #185202).
   */
  if (cur_pred != NULL)
    {
      /* cur_pred->p_name is often NULL here */
      if (pred_is (cur_pred, pred_closeparen))
	{
	  /* e.g. "find \( -true \) \)" */
	  error (EXIT_FAILURE, 0, _("you have too many ')'"));
	}
      else
	{
	  if (cur_pred->p_name)
	    error (EXIT_FAILURE, 0,
		   _("unexpected extra predicate '%s'"), cur_pred->p_name);
	  else
	    error (EXIT_FAILURE, 0, _("unexpected extra predicate"));
	}
    }

  if (options.debug_options & (DebugExpressionTree|DebugTreeOpt))
    {
      fprintf (stderr, "Eval Tree:\n");
      print_tree (stderr, eval_tree, 0);
    }

  estimate_costs (eval_tree);

  /* Rearrange the eval tree in optimal-predicate order. */
  opt_expr (&eval_tree);

  /* Check that the tree is in normalised order (opt_expr does this) */
  check_normalization (eval_tree, true);

  do_arm_swaps (eval_tree);

  /* Check that the tree is still in normalised order */
  check_normalization (eval_tree, true);

  if (options.debug_options & (DebugExpressionTree|DebugTreeOpt))
    {
      fprintf (stderr, "Optimized Eval Tree:\n");
      print_tree (stderr, eval_tree, 0);
      fprintf (stderr, "Optimized command line:\n");
      print_optlist (stderr, eval_tree);
      fprintf (stderr, "\n");
    }

  return eval_tree;
}
Пример #8
0
float
calculate_derived_rates (struct predicate *p)
{
  assert (NULL != p);

  if (p->pred_right)
    calculate_derived_rates (p->pred_right);
  if (p->pred_left)
    calculate_derived_rates (p->pred_left);

  assert (p->p_type != CLOSE_PAREN);
  assert (p->p_type != OPEN_PAREN);

  switch (p->p_type)
    {
    case NO_TYPE:
      assert (NULL == p->pred_right);
      assert (NULL == p->pred_left);
      return p->est_success_rate;

    case PRIMARY_TYPE:
      assert (NULL == p->pred_right);
      assert (NULL == p->pred_left);
      return p->est_success_rate;

    case UNI_OP:
      /* Unary operators must have exactly one operand */
      assert (pred_is (p, pred_negate));
      assert (NULL == p->pred_left);
      p->est_success_rate = (1.0 - p->pred_right->est_success_rate);
      return p->est_success_rate;

    case BI_OP:
      {
	float rate;
	/* Binary operators must have two operands */
	if (pred_is (p, pred_and))
	  {
	    rate = getrate (p->pred_right) * getrate(p->pred_left);
	  }
	else if (pred_is (p, pred_comma))
	  {
	    rate = 1.0f;
	  }
	else if (pred_is (p, pred_or))
	  {
	    rate = getrate (p->pred_right) + getrate(p->pred_left);
	  }
	else
	  {
	    /* only and, or and comma are BI_OP. */
	    assert (0);
	    abort ();
	  }
	p->est_success_rate = constrain_rate (rate);
      }
      return p->est_success_rate;

    case OPEN_PAREN:
    case CLOSE_PAREN:
      p->est_success_rate = 1.0;
      return p->est_success_rate;
    }
  assert (0);
  abort ();
}
Пример #9
0
static enum EvaluationCost
get_pred_cost (const struct predicate *p)
{
  enum EvaluationCost data_requirement_cost = NeedsNothing;
  enum EvaluationCost inherent_cost = NeedsUnknown;

  if (p->need_stat)
    {
      data_requirement_cost = NeedsStatInfo;
    }
  else if (p->need_inum)
    {
      data_requirement_cost = NeedsInodeNumber;
    }
  else if (p->need_type)
    {
      data_requirement_cost = NeedsType;
    }
  else
    {
      data_requirement_cost = NeedsNothing;
    }

  if (pred_is (p, pred_exec) || pred_is(p, pred_execdir))
    {
      if (p->args.exec_vec.multiple)
	inherent_cost = NeedsEventualExec;
      else
	inherent_cost = NeedsImmediateExec;
    }
  else if (pred_is (p, pred_fprintf))
    {
      /* the parser calculated the cost for us. */
      inherent_cost = p->p_cost;
    }
  else
    {
      struct pred_cost_lookup key;
      void *entry;

      if (!pred_table_sorted)
	{
	  qsort (costlookup,
		 sizeof(costlookup)/sizeof(costlookup[0]),
		 sizeof(costlookup[0]),
		 cost_table_comparison);

	  if (!check_sorted (costlookup,
			     sizeof(costlookup)/sizeof(costlookup[0]),
			     sizeof(costlookup[0]),
			     cost_table_comparison))
	    {
	      error (EXIT_FAILURE, 0,
		     "failed to sort the costlookup array");
	    }
	  pred_table_sorted = 1;
	}
      key.fn = p->pred_func;
      entry = bsearch (&key, costlookup,
		       sizeof(costlookup)/sizeof(costlookup[0]),
		       sizeof(costlookup[0]),
		       cost_table_comparison);
      if (entry)
	{
	  inherent_cost = ((const struct pred_cost_lookup*)entry)->cost;
	}
      else
	{
	  /* This message indicates a bug.  If we issue the message, we
	     actually have two bugs: if find emits a diagnostic, its result
	     should be nonzero.  However, not having an entry for a predicate
	     will not affect the output (just the performance) so I think it
	     would be confusing to exit with a nonzero status.
	  */
	  error (0, 0,
		 _("warning: there is no entry in the predicate evaluation "
		   "cost table for predicate %s; please report this as a bug"),
		 p->p_name);
	  inherent_cost = NeedsUnknown;
	}
    }

  if (inherent_cost > data_requirement_cost)
    return inherent_cost;
  else
    return data_requirement_cost;
}