/* 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); }
/* 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; } }
/* 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); }
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. */ } }
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"); } } }
/* 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; }
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; }
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 (); }
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; }