xref: /aosp_15_r20/external/stg/stg.cc (revision 9e3b08ae94a55201065475453d799e8b1378bea6)
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