libabigail/tests/test-read-common.cc
Guillermo E. Martinez 8fd02e0a10 Use the CTF reader by default when applicable
At the moment, the tools abidw, abidiff, abipkgdiff and kmidiff all
use the DWARF front-end by default.  When the "--ctf" option is added
to the command line, they use the CTF front-end.

This patch changes that behaviour in the way described below.

If the "--ctf" command line option is passed to the tool and if the
binary to analyze contains CTF debug info, then the CTF front-end is
used.

If the binary contains ONLY CTF debug info, then the CTF front-end is
used, even if no "--ctf" option was provided.

In all the other cases, the DWARF front-end is used.

Of course, the CTF front-end is not used at all if the CTF
functionality hasn't been enabled at configure time.

This new behaviour is effective for user space and Linux kernel
binaries.

	* doc/manuals/abidiff.rst: Adjust.
	* doc/manuals/abidw.rst: Likewise.
	* doc/manuals/abipkgdiff.rst: Likewise.
	* doc/manuals/kmidiff.rst: Likewise.
	* include/abg-elf-based-reader.h (initialize): Add member function.
	* include/abg-elf-reader.h (has_{dwarf,ctf}_debug_info): Add predicate
	functions.
	* include/abg-tools-utils.h (create_best_elf_based_reader): Add arguments.
	* src/abg-ctf-reader.cc (process_ctf_typedef, process_ctf_base_type)
	(process_ctf_function_type, process_ctf_sou_members, process_ctf_forward_type)
	(process_ctf_struct_type, process_ctf_union_type, process_ctf_array_type)
	(process_ctf_qualified_type, process_ctf_pointer_type, process_ctf_enum_type):
	Remove arguments. Using getters to access required information instead.
	(reader::cur_tu_): Add data member.
	(initialize): Add arguments.
	(cur_transl_unit): Add {get,set)ter.
	(slurp_elf_info): Clear `STATUS_DEBUG_INFO_NOT_FOUND' if corpus is
	`LINUX_KERNEL_BINARY_ORIGIN'.
	(reader::lookup_type): Remove.
	(reader::build_type): New member function.
	* src/abg-elf-reader.cc (reader::reader): Locate ctf debug info
	from binary file.
	(reader::reader): Reset base `fe_iface' constructor.
	(reader::has_{dwarf,ctf}_debug_info): New definitions.
	(reader::read_corpus): Set `STATUS_DEBUG_INFO_NOT_FOUND' according
	to corpus::origin.
	* src/abg-tools-utils.cc (dir_contains_ctf_archive): Define new member.
	(file_has_ctf_debug_info): Looks for kernel ctf debug information archive.
	(maybe_load_vmlinux_{dwarf,ctf}_corpus): Remove.
	(load_vmlinux_corpus): Define function to load IR from kernel
	regardless of the corpus::origin.
	(build_corpus_group_from_kernel_dist_under): Use
	create_best_elf_based_reader to select the front-end.
	(create_best_elf_based_reader): Adjust to allow fallback behaviour
	for different front-ends.
	* tests/data/Makefile.am: Add tests.
	* tests/data/test-diff-pkg-ctf/dirpkg-3-report-2.txt: Adjust.
	* tests/data/test-read-ctf/test-fallback.abi: New test case.
	* tests/data/test-read-ctf/test-fallback.c: Likewise.
	* tests/data/test-read-ctf/test-fallback.o: Likewise.
	* tests/data/test-read-dwarf/test-fallback.abi: Likewise.
	* tests/data/test-read-dwarf/test-fallback.c: Likewise.
	* tests/data/test-read-dwarf/test-fallback.o: Likewise.
	* tests/test-diff-pkg.cc: Adjust.
	* tests/test-read-common.cc (test_task::run_abidw): Use the
	`options:option' field.
	* tests/test-read-common.h (InOutSpec): Add new member.
	* tests/test-read-ctf.cc (in_out_specs): Add option field to test
	suite.  Add new test case.
	* tests/test-read-dwarf.cc: Likewise.
	* tools/abidiff.cc (main): Use create_best_elf_based_reader.
	* tools/abidw.cc: Likewise.
	* tools/abipkgdiff.cc: Likewise.

Signed-off-by: Guillermo E. Martinez <guillermo.e.martinez@oracle.com>
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
2022-11-28 16:52:33 +01:00

270 lines
7.5 KiB
C++

// -*- Mode: C++ -*-
//
/// @file
///
/// This file implements the common functionality for the tests in
/// CTF and DWARF readers, it does the abstraction in the `act` test
/// stage.
#include <fstream>
#include <cstring>
#include "test-read-common.h"
using std::ofstream;
using std::cerr;
using std::dynamic_pointer_cast;
using abigail::tools_utils::emit_prefix;
using abigail::tests::get_build_dir;
using abigail::xml_writer::write_context_sptr;
using abigail::xml_writer::create_write_context;
using abigail::xml_writer::write_corpus;
namespace abigail
{
namespace tests
{
namespace read_common
{
/// Constructor.
///
/// Task to be executed for each test entry in @ref
/// abigail::tests::read_common::InOutSpec.
///
/// @param InOutSpec the set of tests.
///
/// @param a_out_abi_base the output base directory for abixml files.
///
/// @param a_in_elf_base the input base directory for object files.
///
/// @param a_in_elf_base the input base directory for expected
/// abixml files.
test_task::test_task(const InOutSpec &s,
string& a_out_abi_base,
string& a_in_elf_base,
string& a_in_abi_base)
: is_ok(true),
spec(s),
out_abi_base(a_out_abi_base),
in_elf_base(a_in_elf_base),
in_abi_base(a_in_abi_base)
{}
/// Serialize the abixml @p out_abi_path file.
///
/// @param out_abi_path the abixml path file.
///
/// @param corp the ABI @ref abigail::ir::corpus.
///
/// @return true if abixml file was serialized successfully. Otherwise
/// `error_message` is set with @p out_abi_path and false is returned.
bool
test_task::serialize_corpus(const string& out_abi_path,
corpus_sptr corp)
{
ofstream of(out_abi_path.c_str(), std::ios_base::trunc);
if (!of.is_open())
{
error_message = string("failed to read ") + out_abi_path + "\n";
return false;
}
write_context_sptr write_ctxt
= create_write_context(corp->get_environment(), of);
set_type_id_style(*write_ctxt, spec.type_id_style);
is_ok = write_corpus(*write_ctxt, corp, /*indent=*/0);
of.close();
return is_ok;
}
/// Spawn `abidw --abidiff` tool appending @p extargs argument.
///
/// Thew input file object used by `abidw` will be specified by
/// `in_elf_path'.
///
/// @param extargs the extra argument(s) passed to `abidw` tool.
///
/// @return true if `abidw` tool was executed correctly. Otherwise
/// `error_message` shows the full path of the input file object and
/// the full output path for the abixml file.
bool
test_task::run_abidw(const string& extargs)
{
string abidw = string(get_build_dir()) + "/tools/abidw";
string drop_private_types;
string spec_options = spec.options ? spec.options : "";
set_in_abi_path();
if (!in_public_headers_path.empty())
drop_private_types += "--headers-dir " + in_public_headers_path +
" --drop-private-types";
string cmd = abidw + " " + spec_options + drop_private_types +
" --abidiff " + extargs + in_elf_path;
if (system(cmd.c_str()))
{
error_message = string("ABIs differ:\n")
+ in_abi_path
+ "\nand:\n"
+ out_abi_path
+ "\n"
+ "command was: '" + cmd + "'\n";
return false;
}
return true;
}
/// Spawn external `diff` command.
///
/// The files to be compared are: abixml generated by the input
/// object file and the expected abixml file stored in `in_abi_path`.
///
/// @return true if `diff` command didn't find defences. Otherwise
/// `error_message` shows the full path of the input file object and
/// the full output path for the abixml file.
bool
test_task::run_diff()
{
set_in_abi_path();
string cmd = "diff -u " + in_abi_path + " " + out_abi_path;
if (system(cmd.c_str()))
{
error_message = string("ABI files differ:\n")
+ in_abi_path
+ "\nand:\n"
+ out_abi_path
+ "\n"
+ "command was: '" + cmd + "'\n";
return false;
}
return true;
}
/// Write the usage message to @p out stream object.
///
/// @param prog_name the program name.
///
/// @param out the stream object to which want to write.
void
display_usage(const string& prog_name, ostream& out)
{
emit_prefix(prog_name, out)
<< "usage: " << prog_name << " [options]\n"
<< " where options can be: \n"
<< " --help|-h display this message\n"
<< " --no-parallel execute testsuite is a sigle thread\n"
;
}
/// Parse and process test options.
///
/// @param argc the arguments number.
///
/// @param argv the pointer to the arguments.
///
/// @param opts the valid @ref options to be processed/parsed.
///
/// @return true if options was processed/parsed successfully. It returns
/// false when help is requested or an invalid option is supplied.
bool
parse_command_line(int argc, char* argv[], options& opts)
{
for (int i = 1; i < argc; ++i)
{
if (!strcmp(argv[i], "--no-parallel"))
opts.parallel = false;
else if (!strcmp(argv[i], "--help")
|| !strcmp(argv[i], "--h"))
return false;
else
{
if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
opts.wrong_option = argv[i];
return false;
}
}
return true;
}
/// The main entry point to execute the testsuite.
///
/// @param num_tests the number of tests to be executed.
///
/// @param specs the @ref abigail::tests::read_common::InOutSpec
/// tests container.
///
/// @param opts the test execution @ref abigail::tests::read_common::options.
///
/// @param new_test the @ref create_new_test callback function to create
/// a new test task object.
///
/// @return true if `all` tests were performed successfully. Otherwise
/// false is returned.
bool
run_tests(const size_t num_tests, const InOutSpec* specs,
const options& opts, create_new_test new_test)
{
size_t num_workers = (opts.parallel
? std::min(abigail::workers::get_number_of_threads(),
num_tests)
: 1);
// Create a task queue. The max number of worker threads of the
// queue is the number of the concurrent threads supported by the
// processor of the machine this code runs on. But if
// --no-parallel was provided then the number of worker threads
// equals 1.
abigail::workers::queue task_queue(num_workers);
bool is_ok = true;
string out_abi_base = string(get_build_dir()) + "/tests/";
string in_elf_base = string(abigail::tests::get_src_dir()) + "/tests/";
string in_abi_base = in_elf_base;
for (const InOutSpec *s = specs; s->in_elf_path; ++s)
{
test_task_sptr t(new_test(s, out_abi_base,
in_elf_base,
in_abi_base));
ABG_ASSERT(task_queue.schedule_task(t));
}
// Wait for all worker threads to finish their job, and wind down.
task_queue.wait_for_workers_to_complete();
// Now walk the results and print whatever error messages need to be
// printed.
const vector<abigail::workers::task_sptr>& completed_tasks =
task_queue.get_completed_tasks();
ABG_ASSERT(completed_tasks.size() == num_tests);
for (vector<abigail::workers::task_sptr>::const_iterator ti =
completed_tasks.begin();
ti != completed_tasks.end();
++ti)
{
test_task_sptr t = dynamic_pointer_cast<test_task>(*ti);
if (!t->is_ok)
{
is_ok = false;
if (!t->error_message.empty())
cerr << t->error_message << '\n';
}
}
return !is_ok;
}
}//end namespace read_common
}//end namespace tests
}//end namespace abigail