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