• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- mode: C++ -*-
3 //
4 // Copyright 2022-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: Giuliano Procida
19 
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <sys/stat.h>
23 
24 #include <cstring>
25 #include <iostream>
26 #include <map>
27 #include <memory>
28 #include <string>
29 #include <utility>
30 #include <vector>
31 
32 #include <google/protobuf/io/zero_copy_stream_impl.h>
33 #include "deduplication.h"
34 #include "error.h"
35 #include "file_descriptor.h"
36 #include "filter.h"
37 #include "fingerprint.h"
38 #include "graph.h"
39 #include "input.h"
40 #include "proto_writer.h"
41 #include "reader_options.h"
42 #include "runtime.h"
43 #include "type_resolution.h"
44 #include "unification.h"
45 
46 namespace stg {
47 namespace {
48 
49 struct GetInterface {
operator ()stg::__anon43546ae60111::GetInterface50   Interface& operator()(Interface& x) const {
51     return x;
52   }
53 
54   template <typename Node>
operator ()stg::__anon43546ae60111::GetInterface55   Interface& operator()(Node&) const {
56     Die() << "expected an Interface root node";
57   }
58 };
59 
Merge(Runtime & runtime,Graph & graph,const std::vector<Id> & roots)60 Id Merge(Runtime& runtime, Graph& graph, const std::vector<Id>& roots) {
61   bool failed = false;
62   // this rewrites the graph on destruction
63   Unification unification(runtime, graph, Id(0), graph.Limit());
64   std::map<std::string, Id> symbols;
65   std::map<std::string, Id> types;
66   const GetInterface get;
67   for (auto root : roots) {
68     const auto& interface = graph.Apply(get, root);
69     for (const auto& x : interface.symbols) {
70       if (!symbols.insert(x).second) {
71         Warn() << "duplicate symbol during merge: " << x.first;
72         failed = true;
73       }
74     }
75     // TODO: test type roots merge
76     for (const auto& x : interface.types) {
77       const auto [it, inserted] = types.insert(x);
78       if (!inserted && !unification.Unify(x.second, it->second)) {
79         Warn() << "type conflict during merge: " << x.first;
80         failed = true;
81       }
82     }
83     graph.Remove(root);
84   }
85   if (failed) {
86     Die() << "merge failed";
87   }
88   return graph.Add<Interface>(symbols, types);
89 }
90 
FilterSymbols(Graph & graph,Id root,const Filter & filter)91 void FilterSymbols(Graph& graph, Id root, const Filter& filter) {
92   std::map<std::string, Id> symbols;
93   GetInterface get;
94   auto& interface = graph.Apply(get, root);
95   for (const auto& x : interface.symbols) {
96     if (filter(x.first)) {
97       symbols.insert(x);
98     }
99   }
100   std::swap(interface.symbols, symbols);
101 }
102 
Write(Runtime & runtime,const Graph & graph,Id root,const char * output,bool annotate)103 void Write(Runtime& runtime, const Graph& graph, Id root, const char* output,
104            bool annotate) {
105   const FileDescriptor output_fd(
106       output, O_CREAT | O_WRONLY | O_TRUNC,
107       S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
108   google::protobuf::io::FileOutputStream os(output_fd.Value());
109   {
110     const Time x(runtime, "write");
111     proto::Writer writer(graph);
112     writer.Write(root, os, annotate);
113     Check(os.Flush()) << "error writing to '" << output
114                       << "': " << os.GetErrno();
115   }
116 }
117 
118 }  // namespace
119 }  // namespace stg
120 
main(int argc,char * argv[])121 int main(int argc, char* argv[]) {
122   // Process arguments.
123   bool opt_metrics = false;
124   bool opt_keep_duplicates = false;
125   std::unique_ptr<stg::Filter> opt_file_filter;
126   std::unique_ptr<stg::Filter> opt_symbol_filter;
127   stg::ReadOptions opt_read_options;
128   stg::InputFormat opt_input_format = stg::InputFormat::ABI;
129   std::vector<std::pair<stg::InputFormat, const char*>> inputs;
130   std::vector<const char*> outputs;
131   bool opt_annotate = false;
132   static option opts[] = {
133       {"metrics",         no_argument,       nullptr, 'm'},
134       {"keep-duplicates", no_argument,       nullptr, 'd'},
135       {"types",           no_argument,       nullptr, 't'},
136       {"files",           required_argument, nullptr, 'F'},
137       {"file-filter",     required_argument, nullptr, 'F'},
138       {"symbols",         required_argument, nullptr, 'S'},
139       {"symbol-filter",   required_argument, nullptr, 'S'},
140       {"abi",             no_argument,       nullptr, 'a'},
141       {"btf",             no_argument,       nullptr, 'b'},
142       {"elf",             no_argument,       nullptr, 'e'},
143       {"stg",             no_argument,       nullptr, 's'},
144       {"output",          required_argument, nullptr, 'o'},
145       {"annotate",        no_argument,       nullptr, 'A'},
146       {nullptr,           0,                 nullptr, 0  },
147   };
148   auto usage = [&]() {
149     std::cerr << "usage: " << argv[0] << '\n'
150               << "  [-m|--metrics]\n"
151               << "  [-d|--keep-duplicates]\n"
152               << "  [-t|--types]\n"
153               << "  [-F|--files|--file-filter <filter>]\n"
154               << "  [-S|--symbols|--symbol-filter <filter>]\n"
155               << "  [-a|--abi|-b|--btf|-e|--elf|-s|--stg] [file] ...\n"
156               << "  [{-o|--output} {filename|-}] ...\n"
157               << "  [-A|--annotate]\n"
158               << "implicit defaults: --abi\n";
159     stg::FilterUsage(std::cerr);
160     return 1;
161   };
162   while (true) {
163     int ix;
164     const int c = getopt_long(argc, argv, "-mdtS:F:abeso:A", opts, &ix);
165     if (c == -1) {
166       break;
167     }
168     const char* argument = optarg;
169     switch (c) {
170       case 'm':
171         opt_metrics = true;
172         break;
173       case 'd':
174         opt_keep_duplicates = true;
175         break;
176       case 't':
177         opt_read_options.Set(stg::ReadOptions::TYPE_ROOTS);
178         break;
179       case 'F':
180         opt_file_filter = stg::MakeFilter(argument);
181         break;
182       case 'S':
183         opt_symbol_filter = stg::MakeFilter(argument);
184         break;
185       case 'a':
186         opt_input_format = stg::InputFormat::ABI;
187         break;
188       case 'b':
189         opt_input_format = stg::InputFormat::BTF;
190         break;
191       case 'e':
192         opt_input_format = stg::InputFormat::ELF;
193         break;
194       case 's':
195         opt_input_format = stg::InputFormat::STG;
196         break;
197       case 1:
198         inputs.emplace_back(opt_input_format, argument);
199         break;
200       case 'o':
201         if (strcmp(argument, "-") == 0) {
202           argument = "/dev/stdout";
203         }
204         outputs.push_back(argument);
205         break;
206       case 'A':
207         opt_annotate = true;
208         break;
209       default:
210         return usage();
211     }
212   }
213 
214   try {
215     stg::Graph graph;
216     stg::Runtime runtime(std::cerr, opt_metrics);
217     std::vector<stg::Id> roots;
218     roots.reserve(inputs.size());
219     for (auto& [format, input] : inputs) {
220       roots.push_back(stg::Read(runtime, graph, format, input, opt_read_options,
221                                 opt_file_filter));
222     }
223     stg::Id root =
224         roots.size() == 1 ? roots[0] : stg::Merge(runtime, graph, roots);
225     if (opt_symbol_filter) {
226       stg::FilterSymbols(graph, root, *opt_symbol_filter);
227     }
228     if (!opt_keep_duplicates) {
229       {
230         stg::Unification unification(runtime, graph, stg::Id(0), graph.Limit());
231         stg::ResolveTypes(runtime, graph, unification, {root});
232         root = unification.Find(root);
233       }
234       const auto hashes = stg::Fingerprint(runtime, graph, root);
235       root = stg::Deduplicate(runtime, graph, root, hashes);
236     }
237     for (auto output : outputs) {
238       stg::Write(runtime, graph, root, output, opt_annotate);
239     }
240     return 0;
241   } catch (const stg::Exception& e) {
242     std::cerr << e.what();
243     return 1;
244   }
245 }
246