blob: a34a417faa20b0b826017c5af7f18994901e486e [file]
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License v2.0 with LLVM Exceptions (the
// "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
// https://llvm.org/LICENSE.txt
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Giuliano Procida
#include <getopt.h>
#include <cstddef>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
#include <ostream>
#include <string>
#include <unordered_map>
#include <vector>
#include "abigail_reader.h"
#include "btf_reader.h"
#include "elf_reader.h"
#include "error.h"
#include "graph.h"
#include "proto_writer.h"
#include "timing.h"
namespace stg {
namespace {
Times times;
enum class InputFormat { ABI, BTF, ELF };
Id Read(Graph& graph, InputFormat format, const char* input, bool info) {
switch (format) {
case InputFormat::ABI: {
Time read(times, "read ABI");
return abixml::Read(graph, input, info);
}
case InputFormat::BTF: {
Time read(times, "read BTF");
return btf::ReadFile(graph, input, info);
}
case InputFormat::ELF: {
Time read(times, "read ELF");
return elf::Read(graph, input, info);
}
}
}
struct GetSymbols {
const Symbols& operator()(const Symbols& x) {
return x;
}
template <typename Node>
const Symbols& operator()(const Node&) {
Die() << "merge can only merge Symbols nodes";
}
};
Id Merge(Graph& graph, const std::vector<Id>& roots) {
if (roots.size() == 1) {
return roots[0];
}
std::map<std::string, Id> symbols;
GetSymbols get;
for (auto root : roots) {
for (const auto& x : graph.Apply<const Symbols&>(get, root).symbols) {
if (!symbols.insert(x).second) {
Die() << "merge failed with duplicate symbol: " << x.first;
}
}
}
return graph.Add<Symbols>(symbols);
}
void Write(const Graph& graph, Id root, const char* output,
bool stable, bool counters) {
// stable = generate stable external ids and use these for ordering
// counters = print stats about collisions
(void)stable;
(void)counters;
std::ofstream os(output);
{
Time x(times, "write");
proto::Writer writer(graph);
writer.Write(root, os);
os << std::flush;
}
if (!os) {
Die() << "error writing to " << '\'' << output << '\'';
}
}
} // namespace
} // namespace stg
int main(int argc, char* argv[]) {
// Process arguments.
bool opt_info = false;
bool opt_counters = false;
bool opt_times = false;
bool opt_unstable = false;
stg::InputFormat opt_input_format = stg::InputFormat::ABI;
std::vector<const char*> inputs;
std::vector<const char*> outputs;
static option opts[] = {
{"info", no_argument, nullptr, 'i'},
{"counters", no_argument, nullptr, 'c'},
{"times", no_argument, nullptr, 't'},
{"unstable", no_argument, nullptr, 'u'},
{"abi", no_argument, nullptr, 'a'},
{"btf", no_argument, nullptr, 'b'},
{"elf", no_argument, nullptr, 'e'},
{"output", required_argument, nullptr, 'o'},
{nullptr, 0, nullptr, 0 },
};
auto usage = [&]() {
std::cerr << "usage: " << argv[0] << '\n'
<< " [-i|--info]\n"
<< " [-c|--counters]\n"
<< " [-t|--times]\n"
<< " [-u|--unstable]\n"
<< " [-a|--abi|-b|--btf|-e|--elf] [file] ...\n"
<< " [{-o|--output} {filename|-}] ...\n"
<< " implicit defaults: --abi\n"
<< "\n";
return 1;
};
while (true) {
int ix;
int c = getopt_long(argc, argv, "-ictuabeo:", opts, &ix);
if (c == -1)
break;
const char* argument = optarg;
switch (c) {
case 'i':
opt_info = true;
break;
case 'c':
opt_counters = true;
break;
case 't':
opt_times = true;
break;
case 'u':
opt_unstable = true;
break;
case 'a':
opt_input_format = stg::InputFormat::ABI;
break;
case 'b':
opt_input_format = stg::InputFormat::BTF;
break;
case 'e':
opt_input_format = stg::InputFormat::ELF;
break;
case 1:
inputs.push_back(argument);
break;
case 'o':
if (strcmp(argument, "-") == 0)
argument = "/dev/stdout";
outputs.push_back(argument);
break;
default:
return usage();
}
}
try {
stg::Graph graph;
std::vector<stg::Id> roots;
roots.reserve(inputs.size());
for (auto input : inputs) {
roots.push_back(stg::Read(graph, opt_input_format, input, opt_info));
}
stg::Id root = stg::Merge(graph, roots);
for (auto output : outputs) {
stg::Write(graph, root, output, !opt_unstable, opt_counters);
}
if (opt_times) {
stg::Time::report(stg::times, std::cerr);
}
return 0;
} catch (const stg::Exception& e) {
std::cerr << e.what() << '\n';
return 1;
}
}