[3/4,applied] abidw: Add --{follow,list}-dependencies & --add-binaries support

Message ID 871qhk0wxh.fsf@redhat.com
State New
Headers
Series Binaries set comparison support |

Commit Message

Dodji Seketeli July 7, 2023, 11:43 a.m. UTC
  Hello,

This implements the --follow-dependencies , --list-dependencies,
--add-binaries <foo,bar,...> and --added-binaries-dir options for the
abidw command, as documented in the
README-ABIDIFF-BINARIES-SET-SUPPORT.md file.

	* tools/abidw.cc (options::{added_bins_dirs, added_bins,
	follow_dependencies, list_dependencies}): Add new data members.
	(options::options): Initialize follow_dependencies and
	list_dependencies.
	(display_usage): Add usage strings for --add-binaries,
	--follow-dependencies, --list-dependencies, --added-binaries-dir.
	(parse_command_line): Parse options --add-binaries,
	--follow-dependencies, --list-dependencies, --added-binaries-dir.
	(load_corpus_and_write_abixml): Implement the --list-dependencies,
	--follow-dependencies and --add-binaries sub-commands.
	* doc/manuals/abidw.rst: Document the --add-binaries,
	--follow-dependencies, --list-dependencies, --added-binaries-dir
	options.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Applied to master.
---
 doc/manuals/abidw.rst |  72 +++++++++++++++++++++++-
 tools/abidw.cc        | 125 ++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 190 insertions(+), 7 deletions(-)
  

Patch

diff --git a/doc/manuals/abidw.rst b/doc/manuals/abidw.rst
index 3cba434c..1e308e66 100644
--- a/doc/manuals/abidw.rst
+++ b/doc/manuals/abidw.rst
@@ -44,14 +44,49 @@  Options
 
     Display a short help about the command and exit.
 
-  * `--version | -v`
+  * ``--version | -v``
 
     Display the version of the program and exit.
 
-  * `--abixml-version`
+  * ``--abixml-version``
 
     Display the version of the ABIXML format emitted by this program and exit.
 
+  * ``--add-binaries`` <*bin1,bin2,...*>
+
+    For each of the comma-separated binaries given in argument to this
+    option, if the binary is found in the directory specified by the
+    `--added-binaries-dir` option, then load the ABI corpus of the
+    binary and add it to a set of ABI corpora (called a ABI Corpus
+    Group) made of the binary denoted by the Argument of
+    ``abidw``.  That corpus group is then serialized out.
+
+  * ``--follow-dependencies``
+
+    For each dependency of the input binary of ``abidw``, if it is
+    found in the directory specified by the ``--added-binaries-dir``
+    option, then construct an ABI corpus out of the dependency and add
+    it to a set of ABI corpora (called an ABI Corpus Group) along with
+    the ABI corpus of the input binary of the program.  The ABI Corpus
+    Group is then serialized out.
+
+  * ``--list-dependencies``
+
+    For each dependency of the input binary of``abidw``, if it's found
+    in the directory specified by the ``--added-binaries-dir`` option,
+    then the name of the dependency is printed out.
+
+  * ``--added-binaries-dir | --abd`` <*dir-path*>
+
+    This option is to be used in conjunction with the
+    ``--add-binaries``, the ``--follow-dependencies`` or the
+    ``--list-dependencies`` option.  Binaries listed as arguments of
+    the ``--add-binaries`` option or being dependencies of the input
+    binary in the case of the ``--follow-dependencies`` option and
+    found in the directory <*dir-path*> are going to be loaded as ABI
+    corpus and added to the set of ABI corpora (called an ABI corpus
+    group) built and serialized.
+
   * ``--debug-info-dir | -d`` <*dir-path*>
 
     In cases where the debug info for *path-to-elf-file* is in a
@@ -368,6 +403,39 @@  Options
     Emit verbose logs about the progress of miscellaneous internal
     things.
 
+Usage examples
+==============
+
+  1. Emitting an ``ABIXML`` representation of a binary: ::
+
+       $ abidw binary > binary.abi
+
+
+  2. Emitting an ``ABIXML`` representation of a set of binaries
+     specified on the command line: ::
+
+       $ abidw --added-binaries=bin1,bin2,bin3  \
+      	       --added-binaries-dir /some/where \
+	       binary > binaries.abi
+
+     Note that the binaries bin1, bin2 and bin3 are to be found in the
+     directory ``/some/where``.  A representation of the ABI of the
+     set of binaries ``binary, bin1, bin2`` and ``bin3`` called an
+     ``ABI corpus group`` is serialized in the file binaries.abi.
+
+  3. Emitting an ``ABIXML`` representation of a binary and its
+     dependencies: ::
+
+       $ abidw --follow-dependencies              \
+               --added-binaries-dir /some/where   \
+               binary > binary.abi
+
+     Note that only the dependencies that are found in the directory
+     ``/some/where`` are analysed.  Their ABIs, along with the ABI the
+     binary named ``binary`` are represented as an ABI corpus group
+     and serialized in the file ``binary.abi``, in the ABIXML format.
+
+
 Notes
 =====
 
