mirror of
git://sourceware.org/git/libabigail.git
synced 2024-12-14 22:14:35 +00:00
8fd02e0a10
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>
270 lines
7.5 KiB
C++
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
|