kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/cxx/verifier/verifier_main.cc (about)

     1  /*
     2   * Copyright 2014 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  #include <stdio.h>
    18  #include <sys/resource.h>
    19  #include <unistd.h>
    20  
    21  #include <string>
    22  
    23  #include "absl/flags/flag.h"
    24  #include "absl/flags/parse.h"
    25  #include "absl/flags/usage.h"
    26  #include "absl/log/log.h"
    27  #include "absl/strings/str_format.h"
    28  #include "absl/time/clock.h"
    29  #include "absl/time/time.h"
    30  #include "assertion_ast.h"
    31  #include "google/protobuf/io/coded_stream.h"
    32  #include "google/protobuf/io/zero_copy_stream.h"
    33  #include "google/protobuf/io/zero_copy_stream_impl.h"
    34  #include "kythe/cxx/common/init.h"
    35  #include "kythe/proto/storage.pb.h"
    36  #include "verifier.h"
    37  
    38  ABSL_FLAG(bool, show_protos, false,
    39            "Show protocol buffers read from standard in");
    40  ABSL_FLAG(bool, show_goals, false, "Show goals after parsing");
    41  ABSL_FLAG(bool, ignore_dups, false,
    42            "Ignore duplicate facts during verification");
    43  ABSL_FLAG(bool, ignore_code_conflicts, false,
    44            "Ignore conflicting /kythe/code facts during verification");
    45  ABSL_FLAG(bool, graphviz, false,
    46            "Only dump facts as a GraphViz-compatible graph");
    47  ABSL_FLAG(bool, annotated_graphviz, false,
    48            "Solve and annotate a GraphViz graph.");
    49  ABSL_FLAG(bool, minimal_graphviz, false,
    50            "Solve and dump a GraphViz graph eliding unused nodes.");
    51  ABSL_FLAG(std::string, goal_prefix, "//-", "Denote goals with this string.");
    52  ABSL_FLAG(bool, use_file_nodes, false,
    53            "Look for assertions in UTF8 file nodes.");
    54  ABSL_FLAG(bool, check_for_singletons, true, "Fail on singleton variables.");
    55  ABSL_FLAG(std::string, goal_regex, "",
    56            "If nonempty, denote goals with this regex. "
    57            "The regex must match the entire line. Expects one capture group.");
    58  ABSL_FLAG(bool, convert_marked_source, false,
    59            "Convert MarkedSource-valued facts to subgraphs.");
    60  ABSL_FLAG(bool, show_anchors, false, "Show anchor locations instead of @s");
    61  ABSL_FLAG(bool, show_vnames, true,
    62            "Show VNames for nodes which also have labels.");
    63  ABSL_FLAG(bool, show_fact_prefix, true,
    64            "Include the /kythe or /kythe/edge prefix on facts and edges.");
    65  ABSL_FLAG(bool, file_vnames, true,
    66            "Find file vnames by matching file content.");
    67  ABSL_FLAG(bool, allow_missing_file_vnames, false,
    68            "If file_vnames is set, treat missing file vnames as non-fatal.");
    69  ABSL_FLAG(bool, verbose, false,
    70            "If verbose is set, more logging will be emitted.");
    71  ABSL_FLAG(bool, use_fast_solver, true, "Use the fast solver.");
    72  ABSL_FLAG(bool, print_timing_information, false,
    73            "Print timing information for profiling.");
    74  ABSL_FLAG(std::string, default_file_corpus, "",
    75            "Use this corpus for anchor goals for file nodes without a corpus "
    76            "set. In the future, if this flag is left empty, these file nodes "
    77            "will raise an error.");
    78  
    79  namespace {
    80  // The fast solver needs extra stack space for modest programs.
    81  constexpr rlim_t kSolverStack = 64L * 1024L * 1024L;
    82  }  // namespace
    83  
    84  int main(int argc, char** argv) {
    85    GOOGLE_PROTOBUF_VERIFY_VERSION;
    86    kythe::InitializeProgram(argv[0]);
    87    absl::SetProgramUsageMessage(R"(Verification tool for Kythe databases.
    88  Reads Kythe facts from standard input and checks them against one or more rule
    89  files. See https://kythe.io/docs/kythe-verifier.html for more details on
    90  invocation and rule syntax.
    91  
    92  Example:
    93    ${INDEXER_BIN} -i $1 | ${VERIFIER_BIN} --show_protos --show_goals $1
    94    cat foo.entries | ${VERIFIER_BIN} goals1.cc goals2.cc
    95    cat foo.entries | ${VERIFIER_BIN} --use_file_nodes
    96  )");
    97    auto start_time = absl::Now();
    98    std::vector<char*> remain = absl::ParseCommandLine(argc, argv);
    99  
   100    kythe::verifier::Verifier v;
   101    if (absl::GetFlag(FLAGS_goal_regex).empty()) {
   102      v.SetGoalCommentPrefix(absl::GetFlag(FLAGS_goal_prefix));
   103    } else {
   104      std::string error;
   105      if (!v.SetGoalCommentRegex(absl::GetFlag(FLAGS_goal_regex), &error)) {
   106        absl::FPrintF(stderr, "While parsing goal regex: %s\n", error);
   107        return 1;
   108      }
   109    }
   110  
   111    if (absl::GetFlag(FLAGS_ignore_dups)) {
   112      v.IgnoreDuplicateFacts();
   113    }
   114  
   115    if (absl::GetFlag(FLAGS_ignore_code_conflicts)) {
   116      v.IgnoreCodeConflicts();
   117    }
   118  
   119    if (absl::GetFlag(FLAGS_annotated_graphviz)) {
   120      v.SaveEVarAssignments();
   121    }
   122  
   123    if (absl::GetFlag(FLAGS_minimal_graphviz)) {
   124      v.SaveEVarAssignments();
   125      v.ElideUnlabeled();
   126    }
   127  
   128    if (absl::GetFlag(FLAGS_use_file_nodes)) {
   129      v.UseFileNodes();
   130    }
   131  
   132    if (absl::GetFlag(FLAGS_convert_marked_source)) {
   133      v.ConvertMarkedSource();
   134    }
   135  
   136    if (absl::GetFlag(FLAGS_show_anchors)) {
   137      v.ShowAnchors();
   138    }
   139  
   140    if (absl::GetFlag(FLAGS_show_vnames)) {
   141      v.ShowLabeledVnames();
   142    }
   143  
   144    if (!absl::GetFlag(FLAGS_file_vnames)) {
   145      v.IgnoreFileVnames();
   146    }
   147  
   148    if (absl::GetFlag(FLAGS_allow_missing_file_vnames)) {
   149      if (!absl::GetFlag(FLAGS_file_vnames)) {
   150        fprintf(stderr, "--allow_missing_file_vnames needs --file_vnames\n");
   151        return 1;
   152      }
   153      v.AllowMissingFileVNames();
   154    }
   155  
   156    if (absl::GetFlag(FLAGS_verbose)) {
   157      v.Verbose();
   158    }
   159  
   160    if (absl::GetFlag(FLAGS_show_fact_prefix)) {
   161      v.ShowFactPrefix();
   162    }
   163  
   164    if (!absl::GetFlag(FLAGS_default_file_corpus).empty()) {
   165      v.UseDefaultFileCorpus(absl::GetFlag(FLAGS_default_file_corpus));
   166    }
   167  
   168    v.UseFastSolver(absl::GetFlag(FLAGS_use_fast_solver));
   169  
   170    if (absl::GetFlag(FLAGS_use_fast_solver)) {
   171      struct rlimit rl;
   172      int r = getrlimit(RLIMIT_STACK, &rl);
   173      if (r != 0) {
   174        perror("failed getting solver stack size");
   175        return 1;
   176      }
   177      if (rl.rlim_cur < kSolverStack) {
   178        rl.rlim_cur = kSolverStack;
   179        r = setrlimit(RLIMIT_STACK, &rl);
   180        if (r != 0) {
   181          perror("failed increasing solver stack size");
   182          return 1;
   183        }
   184      }
   185    }
   186  
   187    std::string dbname = "database";
   188    size_t facts = 0;
   189    auto start_read_time = absl::Now();
   190    kythe::proto::Entry entry;
   191    uint32_t byte_size;
   192    google::protobuf::io::FileInputStream raw_input(STDIN_FILENO);
   193    for (;;) {
   194      google::protobuf::io::CodedInputStream coded_input(&raw_input);
   195      coded_input.SetTotalBytesLimit(INT_MAX);
   196      if (!coded_input.ReadVarint32(&byte_size)) {
   197        break;
   198      }
   199      auto limit = coded_input.PushLimit(byte_size);
   200      if (!entry.ParseFromCodedStream(&coded_input)) {
   201        absl::FPrintF(stderr, "Error reading around fact %zu\n", facts);
   202        return 1;
   203      }
   204      if (absl::GetFlag(FLAGS_show_protos)) {
   205        entry.PrintDebugString();
   206        putchar('\n');
   207      }
   208      if (!v.AssertSingleFact(&dbname, facts, entry)) {
   209        absl::FPrintF(stderr, "Error asserting fact %zu\n", facts);
   210        return 1;
   211      }
   212      ++facts;
   213    }
   214    auto done_read_time = absl::Now();
   215  
   216    if (!absl::GetFlag(FLAGS_use_fast_solver) && !v.PrepareDatabase()) {
   217      return 1;
   218    }
   219    auto done_db_time = absl::Now();
   220  
   221    if (!absl::GetFlag(FLAGS_graphviz)) {
   222      std::vector<std::string> rule_files(remain.begin() + 1, remain.end());
   223      if (rule_files.empty() && !absl::GetFlag(FLAGS_use_file_nodes)) {
   224        absl::FPrintF(stderr, "No rule files specified\n");
   225        return 1;
   226      }
   227  
   228      for (const auto& rule_file : rule_files) {
   229        if (rule_file.empty()) {
   230          continue;
   231        }
   232        if (!v.LoadInlineRuleFile(rule_file)) {
   233          absl::FPrintF(stderr, "Failed loading %s.\n", rule_file);
   234          return 2;
   235        }
   236      }
   237    }
   238  
   239    if (absl::GetFlag(FLAGS_check_for_singletons) && v.CheckForSingletonEVars()) {
   240      return 1;
   241    }
   242  
   243    if (absl::GetFlag(FLAGS_show_goals)) {
   244      v.ShowGoals();
   245    }
   246  
   247    int result = 0;
   248  
   249    auto pre_solve_time = absl::Now();
   250    if (!v.VerifyAllGoals()) {
   251      // Flush stdout in case any data is stuck in buffers, to avoid mangling a
   252      // joined output stream.
   253      fflush(stdout);
   254      absl::FPrintF(
   255          stderr, "Could not verify all goals. The furthest we reached was:\n  ");
   256      v.DumpErrorGoal(v.highest_group_reached(), v.highest_goal_reached());
   257      result = 1;
   258    }
   259    auto post_solve_time = absl::Now();
   260  
   261    if (absl::GetFlag(FLAGS_graphviz) ||
   262        absl::GetFlag(FLAGS_annotated_graphviz) ||
   263        absl::GetFlag(FLAGS_minimal_graphviz)) {
   264      v.DumpAsDot();
   265    }
   266  
   267    if (absl::GetFlag(FLAGS_print_timing_information)) {
   268      auto print_timer = [](absl::Duration duration, absl::string_view name) {
   269        absl::FPrintF(stderr, "%s took %ld msec\n", name,
   270                      absl::ToInt64Milliseconds(duration));
   271      };
   272      print_timer(start_read_time - start_time, "startup");
   273      print_timer(done_read_time - start_read_time, "read input stream");
   274      print_timer(done_db_time - done_read_time, "database preparation");
   275      print_timer(pre_solve_time - done_db_time, "other prep");
   276      print_timer(post_solve_time - pre_solve_time, "solve");
   277      print_timer(post_solve_time - start_time, "total elapsed");
   278    }
   279  
   280    return result;
   281  }