[16/17] abidw: Support the --abidiff option for Linux Kernel trees

Message ID 20240716145541.473065-17-dodji@redhat.com
State New
Headers
Series Support self comparison of vmlinux & modules using BTF/CTF |

Commit Message

Dodji Seketeli July 16, 2024, 2:55 p.m. UTC
  From: Dodji Seketeli <dodji@redhat.com>

It turned out abidw --abidiff (self-comparison) doesn't work when
analyzing a Linux Kernel tree.  In other words, abidw cannot
self-compare a Linux Kernel tree.  Embarrassing.  Fixed thus.

	* include/abg-corpus.h (is_corpus_group): Declare ...
	* src/abg-corpus.cc (is_corpus_group): ... new function.
	* src/abg-ctf-reader.cc (create_reader): Support --debug-abidiff
	when the package is configured with --enable-debug-self-comparison.
	* tools/abidw.cc (load_corpus_and_write_abixml): Factorize self
	comparison code out into ...
	(perform_self_comparison): ... this.
	(load_kernel_corpus_group_and_write_abixml): Use the new
	perform_self_comparison to perform self comparison when the
	--abidiff option is provided.  Also, support --debug-abidiff when
	the package is configured with --enable-debug-self-comparison.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 include/abg-corpus.h  |   2 +
 src/abg-corpus.cc     |  10 ++
 src/abg-ctf-reader.cc |   6 ++
 tools/abidw.cc        | 208 +++++++++++++++++++++++++-----------------
 4 files changed, 144 insertions(+), 82 deletions(-)
  

Patch

diff --git a/include/abg-corpus.h b/include/abg-corpus.h
index 37ff2879..cbadbdb2 100644
--- a/include/abg-corpus.h
+++ b/include/abg-corpus.h
@@ -444,6 +444,8 @@  public:
   operator==(const corpus_group&) const;
 }; // end class corpus_group
 
+corpus_group_sptr
+is_corpus_group(const corpus_sptr&);
 }// end namespace ir
 }//end namespace abigail
 #endif //__ABG_CORPUS_H__
diff --git a/src/abg-corpus.cc b/src/abg-corpus.cc
index 994307fb..e9ba92ae 100644
--- a/src/abg-corpus.cc
+++ b/src/abg-corpus.cc
@@ -2247,6 +2247,16 @@  bool
 corpus_group::recording_types_reachable_from_public_interface_supported()
 {return !get_public_types_pretty_representations()->empty();}
 
+/// Test if a @ref corpus is a @ref corpus_group.
+///
+/// @param corpus the corpus to consider.
+///
+/// @return the @ref corpus_group is @p corpus is a corpus group, or
+/// nil.
+corpus_group_sptr
+is_corpus_group(const corpus_sptr& corpus)
+{return std::dynamic_pointer_cast<corpus_group>(corpus);}
+
 // </corpus_group stuff>
 
 }// end namespace ir
diff --git a/src/abg-ctf-reader.cc b/src/abg-ctf-reader.cc
index c36323cf..6879c6e0 100644
--- a/src/abg-ctf-reader.cc
+++ b/src/abg-ctf-reader.cc
@@ -1729,6 +1729,12 @@  create_reader(const std::string& elf_path,
   reader_sptr result(new reader(elf_path,
 				debug_info_root_paths,
 				env));
+
+#ifdef WITH_DEBUG_SELF_COMPARISON
+    if (env.self_comparison_debug_is_on())
+      env.set_self_comparison_debug_input(result->corpus());
+#endif
+
   return result;
 }
 
diff --git a/tools/abidw.cc b/tools/abidw.cc
index 14f2078c..6bb1ef55 100644
--- a/tools/abidw.cc
+++ b/tools/abidw.cc
@@ -624,6 +624,121 @@  set_generic_options(abigail::elf_based_reader& rdr, options& opts)
   rdr.options().load_undefined_interfaces = opts.load_undefined_interfaces;
 }
 
