• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- mode: C++ -*-
3 //
4 // Copyright 2020-2023 Google LLC
5 //
6 // Licensed under the Apache License v2.0 with LLVM Exceptions (the
7 // "License"); you may not use this file except in compliance with the
8 // License.  You may obtain a copy of the License at
9 //
10 //     https://llvm.org/LICENSE.txt
11 //
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 //
18 // Author: Maria Teguiani
19 // Author: Giuliano Procida
20 // Author: Siddharth Nayyar
21 
22 #include <getopt.h>
23 
24 #include <cstddef>
25 #include <cstring>
26 #include <fstream>
27 #include <iostream>
28 #include <optional>
29 #include <ostream>
30 #include <unordered_set>
31 #include <utility>
32 #include <vector>
33 
34 #include "comparison.h"
35 #include "equality.h"
36 #include "error.h"
37 #include "fidelity.h"
38 #include "graph.h"
39 #include "input.h"
40 #include "naming.h"
41 #include "reader_options.h"
42 #include "reporting.h"
43 #include "runtime.h"
44 
45 namespace {
46 
47 const int kAbiChange = 4;
48 const int kFidelityChange = 8;
49 const size_t kMaxCrcOnlyChanges = 3;
50 
51 using Inputs = std::vector<std::pair<stg::InputFormat, const char*>>;
52 using Outputs =
53     std::vector<std::pair<stg::reporting::OutputFormat, const char*>>;
54 
Read(stg::Runtime & runtime,const Inputs & inputs,stg::Graph & graph,stg::ReadOptions options)55 std::vector<stg::Id> Read(stg::Runtime& runtime, const Inputs& inputs,
56                           stg::Graph& graph, stg::ReadOptions options) {
57   std::vector<stg::Id> roots;
58   for (const auto& [format, filename] : inputs) {
59     roots.push_back(stg::Read(runtime, graph, format, filename, options,
60                               nullptr));
61   }
62   return roots;
63 }
64 
RunFidelity(const char * filename,const stg::Graph & graph,const std::vector<stg::Id> & roots)65 int RunFidelity(const char* filename, const stg::Graph& graph,
66                 const std::vector<stg::Id>& roots) {
67   std::ofstream output(filename);
68   const auto fidelity_diff =
69       stg::GetFidelityTransitions(graph, roots[0], roots[1]);
70   const bool diffs_reported =
71       stg::reporting::FidelityDiff(fidelity_diff, output);
72   output << std::flush;
73   if (!output) {
74     stg::Die() << "error writing to " << '\'' << filename << '\'';
75   }
76   return diffs_reported ? kFidelityChange : 0;
77 }
78 
RunExact(stg::Runtime & runtime,const Inputs & inputs,stg::ReadOptions options)79 int RunExact(stg::Runtime& runtime, const Inputs& inputs,
80              stg::ReadOptions options) {
81   stg::Graph graph;
82   const auto roots = Read(runtime, inputs, graph, options);
83 
84   struct PairCache {
85     std::optional<bool> Query(const stg::Pair& comparison) const {
86       return equalities.find(comparison) != equalities.end()
87           ? std::make_optional(true)
88           : std::nullopt;
89     }
90     void AllSame(const std::vector<stg::Pair>& comparisons) {
91       for (const auto& comparison : comparisons) {
92         equalities.insert(comparison);
93       }
94     }
95     void AllDifferent(const std::vector<stg::Pair>&) {}
96     std::unordered_set<stg::Pair> equalities;
97   };
98 
99   const stg::Time compute(runtime, "equality check");
100   PairCache equalities;
101   return stg::Equals<PairCache>(graph, equalities)(roots[0], roots[1])
102              ? 0
103              : kAbiChange;
104 }
105 
Run(stg::Runtime & runtime,const Inputs & inputs,const Outputs & outputs,stg::Ignore ignore,stg::ReadOptions options,std::optional<const char * > fidelity)106 int Run(stg::Runtime& runtime, const Inputs& inputs, const Outputs& outputs,
107         stg::Ignore ignore, stg::ReadOptions options,
108         std::optional<const char*> fidelity) {
109   // Read inputs.
110   stg::Graph graph;
111   const auto roots = Read(runtime, inputs, graph, options);
112 
113   // Compute differences.
114   stg::Compare compare{runtime, graph, ignore};
115   std::pair<bool, std::optional<stg::Comparison>> result;
116   {
117     const stg::Time compute(runtime, "compute diffs");
118     result = compare(roots[0], roots[1]);
119   }
120   stg::Check(compare.scc.Empty()) << "internal error: SCC state broken";
121   const auto& [equals, comparison] = result;
122   int status = equals ? 0 : kAbiChange;
123 
124   // Write reports.
125   stg::NameCache names;
126   for (const auto& [format, filename] : outputs) {
127     std::ofstream output(filename);
128     if (comparison) {
129       const stg::Time report(runtime, "report diffs");
130       const stg::reporting::Options options{format, kMaxCrcOnlyChanges};
131       const stg::reporting::Reporting reporting{graph, compare.outcomes,
132         options, names};
133       Report(reporting, *comparison, output);
134       output << std::flush;
135     }
136     if (!output) {
137       stg::Die() << "error writing to " << '\'' << filename << '\'';
138     }
139   }
140 
141   // Compute fidelity diff if requested.
142   if (fidelity) {
143     const stg::Time report(runtime, "fidelity");
144     status |= RunFidelity(*fidelity, graph, roots);
145   }
146 
147   return status;
148 }
149 
150 }  // namespace
151 
main(int argc,char * argv[])152 int main(int argc, char* argv[]) {
153   // Process arguments.
154   bool opt_metrics = false;
155   bool opt_exact = false;
156   stg::ReadOptions opt_read_options;
157   std::optional<const char*> opt_fidelity = std::nullopt;
158   stg::Ignore opt_ignore;
159   stg::InputFormat opt_input_format = stg::InputFormat::ABI;
160   stg::reporting::OutputFormat opt_output_format =
161       stg::reporting::OutputFormat::PLAIN;
162   Inputs inputs;
163   Outputs outputs;
164   static option opts[] = {
165       {"metrics",        no_argument,       nullptr, 'm'},
166       {"abi",            no_argument,       nullptr, 'a'},
167       {"btf",            no_argument,       nullptr, 'b'},
168       {"elf",            no_argument,       nullptr, 'e'},
169       {"stg",            no_argument,       nullptr, 's'},
170       {"exact",          no_argument,       nullptr, 'x'},
171       {"types",          no_argument,       nullptr, 't'},
172       {"ignore",         required_argument, nullptr, 'i'},
173       {"format",         required_argument, nullptr, 'f'},
174       {"output",         required_argument, nullptr, 'o'},
175       {"fidelity",       required_argument, nullptr, 'F'},
176       {nullptr,          0,                 nullptr, 0  },
177   };
178   auto usage = [&]() {
179     std::cerr << "usage: " << argv[0] << '\n'
180               << "  [-m|--metrics]\n"
181               << "  [-a|--abi|-b|--btf|-e|--elf|-s|--stg] file1\n"
182               << "  [-a|--abi|-b|--btf|-e|--elf|-s|--stg] file2\n"
183               << "  [-x|--exact]\n"
184               << "  [-t|--types]\n"
185               << "  [{-i|--ignore} <ignore-option>] ...\n"
186               << "  [{-f|--format} <output-format>] ...\n"
187               << "  [{-o|--output} {filename|-}] ...\n"
188               << "  [{-F|--fidelity} {filename|-}]\n"
189               << "implicit defaults: --abi --format plain\n"
190               << "--exact (node equality) cannot be combined with --output\n"
191               << stg::reporting::OutputFormatUsage()
192               << stg::IgnoreUsage();
193     return 1;
194   };
195   while (true) {
196     int ix;
197     const int c = getopt_long(argc, argv, "-mabesxti:f:o:F:", opts, &ix);
198     if (c == -1) {
199       break;
200     }
201     const char* argument = optarg;
202     switch (c) {
203       case 'm':
204         opt_metrics = true;
205         break;
206       case 'a':
207         opt_input_format = stg::InputFormat::ABI;
208         break;
209       case 'b':
210         opt_input_format = stg::InputFormat::BTF;
211         break;
212       case 'e':
213         opt_input_format = stg::InputFormat::ELF;
214         break;
215       case 's':
216         opt_input_format = stg::InputFormat::STG;
217         break;
218       case 'x':
219         opt_exact = true;
220         break;
221       case 't':
222         opt_read_options.Set(stg::ReadOptions::TYPE_ROOTS);
223         break;
224       case 1:
225         inputs.emplace_back(opt_input_format, argument);
226         break;
227       case 'i':
228         if (const auto ignore = stg::ParseIgnore(argument)) {
229           opt_ignore.Set(ignore.value());
230         } else {
231           std::cerr << "unknown ignore option: " << argument << '\n'
232                     << stg::IgnoreUsage();
233           return 1;
234         }
235         break;
236       case 'f':
237         if (const auto format = stg::reporting::ParseOutputFormat(argument)) {
238           opt_output_format = format.value();
239         } else {
240           std::cerr << "unknown output format: " << argument << '\n'
241                     << stg::reporting::OutputFormatUsage();
242           return 1;
243         }
244         break;
245       case 'o':
246         if (strcmp(argument, "-") == 0) {
247           argument = "/dev/stdout";
248         }
249         outputs.emplace_back(opt_output_format, argument);
250         break;
251       case 'F':
252         if (strcmp(argument, "-") == 0) {
253           argument = "/dev/stdout";
254         }
255         opt_fidelity.emplace(argument);
256         break;
257       default:
258         return usage();
259     }
260   }
261   if (inputs.size() != 2 || opt_exact > outputs.empty()) {
262     return usage();
263   }
264 
265   try {
266     stg::Runtime runtime(std::cerr, opt_metrics);
267     return opt_exact ? RunExact(runtime, inputs, opt_read_options)
268                      : Run(runtime, inputs, outputs, opt_ignore,
269                            opt_read_options, opt_fidelity);
270   } catch (const stg::Exception& e) {
271     std::cerr << e.what();
272     return 1;
273   }
274 }
275