diff options
Diffstat (limited to 'interceptor/analysis.cc')
-rw-r--r-- | interceptor/analysis.cc | 221 |
1 files changed, 0 insertions, 221 deletions
diff --git a/interceptor/analysis.cc b/interceptor/analysis.cc deleted file mode 100644 index 03292af..0000000 --- a/interceptor/analysis.cc +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ - -#include <getopt.h> -#include <google/protobuf/text_format.h> -#include <google/protobuf/util/json_util.h> -#include <sysexits.h> -#include <cstdlib> -#include <cstring> -#include <filesystem> -#include <fstream> -#include <iostream> -#include <iterator> -#include <unordered_set> - -#include "log.pb.h" - -namespace fs = std::filesystem; - -enum class OutputFormat { TEXT, COMPDB }; - -struct Options { - fs::path command_log; - OutputFormat output_format = OutputFormat::TEXT; - fs::path output; -}; - -static Options parse_arguments(int argc, char* argv[]) { - Options result; - - const static option opts[] = { - {"command-log", required_argument, nullptr, 'l'}, - {"output-type", required_argument, nullptr, 't'}, - {"output", required_argument, nullptr, 'o'}, - {nullptr, 0, nullptr, 0}, - }; - const auto usage = [&]() { - std::cerr << "usage: " << argv[0] << '\n' - << " -l|--command-log filename\n" - << " -o|--output filename\n" - << " [-t|--output-type (text|compdb)]\n"; - exit(EX_USAGE); - }; - while (true) { - int ix; - int c = getopt_long(argc, argv, "-l:t:o:", opts, &ix); - if (c == -1) { - break; - } - switch (c) { - case 'l': - result.command_log = fs::absolute(optarg); - break; - case 't': - if (strcmp(optarg, "text") == 0) { - result.output_format = OutputFormat::TEXT; - } - if (strcmp(optarg, "compdb") == 0) { - result.output_format = OutputFormat::COMPDB; - } else { - usage(); - } - break; - case 'o': - result.output = fs::absolute(optarg); - break; - default: - usage(); - } - } - - if (result.command_log.empty() || result.output.empty()) { - usage(); - } - - if (!fs::exists(result.command_log)) { - std::cerr << "No such file: " << result.command_log << "\n"; - } - - return result; -} - -interceptor::Log read_log(const fs::path& log_file) { - interceptor::Log result; - std::ifstream input(log_file); - if (!input) { - std::cerr << "Could not open input file for reading.\n"; - exit(EX_NOINPUT); - } - result.ParseFromIstream(&input); - return result; -} - -void text_to_file(const interceptor::Log& log, const fs::path& output) { - std::string content; - google::protobuf::TextFormat::PrintToString(log, &content); - std::ofstream os(output); - if (!os) { - std::cerr << "Could not open output file for writing.\n"; - exit(EX_CANTCREAT); - } - os << content; - if (!os.flush()) { - std::cerr << "Failed to write to output file.\n"; - exit(EX_CANTCREAT); - } -} - -void compdb_to_file(const interceptor::Log& log, const fs::path& output) { - static const std::unordered_set<std::string_view> kCompileExtensions = { - ".c", ".cc", ".cpp", ".cxx", ".S", - }; - static const std::unordered_set<std::string_view> kCompilers = { - "clang", - "clang++", - "gcc", - "g++", - }; - - interceptor::CompilationDatabase compdb; - - for (const auto& command : log.commands()) { - if (command.arguments().empty()) { - continue; - } - - // skip anything that is not a compiler invocation - if (!kCompilers.count(fs::path(command.arguments(0)).filename().native())) { - continue; - } - - // determine if we have a uniquely identifyable output - const std::string single_output = [&]() { - std::vector<std::string> outputs; - for (const auto& output : command.outputs()) { - // skip .d files. They are conventionally used for make dependency files - if (fs::path(output).extension() != ".d") { - outputs.push_back(output); - } - } - return (outputs.size() == 1) ? outputs[0] : ""; - }(); - - // skip preprocessor invocations - if (std::find(command.arguments().cbegin(), command.arguments().cend(), "-E") != - command.arguments().cend()) { - continue; - } - - // now iterate over all inputs, emitting an entry for each source file - for (const auto& input : command.inputs()) { - // skip anything that does not look like a source file (object files, - // force included headers, etc.) - if (!kCompileExtensions.count(fs::path(input).extension().native())) { - continue; - } - - // ok, now we have a new command - auto& compile_command = *compdb.add_commands(); - - compile_command.set_directory(fs::path(log.root_directory()) / command.current_directory()); - compile_command.set_file(input); - if (!single_output.empty()) { - compile_command.set_output(single_output); - } - *compile_command.mutable_arguments() = {command.arguments().cbegin(), - command.arguments().cend()}; - } - } - - std::ofstream out(output); - - if (!compdb.commands_size()) { - out << "[]\n"; - return; - } - - std::string out_str; - auto options = google::protobuf::util::JsonPrintOptions{}; - options.add_whitespace = true; - google::protobuf::util::MessageToJsonString(compdb, &out_str, options); - - // this would emit {"command":[yadayada]}, but we want only [yadayada] - // the additional characters come from options.add_whitespace - // - // TODO: make this better, but as of now there is not much we can do as - // util::MessageToJsonString() takes a message and that is always represented - // as a dictionary, while the top level structure of compile_command.json is - // an array. So, we have to chop of the leading and trailing characters to - // find the contained array. - const auto left_offset = out_str.find('['); - const auto length = out_str.rfind(']') - left_offset + 1; - out << std::string_view(out_str).substr(left_offset, length); -} - -int main(int argc, char* argv[]) { - const auto options = parse_arguments(argc, argv); - const auto log = read_log(options.command_log); - - switch (options.output_format) { - case OutputFormat::TEXT: - text_to_file(log, options.output); - break; - case OutputFormat::COMPDB: - compdb_to_file(log, options.output); - break; - } -} |