diff --git a/tools/abidw.cc b/tools/abidw.cc
index eead7889..035dd9ce 100644
--- a/tools/abidw.cc
+++ b/tools/abidw.cc
@@ -22,6 +22,7 @@ 
 #include <memory>
 #include <string>
 #include <vector>
+#include <set>
 #include "abg-config.h"
 #include "abg-tools-utils.h"
 #include "abg-corpus.h"
@@ -43,7 +44,9 @@  using std::cout;
 using std::ostream;
 using std::ofstream;
 using std::vector;
+using std::set;
 using std::shared_ptr;
+using std::static_pointer_cast;
 using abg_compat::optional;
 using abigail::tools_utils::emit_prefix;
 using abigail::tools_utils::temp_file;
@@ -52,6 +55,9 @@  using abigail::tools_utils::check_file;
 using abigail::tools_utils::build_corpus_group_from_kernel_dist_under;
 using abigail::tools_utils::timer;
 using abigail::tools_utils::create_best_elf_based_reader;
+using abigail::tools_utils::stick_corpus_and_dependencies_into_corpus_group;
+using abigail::tools_utils::stick_corpus_and_binaries_into_corpus_group;
+using abigail::tools_utils::add_dependencies_into_corpus_group;
 using abigail::ir::environment_sptr;
 using abigail::ir::environment;
 using abigail::corpus;
@@ -71,6 +77,7 @@  using abigail::xml_writer::create_write_context;
 using abigail::xml_writer::type_id_style_kind;
 using abigail::xml_writer::write_context_sptr;
 using abigail::xml_writer::write_corpus;
+using abigail::xml_writer::write_corpus_group;
 using abigail::abixml::read_corpus_from_abixml_file;
 
 using namespace abigail;
@@ -84,6 +91,8 @@  struct options
   vector<char**>	prepared_di_root_paths;
   vector<string>	headers_dirs;
   vector<string>	header_files;
+  vector<string>	added_bins_dirs;
+  vector<string>	added_bins;
   string		vmlinux;
   vector<string>	suppression_paths;
   vector<string>	kabi_whitelist_paths;
@@ -104,6 +113,8 @@  struct options
   bool			corpus_group_for_linux;
   bool			show_stats;
   bool			noout;
+  bool			follow_dependencies;
+  bool			list_dependencies;
 #ifdef WITH_CTF
   bool			use_ctf;
 #endif
@@ -148,6 +159,8 @@  struct options
       corpus_group_for_linux(false),
       show_stats(),
       noout(),
+      follow_dependencies(),
+      list_dependencies(),
 #ifdef WITH_CTF
       use_ctf(false),
 #endif
@@ -229,6 +242,12 @@  display_usage(const string& prog_name, ostream& out)
     << "  --vmlinux <path>  the path to the vmlinux binary to consider to emit "
        "the ABI of the union of vmlinux and its modules\n"
     << "  --abidiff  compare the loaded ABI against itself\n"
+    << "  --add-binaries <bin1,bin2,...>  build a corpus group with "
+    "the added inaries\n"
+    << "  --follow-dependencies  build a corpus group with the dependencies\n"
+    << "  --list-dependencies  list the dependencies of a given binary\n"
+    << "  --added-binaries-dir|--abd <dir-of-deps>  where to look for dependencies "
+    "or added binaries\n"
 #ifdef WITH_DEBUG_SELF_COMPARISON
     << "  --debug-abidiff  debug the process of comparing the loaded ABI against itself\n"
 #endif
@@ -294,6 +313,15 @@  parse_command_line(int argc, char* argv[], options& opts)
 	  opts.headers_dirs.push_back(argv[j]);
 	  ++i;
 	}
+      else if (!strcmp(argv[i], "--added-binaries-dir")
+	       || !strcmp(argv[i], "--abd"))
+	{
+	  int j = i + 1;
+	  if (j >= argc)
+	    return false;
+	  opts.added_bins_dirs.push_back(argv[j]);
+	  ++i;
+	}
       else if (!strcmp(argv[i], "--header-file")
 	       || !strcmp(argv[i], "--hf"))
 	{
@@ -344,6 +372,28 @@  parse_command_line(int argc, char* argv[], options& opts)
 	}
       else if (!strcmp(argv[i], "--noout"))
 	opts.noout = true;
+      else if (!strcmp(argv[i], "--follow-dependencies"))
+	opts.follow_dependencies = true;
+      else if (!strcmp(argv[i], "--list-dependencies"))
+	opts.list_dependencies = true;
+      else if (!strncmp(argv[i], "--add-binaries=",
+			strlen("--add-binaries=")))
+	tools_utils::get_comma_separated_args_of_option(argv[i],
+							"--add-binaries=",
+							opts.added_bins);
+      else if (!strcmp(argv[i], "--add-binaries"))
+	{
+	  int j = i + 1;
+	  if (j >= argc)
+	    return false;
+
+	  string s = argv[j];
+	  if (s.find(','))
+	    tools_utils::split_string(s, ",", opts.added_bins);
+	  else
+	    opts.added_bins.push_back(s);
+	  ++i;
+	}
 #ifdef WITH_CTF
         else if (!strcmp(argv[i], "--ctf"))
           opts.use_ctf = true;