+/// Given a corpus (or a corpus group), write it as ABIXML, read it
+/// back into another corpus and compare the resulting two corpora.
+///
+/// The result of the comparison should be the empty set.
+///
+/// @param write_ctxt the write context to use for writing the corpus
+/// to ABIXML.
+///
+/// @param corp the input corpus (or corpus group) to serialize to
+/// ABIXML.
+///
+/// @param env the environment used for computing.
+///
+/// @param t the timer to be used for the logs.
+///
+/// @param opts the options passed to the main program.
+///
+/// @param argv the vector of arguments of the main program.
+///
+/// @return 0 if the self comparison did yield the empty set, 1
+/// otherwise.  If the comparison does (wronly) yield a result, that
+/// result if emitted on std::cerr.
+static int
+perform_self_comparison(const write_context_sptr& write_ctxt,
+			const corpus_sptr& corp,
+			environment& env,
+			timer& t,
+			options& opts,
+			char* argv[])
+{
+  // Save the abi in abixml format in a temporary file, read
+  // it back, and compare the ABI of what we've read back
+  // against the ABI of the input ELF file.
+  temp_file_sptr tmp_file = temp_file::create();
+  set_ostream(*write_ctxt, tmp_file->get_stream());
+  corpus_group_sptr corp_group = is_corpus_group(corp);
+
+  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
+  if (opts.debug_abidiff)
+    {
+      opts.type_id_file_path = tmp_file->get_path() + string(".typeid");
+      write_canonical_type_ids(*write_ctxt, opts.type_id_file_path);
+    }
+#endif
+  fe_iface_sptr rdr = abixml::create_reader(tmp_file->get_path(), env);
+
+#ifdef WITH_DEBUG_SELF_COMPARISON
+  if (opts.debug_abidiff
+      && !opts.type_id_file_path.empty())
+    load_canonical_type_ids(*rdr, opts.type_id_file_path);
+#endif
+
+  t.start();
+  fe_iface::status 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)
+      << "Read corpus in: " << t << "\n";
+
+#ifdef WITH_DEBUG_SELF_COMPARISON
+  if (opts.debug_abidiff
+      && !opts.type_id_file_path.empty())
+    remove(opts.type_id_file_path.c_str());
+#endif
+
+  if (!corp2 && !corp_group2)
+    {
+      emit_prefix(argv[0], cerr)
+	<< "Could not read temporary XML representation of "
+	"elf file back\n";
+      return 1;
+    }
+
+  diff_context_sptr ctxt(new diff_context);
+  set_diff_context(ctxt);
+  ctxt->show_locs(opts.show_locs);
+  t.start();
+  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)
+      << "computed diff in: " << t << "\n";
+
+  bool has_error = diff->has_changes();
+  if (has_error)
+    {
+      t.start();
+      diff->report(cerr);
+      t.stop();
+      if (opts.do_log)
+	emit_prefix(argv[0], cerr)
+	  << "emitted report in: " << t << "\n";
+      return 1;
+    }
+  return 0;
+}
+
 /// Load an ABI @ref corpus (the internal representation of the ABI of
 /// a binary) and write it out as an abixml.
 ///
@@ -841,88 +956,9 @@  load_corpus_and_write_abixml(char* argv[],
       << t << "\n";
 
   if (opts.abidiff)
-    {
-      // Save the abi in abixml format in a temporary file, read
-      // it back, and compare the ABI of what we've read back
-      // against the ABI of the input ELF file.
-      temp_file_sptr tmp_file = temp_file::create();
-      set_ostream(*write_ctxt, tmp_file->get_stream());
-      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
-      if (opts.debug_abidiff)
-        {
-          opts.type_id_file_path = tmp_file->get_path() + string(".typeid");
-          write_canonical_type_ids(*write_ctxt, opts.type_id_file_path);
-        }
-#endif
-      fe_iface_sptr rdr = abixml::create_reader(tmp_file->get_path(), env);
-
-#ifdef WITH_DEBUG_SELF_COMPARISON
-      if (opts.debug_abidiff
-          && !opts.type_id_file_path.empty())
-        load_canonical_type_ids(*rdr, opts.type_id_file_path);
-#endif
-      t.start();
-      fe_iface::status 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)
-          << "Read corpus in: " << t << "\n";
-
-      if (!corp2)
-        {
-          emit_prefix(argv[0], cerr)
-            << "Could not read temporary XML representation of "
-            "elf file back\n";
-          return 1;
-        }
-
-      diff_context_sptr ctxt(new diff_context);
-      set_diff_context(ctxt);
-      ctxt->show_locs(opts.show_locs);
-      t.start();
-      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)
-          << "computed diff in: " << t << "\n";
-
-      bool has_error = diff->has_changes();
-      if (has_error)
-        {
-          t.start();
-          diff->report(cerr);
-          t.stop();
-          if (opts.do_log)
-            emit_prefix(argv[0], cerr)
-              << "emitted report in: " << t << "\n";
-          return 1;
-        }
-      return 0;
-    }
-
-#ifdef WITH_DEBUG_SELF_COMPARISON
-  if (opts.debug_abidiff
-      && !opts.type_id_file_path.empty())
-    remove(opts.type_id_file_path.c_str());
-#endif
+    return perform_self_comparison(write_ctxt,
+				   corp_group ? corp_group : corp,
+				   env, t, opts, argv);
 
   if (opts.noout)
     return 0;
@@ -992,6 +1028,11 @@  load_kernel_corpus_group_and_write_abixml(char* argv[],
     if (!abigail::tools_utils::check_file(opts.vmlinux, cerr, argv[0]))
       return 1;
 
+#ifdef WITH_DEBUG_SELF_COMPARISON
+  if (opts.debug_abidiff)
+    env.self_comparison_debug_is_on(true);
+#endif
+
   timer t, global_timer;
   suppressions_type supprs;
 
@@ -1038,6 +1079,9 @@  load_kernel_corpus_group_and_write_abixml(char* argv[],
 	  = xml_writer::create_write_context(env, cout);
       set_common_options(*ctxt, opts);
 
+      if (opts.abidiff)
+	return perform_self_comparison(ctxt, group, env, t, opts, argv);
+
       if (!opts.out_file_path.empty())
 	{
 	  ofstream of(opts.out_file_path.c_str(), std::ios_base::trunc);