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