@@ -598,6 +648,7 @@  load_corpus_and_write_abixml(char* argv[],
 #endif
 
   corpus_sptr corp;
+  corpus_group_sptr corp_group;
   fe_iface::status s = fe_iface::STATUS_UNKNOWN;
   corpus::origin requested_fe_kind = corpus::DWARF_ORIGIN;
 #ifdef WITH_CTF
@@ -720,6 +771,50 @@  load_corpus_and_write_abixml(char* argv[],
       return 1;
     }
 
+  if (opts.list_dependencies)
+    {
+      // Show the dependencies of the corpus and display them.
+      set<string> dependencies;
+      if (tools_utils::get_dependencies(*corp, opts.added_bins_dirs,
+					dependencies))
+	{
+	  cout << "Dependencies of '" << corp->get_path()
+	       << "':\n\t";
+	  int n = 0;
+	  for (const auto& dep : dependencies)
+	    {
+	      if (n)
+		cout << ", ";
+	      cout << dep;
+	      ++n;
+	    }
+	  cout << "\n";
+	}
+    }
+
+  if (!opts.added_bins.empty())
+    corp_group =
+      stick_corpus_and_binaries_into_corpus_group(reader, corp,
+						  opts.added_bins,
+						  opts.added_bins_dirs);
+
+  if (opts.follow_dependencies)
+    {
+      // load the dependencies of the corpus and put them all into a
+      // corpus group.
+
+      // If a corpus_group already exists, use that one ...
+      if (!corp_group->is_empty())
+	add_dependencies_into_corpus_group(reader, *corp,
+					   opts.added_bins_dirs,
+					   *corp_group);
+      else
+	// .. otherwise, create a new corpus group.
+	corp_group =
+	  stick_corpus_and_dependencies_into_corpus_group(reader, corp,
+							  opts.added_bins_dirs);
+    }
+
   // Clear some resources to gain back some space.
   t.start();
   reader.reset();
@@ -744,7 +839,10 @@  load_corpus_and_write_abixml(char* argv[],
       // against the ABI of the input ELF file.
       temp_file_sptr tmp_file = temp_file::create();
       set_ostream(*write_ctxt, tmp_file->get_stream());
-      write_corpus(*write_ctxt, corp, 0);
+      if (corp_group)
+	write_corpus_group(*write_ctxt, corp_group, 0);
+      else
+	write_corpus(*write_ctxt, corp, 0);
       tmp_file->get_stream().flush();
 
 #ifdef WITH_DEBUG_SELF_COMPARISON
@@ -763,7 +861,14 @@  load_corpus_and_write_abixml(char* argv[],
 #endif
       t.start();
       fe_iface::status sts;
-      corpus_sptr corp2 = rdr->read_corpus(sts);
+      corpus_sptr corp2;
+      corpus_group_sptr corp_group2;
+
+      if (corp_group)
+	corp_group2 = abixml::read_corpus_group_from_input(*rdr);
+      else
+      corp2 = rdr->read_corpus(sts);
+
       t.stop();
       if (opts.do_log)
         emit_prefix(argv[0], cerr)
@@ -781,7 +886,11 @@  load_corpus_and_write_abixml(char* argv[],
       set_diff_context(ctxt);
       ctxt->show_locs(opts.show_locs);
       t.start();
-      corpus_diff_sptr diff = compute_diff(corp, corp2, ctxt);
+      corpus_diff_sptr diff =
+	corp_group2
+	? compute_diff(corp_group, corp_group2, ctxt)
+	: compute_diff(corp, corp2, ctxt);
+
       t.stop();
       if (opts.do_log)
         emit_prefix(argv[0], cerr)
@@ -822,7 +931,10 @@  load_corpus_and_write_abixml(char* argv[],
         }
       set_ostream(*write_ctxt, of);
       t.start();
-      write_corpus(*write_ctxt, corp, 0);
+      if (corp_group)
+	write_corpus_group(*write_ctxt, corp_group, 0);
+      else
+	write_corpus(*write_ctxt, corp, 0);
       t.stop();
       if (opts.do_log)
         emit_prefix(argv[0], cerr)
@@ -833,7 +945,10 @@  load_corpus_and_write_abixml(char* argv[],
   else
     {
       t.start();
-      exit_code = !write_corpus(*write_ctxt, corp, 0);
+      exit_code =
+	corp_group
+	? !write_corpus_group(*write_ctxt, corp_group, 0)
+	: !write_corpus(*write_ctxt, corp, 0);
       t.stop();
       if (opts.do_log)
         emit_prefix(argv[0], cerr)